【问题标题】:How to debug the .NET Windows Service OnStart method?如何调试 .NET Windows Service OnStart 方法?
【发布时间】:2010-11-14 20:31:56
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这是一模一样的副本。现在找不到原版了答案是一样的。

标签: c# .net debugging


【解决方案1】:

正如其他人指出的那样,您必须在 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)

【讨论】:

    【解决方案2】:

    我有一个有趣的方法,我添加了另一个名为 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
    

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

    #if DEBUGNOSERVICE
    ...
    static void Main() 
    {
        Form     form;
    
        Application.EnableVisualStyles();
        Application.DoEvents();
    
        form = new <the name of the form>();
    
        Application.Run(form);
    }
    ...
    #endif
    

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

    If 看起来工作量很大,但它始终有效,并且使调试代码变得非常非常容易。您可以将各种输出添加到表单中,以便观看它的运行。

    【讨论】:

      【解决方案3】:

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

      System.Diagnostics.Debugger.Launch();
      

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

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

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

      【讨论】:

        【解决方案4】:

        在我进入主题之前,请给我一个建议。如果您是服务器端开发人员,请始终特别使用日志。因为在 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 标志,因此我会将其作为控制台应用程序运行,除此之外,如果您想对其进行测试,您甚至可以通过双击它并附加调试器来运行产品构建。

        【讨论】:

          【解决方案5】:

          如果您在 OnStart 方法中添加 Debugger.Launch() 并且它不起作用,您可能会遇到与我相同的问题,即异常发生在构造函数中,因此永远不会调用 OnStart。 (拍脑袋)

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

          【讨论】:

            【解决方案6】:

            一旦您拥有使用installutil.exe 安装的服务,您可以更改Start Parameters 以在服务启动时跳转到调试器:

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

            要支持此功能,请更改服务的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 打开异常。当您继续调试时,它将在引发异常的确切行中断。

            【讨论】:

            • 我喜欢命令行 arg 的想法,我将开始使用它。由于 [-d]ebugWithVisualStudio 包含“-d”,因此您检查 args Contains("-d") 的条件应该足以满足这两项检查的要求,因此您的条件中的 OR 不是必需的。谢谢!
            • @Adam 你是对的,可以通过删除“-debugWithVisualStudio”行来优化代码。但是,这行代码的目的是为了代码清晰,因此任何其他程序员都可以立即清楚代码的意图。我通常会添加所有命令行参数的两个版本 - 一个简短版本,以及一个详细解释其功能的详细版本。
            • @Adam Plocher 我认为对集合的 Contains() 调用正在寻找集合是否包含那些确切的字符串。
            【解决方案7】:

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

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

            然后从控制面板 -> 管理工具 -> 计算机管理 -> 服务和应用程序 -> 服务 -> 您的服务名称开始您的服务

            【讨论】:

              【解决方案8】:

              我知道这已经晚了,但这就是我们处理调试 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");
                      }
              }
              

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

              希望对你有帮助

              【讨论】:

                【解决方案9】:

                上述选项似乎不适用于 Windows 8。

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

                【讨论】:

                • 相同 - 在 Windows 8 下以编程方式附加调试器似乎不起作用:/
                • 在 Windows 8.1 中确实如此。 Visual Studio 2013 专业版
                【解决方案10】:

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

                【讨论】:

                  【解决方案11】:

                  效果很好!

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

                  【讨论】:

                  • 我按照你在 OnStart 中所说的添加了这段代码,但是当我仍然无法调试我的服务时,我正在使用 VS 2013,你能帮帮我吗?
                  【解决方案12】:

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

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

                  马特

                  【讨论】:

                    【解决方案13】:

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

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

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

                    【讨论】:

                      【解决方案14】:

                      尝试在有问题的方法中添加 Debugger.Break。当服务启动时,将引发异常,寡妇应提供使用 Visual Studio 对其进行调试。

                      【讨论】:

                        【解决方案15】:

                        你可以像这样添加一行代码:

                        System.Diagnostics.Debugger.Break()
                        

                        这将弹出一个窗口,提示您选择使用哪个调试器进行调试,例如允许您附加 Visual Studio 并单步执行代码。

                        见:

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

                        【讨论】:

                          【解决方案16】:

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

                          System.Diagnostics.Debugger.Launch()
                          

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

                          【讨论】:

                          • 这对我不起作用。我得到的错误是 xxxxService.exe 中 0x7c812aeb 处的未处理异常:0xC06D007E:找不到模块,并且从未到达断点。然而,这与我在没有包含 debugger.Launch 代码的情况下得到的错误完全不同。
                          • 您是否在运行服务的机器上安装了 Visual Studio?
                          • 是的,我的机器上安装了 Visual Studio 2005 Professional 和 Visual Studio 2008 Professional。
                          • 嗯....运行的二进制文件是在调试模式下编译的吗?我以前从未见过这样行不通。
                          • 我将 Debugger.Launch 放在 main 方法中,现在我可以单步执行代码了。谢谢!
                          猜你喜欢
                          • 1970-01-01
                          • 2019-09-12
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多