85

Is it possible to debug the Windows services in Visual Studio?

I used code like

System.Diagnostics.Debugger.Break();

but it is giving some code error like:

I got two event error: eventID 4096 VsJITDebugger and "The service did not respond to the start or control request in a timely fashion."

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
PawanS
  • 6,553
  • 13
  • 41
  • 68

17 Answers17

124

Use the following code in service OnStart method:

System.Diagnostics.Debugger.Launch();

Choose the Visual Studio option from the pop up message.

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

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Chirag
  • 3,622
  • 1
  • 26
  • 24
  • 9
    Remember to run VS as Administrator. Then it will be available on the list. – Michael Dec 19 '14 at 10:41
  • 1
    Can someone clarify what is meant by the Pop up message? When / how does that appear? – Mike Jun 17 '15 at 13:50
  • @Mike, install & start the service, it will appear. – Harshit Sep 21 '15 at 05:47
  • @Mike, its a Windows dialog box that pops up on the interactive (logged in) desktop and asks if you want to pick an app to debug the process with. If you pick VS it will launch the debugger and attach to the process – Chris Johnson Jan 13 '16 at 22:57
64

You can also try this.

  1. Create your Windows service and install and start…. That is, Windows services must be running in your system.
  2. While your service is running, go to the Debug menu, click on Attach Process (or process in old Visual Studio)
  3. Find your running service, and then make sure the Show process from all users and Show processes in all sessions is selected, if not then select it.

enter image description here

  1. Click the Attach button
  2. Click OK
  3. Click Close
  4. Set a break point to your desirable location and wait for execute. It will debug automatic whenever your code reaches to that point.
  5. Remember, put your breakpoint at reachable place, if it is onStart(), then stop and start the service again

(After a lot of googling, I found this in "How to debug the Windows Services in Visual Studio".)

Chris Schiffhauer
  • 16,430
  • 15
  • 74
  • 84
PawanS
  • 6,553
  • 13
  • 41
  • 68
23

You should separate out all the code that will do stuff from the service project into a separate project, and then make a test application that you can run and debug normally.

The service project would be just the shell needed to implement the service part of it.

Lasse V. Karlsen
  • 350,178
  • 94
  • 582
  • 779
  • OOW!! ctrl+C then ctrl+V, i mean to new project. Ya that only I am doing. Is it not possible to attach any process to debug or any other option rather than separate project.? – PawanS Jan 13 '11 at 10:15
  • 2
    Of course it is possible, but it's much easier to develop a windows service if you take out the service part during development. – Lasse V. Karlsen Jan 13 '11 at 10:17
  • hmmm... that is good way but simply it doubles the work. I thought any other way would exist. – PawanS Jan 13 '11 at 10:26
  • 9
    I don't see how it would "double" the work. Sure, it'll add a small bit of overhead in making the extra project and separating out the code in the service to a third project, but other than that, you wouldn't make a copy of the code, you would move it, and reference that project. – Lasse V. Karlsen Jan 13 '11 at 10:46
  • 3
    ^+1. It double the work to managing the service, which is pretty much zero in terms of development time, and you only do it once. Debugging a serice is PAINFUL - rather make it dual start as command line. Check google for predefined wrapper classes allowing doing this (they use reflection to simulate the start / stop no teh service class). one hour work, tons of savings, net loss: negative - you gain time. – TomTom Jan 17 '11 at 07:12
  • @TomTom Any example or tutorial of this? I googled and found nothing – KansaiRobot Sep 03 '19 at 07:48
14

Either that as suggested by Lasse V. Karlsen, or set up a loop in your service that will wait for a debugger to attach. The simplest is

while (!Debugger.IsAttached)
{
    Thread.Sleep(1000);
}

... continue with code

That way you can start the service and inside Visual Studio you choose "Attach to Process..." and attach to your service which then will resume normal exution.

Pauli Østerø
  • 6,728
  • 1
  • 28
  • 48
7

Given that ServiceBase.OnStart has protected visibility, I went down the reflection route to achieve the debugging.

private static void Main(string[] args)
{
    var serviceBases = new ServiceBase[] {new Service() /* ... */ };

#if DEBUG
    if (Environment.UserInteractive)
    {
        const BindingFlags bindingFlags =
            BindingFlags.Instance | BindingFlags.NonPublic;

        foreach (var serviceBase in serviceBases)
        {
            var serviceType = serviceBase.GetType();
            var methodInfo = serviceType.GetMethod("OnStart", bindingFlags);

            new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase);
        }

        return;
    }
#endif

    ServiceBase.Run(serviceBases);
}

Note that Thread is, by default, a foreground thread. returning from Main while the faux-service threads are running won't terminate the process.

ta.speot.is
  • 25,998
  • 8
  • 62
  • 93
  • Since OnStart is supposed to return quickly, you shouldn't need to do it in another thread. However, if the service doesn't start another thread, your process will exit immediately. – Matt Connolly Dec 06 '13 at 05:41
  • @MattConnolly On the latter: if necessary, I change the above code to start a foreground thread that sleeps forever (before the normal processing). – ta.speot.is Dec 06 '13 at 06:03
  • This should be a real answer. Works beautifully! – lentyai Nov 21 '19 at 06:15
4

A Microsoft article explains how to debug a Windows service here and what part anyone can miss if they debug it by attaching to a process.

Below is my working code. I have followed the approach suggested by Microsoft.

Add this code to program.cs:

static void Main(string[] args)
{
    // 'If' block will execute when launched through Visual Studio
    if (Environment.UserInteractive)
    {
        ServiceMonitor serviceRequest = new ServiceMonitor();
        serviceRequest.TestOnStartAndOnStop(args);
    }
    else // This block will execute when code is compiled as a Windows application
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new ServiceMonitor()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

Add this code to the ServiceMonitor class.

internal void TestOnStartAndOnStop(string[] args)
{
    this.OnStart(args);
    Console.ReadLine();
    this.OnStop();
}

Now go to Project Properties, select tab "Application" and select Output Type as "Console Application" when debugging, or "Windows Application" when done with debugging, recompile and install your service.

Enter image description here

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
kumar chandraketu
  • 1,546
  • 2
  • 13
  • 20
  • 1
    is there a way to set output to Console Application in Debug and Window Application in release ? – kofifus Jun 05 '18 at 01:42
3

You can make a console application. I use this main function:

    static void Main(string[] args)
    {
        ImportFileService ws = new ImportFileService();
        ws.OnStart(args);
        while (true)
        {
            ConsoleKeyInfo key = System.Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
                break;
        }
        ws.OnStop();
    }

My ImportFileService class is exactly the same as in my Windows service's application, except the inheritant (ServiceBase).

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
kerrubin
  • 1,526
  • 1
  • 18
  • 25
  • is it on the same project or I another project for this console app – PawanS Jan 13 '11 at 10:23
  • It's 2 differents projects with similar classes. In my case, it's a simple service with only the ImportFileService class wihch are duplicate. When I want to dev/test, I use consoleapp, and then copy/paste. Like Lasse V. Karlsen said, it's a debug program, all the logic (business) is on a third project. – kerrubin Jan 13 '11 at 10:27
  • Yes, is is. It's why I said "except the inheritant (`ServiceBase`).". I find it easier to debug in a Console App, but I understand if that doesn't convince everyone. – kerrubin Dec 07 '12 at 09:12
3

I use a great Nuget package called ServiceProcess.Helpers.

And I quote...

It helps windows services debugging by creating a play/stop/pause UI when running with a debugger attached, but also allows the service to be installed and run by the windows server environment.

All this with one line of code.

http://windowsservicehelper.codeplex.com/

Once installed and wired in all you need to do is set your windows service project as the start-up project and click start on your debugger.

2

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

2

I just added this code to my service class so I could indirectly call OnStart, similar for OnStop.

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
RichardHowells
  • 5,178
  • 3
  • 21
  • 24
2

I'm using the /Console parameter in the Visual Studio project DebugStart OptionsCommand line arguments:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
         var runMode = args.Contains(@"/Console")
             ? WindowsService.RunMode.Console
             : WindowsService.RunMode.WindowsService;
         new WinodwsService().Run(runMode);
    }
}


public class WindowsService : ServiceBase
{
    public enum RunMode
    {
        Console,
        WindowsService
    }

    public void Run(RunMode runMode)
    {
        if (runMode.Equals(RunMode.Console))
        {
            this.StartService();
            Console.WriteLine("Press <ENTER> to stop service...");
            Console.ReadLine();

            this.StopService();
            Console.WriteLine("Press <ENTER> to exit.");
            Console.ReadLine();
        }
        else if (runMode.Equals(RunMode.WindowsService))
        {
            ServiceBase.Run(new[] { this });
        }
    }

    protected override void OnStart(string[] args)
    {
        StartService(args);
    }

    protected override void OnStop()
    {
        StopService();
    }

    /// <summary>
    /// Logic to Start Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StartService(params string[] args){ ... }

    /// <summary>
    /// Logic to Stop Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StopService() {....}
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Sean M
  • 586
  • 5
  • 15
2

I found this question, but I think a clear and simple answer is missing.

I don't want to attach my debugger to a process, but I still want to be able to call the service OnStart and OnStop methods. I also want it to run as a console application so that I can log information from NLog to a console.

I found these brilliant guides that does this:

Start by changing the projects Output type to Console Application.

Enter image description here

Change your Program.cs to look like this:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        // Startup as service.
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };

        if (Environment.UserInteractive)
        {
            RunInteractive(ServicesToRun);
        }
        else
        {
            ServiceBase.Run(ServicesToRun);
        }
    }
}

Then add the following method to allow services running in interactive mode.

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart",
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop",
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ogglas
  • 38,157
  • 20
  • 203
  • 266
  • Great Code! Simple, effective. +1. But just as easy I made this a Forms app. I really hate console apps. Also, you can easily implement a Forms Button for each service event. – Roland Nov 11 '19 at 16:41
1

Unfortunately, if you're trying to debug something at the very start of a Windows Service operation, "attaching" to the running process won't work. I tried using Debugger.Break() within the OnStart procecdure, but with a 64-bit, Visual Studio 2010 compiled application, the break command just throws an error like this:

System error 1067 has occurred.

At that point, you need to set up an "Image File Execution" option in your registry for your executable. It takes five minutes to set up, and it works very well. Here's a Microsoft article where the details are:

How to: Launch the Debugger Automatically

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Brian
  • 3,538
  • 1
  • 19
  • 31
1

Try Visual Studio's very own post-build event command line.

Try to add this in post-build:

@echo off
sc query "ServiceName" > nul
if errorlevel 1060 goto install
goto stop

:delete
echo delete
sc delete "ServiceName" > nul
echo %errorlevel%
goto install

:install
echo install
sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul
echo %errorlevel%
goto start

:start
echo start
sc start "ServiceName" > nul
echo %errorlevel%
goto end

:stop
echo stop
sc stop "ServiceName" > nul
echo %errorlevel%
goto delete

:end

If the build error with a message like Error 1 The command "@echo off sc query "ServiceName" > nul so on, Ctrl + C then Ctrl + V the error message into Notepad and look at the last sentence of the message.

It could be saying exited with code x. Look for the code in some common error here and see how to resolve it.

1072 -- Marked for deletion → Close all applications that maybe using the service including services.msc and Windows event log.
1058 -- Can't be started because disabled or has no enabled associated devices → just delete it.
1060 -- Doesn't exist → just delete it.
1062 -- Has not been started → just delete it.
1053 -- Didn't respond to start or control → see event log (if logged to event log). It may be the service itself throwing an exception.
1056 -- Service is already running → stop the service, and then delete.

More on error codes here.

And if the build error with message like this,

Error    11    Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed.    ServiceName
Error    12    Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process.    ServiceName

open cmd, and then try to kill it first with taskkill /fi "services eq ServiceName" /f

If all is well, F5 should be sufficient to debug it.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
asakura89
  • 501
  • 1
  • 11
  • 19
0

In the OnStart method, do the following.

protected override void OnStart(string[] args)
{
    try
    {
        RequestAdditionalTime(600000);
        System.Diagnostics.Debugger.Launch(); // Put breakpoint here.

        .... Your code
    }
    catch (Exception ex)
    {
        .... Your exception code
    }
}

Then run a command prompt as administrator and put in the following:

c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand

The above line will create test-xyzService in the service list.

To start the service, this will prompt you to attach to debut in Visual Studio or not.

c:\> sc start text-xyzService

To stop the service:

c:\> sc stop test-xyzService

To delete or uninstall:

c:\> sc delete text-xyzService
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
0

Debug a Windows Service over http (tested with VS 2015 Update 3 and .Net FW 4.6)

Firstly, you have to create a Console Project within your VS Solution(Add -> New Project -> Console Application).

Within the new project, create a class "ConsoleHost" with that code:

class ConsoleHost : IDisposable
{
    public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex);
    private ServiceHost host;

    public void Start(Uri baseAddress)
    {
        if (host != null) return;

        host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress);

        //binding
        var binding = new BasicHttpBinding()
        {
            Name = "MyService",
            MessageEncoding = WSMessageEncoding.Text,
            TextEncoding = Encoding.UTF8,
            MaxBufferPoolSize = 2147483647,
            MaxBufferSize = 2147483647,
            MaxReceivedMessageSize = 2147483647
        };

        host.Description.Endpoints.Clear();
        host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress);

        // Enable metadata publishing.
        var smb = new ServiceMetadataBehavior
        {
            HttpGetEnabled = true,
            MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 },
        };

        host.Description.Behaviors.Add(smb);

        var defaultBehaviour = host.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();
        if (defaultBehaviour != null)
        {
            defaultBehaviour.IncludeExceptionDetailInFaults = true;
        }

        host.Open();
    }

    public void Stop()
    {
        if (host == null)
            return;

        host.Close();
        host = null;
    }

    public void Dispose()
    {
        this.Stop();
    }
}

And this is the code for the Program.cs class:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        var baseAddress = new Uri(http://localhost:8161/MyService);
        var host = new ConsoleHost();
        host.Start(null);
        Console.WriteLine("The service is ready at {0}", baseAddress);
        Console.WriteLine("Press <Enter> to stop the service.");
        Console.ReadLine();
        host.Stop();
    }
}

Configurations such as connectionstrings should be copied in the App.config file of the Console project.

To sturt up the console, righ-click on Console project and click Debug -> Start new instance.

mggSoft
  • 922
  • 1
  • 18
  • 28
0

Just add a contructor to your service class (if you don't have it already). Below, you can check and example for visual basic .net.

Public Sub New()
   OnStart(Nothing) 
End Sub

After, that, right-click on project and select "Debug -> Start a new instance".

Dharman
  • 21,838
  • 18
  • 57
  • 107
Pedro Martín
  • 106
  • 1
  • 6