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


当前回答

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

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

其他回答

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

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

更新

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

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

为了调试Windows服务,我结合了GFlags和一个由regedit创建的.reg文件。

运行GFlags,指定ex -name和vsjitdebugger 运行regedit并转到GFlags设置选项的位置 从文件菜单中选择“导出密钥” 将该文件保存在扩展名为.reg的某处 任何时候你想调试服务:双击.reg文件 如果要停止调试,请双击第二个.reg文件

或者保存以下代码片段并用所需的可执行名称替换servicename.exe。


debugon.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"
"Debugger"="vsjitdebugger.exe"

debugoff.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"

这段YouTube视频由Fabio Scopel解释了如何调试一个Windows服务相当好…实际的方法从视频中的4:45开始…

下面是视频中解释的代码…在你的Program.cs文件中,添加Debug部分的东西…

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

在你的Service1.cs文件中,添加OnDebug()方法…

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

它是如何工作的

基本上,你必须创建一个公共void OnDebug(),调用OnStart(string[] args),因为它是受保护的,不能在外部访问。void Main()程序添加了#if预处理器和#DEBUG。

如果项目在DEBUG模式下编译,Visual Studio定义DEBUG。这将允许调试部分(下面)在条件为真时执行

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

它将像控制台应用程序一样运行,一旦一切顺利,您可以更改模式Release和常规else部分将触发逻辑

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