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


当前回答

下面是我用来测试服务的简单方法,没有任何额外的“调试”方法,并带有集成的VS单元测试。

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}

其他回答

更新

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

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

您也可以通过命令提示符(sc.exe)启动服务。

就我个人而言,我会在调试阶段将代码作为独立程序运行,当大多数错误被解决后,再改为作为服务运行。

有时,分析在服务启动期间发生了什么是很重要的。附加到流程在这里没有帮助,因为在服务启动时附加调试器的速度不够快。


简单的回答是,我使用以下4行代码来做到这一点:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

这些被插入到服务的OnStart方法中,如下所示:

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

对于那些以前没有做过的人,我在下面列出了详细的提示,因为你很容易陷入困境。以下提示适用于Windows 7x64和Visual Studio 2010 Team Edition,但也适用于其他(更新的)环境。


重要提示:以“手动”模式部署服务(在VS命令提示符中使用InstallUtil实用程序或运行您准备好的服务安装程序项目)。在启动服务之前打开Visual Studio并加载包含服务源代码的解决方案——在Visual Studio中根据需要设置额外的断点——然后通过服务控制面板启动服务。

因为调试器。启动代码,这将导致一个对话框“一个未处理的Microsoft . net Framework异常发生在Servicename.exe.”出现。单击“是”,调试Servicename.exe,如下图所示:


之后,Windows UAC可能会提示您输入管理凭据。输入它们并继续执行Yes:

之后,著名的Visual Studio即时调试器窗口出现。它询问您是否要使用已删除的调试器进行调试。在单击Yes之前,选择不想打开新实例(第二个选项)——在这里,新实例没有帮助,因为不会显示源代码。所以你选择之前打开的Visual Studio实例:

单击Yes后,过一会儿Visual Studio将在调试器所在的行中显示黄色箭头。启动语句,您可以调试您的代码(方法MyInitOnStart,其中包含初始化)。

按F5继续立即执行,直到到达您准备的下一个断点。

提示:要保持服务运行,请选择Debug -> Detach all。这允许您在服务正确启动并完成调试启动代码之后运行与服务通信的客户机。如果您按下Shift+F5(停止调试),这将终止服务。您应该使用服务控制面板来停止它,而不是这样做。



请注意,

If you build a Release, then the debug code is automatically removed and the service runs normally. I am using Debugger.Launch(), which starts and attaches a debugger. I have tested Debugger.Break() as well, which did not work, because there is no debugger attached on start up of the service yet (causing the "Error 1067: The process terminated unexpectedly."). RequestAdditionalTime sets a longer timeout for the startup of the service (it is not delaying the code itself, but will immediately continue with the Debugger.Launch statement). Otherwise the default timeout for starting the service is too short and starting the service fails if you don't call base.Onstart(args) quickly enough from the debugger. Practically, a timeout of 10 minutes avoids that you see the message "the service did not respond..." immediately after the debugger is started. Once you get used to it, this method is very easy because it just requires you to add 4 lines to an existing service code, allowing you quickly to gain control and debug.

对于现有Windows服务程序的故障排除,请像其他人建议的那样使用'Debugger.Break()'。

对于新的Windows服务程序,我建议使用James Michael Hare的方法http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx

我过去所做的是有一个命令行开关,它可以作为服务或常规应用程序启动程序。然后,在我的IDE中,我将设置开关,以便我可以分步执行我的代码。

对于某些语言,您实际上可以检测它是否运行在IDE中,并自动执行此切换。

你在用什么语言?