54

I have code written in .NET that only fails when installed as a Windows service. The failure doesn't allow the service to even start. I can't figure out how I can step into the OnStart method.

How to: Debug Windows Service Applications gives a tantalizing clue:

Attaching to the service's process allows you to debug most but not all of the service's code; for example, because the service has already been started, you cannot debug the code in the service's OnStart method this way, or the code in the Main method that is used to load the service. One way to work around this is to create a temporary second service in your service application that exists only to aid in debugging. You can install both services, and then start this "dummy" service to load the service process. Once the temporary service has started the process, you can then use the Debug menu in Visual Studio to attach to the service process.

However, I'm not clear how it is exactly that you are supposed to create the dummy service to load the service process.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Nathan
  • 10,097
  • 9
  • 59
  • 81

16 Answers16

97

One thing you could do as a temporary workaround is to launch the debugger as the first line of code in the OnStart

System.Diagnostics.Debugger.Launch()

This will prompt you for the debugger you'd like to use. Simply have the solution already open in Visual Studio and choose that instance from the list.

palehorse
  • 22,870
  • 3
  • 36
  • 47
  • This is not working for me. The error I get is Unhandled exception at 0x7c812aeb in xxxxService.exe: 0xC06D007E: Module not found, and the breakpoints are never reached. This is however, quite different then the error I am getting without the debugger.Launch code included. – Nathan Jul 28 '09 at 22:45
  • Do you have Visual Studio installed on the machine where the service is running? – palehorse Jul 29 '09 at 12:52
  • Yes, I have both Visual Studio 2005 Professional, and Visual Studio 2008 professional installed on the machine. – Nathan Jul 29 '09 at 14:13
  • Hmm....are the binaries that are running compiled in debug mode? I've never seen that not work before. – palehorse Jul 29 '09 at 17:26
  • The build script does appear to be setting the configuration to debug mode. Is there any way to directly check the built assemblies? – Nathan Jul 30 '09 at 15:48
  • Is it producing .dbd files? Are they located in the same folder as the .dll's? – palehorse Jul 30 '09 at 16:53
  • Also, if I click "break" when the exception window comes up, I get "No symbols are loaded for any call stack frame. The source code cannot be displayed." – Nathan Jul 30 '09 at 18:24
  • I am not seeing any .dbd files. I am also not familiar with .dbd files within the .net framework. Did you perhaps mean .pdb files? The .pdb files are in the root ouput of the build script, but I will need to check if they are getting copied by installutil - which I am a little bit new to. – Nathan Jul 30 '09 at 18:34
  • Yes, I did mean .pdb files, sorry about that. InstallUtil should not be copying files anywhere, rather it simply does the necessary registrations to let Windows know about the service. It's possible that the exceptions are happening before your OnStart code, for example in the constructor or in the service controller class that creates the instance of your service. You could try putting the Debugger.Launch() statements in one of those places. – palehorse Jul 31 '09 at 13:59
  • 2
    I put the Debugger.Launch in the main method, and now I am able to step into the code. Thanks! – Nathan Jul 31 '09 at 22:15
  • I could not get this to work, the service kept stopping without prompting me. My solution was to add on the first line of the OnStop event handler System.Threading.Thread.Sleep(30000). This introduces a 30 second pause, and during that time I can attach to the process with Visual Studio. – Jay Imerman Dec 20 '17 at 19:32
  • Normally if the OnStart is not attaching the debugger there's something earlier in the pipeline that's causing it. You can add the line to the constructor or even into the main method that instantiates the service. – palehorse Dec 21 '17 at 20:08
11

I tend to add a method like this:

    [Conditional("DEBUG")]
    private void AttachDebugger()
    {
        Debugger.Break();
    }

it will only get called on Debug builds of you project and it will pause execution and allow you to attach the debugger.

HasaniH
  • 7,514
  • 5
  • 36
  • 55
8

Once you have a service that is installed using installutil.exe, you can alter the Start Parameters to jump into the debugger if the service is started:

enter image description here

When you manually start the service with the parameter -debugWithVisualStudio (or simply -d), it will automatically detect the correct project, and fire up the interactive debugger in Visual Studio:

enter image description here

To support this functionality, change the service's OnStart() function:

/// <summary>
///     Executed when the service is started.
/// </summary>
/// <param name="args">Command line arguments.</param>
protected override void OnStart(string[] args)
{
    try
    {
        //How to debug when running a Windows Service:
        // 1. Right click on the service name in Windows Service Manager.
        // 2. Select "Properties".
        // 3. In "Start Parameters", enter "-d" (or "-debugWithVisualStudio").
        // 4. Now, when you start the service, it will fire up Visual Studio 2012 and break on the line below.
        // 5. Make sure you have UAC (User Access Control) turned off, and have Administrator privileges.
#if DEBUG
        if (((ICollection<string>)args).Contains("-d")
            || ((ICollection<string>)args).Contains("-debugWithVisualStudio"))
        {
            Debugger.Launch(); // Launches VS2012 debugger.
        }
#endif
        ShellStart(args);
        base.OnStart(args);
    }
    catch (Exception ex)
    {
        // Log exception here.
    }
}

(optional) If you want to narrow down to the exact line of code where the service is throwing an error, switch on exceptions from the Visual Studio menu DEBUG .. Exceptions. When you continue debugging, it will break on the exact line that is throwing the exception.

enter image description here

Contango
  • 65,385
  • 53
  • 229
  • 279
  • 1
    I love the command line arg idea, I'm going to start using this. Your condition of checking if args Contains("-d") should suffice for both checks since [-d]ebugWithVisualStudio contains "-d", so the OR in your condition isn't necessary. Thanks! – Adam Plocher Jul 06 '14 at 09:38
  • @Adam You're correct, the code could be optimized by removing the "-debugWithVisualStudio" line. However, the purpose of this line is for code clarity, so the intent of the code is immediately clear to any other programmer. I usually add two versions of all command line parameters - a short version, and a verbose version that explains exactly what it does. – Contango Jul 09 '14 at 04:33
  • @Adam Plocher I think the Contains() call on the collection is looking for whether the collection contains those exact strings. – Jason Goemaat Mar 16 '16 at 01:45
7

It works just fine!

protected override void OnStart(string[] args)
{
    System.Diagnostics.Debugger.Launch();
}
Francis B.
  • 6,596
  • 2
  • 30
  • 54
DmitryBoyko
  • 32,983
  • 69
  • 281
  • 458
  • i add this code as you said in OnStart, but when i still can not debug my service, i'm using VS 2013, can you help me? – pejman Jun 23 '14 at 05:10
5

The options above did not appear to work on Windows 8.

I have added Thread.Sleep(15000); into my OnStart() method and set a breakpoint on the next line of the code. This give me 15 seconds to attach VS debugger to my process after starting the service and allowed me to debug the OnStart() method nicely.

Boris Hurinek
  • 71
  • 1
  • 3
4

You can add a line of code like this:

System.Diagnostics.Debugger.Break()

which will bring up a window prompting you to choose which debugger to use to debug, e.g. allowing you to attach with Visual Studio and step into the code.

see:

http://msdn.microsoft.com/en-us/library/system.diagnostics.debugger.break.aspx

Alex Black
  • 12,895
  • 15
  • 73
  • 97
2

Use following Code in Service OnStart Method:

System.Diagnostics.Debugger.Launch();

Choose Visual Studio option from Pop Up message. Remember to run Visual Studio as Administrator.

Note: To use it in only Debug mode, #if DEBUG compiler directive can be used, as follows. This will prevent accidental or Debugging in Release mode on Production server.

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Chirag
  • 3,622
  • 1
  • 26
  • 24
2

As others have pointed out, you have to add a debugger break to the OnStart-Method:

#if DEBUG
    System.Diagnostics.Debugger.Break()
#endif

Also start VisualStudio as Administrator and allow, that a process can automatically be debugged by a different user (as explained here):

reg add "HKCR\AppID\{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

(I also explained this here: https://stackoverflow.com/a/35715389/5132456 )

Community
  • 1
  • 1
wotanii
  • 1,507
  • 11
  • 27
2

It's possible to set up a companion project to the Windows Service that runs as a console app, but accesses the service methods using Reflection. See here for details and an example: http://ryan.kohn.ca/articles/how-to-debug-a-windows-service-in-csharp-using-reflection/.

Ryan Kohn
  • 11,921
  • 10
  • 50
  • 80
1

I know this is late but this is how we handle debuging Windows services

First create a class which will act as the service.

Add the appropriate methods for starting, stopping, pausing, etc...

Add a windows form to the service project.

In the service code create the service class created above and make the calls needed to start and stop the service in the ServiceBase class

Open the Program.cs and add the following

#if DEBUG
    [STAThread]
#endif
    static void Main()
    {
try
        {
#if DEBUG
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new DebugForm());
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
        { 
            new YourWindowsService() 
        };
            ServiceBase.Run(ServicesToRun);
#endif
        }
        catch (Exception e)
        {
            logger.Error(DateTime.Now.ToString() + " - " + e.Source + " - " + e.ToString() + "\r\n------------------------------------\r\n");
        }
}

When you run in DEBUG mode the windows form will launch. Just remember to build in Release mode when finished. Of course the conditional compile variable can be anything you like. You could even create seperate projects so the debug form is its own project.

Hope this helps

Coach David
  • 157
  • 2
  • 8
1

You can also try System.Diagnostics.Debugger.Launch() method. It helps in taking the debugger pointer to the specified location and you can then debug you code.

Before this step please install your service.exe using the command line of Visual Studio command prompt - installutil projectservice.exe

Then start your service from the Control Panel -> Administrative Tools -> Computer Management ->Service and Application -> Services -> Your Service Name

1

If you add Debugger.Launch() in your OnStart method and it doesn't work, you could have the same issue I had, namely, the exception was occurring in the constructor so the OnStart was never called. (head slap)

(sorry if this should have been a comment on someone else' answer, but i don't have enough cred to make comments)

and... break
  • 458
  • 4
  • 5
0

Try adding Debugger.Break inside the problematic method. When the service will start an exception will be thrown and widows should offer to debug it using visual studio.

Dror Helper
  • 28,989
  • 15
  • 76
  • 129
0

I usually have a console app that pretends to be the SCM e.g. calls Start, Stop which I can then just F5 into for my main coding/debugging purposes, and use the Debugger.Break for debugging when the service has been installed and started via the SCM.

It means a bit more work to start with, I have a class lib that contains all the service code, with a class that exposes Start and Stop that the Windows Service class and the console app can both call.

Matt

Matt
  • 2,894
  • 1
  • 20
  • 29
0

Before I go in the topic one advise. Always use log specially if you are server side developer. Because there are some certain condition which you might not be able to produce while debugging the code in visual studio.

Coming back to topic, I use Envoirnment.UserInteractive flag this is really handy see my code below

public static void Main(string[] args)
{

    if (System.Environment.UserInteractive)
    {
        string parameter = string.Concat(args);

        switch (parameter)
        {
            case "--install":
                ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
                break;
            case "--uninstall":
                ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
                break;
            default:
                WindowsService service = new WindowsService();
                service.OnStart(args);
                Console.ReadKey();
                service.OnStop();
                break;
        }
    }
    else
    {
        ServiceBase.Run(new WindowsService());
    }
}

From visual studio you will get UserInteractive flag set so i would run it as console application, In addition to that even you can run product build by double clicking it and attaching debugger with it if you like to test it.

Mubashar
  • 11,498
  • 11
  • 61
  • 92
0

I have an interesting way of doing this I add another Configuration called DebugNoService

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugNoService|AnyCPU' ">
    <OutputPath>.\</OutputPath>
    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
    <BaseAddress>285212672</BaseAddress>
    <CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
    <ConfigurationOverrideFile>
    </ConfigurationOverrideFile>
    <DefineConstants>DEBUG;TRACE;DEBUGNOSERVICE</DefineConstants>
    <DocumentationFile>
    </DocumentationFile>
    <DebugSymbols>true</DebugSymbols>
    <FileAlignment>4096</FileAlignment>
    <NoStdLib>false</NoStdLib>
    <NoWarn>
    </NoWarn>
    <Optimize>false</Optimize>
    <RegisterForComInterop>false</RegisterForComInterop>
    <RemoveIntegerChecks>false</RemoveIntegerChecks>
    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
    <WarningLevel>4</WarningLevel>
    <DebugType>full</DebugType>
    <ErrorReport>prompt</ErrorReport>
    <UseVSHostingProcess>false</UseVSHostingProcess>
  </PropertyGroup>

I use the #if directive. ProjectInstaller.cs

#if !DEBUGNOSERVICE    
   static void Main()
   {
      System.ServiceProcess.ServiceBase[] ServicesToRun;
      .....
   }
#endif

I add a windows form and I also wrap the windows form in

#if DEBUGNOSERVICE
...
static void Main() 
{
    Form     form;

    Application.EnableVisualStyles();
    Application.DoEvents();

    form = new <the name of the form>();

    Application.Run(form);
}
...
#endif

depending on the configuration selected the code either runs as a windows form application that can be easily debugged or as a service.

If seems like a lot of work but it has always worked and makes debugging the code very very easy. You can have all sorts of output added to the form so you can watch it run.

M pollack
  • 64
  • 4