是否有比通过Windows服务控制管理器启动服务,然后将调试器附加到线程更简单的方法来逐步遍历代码?这有点麻烦,我想知道是否有更直接的方法。


当前回答

我使用了JOP的答案的变体。使用命令行参数,您可以在IDE中使用项目属性或通过Windows服务管理器设置调试模式。

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}

其他回答


static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}

更新

这种方法是目前为止最简单的:

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

我把最初的答案留给后人。


我的服务倾向于有一个封装定时器的类,因为我想让服务定期检查它是否有任何工作要做。

我们在服务启动期间更新类并调用StartEventLoop()。(这个类也可以很容易地从控制台应用程序中使用。)

这种设计的一个很好的副作用是,可以使用用来设置Timer的参数在服务实际开始工作之前有一个延迟,这样就有时间手动附加调试器。

p.s. .如何手动将调试器附加到正在运行的进程…?

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

我也曾经做过以下事情(在之前的回答中已经提到过,但是使用条件编译器[#if]标志来帮助避免它在发布构建中被触发)。

我不再这样做了,因为有时我们会忘记在发布中构建,并在客户端演示上运行的应用程序中出现调试器中断(尴尬!)

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif

我喜欢能够调试我的服务的每个方面,包括在OnStart()中的任何初始化,同时仍然在SCM框架内执行完整的服务行为…没有“控制台”或“应用程序”模式。

为此,我在同一个项目中创建了第二个用于调试的服务。调试服务在正常启动时(即在服务MMC插件中),创建服务主机进程。这为您提供了一个可以附加调试器的进程,即使您还没有启动真正的服务。在将调试器附加到进程之后,启动真正的服务,您可以在服务生命周期中的任何位置进入它,包括OnStart()。

因为调试服务只需要极少的代码侵入,所以可以很容易地将其包含在服务设置项目中,并且通过注释掉一行代码和删除单个项目安装程序就可以很容易地从产品版本中删除调试服务。

细节:

1)假设你正在实现MyService,也创建MyServiceDebug。将两者添加到Program.cs中的ServiceBase数组中,如下所示:

    /// <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)将真实服务和调试服务添加到服务项目的项目安装程序中:

在将服务项目输出添加到服务的设置项目时,将包括两个服务(真实的和调试的)。安装后,这两个服务都将出现在服务中。MMC插件。

3)启动MMC中的调试服务。

4)在Visual Studio中,将调试器附加到调试服务启动的进程。

5)启动真正的服务,享受调试。

我通常做的是将服务的逻辑封装在一个单独的类中,并从一个“runner”类开始。这个运行器类可以是实际的服务,也可以只是一个控制台应用程序。所以你的解决方案至少有3个项目:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif