如何调试.NET Windows服务OnStart方法?

时间:2009-07-28 20:27:52

标签: c# .net debugging

我使用.NET编写的代码只有在作为Windows服务安装时才会失败。失败不允许服务甚至启动。我无法弄清楚如何进入OnStart方法。

How to: Debug Windows Service Applications 给出了一个诱人的线索:

  

附加到服务的过程允许您调试大多数但不是所有服务的代码;例如,因为服务已经启动,所以无法以这种方式调试服务的OnStart方法中的代码,或者用于加载服务的Main方法中的代码。 解决此问题的一种方法是在服务应用程序中创建临时的第二个服务,该服务仅用于帮助调试。您可以安装这两个服务,然后启动此“虚拟”服务以加载服务进程。一旦临时服务启动了该过程,您就可以使用Visual Studio中的“调试”菜单附加到服务进程。

但是,我不清楚你应该如何创建虚拟服务来加载服务进程。

16 个答案:

答案 0 :(得分:94)

作为临时解决方法,您可以做的一件事是将调试器作为OnStart中的第一行代码启动

System.Diagnostics.Debugger.Launch()

这将提示您输入要使用的调试器。只需在Visual Studio中打开解决方案,然后从列表中选择该实例。

答案 1 :(得分:10)

我倾向于添加这样的方法:

    [Conditional("DEBUG")]
    private void AttachDebugger()
    {
        Debugger.Break();
    }

它只会在你项目的Debug版本上被调用,它会暂停执行并允许你附加调试器。

答案 2 :(得分:8)

使用installutil.exe安装服务后,如果服务已启动,您可以更改Start Parameters以跳转到调试器:

enter image description here

当您使用参数-debugWithVisualStudio(或简称-d)手动启动服务时,它将自动检测正确的项目,并在Visual Studio中启动交互式调试器:

enter image description here

要支持此功能,请更改服务的OnStart()功能:

/// <summary>
///     Executed when the service is started.
/// </summary>
/// <param name="args">Command line arguments.</param>
protected override void OnStart(string[] args)
{
    try
    {
        //How to debug when running a Windows Service:
        // 1. Right click on the service name in Windows Service Manager.
        // 2. Select "Properties".
        // 3. In "Start Parameters", enter "-d" (or "-debugWithVisualStudio").
        // 4. Now, when you start the service, it will fire up Visual Studio 2012 and break on the line below.
        // 5. Make sure you have UAC (User Access Control) turned off, and have Administrator privileges.
#if DEBUG
        if (((ICollection<string>)args).Contains("-d")
            || ((ICollection<string>)args).Contains("-debugWithVisualStudio"))
        {
            Debugger.Launch(); // Launches VS2012 debugger.
        }
#endif
        ShellStart(args);
        base.OnStart(args);
    }
    catch (Exception ex)
    {
        // Log exception here.
    }
}

(可选)如果要缩小服务引发错误的确切代码行,请从Visual Studio菜单DEBUG .. Exceptions启用异常。当你继续调试时,它将在抛出异常的确切行上中断。

enter image description here

答案 3 :(得分:7)

一切正常!

protected override void OnStart(string[] args)
{
    System.Diagnostics.Debugger.Launch();
}

答案 4 :(得分:5)

上述选项似乎无法在Windows 8上运行。

我添加了Thread.Sleep(15000);进入我的OnStart()方法并在代码的下一行设置断点。这让我在启动服务后将VS调试器附加到我的进程15秒,并允许我很好地调试OnStart()方法。

答案 5 :(得分:4)

您可以添加一行代码:

System.Diagnostics.Debugger.Break()

将弹出一个窗口,提示您选择要用于调试的调试器,例如允许您使用Visual Studio附加并进入代码。

请参阅:

http://msdn.microsoft.com/en-us/library/system.diagnostics.debugger.break.aspx

答案 6 :(得分:2)

可以将Windows配置项目设置为作为控制台应用程序运行的Windows服务,但可以使用Reflection访问服务方法。有关详细信息和示例,请参阅此处:http://ryan.kohn.ca/articles/how-to-debug-a-windows-service-in-csharp-using-reflection/

答案 7 :(得分:2)

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

System.Diagnostics.Debugger.Launch();

从弹出消息中选择Visual Studio选项。请记住以管理员身份运行Visual Studio。

注意:要仅在调试模式下使用它,可以使用#if DEBUG编译器指令,如下所示。这样可以防止生产服务器上的发布模式中的意外或调试。

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

答案 8 :(得分:2)

正如其他人所指出的那样,你必须在OnStart-Method中添加一个调试器中断:

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

同时以管理员身份启动VisualStudio,并允许其他用户自动调试进程(如here所述):

reg add "HKCR\AppID\{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

(我在这里也解释过:https://stackoverflow.com/a/35715389/5132456

答案 9 :(得分:1)

我知道这已经晚了,但这就是我们处理Windows服务的方法

首先创建一个充当服务的类。

添加适当的启动,停止,暂停等方法......

将一个Windows窗体添加到服务项目中。

在服务代码中创建上面创建的服务类,并在ServiceBase类中进行启动和停止服务所需的调用

打开Program.cs并添加以下内容

#if DEBUG
    [STAThread]
#endif
    static void Main()
    {
try
        {
#if DEBUG
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new DebugForm());
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
        { 
            new YourWindowsService() 
        };
            ServiceBase.Run(ServicesToRun);
#endif
        }
        catch (Exception e)
        {
            logger.Error(DateTime.Now.ToString() + " - " + e.Source + " - " + e.ToString() + "\r\n------------------------------------\r\n");
        }
}

当您在DEBUG模式下运行时,Windows窗体将启动。只需记住在完成后构建Release模式。当然,条件编译变量可以是你喜欢的任何东西。您甚至可以创建单独的项目,因此调试表单是它自己的项目。

希望这有帮助

答案 10 :(得分:1)

您还可以尝试 System.Diagnostics.Debugger.Launch()方法。它有助于将调试器指针指向指定的位置,然后您可以调试代码。

在此步骤之前,请使用Visual Studio命令提示符的命令行安装service.exe - installutil projectservice.exe

然后从控制面板 - &gt;启动您的服务管理工具 - &gt;计算机管理 - &gt;服务和应用 - &gt;服务 - &gt;您的服务名称

答案 11 :(得分:1)

如果在OnStart方法中添加Debugger.Launch()并且它不起作用,则可能会遇到同样的问题,即构造函数中发生异常,因此从未调用OnStart。 (头颅)

(对不起,如果这应该是对其他人的答案的评论,但我没有足够的信誉来发表评论)

答案 12 :(得分:0)

尝试在有问题的方法中添加Debugger.Break。当服务启动时,将抛出异常,寡妇应该提供使用visual studio进行调试。

答案 13 :(得分:0)

我通常有一个假装成SCM的控制台应用程序,例如调用Start,Stop然后我可以将F5插入到我的主要编码/调试目的,并在通过SCM安装和启动服务时使用Debugger.Break进行调试。

这意味着要开始做更多的工作,我有一个包含所有服务代码的类库,其中一个类公开了Windows服务类和控制台应用程序都可以调用的Start和Stop。

马特

答案 14 :(得分:0)

在我进入主题之前,我建议一下。如果您是服务器端开发人员,请始终使用日志。因为在Visual Studio中调试代码时可能无法生成某些特定条件。

回到主题,我使用Envoirnment.UserInteractive标志,这非常方便,请看下面的代码

public static void Main(string[] args)
{

    if (System.Environment.UserInteractive)
    {
        string parameter = string.Concat(args);

        switch (parameter)
        {
            case "--install":
                ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
                break;
            case "--uninstall":
                ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
                break;
            default:
                WindowsService service = new WindowsService();
                service.OnStart(args);
                Console.ReadKey();
                service.OnStop();
                break;
        }
    }
    else
    {
        ServiceBase.Run(new WindowsService());
    }
}

从visual studio中你将获得UserInteractive标志设置,所以我将它作为控制台应用程序运行,除此之外,即使你可以通过双击它并附加调试器来运行产品构建,如果你想测试它。

答案 15 :(得分:0)

我有一个有趣的方法,我添加另一个名为DebugNoService的配置

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugNoService|AnyCPU' ">
    <OutputPath>.\</OutputPath>
    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
    <BaseAddress>285212672</BaseAddress>
    <CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
    <ConfigurationOverrideFile>
    </ConfigurationOverrideFile>
    <DefineConstants>DEBUG;TRACE;DEBUGNOSERVICE</DefineConstants>
    <DocumentationFile>
    </DocumentationFile>
    <DebugSymbols>true</DebugSymbols>
    <FileAlignment>4096</FileAlignment>
    <NoStdLib>false</NoStdLib>
    <NoWarn>
    </NoWarn>
    <Optimize>false</Optimize>
    <RegisterForComInterop>false</RegisterForComInterop>
    <RemoveIntegerChecks>false</RemoveIntegerChecks>
    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
    <WarningLevel>4</WarningLevel>
    <DebugType>full</DebugType>
    <ErrorReport>prompt</ErrorReport>
    <UseVSHostingProcess>false</UseVSHostingProcess>
  </PropertyGroup>

我使用#if指令。 ProjectInstaller.cs

#if !DEBUGNOSERVICE    
   static void Main()
   {
      System.ServiceProcess.ServiceBase[] ServicesToRun;
      .....
   }
#endif

我添加了一个窗体,我还将窗体包装在

#if DEBUGNOSERVICE
...
static void Main() 
{
    Form     form;

    Application.EnableVisualStyles();
    Application.DoEvents();

    form = new <the name of the form>();

    Application.Run(form);
}
...
#endif

根据所选的配置,代码可以作为可以轻松调试的Windows窗体应用程序运行,也可以作为服务运行。

如果看似很多工作,但它始终有效,并使调试代码非常容易。您可以将各种输出添加到表单中,以便您可以观看它的运行。