Debugging Windows Servicesby Wei-Meng Lee, author of Windows XP Unwired
As I mentioned in my last article on Developing Windows Services, debugging Windows services is not as trivial as debugging Windows applications because they do not run directly within Visual Studio .NET. Hence, more elaborate techniques must be deployed to effectively develop and debug Windows services. In this article, I will discuss two techniques that you can use to debug and test your Windows services.
Method 1: Attaching a Debugger
The first method to debug a Windows service is to attach a debugger to a running service. You can then set breakpoints to cause the Windows service to suspend, giving you the ability to step through the code. Let's see how we can do this using the sample TimeService Windows service illustrated in my last article.
First, you need to install your Windows service. (You can refer to my last article for the steps to install a Windows service.) After installation, start the service either using the Services manager (located in Control Panel, Administrative Tools), or the command line.
In Visual Studio .NET 2003, set a breakpoint at the statement that you wish to step through (see Figure 1).
|Figure 1: Setting a breakpoint in Visual Studio .NET 2003|
Now, select the menu item Debug->Processes...(see Figure 2) to invoke the Processes window (see Figure 3).
|Figure 2: Invoking the Processes window|
In the Available Processes group (see Figure 3), locate the service that you want to debug (TimeService.exe in my case); select it and click the Attach... button.
|Figure 3: Attaching the debugger to a process in the Processes window|
The Attach to Process window will appear. Click OK and then the Close button on the Processes window. You are now in debugging mode.
listener.AcceptTcpClient() method (which I used in my Windows service)
is a blocking call, my Windows service will only break for debugging
when a client sends an incoming message. So, execute your client (use
the sample console application from my last article), and send a message
to the service so that we can proceed with the debugging.
Assuming that you've received an incoming message, the Windows service should now be pending at the breakpoint (see Figure 4).
|Figure 4: Visual Studio .NET displaying the paused breakpoint|
You can now perform the usual debugging procedures, such as running QuickWatch, Step Into, Step Out, and Step Over operations.
You can also set breakpoints at the
methods and observe what happens when the service stops, pauses,
and continues, respectively.
Downsides of Using the Debugger
The main problem with using the debugger in Visual Studio .NET 2003
is that you can't debug the
OnStart() method; neither can you debug
Main method. This is because you can only attach a debugger to a
service after it has started running, which by then, the
has already completed its execution and returned the control to the
operating system. There are workarounds to debugging the
(as documented in MSDN), but even if you could debug the
you have only 30 seconds to step through the code in the method as the
Windows Service Manager imposes a 30-second limit on all attempts to
start a service.
My suggestion would be to minimize the number of statements in the
method and move the bulk of the work to a subroutine or a function. By
doing so, you can then set your breakpoints in the subroutine and not
It's a tedious task to debug a Windows service after it has started running, so I recommend the second method, as described in the next section.
Method 2: Encapsulating the Service Logic in a Class
In this second method, a better way to debug Windows services is to test the logic of the Windows service independently, preferably as a console or Windows application. The idea is to contain the service logic as an independent unit, ideally in a DLL.
In this section, I will create a class (project name TimeServiceClass) to store the application logic of the TimeService Windows service.
Here is the full source for the class:
Imports System.Net.Sockets Imports System.Net Imports System.Text Public Class TimeService Private canStopListening As Boolean = False Private pause As Boolean = False Public Sub startService() Listen() End Sub Public Sub pauseService() pause = True End Sub Public Sub continueService() pause = False End Sub Public Sub stopService() canStopListening = True End Sub Private Sub Listen() Const portNo As Integer = 500 Dim localAdd As System.Net.IPAddress = _ IPAddress.Parse("127.0.0.1") Dim listener As New TcpListener(localAdd, portNo) listener.Start() Do If Not pause Then Dim tcpClient As TcpClient = listener.AcceptTcpClient() Dim NWStream As NetworkStream = tcpClient.GetStream Dim bytesToRead(tcpClient.ReceiveBufferSize) As Byte '---read incoming stream Dim numBytesRead As Integer = _ NWStream.Read(bytesToRead, 0, _ CInt(tcpClient.ReceiveBufferSize)) '---write to console Console.WriteLine("Received :" & _ Encoding.ASCII.GetString(bytesToRead, _ 0, numBytesRead) & " @ " & Now) '---write time back to client Dim time() As Byte = _ Encoding.ASCII.GetBytes(Now.ToString) NWStream.Write(time, 0, time.Length) tcpClient.Close() End If Loop Until canStopListening = True listener.Stop() End Sub End Class
Basically, I have encapsulated the logic of the TimeService within a class and exposed the various methods to start, stop, pause, and continue the time service. The class is then compiled into a DLL, which I can test by creating a Windows or console application.
Pages: 1, 2