Easier way to debug a Windows service

后端 未结 28 1567
春和景丽
春和景丽 2020-11-22 15:36

Is there an easier way to step through the code than to start the service through the Windows Service Control Manager and then attaching the debugger to the thread? It\'s ki

相关标签:
28条回答
  • 2020-11-22 16:24

    When I set up a new service project a few weeks ago I found this post. While there are many great suggestions, I still didn't find the solution I wanted: The possibility to call the service classes' OnStart and OnStop methods without any modification to the service classes.

    The solution I came up with uses the Environment.Interactive the select running mode, as suggested by other answers to this post.

    static void Main()
    {
        ServiceBase[] servicesToRun;
        servicesToRun = new ServiceBase[] 
        {
            new MyService()
        };
        if (Environment.UserInteractive)
        {
            RunInteractive(servicesToRun);
        }
        else
        {
            ServiceBase.Run(servicesToRun);
        }
    }
    

    The RunInteractive helper uses reflection to call the protected OnStart and OnStop methods:

    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);
    }
    

    This is all the code required, but I also wrote walkthrough with explanations.

    0 讨论(0)
  • 2020-11-22 16:25

    UPDATE

    This approach is by far the easiest:

    http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

    I leave my original answer below for posterity.


    My services tend to have a class that encapsulates a Timer as I want the service to check at regular intervals whether there is any work for it to do.

    We new up the class and call StartEventLoop() during the service start-up. (This class could easily be used from a console app too.)

    The nice side-effect of this design is that the arguments with which you set up the Timer can be used to have a delay before the service actually starts working, so that you have time to attach a debugger manually.

    p.s. How to attach the debugger manually to a running process...?

    using System;
    using System.Threading;
    using System.Configuration;    
    
    public class ServiceEventHandler
    {
        Timer _timer;
        public ServiceEventHandler()
        {
            // get configuration etc.
            _timer = new Timer(
                new TimerCallback(EventTimerCallback)
                , null
                , Timeout.Infinite
                , Timeout.Infinite);
        }
    
        private void EventTimerCallback(object state)
        {
            // do something
        }
    
        public void StartEventLoop()
        {
            // wait a minute, then run every 30 minutes
            _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
        }
    }
    

    Also I used to do the following (already mentioned in previous answers but with the conditional compiler [#if] flags to help avoid it firing in a Release build).

    I stopped doing it this way because sometimes we'd forget to build in Release and have a debugger break in an app running on a client demo (embarrasing!).

    #if DEBUG
    if (!System.Diagnostics.Debugger.IsAttached)
    {
        System.Diagnostics.Debugger.Break();
    }
    #endif
    
    0 讨论(0)
  • 2020-11-22 16:25

    I like to be able to debug every aspect of my service, including any initialization in OnStart(), while still executing it with full service behavior within the framework of the SCM... no "console" or "app" mode.

    I do this by creating a second service, in the same project, to use for debugging. The debug service, when started as usual (i.e. in the services MMC plugin), creates the service host process. This gives you a process to attach the debugger to even though you haven't started your real service yet. After attaching the debugger to the process, start your real service and you can break into it anywhere in the service lifecycle, including OnStart().

    Because it requires very minimal code intrusion, the debug service can easily be included in your service setup project, and is easily removed from your production release by commenting out a single line of code and deleting a single project installer.

    Details:

    1) Assuming you are implementing MyService, also create MyServiceDebug. Add both to the ServiceBase array in Program.cs like so:

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
            { 
                new MyService(),
                new MyServiceDebug()
            };
            ServiceBase.Run(ServicesToRun);
        }
    

    2) Add the real service AND the debug service to the project installer for the service project:

    enter image description here

    Both services (real and debug) get included when you add the service project output to the setup project for the service. After installation, both services will appear in the service.msc MMC plugin.

    3) Start the debug service in MMC.

    4) In Visual Studio, attach the debugger to the process started by the debug service.

    5) Start the real service and enjoy debugging.

    0 讨论(0)
  • 2020-11-22 16:26

    Use the TopShelf library.

    Create a console application then configure setup in your Main

    class Program
        {
            static void Main(string[] args)
            {
                HostFactory.Run(x =>
                {
    
                    // setup service start and stop.
                    x.Service<Controller>(s =>
                    {
                        s.ConstructUsing(name => new Controller());
                        s.WhenStarted(controller => controller.Start());
                        s.WhenStopped(controller => controller.Stop());
                    });
    
                    // setup recovery here
                    x.EnableServiceRecovery(rc =>
                    {
                        rc.RestartService(delayInMinutes: 0);
                        rc.SetResetPeriod(days: 0);
                    });
    
                    x.RunAsLocalSystem();
                });
            }
    }
    
    public class Controller
        {
            public void Start()
            {
    
            }
    
            public void Stop()
            {
    
            }
        }
    

    To debug your service, just hit F5 in visual studio.

    To install service, type in cmd "console.exe install"

    You can then start and stop service in the windows service manager.

    0 讨论(0)
  • 2020-11-22 16:26

    How about Debugger.Break() in the first line?

    0 讨论(0)
  • 2020-11-22 16:27

    If I want to quickly debug the service, I just drop in a Debugger.Break() in there. When that line is reached, it will drop me back to VS. Don't forget to remove that line when you are done.

    UPDATE: As an alternative to #if DEBUG pragmas, you can also use Conditional("DEBUG_SERVICE") attribute.

    [Conditional("DEBUG_SERVICE")]
    private static void DebugMode()
    {
        Debugger.Break();
    }
    

    On your OnStart, just call this method:

    public override void OnStart()
    {
         DebugMode();
         /* ... do the rest */
    }
    

    There, the code will only be enabled during Debug builds. While you're at it, it might be useful to create a separate Build Configuration for service debugging.

    0 讨论(0)
提交回复
热议问题