Earlier I wrote about how you can debug Windows Services without leaving Visual Studio. Yay!

Another common issue that comes up when building Windows Services is the lack of common wrappers around the service container. One such scenario is a service that runs indefinitely and performs an action at fixed time intervals.

Below is some code I wrote to create a generic container to manage your Windows Service when it needs to run at a predetermined interval (eg. every 5 seconds). All you need to do is add and extend the TimedIntervalServiceBase.cs file and override IntervalInSeconds to the desired time interval you would like and you’re done.

The cool thing about TimedIntervalServiceBase is it guarantees the event firings are atomic so there is no overlap between invocations.

Plus it maps all of the standard operations you can perform on a Windows Service to the event firings. So if you stop or pause the service, the events will stop being fired at the time intervals.

public class MyWindowsService : TimedIntervalServiceBase
{
	protected override int IntervalInSeconds {
		get { return 5; }	
	}
	
	protected override void ElapsedTimeEvent(object sender, ElapsedEventArgs e) {
		// Write the code you want to fire here.
	}
}
using System;
using System.ServiceProcess;
using System.Timers;

    /// <summary>
    /// Provides time-based event firing for Windows services.
    /// </summary>
    public abstract class TimedIntervalServiceBase : ServiceBase
    {
        #region Fields

        /// <summary>
        /// Internal timer responsible for event firings.
        /// </summary>
        private readonly Timer _timer;


        /// <summary>
        /// Used for thread locking.
        /// </summary>
        private readonly object _eventLock;


        /// <summary>
        /// Flag indicates if an event is currently processing.
        /// </summary>
        private bool _eventCurrentlyProcessing;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the interval firing in seconds.
        /// </summary>
        /// <value>
        /// The interval in seconds.
        /// </value>
        protected abstract int IntervalInSeconds { get; }

        /// <summary>
        /// Gets or sets a value indicating whether the time interval should fire immediately upon startup 
        /// or wait for the first time interval to elapse. Default is <c>true</c>.
        /// </summary>
        /// <value>
        /// <c>true</c> fires immediately before the first time interval starts; 
        /// otherwise, <c>false</c> will wait until the first time interval has elapsed.
        /// </value>
        protected bool FireImmediatelyOnStartup { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TimedIntervalServiceBase"/> class.
        /// </summary>
        protected TimedIntervalServiceBase()
        {
            _timer = new Timer();
            _eventLock = new object();
            _eventCurrentlyProcessing = false;
            FireImmediatelyOnStartup = true;
            _timer.Elapsed += BaseElapsedTimeEvent;
        }

        #endregion

        #region ServiceBase Overrides

        /// <summary>
        /// Executes when a Start command is sent to the service by the Service Control Manager (SCM) or 
        /// when the operating system starts (for a service that starts automatically). 
        /// Specifies actions to take when the service starts.
        /// </summary>
        /// <param name="args">Data passed by the start command.</param>
        protected override void OnStart(string[] args)
        {
                UpdateTimeInterval();
                _timer.Start();

                if (FireImmediatelyOnStartup)
                    Fire();
        }

        /// <summary>
        /// Executes when a Stop command is sent to the service by the Service Control Manager (SCM). 
        /// Specifies actions to take when a service stops running.
        /// </summary>
        protected override void OnStop()
        {
                _timer.Stop();
        }

        /// <summary>
        /// Executes when a Pause command is sent to the service by the Service Control Manager (SCM). 
        /// Specifies actions to take when a service pauses.
        /// </summary>
        protected override void OnPause()
        {
                _timer.Stop();
        }

        /// <summary>
        /// <see cref="M:System.ServiceProcess.ServiceBase.OnContinue" /> runs when a Continue command is 
        /// sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service 
        /// resumes normal functioning after being paused.
        /// </summary>
        protected override void OnContinue()
        {
                UpdateTimeInterval();
                _timer.Start();

                if (FireImmediatelyOnStartup)
                    Fire();
        }

        #endregion

        # region Custom Code

        /// <summary>
        /// Refreshes the time interval.
        /// </summary>
        private void UpdateTimeInterval()
        {
            var oldTimeIntervalInSeconds = (int)TimeSpan.FromMilliseconds(_timer.Interval).TotalSeconds;

            if (oldTimeIntervalInSeconds != IntervalInSeconds)
            {
                _timer.Interval = TimeSpan.FromSeconds(IntervalInSeconds).TotalMilliseconds;
            }
        }

        /// <summary>
        /// Triggers an ElapsedTimeEvent immediately.
        /// </summary>
        private void Fire()
        {
            var tempTimer = new Timer(1);
            tempTimer.Elapsed += BaseElapsedTimeEvent;
            tempTimer.AutoReset = false;
            tempTimer.Start();
        }

        /// <summary>
        /// BaseElapsedTimeEvent
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="ElapsedEventArgs"/> instance containing the event data.</param>
        protected void BaseElapsedTimeEvent(object sender, ElapsedEventArgs e)
        {
            lock (_eventLock)
            {
                if (_timer.Enabled && !_eventCurrentlyProcessing)
                {
                    _eventCurrentlyProcessing = true;
                    ElapsedTimeEvent(sender, e);
                    _eventCurrentlyProcessing = false;
                }
            }
        }

        /// <summary>
        /// ElapsedTimeEvent fires when the time interval is reached.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="ElapsedEventArgs"/> instance containing the event data.</param>
        protected abstract void ElapsedTimeEvent(object sender, ElapsedEventArgs e);

        #endregion
    }

2 Comments A Generic Time Interval Container for Windows Services

  1. Nick

    Have you looked at using the Quartz Scheduler for .NET? It a lot of functionality for controlling when processes in your windows services should run.

    1. Adam

      I came across Quartz in the past but haven’t looked into it recently. That would certainly make for a more elegant internal scheduling layer if the need arises. Thanks for reminding me of it.

Comments are closed.