We are now ready to add programming to the service to make it do something.
The Service Class (Code-behind)
The Service class (e.g. EmptyService) will receive the Start and Stop commands for the service and must therefore implement OnStart() and OnStop(), which both override methods in the base ServiceProcess class. When you implement the more simple Service, with a Timer control, you would set the Enabled property of the Timer to True in OnStart() and set it to False in OnStop(). For our example, we will use these start/stop event handlers to create and destroy threads which will run the meat of our program.
The next section of the article will discuss the contents of the Worker class. For now, we will create a private member of the Service class of type Worker. This is a singleton object that attaches to the main running program thread and performs work. In the OnStart() event handler, a new thread is created, with its start-point set to the Worker object’s main method (called “DoWork()”). Then the thread is started. In the OnStop() event handler, the worker object is torn down by first calling its StopWork() method and then freeing any extra objects used by the service.
Windows Service processes are started by the SCM, and hence their working directory is that of the system, by default. An optional step here is to move the working directory of the service to its program directory. I always do this to simplify program configuration inputs, logging outputs, etc.
You can also implement OnPause(), and OnContinue() here. Remember these will never get called if you do not set the CanPauseAndContinue property of the Service. These handlers have no arguments, and should look very similar to the OnStart()/OnStop(), except there should be no building or tearing down of objects.
Custom commands are out of the scope of this article. However, the OnCustomCommand() event handler is available to extend your service to respond to custom events that you may have other programs execute via the SCM. Use this method to add other interactions to your service (http://msdn2.microsoft.com/en-us/library/system.serviceprocess.servicebase.oncustomcommand.aspx).
Public Class EmptyService
Private worker As New worker()
Protected Overrides Sub OnStart(ByVal args() As String)
Dim wt As System.Threading.Thread
Dim ts As System.Threading.ThreadStart
ts = AddressOf worker.DoWork
wt = New System.Threading.Thread(ts)
Protected Overrides Sub OnStop()
Function GetMyDir() As String
Dim fi As System.IO.FileInfo
Dim di As System.IO.DirectoryInfo
Dim pc As System.Diagnostics.Process
pc = System.Diagnostics.Process.GetCurrentProcess
fi = New System.IO.FileInfo(pc.MainModule.FileName)
di = fi.Directory
GetMyDir = di.FullName
fi = Nothing
di = Nothing
pc = Nothing
The Worker Class
The Worker class controls the thread in which all program processing will occur. It is called when a new thread is started by the Service class, and as mentioned previously, its entry point is the DoWork() method–this is where processing will begin. When DoWork() is called, it attaches to the current thread, gives it a name, and then starts a loop in which processing occurs. For this example, the only processing is to write an entry to the Windows Event Log, sleep, and then write another.
Public Class Worker
Private m_thMain As System.Threading.Thread
Private m_booMustStop As Boolean = False
Private m_rndGen As New Random(Now.Millisecond)
Public Sub StopWork()
m_booMustStop = True
If Not m_thMain Is Nothing Then
If Not m_thMain.Join(100) Then
Public Sub DoWork()
m_thMain = System.Threading.Thread.CurrentThread
Dim i As Integer = m_rndGen.Next
m_thMain.Name = "Thread" & i.ToString
While Not m_booMustStop
System.Diagnostics.EventLog.WriteEntry("EmptyService", "Start work: " & m_thMain.Name)
System.Diagnostics.EventLog.WriteEntry("EmptyService", "Finish work: " & m_thMain.Name)