47

I read the MSDN article on the topic. To quote:

Because a service must be run from within the context of the Services Control Manager rather than from within Visual Studio, debugging a service is not as straightforward as debugging other Visual Studio application types. To debug a service, you must start the service and then attach a debugger to the process in which it is running. You can then debug your application using all of the standard debugging functionality of Visual Studio.

Now my problem is that my service fails to start in the first place. First it crashes, and says:

An unhandled exception (System.Runtime.InteropServices.COMException) occurred in MyServiceName.exe[3596])

and suggests me to debug it (the debugger instance instantly crashes when I choose one). Then it says

Could not start the MyServiceName service on Local Computer. Error 1053: The service did not respond to the start or control request in a timely fashion

So, how can I investigate/debug the reason that my service won't start? The thing is I created a console application that does EXACTLY what the service does and it works fine. (I mean I just copied the OnStart() method's and the main loop's contents to main).

Any help would be appreciated.

The Service is written in C# with heavy use of interop. I am using VS2008

Armen Tsirunyan
  • 120,726
  • 52
  • 304
  • 418

15 Answers15

38

You could use a parameter to let your application decide whether to start as service or regular app (i.e. in this case show a Form or start the service):

static void Main(string[] args)
{
    if ((1 == args.Length) && ("-runAsApp" == args[0]))
    {
        Application.Run(new application_form());
    }
    else
    {
        System.ServiceProcess.ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new MyService() };
        System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }
}

Now if you pass the parameter "-runAsApp" you can debug the application normally - the SCM won't pass this parameter, so you can also use it as service w/o any code change (provided you derive from ServiceBase)

Edit:

The other difference with windows services is identity (this might be especially important with InterOp) - you want to make sure you are testing under the same identity in "app" mode as well as service mode.

To do so you can use impersonation (I can post a C# wrapper if it helps, but this can be easily googled) in app mode to use the same identity your windows service will be running under i.e. usually LocalService or NetworkService.

If another identity is required you can add settings to the app.config that allow you to decide whether to use credentials, and if so which user to impersonate - these settings would be active when running as app, but turned off for the windows service (since the service is already running under the desired identity):

  <appSettings>
    <add key="useCredentials" value="false"/>
    <add key="user" value="Foo"/>
    <add key="password" value="Bar"/>
  </appSettings>
BrokenGlass
  • 149,257
  • 27
  • 271
  • 318
21

I usually just manually set a breakpoint, then point it to the currently open project in c#. The code to set a breakpoint is:

System.Diagnostics.Debugger.Break();

That should get you started, then you can just step through your code and see what's really happening.

Davido
  • 2,885
  • 21
  • 38
  • How can he do this if it isn't starting? – user541686 Mar 01 '11 at 15:33
  • The thing is, if you place a manual breakpoint in your code, it will let you watch what is happening while it is starting, then you can see why exactly it is failing. – Davido Mar 01 '11 at 15:34
  • Doesn't your debugger have to be attached for this line of code to do anything? – Phil Mar 01 '11 at 15:42
  • All you do is keep your project window open, then run your service like normal. It will pop a window asking which code session you want to attach the debugger to. Just select your project, and you're good to go. – Davido Mar 01 '11 at 15:49
  • @Phil: no, see http://msdn.microsoft.com/en-us/library/system.diagnostics.debugger.break.aspx: If no debugger is attached, users are asked if they want to attach a debugger. If yes, the debugger is started. – Daniel Hilgarth Mar 01 '11 at 16:28
  • and even better, if the user cancels the messagebox the service/program continues normal. – ralf.w. Mar 08 '11 at 19:20
14

I stole this from C. Lawrence Wenham, so I can't really take credit, but you can programmatically attach a debugger to a service, WITHOUT breaking execution at that point, with the following code:

System.Diagnostics.Debugger.Launch();

Put this in your service's OnStart() method, as the first line, and it will prompt you to choose an instance of VS to attach its debugger. From there, the system will stop at breakpoints you set, and on exceptions thrown out. I would put an #if DEBUG clause around the code so a Release build won't include it; or you can just strip it out after you find the problem.

KeithS
  • 65,745
  • 16
  • 102
  • 161
9

You can use WinDbg/NTSD (another debugger from the "Debugging tools for windows" package) to start a debugger together with your service.

To do this open "gflags" (also available in the above mentioned package) to the "Image file" tab and set the path to debugger executable for your image file (service);

If your service is marked as interactive (only possible if it runs under the SYSTEM account) you can directly start WinDbg, just set the debugger to something like "PATH_TO_WINDBG\windbg.exe -g -G" (the -g / -G are needed so that the debugger doesn't break execution on application start or end - the default behaviour). Now when starting your service the windbg window should pop-up and will catch any unhandled exception.

If your service is not interactive you can start the NTSD debugger (a command line debugger) in remote mode and connect to it from WinDbg (that can even be running in another PC). To do this set the debugger in gflags to something like "PATH_TO_NTSD\ntsd -remote tcp:port=6666,server=localhost". Then connect to the remote debugger by starting windbg with something like "windbg -remote tcp:port=6666,server=localhost" and you should have complete control over the other debugging session.

As for finding the source of the exception itself a windbg tutorial is over the topic here but as a start try to execute the "!analyze -v" command after the exception was caught - with some luck this is all information you'll need..

Note: maybe this is overkill for your case but with this approach you can even debug services during system start-up (I had once a timing problem with a service had an issue only when starting the first time with the system)

floyd73
  • 1,200
  • 8
  • 12
7

One thing I do (which may be kind of a hack) is put a Thread.Sleep(10000) right at the beginning of my OnStart() method. This gives me a 10-second window to attach my debugger to the service before it does anything else.

Of course I remove the Thread.Sleep() statement when I'm done debugging.

One other thing you may do is the following:

public override void OnStart()
{
    try
    {
        // all your OnStart() logic here
    }
    catch(Exception ex)
    {
        // Log ex.Message
        if (!EventLog.SourceExists("MyApplication"))
            EventLog.CreateEventSource("MyApplication", "Application");

        EventLog.WriteEntry("MyApplication", "Failed to start: " + ex.Message);
        throw;
    }
}

When you log ex.Message, you may get a more detailed error message. Furthermore, you could just log ex.ToString() to get the whole stack trace, and if your .pdb files are in the same directory as your executable, it will even tell you what line the Exception occurred on.

Phil
  • 6,085
  • 2
  • 39
  • 64
6

Add lots of verbose logging in your OnStart. It's painful and old school, but it works.

RQDQ
  • 14,445
  • 1
  • 27
  • 53
5

Seems like the problem is with the user context. Let me confirm whether my assumptions are right.

  1. When you say that the code works perfectly from console application, I assume you are executing the Console application under the same user which you had logged in.

  2. When you say that the same code crashes while called from the windows service, I assume the service is running in "Local System" account in your development machine.

If both my assumptions are right, please try out the following steps.

  1. In the services listing right-click your service, select properties and then "Log On" tab.

  2. Select the option "This account" and provide the existing username and password.

  3. Now try starting the service. It should now start without any errors.

Following could be the root cause of your error

  1. If you are using SQL Server make sure you are not using SSPI authentication.

  2. If you are trying to read any shared folder\resource which you don't have permission when using "local system" account.

  3. If any of the required dependencies required by the application is in a different folder which the "Local System" user doesn't have permission to access.

  4. If you are using VBA automation which wont work in "Local System" account.

  5. Try disabling your firewall or antivirus.

AbrahamJP
  • 3,245
  • 6
  • 28
  • 37
4

You could add some logging around the interop calls to find out which one fails.

Also services by default aren't associated with a desktop; if you open the services.msc control panel applet, get the properties of your service, go to the "Log On" tab, you could check "Allow service to interact with desktop". This could fix the problem for you in some cases.

C.Evenhuis
  • 24,516
  • 2
  • 54
  • 68
  • While this may be useful for debugging, it's important to note that allowing a service to interact with the desktop is considered bad practice. Don't do it for production. – Phil Mar 01 '11 at 15:40
3

I would assume the reason could be causing because of heavy use of interops. So you need to tackle this problem differently. I would suggest create a windows or console app with same logic of you service and make sure that it works first without any issues, and then you may want to go with creation of the Win service.

Kumar
  • 1,009
  • 5
  • 8
  • Sorry I have missed that part in your question. If you copied all of the service code into winform app and seen its working then it might have to do with not loading dependent DLLs in the service, can you verify with the permissions ? – Kumar Mar 01 '11 at 15:30
3

Debugging services is a pain, particularly since startup seems to be when many of the problems manifest (at least for us).

What we typically do is extract as much of the logic as possible to a single class that has start and stop methods. Those class methods are all that the service calls directly. We then create a WinForm application that has two buttons: one to invoke start, another to invoke stop. We can then run this WinForm applicaiton directly from the debugger and see what is happening.

Not the most elegant solution, but it works for us.

Mike Chess
  • 2,658
  • 5
  • 30
  • 37
3

Check out this question, which discusses how to catch unhandled exceptions in a window service.

Community
  • 1
  • 1
Holstebroe
  • 4,667
  • 1
  • 26
  • 44
3

In order to attach a debugger to the Windows Service, it needs to be started first. The reason why the service failed to start can be checked in Windows Event Log.

After that the process of attaching a debugger is pretty straight forward from Visual Studio Debug->Attach To Process.

TheBoyan
  • 6,380
  • 3
  • 40
  • 56
  • As the OP said, his problem is that the service doesn't run long enough to attach a debugger. He knows why Windows says the service failed to start; that doesn't help him fix the error because it's too generic. – KeithS Mar 01 '11 at 16:42
2

Use following Code in Service OnStart Method:

System.Diagnostics.Debugger.Launch();

Choose Visual Studio option from Pop Up message

Chirag
  • 3,622
  • 1
  • 26
  • 24
2

What I've done is implemented by OnStart() to look something like this:

_myBusinessObject = new MyBusinessObject();

After the Business Object has been constructed, timers and IPC handlers do all the real (Service) work.

Doing it like this allows you to create a Forms/WPF application that call the same code above in the Form_Loaded handler. This way, debugging the Forms application is the exact same as debugging the Service.

The only issue is that if you are using app.config values, there will be a second app.config file that needs to be kept up-to-date.

Onion-Knight
  • 3,257
  • 7
  • 29
  • 34