private static void tick(object state)
        {
            AppTools.ExecuteBlockWithStandardExceptionHandling(
                delegate {
                // We need to schedule the next tick even if there is an exception thrown in this one. Use try-finally instead of CallEveryMethod so we don't lose
                // exception stack traces.
                try {
                    foreach (var key in periodicEvictionKeys)
                    {
                        var entryWrapper = cache.Get(key) as Lazy <object>;
                        if (entryWrapper == null)
                        {
                            continue;
                        }
                        var entry = entryWrapper.Value as PeriodicEvictionCompositeCacheEntry;
                        if (entry == null)
                        {
                            continue;
                        }

                        entry.EvictOldEntries();
                    }
                }
                finally {
                    try {
                        timer.Change(tickInterval, Timeout.Infinite);
                    }
                    catch (ObjectDisposedException) {
                        // This should not be necessary with the Timer.Dispose overload we are using, but see http://stackoverflow.com/q/12354883/35349.
                    }
                }
            });
        }
        private void tick(object state)
        {
            AppTools.ExecuteBlockWithStandardExceptionHandling(
                () => {
                // We need to schedule the next tick even if there is an exception thrown in this one. Use try-finally instead of CallEveryMethod so we don't lose
                // exception stack traces.
                try {
                    var now = DateTime.Now;
                    if (AppTools.IsLiveInstallation && !ConfigurationStatics.MachineIsStandbyServer &&
                        new[] { lastHealthCheckDateAndTime, now }.Any(dt => dt.Date.IsBetweenDateTimes(lastHealthCheckDateAndTime, now)))
                    {
                        StandardLibraryMethods.SendHealthCheckEmail(WindowsServiceMethods.GetServiceInstalledName(service));
                    }
                    lastHealthCheckDateAndTime = now;

                    service.Tick();
                }
                finally {
                    try {
                        timer.Change(tickInterval, Timeout.Infinite);
                    }
                    catch (ObjectDisposedException) {
                        // This should not be necessary with the Timer.Dispose overload we are using, but see http://stackoverflow.com/q/12354883/35349.
                    }
                }
            });
        }
        /// <summary>
        /// Private use only.
        /// </summary>
        protected override void OnStop()
        {
            AppTools.ExecuteBlockWithStandardExceptionHandling(
                () => {
                if (timer != null)
                {
                    var waitHandle = new ManualResetEvent(false);
                    timer.Dispose(waitHandle);
                    waitHandle.WaitOne();
                }

                service.CleanUp();
            });
        }
        /// <summary>
        /// Private use only.
        /// </summary>
        protected override void OnStart(string[] args)
        {
            if (AppTools.SecondaryInitFailed)
            {
                ExitCode = 0x425;                 // Win32 error code; see http://msdn.microsoft.com/en-us/library/cc231199.aspx.
                Stop();
                return;
            }

            Action method = () => {
                lastHealthCheckDateAndTime = DateTime.Now;
                service.Init();

                timer = new Timer(tick, null, tickInterval, Timeout.Infinite);
            };

            if (!AppTools.ExecuteBlockWithStandardExceptionHandling(method))
            {
                ExitCode = 0x428;                 // Win32 error code; see http://msdn.microsoft.com/en-us/library/cc231199.aspx.
                Stop();
            }
        }