你如何调试Windows服务?

时间:2011-03-01 15:18:10

标签: c# c++ debugging windows-services

我阅读了有关该主题的MSDN文章。引用:

  

因为必须运行服务   在服务范围内   控制管理器而不是   在Visual Studio中,调试一个   服务不是那么简单   调试其他Visual Studio   应用类型。要调试服务,   你必须启动服务然后   将调试器附加到进程中   它正在运行。那你可以   使用全部调试您的应用程序   标准调试功能   Visual Studio。

现在我的问题是我的服务首先无法启动。首先它崩溃,然后说:

  

未处理的异常   (System.Runtime.InteropServices.COMException)   发生在MyServiceName.exe [3596])

并建议我调试它(​​当我选择一个时,调试器实例会立即崩溃)。然后它说

  

无法启动MyServiceName   本地计算机上的服务。错误   1053:该服务没有回应   a中的启动或控制请求   及时时尚

那么,我如何调查/调试我的服务无法启动的原因?问题是我创建了一个控制台应用程序,它完全可以完成服务的工作,并且工作正常。 (我的意思是我只是将OnStart()方法和主循环的内容复制到main中。

任何帮助都将不胜感激。

服务是用C#编写的,大量使用互操作。我正在使用VS2008

15 个答案:

答案 0 :(得分:37)

您可以使用参数让您的应用程序决定是作为服务还是常规应用程序启动(即在本例中显示表单或启动服务):

static void Main(string[] args)
{
    if ((1 == args.Length) && ("-runAsApp" == args[0]))
    {
        Application.Run(new application_form());
    }
    else
    {
        System.ServiceProcess.ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new MyService() };
        System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }
}

现在,如果您传递参数“-runAsApp”,您可以正常调试应用程序 - SCM不会传递此参数,因此您也可以将其用作任何代码更改的服务(前提是您派生自{{ 1}})

修改

与Windows服务的另一个区别是身份(这可能对InterOp尤为重要) - 您希望确保在“app”模式和服务模式下以相同的身份进行测试。

为此,您可以在应用模式下使用模拟(我可以发布C#包装器,如果它有帮助,但这可以很容易地用Google搜索),以使用您的Windows服务将运行的相同标识,即通常是LocalService或NetworkService。 / p>

如果需要其他身份,您可以在app.config中添加设置,以便您决定是否使用凭据,如果是这样,哪个用户可以模拟 - 这些设置在作为应用程序运行时会处于活动状态,但会关闭Windows服务(因为服务已经在所需的身份下运行):

ServiceBase

答案 1 :(得分:21)

我通常只是手动设置断点,然后将其指向c#中当前打开的项目。设置断点的代码是:

System.Diagnostics.Debugger.Break();

这应该让你开始,然后你可以单步执行你的代码,看看到底发生了什么。

答案 2 :(得分:14)

我从C. Lawrence Wenham那里偷了这个,所以我不能真正理解,但你可以通过以下代码以编程方式将调试器附加到服务上,而不会破坏执行时间:

System.Diagnostics.Debugger.Launch();

将它放在服务的OnStart()方法中,作为第一行,它将提示您选择VS的实例来附加其调试器。从那里,系统将在您设置的断点处停止,并在抛出异常时停止。我会在代码周围加上#if DEBUG子句,因此Release版本不会包含它;或者你可以在发现问题后将其剥离。

答案 3 :(得分:9)

您可以使用WinDbg / NTSD(" Windows调试工具"包中的另一个调试器)与您的服务一起启动调试器。

要做到这一点,打开" gflags" (也可在上述包中找到)到"图像文件"选项卡并设置图像文件(服务)的调试器可执行文件的路径;

如果您的服务被标记为交互式(只有在SYSTEM帐户下运行),您可以直接启动WinDbg,只需将调试器设置为" PATH_TO_WINDBG \ windbg.exe -g -G& #34; (需要使用-g / -G,以便调试器不会在应用程序启动或结束时中断执行 - 默认行为)。现在,在启动服务时,应该弹出windbg窗口并捕获任何未处理的异常。

如果您的服务不是交互式的,您可以在远程模式下启动NTSD调试器(命令行调试器)并从WinDbg连接到它(甚至可以在另一台PC上运行)。为此,请将gflags中的调试器设置为" PATH_TO_NTSD \ ntsd -remote tcp:port = 6666,server = localhost" 。然后通过启动windbg连接到远程调试器,例如" windbg -remote tcp:port = 6666,server = localhost" ,您应该完全控制其他调试会话。

至于查找异常的来源本身,这里有一个windbg教程,但作为一个开始尝试在异常后执行"!analyze -v" 命令被抓住了 - 幸运的是,这是你需要的所有信息..

注意:也许这对您的情况来说太过分了,但是通过这种方法,您甚至可以在系统启动期间调试服务(我曾经遇到过服务的时间问题,只有在启动时才会出现问题第一次使用该系统)

答案 4 :(得分:7)

我做的一件事(可能是一种黑客攻击)是在Thread.Sleep(10000)方法的开头放置OnStart()。这给了我一个10秒的窗口,可以在我做任何其他事情之前将我的调试器连接到服务。

当我完成调试时,我会删除Thread.Sleep()语句。

您可能做的另一件事是:

public override void OnStart()
{
    try
    {
        // all your OnStart() logic here
    }
    catch(Exception ex)
    {
        // Log ex.Message
        if (!EventLog.SourceExists("MyApplication"))
            EventLog.CreateEventSource("MyApplication", "Application");

        EventLog.WriteEntry("MyApplication", "Failed to start: " + ex.Message);
        throw;
    }
}

当您记录ex.Message时,您可能会收到更详细的错误消息。此外,您只需记录ex.ToString()即可获得整个堆栈跟踪,如果您的.pdb文件与可执行文件位于同一目录中,它甚至会告诉您异常发生在哪一行。

答案 5 :(得分:6)

在OnStart中添加大量详细日志记录。这是痛苦的和旧的学校,但它的确有效。

答案 6 :(得分:5)

似乎问题在于用户上下文。让我确认一下我的假设是否正确。

  1. 如果您说代码在控制台应用程序中运行良好,我假设您正在您登录的同一用户下执行控制台应用程序。

  2. 当您说从Windows服务调用时相同的代码崩溃时,我认为该服务正在您的开发计算机的“本地系统”帐户中运行。

  3. 如果我的假设都是正确的,请尝试以下步骤。

    1. 在服务列表中,右键单击您的服务,选择属性,然后选择“登录”选项卡。

    2. 选择“此帐户”选项并提供现有用户名和密码。

    3. 现在尝试启动该服务。它现在应该没有任何错误地开始。

    4. 以下可能是您错误的根本原因

      1. 如果您使用的是SQL Server,请确保您没有使用SSPI身份验证。

      2. 如果您尝试阅读使用“本地系统”帐户时没有权限的任何共享文件夹\资源。

      3. 如果应用程序所需的任何所需依赖项位于“本地系统”用户无权访问的其他文件夹中。

      4. 如果您使用的VBA自动化无法在“本地系统”帐户中使用。

      5. 尝试停用防火墙或防病毒软件。

答案 7 :(得分:4)

您可以在互操作调用周围添加一些日志记录,以找出哪一个失败。

默认情况下,服务也与桌面无关;如果您打开services.msc控制面板小程序,获取服务的属性,请转到“登录”选项卡,您可以选中“允许服务与桌面交互”。在某些情况下,这可以解决您的问题。

答案 8 :(得分:3)

我认为原因可能是因为大量使用了互操作。所以你需要以不同的方式解决这个问题。我建议使用相同的服务逻辑创建一个Windows或控制台应用程序,并确保它首先工作没有任何问题,然后你可能想要创建Win服务。

答案 9 :(得分:3)

调试服务很痛苦,特别是因为启动似乎是许多问题出现的时候(至少对我们而言)。

我们通常做的是尽可能多地将逻辑提取到具有start和stop方法的单个类中。这些类方法都是服务直接调用的。然后我们创建一个WinForm应用程序,它有两个按钮:一个用于调用start,另一个用于调用stop。然后,我们可以直接从调试器运行此WinForm应用程序,看看发生了什么。

不是最优雅的解决方案,但它适用于我们。

答案 10 :(得分:3)

查看this question,其中讨论了如何在窗口服务中捕获未处理的异常。

答案 11 :(得分:3)

为了将调试器附加到Windows服务,需要先启动它。可以在Windows事件日志中检查服务无法启动的原因。

之后,从Visual Studio Debug-> Attach To Process中附加调试器的过程非常简单。

答案 12 :(得分:2)

我所做的是由OnStart()实现,看起来像这样:

_myBusinessObject = new MyBusinessObject();

构建业务对象后,计时器和IPC处理程序完成所有实际(服务)工作。

这样做可以创建一个Forms / WPF应用程序,在Form_Loaded处理程序中调用上面相同的代码。这样,调试Forms应用程序与调试服务完全相同。

唯一的问题是,如果您使用app.config值,则会有第二个app.config文件需要保持最新。

答案 13 :(得分:2)

在服务OnStart方法中使用以下代码:

System.Diagnostics.Debugger.Launch();

从弹出消息中选择Visual Studio选项

答案 14 :(得分:1)