Using Timers in a Windows Service

Welcome to the next installment of the .NET Nuts & Bolts column. In one of the prior columns, "Creating a Windows Service in .NET," we looked at the basics of creating a Windows Service. As a result of that article, I received a number of requests about how to create a service that takes an action on a scheduled interval. In this column, we'll get more in depth with Windows Services and explore how to use timers within them. It will involve using classes in the System.ServiceProcess.ServiceBase and System.Timers namespaces, among others.

Windows Service Recap

A Windows Service is a long-running application without a user interface or any visual output. They are ideal for server environments where an application needs to continue to run regardless of whether a user is logged into the console or not. Microsoft server products, such as SQL Server and Exchange, are examples of Windows Service applications. For more basic information on Windows Services, refer to the prior .NET Nuts & Bolts column titled "Creating a Windows Service in .NET."

What Is a Timer?

A timer is an object that can be used in your applications to cause an event to occur on a specified interval. There are three timer controls available within the Microsoft .NET Framework. Each one has it specific nuances. The three types of timers are as follows:

  • System.Windows.Forms.Timer—is designed for use in a single threaded Windows Forms based application. This is the same timer design as what has been available since early versions of Visual Basic. A common mistake is attempting to use this timer in a Windows Service application.
  • System.Timers.Timer—is an updated version of the Windows Forms timer that has been optimized to run in a server environment where a user interface does not exist. This timer is ideal for use in Windows Services.
  • System.Threading.Timer—is designed for use with ThreadPools. It uses callback methods rather than events and therefore does not rely on the operating system to support timers. We won't cover this timer as a part of this article.

Controlling a Timer

Each of the timers exposes some basic properties and methods that allow you to control its behavior. The Windows.Forms.Timer and Timers.Timer classes expose the same items:

  • Enabled—property indicating whether or not the timer is enabled and should be raising the event.
  • Interval—property allowing you to set the number of milliseconds between events.
  • Start—method allowing you to start the timer.
  • Stop—method allowing you to stop the timer.

The System.Threading.Timer object has different properties and methods for controlling its behavior. We're not going to cover the use of this timer here, so we'll skip the particulars.

Using a System.Timers.Timer Sample Code

The following sample code briefly demonstrates the programmatic setup of a timer in the System.Timers namespace. This example uses a Windows console application for simplicity in being able to see the outcome without requiring a lot of setup.

using System;

namespace ConsoleTimer
{
   /// <summary>
   /// Sample class for testing a server based timer.
   /// </summary>
   class Class1
   {
      System.Timers.Timer testTimer = new System.Timers.Timer();

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         Class1 test = new Class1();
         test.testTimer.Enabled = true;
         test.testTimer.Interval = 5000;    // Execute timer every
                                            // five seconds
         test.testTimer.Elapsed += new
            System.Timers.ElapsedEventHandler(test.testTimer_Elapsed);

         // Sit and wait so we can see some output
         Console.ReadLine();
      }

      private void testTimer_Elapsed(object sender,
         System.Timers.ElapsedEventArgs e)
      {
         System.Console.WriteLine("myTimer event occurred");
      }
   }
}

Using a System.Timers.Timer in a Windows Service

Now that we have seen a simple example of using a timer, we'll get more complex and apply them to a Windows Service. The example in the prior column showed the creation and use of a single timer. This time around, we'll spin up a number of threads and execute multiple timers. This will give us the opportunity to show how to set configurations in threads as well. The first thing we'll need to do is create our item that will run as a thread. Then, we'll create the Windows Service to create the threads. Finally, we'll add an installer to handle the installation of the Windows Service.

TimedItem Sample Code

The following sample code contains an object with a single timer. It simply starts and stops the timer and responds to the timer's event when appropriate. Because parameters cannot be passed to threads, we create a class like this and set the properties equal to the values we would want to pass as parameters to the method running as a thread. Each time the event fires for the item, an entry will be written to the Application log.

using System;

namespace CodeGuru.TimerService
{
   /// <summary>
   /// Demonstrate the use of a timer.
   /// </summary>
   public class TimedItem
   {
      private System.Diagnostics.EventLog _AppEventLog;
      private System.Timers.Timer _Timer;

      private int _Interval = 30000;
      public int Interval
      {
         get { return this._Interval; }
         set { this._Interval = value; }
      }

      private string _Name = "";
      public string Name
      {
         get { return this._Name; }
         set { this._Name = value; }
      }

      /// <summary>
      /// Constructor
      /// </summary>
      public TimedItem(System.Diagnostics.EventLog AppEventLog)
      {
         this._Timer = new System.Timers.Timer();
         this._Timer.Elapsed += new
         System.Timers.ElapsedEventHandler(_Timer_Elapsed);
         this._Timer.Enabled = false;
         this._Timer.Interval = this.Interval;
         this._AppEventLog = AppEventLog;
      }

      /// <summary>
      /// Start the timer.
      /// </summary>
      public void StartTimer()
      {
         this._Timer.Enabled = true;
      }

      /// <summary>
      /// Stop the timer.
      /// </summary>
      public void StopTimer()
      {
         this._Timer.Enabled = false;
      }


      /*
       * Respond to the _Timer elapsed event.
       */
      private void _Timer_Elapsed(object sender,
         System.Timers.ElapsedEventArgs e)
      {
         this._AppEventLog.WriteEntry("Time elapsed for " + this.Name,
            System.Diagnostics.EventLogEntryType.Information);
      }
   }
}

Using Timers in a Windows Service

Windows Service Sample Code

Now, we'll create our service. The service will create a number of threads and execute the StartTimer method of the TimerItem on each thread. We'll add an event log instance to record the start of the service and the timer events.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;

namespace CodeGuru.TimerService
{
   public class TimerService : System.ServiceProcess.ServiceBase
   {
      private System.Diagnostics.EventLog _AppEventLog;
      private Thread[] _WorkerThreads;
      private TimedItem[] _Timers;

      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;

      public TimerService()
      {
         // This call is required by the Windows.Forms Component
         // Designer.
         InitializeComponent();

         // TODO: Add any initialization after the InitComponent
         // call
      }

      // The main entry point for the process
      static void Main()
      {
         System.ServiceProcess.ServiceBase[] ServicesToRun;
         ServicesToRun = new System.ServiceProcess.ServiceBase[] { 
            new TimerService() };

         System.ServiceProcess.ServiceBase.Run(ServicesToRun);
      }

      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         this._AppEventLog = new System.Diagnostics.EventLog();
            ((System.ComponentModel.ISupportInitialize)
             (this._AppEventLog)).BeginInit();
         //
         // _AppEventLog
         //
         this._AppEventLog.Log = "Application";
         this._AppEventLog.Source = "TimerService";
         //
         // TimerService
         //
         this.ServiceName = "TimerService";
            ((System.ComponentModel.ISupportInitialize)
             (this._AppEventLog)).EndInit();
      }

      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if (components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose( disposing );
      }

      /// <summary>
      /// Set things in motion so your service can do its work.
      /// </summary>
      protected override void OnStart(string[] args)
      {
         this._Timers = new TimedItem[5];
         _WorkerThreads = new Thread[5];

         for( int i = 0; i < 5; i++ )
         {
            _Timers[i] = new TimedItem(this._AppEventLog);
            _Timers[i].Name = "Timer #" + i.ToString();
            _WorkerThreads[i] = new Thread(new
               System.Threading.ThreadStart(_Timers[i].StartTimer));
            _WorkerThreads[i].Start();
         }
      }

      /// <summary>
      /// Stop this service.
      /// </summary>
      protected override void OnStop()
      {
         for( int i = 0; i < 5; i++ )
         {
            _Timers[i].StopTimer();
         }
      }
   }
}

Windows Service Installer Sample Code

Now, we include the installer to control the installation of the service. If you have any problems getting this to install using the InstallUtil.exe, make sure you are using the InstallUtil.exe for the appropriate version of the .NET Framework you are using. If you have the 1.0 and 1.1 framework installed, there is a chance the InstallUtil.exe will point to the wrong version of the framework; this will result in an error when you try to install your 1.1-based Windows Service.

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;

namespace CodeGuru.TimerService
{
   /// <summary>
   /// Summary description for ProjectInstaller.
   /// </summary>
   [RunInstaller(true)]
   public class ProjectInstaller : System.Configuration.Install.Installer
   {
      private System.ServiceProcess.ServiceProcessInstaller
         _TimerServiceProcessInstaller;
      private System.ServiceProcess.ServiceInstaller _TimerServiceInstaller;
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;

      public ProjectInstaller()
      {
         // This call is required by the Designer.
         InitializeComponent();

         // TODO: Add any initialization after the
         // InitializeComponent call
      }

      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if(components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose( disposing );
      }


      #region Component Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         this._TimerServiceProcessInstaller = new
         System.ServiceProcess.ServiceProcessInstaller();
         this._TimerServiceInstaller = new
         System.ServiceProcess.ServiceInstaller();
         //
         // _TimerServiceProcessInstaller
         //
         this._TimerServiceProcessInstaller.Account =
            System.ServiceProcess.ServiceAccount.LocalSystem;
         this._TimerServiceProcessInstaller.Password = null;
         this._TimerServiceProcessInstaller.Username = null;
         //
         // _TimerServiceInstaller
         //
         this._TimerServiceInstaller.ServiceName = "TimerService";
         this._TimerServiceInstaller.StartType =
            System.ServiceProcess.ServiceStartMode.Automatic;
         //
         // ProjectInstaller
         //
         this.Installers.AddRange(new
            System.Configuration.Install.Installer[] {
            this._TimerServiceInstaller,
            this._TimerServiceProcessInstaller});

      }
      #endregion
   }
}

Possible Enhancements

Now you have seen a few ways in which timers can be used in a Windows Service application. Hopefully, those of you who contacted me with questions about how to use a timer in a service application will find this helpful. A few enhancements you can consider to make this more useful for yourself are as follows:

  • Add a configuration file to the application and include the number of threads to create as a configurable item.
  • Change the polling interval to be randomly generated for each thread. This will ensure that each thread is not taking action at the same time.
  • Create an event log dedicated to the application. This will prevent the Application log from becoming overly cluttered with entries as you execute the application.

Future Columns

The topic of the next column is yet to be determined. If you have something in particular that you would like to see explained here, you could reach me at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer, MCSD, MCSE, MCDBA is a Senior Architect of .NET applications for large and mid-size organizations. Mark is a technology leader with Crowe Chizek in Indianapolis, Indiana. He specializes in the architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C#. You can reach Mark at mstrawmyer@crowechizek.com.



About the Author

Mark Strawmyer

Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at mark.strawmyer@crowehorwath.com.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds