Debugging Windows Service Projects (C#)

Many developers find it annoying to debug the built-in Windows Service projects in Visual Studio. You can’t simply create a new project and click the “Run” button in Visual Studio and expect it to run with a debugger attached, like you can with most other project types. Normally you would install the service, start it, and attach a debugger to the process, but this is a hassle and doesn’t help much if you need to debug issues in the OnStart method.

To solve this problem and help with diagnostics, I typically add some boilerplate logic to my Windows Service projects. By passing in command line args to my service .exe, I can either have it launch in a light-weight debug console or make it wait to call the OnStart method until a debugger is attached.

Some of the pros and cons of each:

Option 1: Debug Console wrapper

When the .exe is ran with a specific command line arg the Windows Service will run as a stand-alone console app. In most cases, I recommend this approach, but it does have one important difference to note…

Pros:

The easiest to use

Does not require the developer to register the Windows Service in Windows

Does not require the developer to Stop the service, update the files, and start the service again

Can be immediately debugged from Visual Studio by clicking the Run button and doesn’t require any manual attachment of the debugger.

Service doesn’t need to be installed on each dev/test machine before testing it.

Cons:

Will run under the current users security context, not the standard service account credentials (so not as Local Service, Network Service, or Local System).

Option 2: Wait for Debugger

A part of my boilerplate logic involves adding the ability to restart the service with a command line argument that will cause it to run but sit in limbo until a debugger is attached or an optional timeout period is reached. This could mean having the service actively send a prompt to the user, or just sit passively and wait.

Pros:

It runs exactly how the Windows Service would run normally, only asks the user if they would like to attach a debugger or waits until one attached.

Cons:

Requires the normal jumping through hoops to stop the existing service, build it, copy it, start the service back up (with the command line switch).

Must be installed on each machine that needs to test and run it

Requires manually attaching the debugger

Debugging in a Console Application window is the best option 90% of the time (IMO). Once the code is added to the project and checked-in, any developer on the team should be able to just click “Run” in Visual Studio and start debugging. The second option, waiting for the debugger to attach, should always be checked before any software release, but the command prompt is easier to develop against on a day-to-day basis.

To begin, create a new Windows Service project in Visual Studio (or open an existing one):

Setting up debug console boilerplate (option 1)

Note: You could setup a separate Console App project that references your service and use that to host the debug console, or you can embed it directly into the project. For this tutorial, I will be embedding it into the one Windows Service project. I would recommend thinking out the architecture and implementation a little bit, this post just covers the concept and a most basic implementation.

Open up Service1.cs (or the .cs file with your Service code). In order to call the OnStart method to simulate the starting of your service, you will need to call it with reflection or do it from within the class or from an inherited class, due to the method being protected.

We will also need to allocate a console session by calling AllocConsole in kernel32.dll when we’re running it with the /DebugInConsole command line argument. To do that, add the following code to your Service1.cs file:

Add method to Service1.cs

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

internalvoidStartServiceConsole()

{

AllocConsole();

Console.WriteLine("Running Windows Service in Debug Console");

Console.WriteLine("Press ENTER to simulate start of service (OnStart)");

Console.ReadLine();

this.OnStart(Environment.GetCommandLineArgs());

Console.WriteLine(" - OnStart Called...");

Console.WriteLine();

Console.WriteLine("Press ENTER to simulate stop of service (OnStop)");

Everything should now build, but if you try to run it in Visual Studio you’ll still get this error:

Since we’re running it like before, we’re not passing in the /DebugInConsole command line argument.

To configure Visual Studio to pass in the /DebugInConsole command line switch whenever you run it from VS, open the project properties, go to the Debug screen and enter /DebugInConsole in the Command Line Arguments field.

Now when you run it, you should get a console:

Now you will be able to set break points in your Windows Service’s OnStart method and step through the process of starting your Service. You can also call Console.Write or Console.WriteLine to display information from the Service.

Windows Service Attach Debugger boilerplate (option 2)

To run the application as a Windows Service, but have it wait to call OnStart we need to add a few command line arguments:

Command Line Argument

Description

/Debug

Enable debug mode for the Windows Service. The OnStart method will not get called until a debugger becomes attached.

/DebugMode={value}

Options: Prompt [default] or Wait. When set to Prompt, the Service will attempt to call Debugger.Launch() in order to ask the user to attach a debugger. When set to wait, the Service will simply sit there and not do anything until a debugger becomes attached. In this situation, the developer would need to manually attach a debugger (for example, “Attach to Process” in Visual Studio).Example:Service1.exe /Debug /DebugMode=ActiveorService1.exe /Debug /DebugMode=Passive

/DebugPassiveTimeout={value}

Default: 0A numeric value that represents the number of seconds to for a debugger to become attached. If the time runs out, the service will start as usual. When set to 0, the application will wait indefinitely until someone connects.

Open Service1.cs and add the following code:

Service debug code

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

publicboolServiceDebugEnabled{get;privateset;}

publicServiceDebugModesServiceDebugMode{get;privateset;}

publicintServiceDebugPassiveTimeout{get;privateset;}

privatevoidDebugInit(string[]args)

{

CheckForServiceDebugArgs(args);

if(!this.ServiceDebugEnabled)

return;

if(Debugger.IsAttached)

return;

switch(ServiceDebugMode)

{

caseServiceDebugModes.Prompt:

Debugger.Launch();

break;

caseServiceDebugModes.Wait:

varstopWatch=newStopwatch();

if(ServiceDebugPassiveTimeout>0)

stopWatch.Start();

while(!Debugger.IsAttached)

{

Thread.Sleep(1000);

if(ServiceDebugPassiveTimeout>0&&

stopWatch.Elapsed.TotalSeconds>ServiceDebugPassiveTimeout)

{

thrownewTimeoutException("No debugger attached to this process within the allotted time of "+ServiceDebugPassiveTimeout+" seconds");

Then add DebugInit(args) to the first line of your OnStart method, so it should look something like this:

1

2

3

4

5

6

7

protectedoverride voidOnStart(string[]args)

{

// This must be the first line of your OnStart method

DebugInit(args);

// Your OnStart service code below...

}

With this code in place, you can now launch a service with the /debug command line argument to attach a debugger before your OnStart code is called.

Adding a command line argument is pretty simple, you can either use the sc.exe command line utility built-in to Windows, or use the Windows Services admin tool (services.msc):

If you set the DebugMode to “prompt” or leave it blank you should receive the following prompt:

The service will block until a debugger is attached (or it times out) and then the remaining logic in OnStart will execute. I recommend putting this code into an abstract base class, that way anyone working on the actual service implementation can’t accidentally place code before the debug code.

Conclusion

The methods mentioned are really just the bare implementation and I would highly recommend thinking through the architecture more or using my VS Project Template (see below). The code provided is really meant to be expanded but was kept basic in order to demonstrate the concepts, but not convolute it with architectural overhead.

I have a project on GitHub for a Visual Studio Project Template that implements these methods, which you can see at: