调试 Windows 服务的更简单方法

Easier way to debug a Windows service

提问人: 提问时间:9/24/2008 最后编辑:7 revs, 6 users 50%Mats 更新时间:11/17/2023 访问量:288857

问:

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

C# .NET 调试 Windows-services

评论

0赞 David 9/11/2018
我创建了此用户语音票证。考虑投票支持它:visualstudio.uservoice.com/forums/121579-visual-studio-ide/......
0赞 MensSana 8/17/2022
有一个 GitHub 项目(虽然是法语),它作为基本的自安装和可调试服务来做到这一点(以及更多)。当需要作为控制台应用程序时,它还处理 UAC:GitHub 项目

答:

10赞 akauppi 9/24/2008 #1

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

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

10赞 RB. 9/24/2008 #2

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

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

你用的是什么语言?

40赞 Paul van Brenk 9/24/2008 #3

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

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

评论

1赞 RobS 2/24/2009
我以前也使用过这种方法,但我认为将这种方法与上面的答案结合起来是一种享受。
8赞 RichS 9/24/2008 #4

我认为这取决于您使用的操作系统,由于会话之间的分离,Vista 更难附加到服务。

我过去使用的两个选项是:

  • 使用 GFlags(在 Windows 调试工具中)为进程设置永久调试器。它存在于“图像文件执行选项”注册表项中,非常有用。我认为您需要调整服务设置以启用“与桌面交互”。我将其用于所有类型的调试,而不仅仅是服务。
  • 另一种选择是将代码分开一点,以便服务部分可以与正常应用启动互换。这样,您可以使用简单的命令行标志,并作为进程(而不是服务)启动,这使得调试变得更加容易。

希望这会有所帮助。

评论

0赞 Chris Gillum 12/29/2009
+1 表示 GFlags。如果您无法修改源代码(或者您没有源代码),这将特别有用。
1赞 Sam 9/24/2008 #5

对于常规的小东西编程,我做了一个非常简单的技巧来轻松调试我的服务:

在服务启动时,我检查命令行参数“/debug”。如果使用此参数调用服务,我不会执行通常的服务启动,而是启动所有侦听器,并仅显示一个消息框“调试正在进行中,按确定结束”。

因此,如果我的服务以通常的方式启动,它将作为服务启动,如果它使用命令行参数 /debug 启动,它将像普通程序一样运行。

在 VS 中,我只需添加 /debug 作为调试参数并直接启动服务程序。

这样我就可以很容易地调试大多数小问题。当然,有些东西仍然需要作为服务进行调试,但对于 99% 来说,这已经足够了。

5赞 Nir 9/24/2008 #6

当我编写服务时,我将所有服务逻辑放在一个 dll 项目中,并创建两个调用此 dll 的“主机”,一个是 Windows 服务,另一个是命令行应用程序。

我使用命令行应用程序进行调试,并将调试器附加到实际服务,仅针对我无法在命令行应用程序中重现的错误。

我使用这种方法,请记住,在实际服务中运行时必须测试所有代码,虽然命令行工具是一个很好的调试辅助工具,但它是一个不同的环境,它的行为与实际服务不完全相同。

4赞 Maurice 9/24/2008 #7

在开发和调试 Windows 服务时,我通常通过添加 /console 启动参数并检查此参数将其作为控制台应用程序运行。让生活更轻松。

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}

评论

0赞 leppie 9/24/2008
直到您必须调试特定于服务的问题。
0赞 Maurice 9/24/2008
如果为 true,则必须将调试器附加到实际服务进程。但在大多数情况下,无论哪种方式,错误都会出现,开发要容易得多。
4赞 leppie 9/24/2008 #8

第一行的 Debugger.Break() 怎么样?

279赞 jop 9/24/2008 #9

如果我想快速调试服务,我只需插入其中即可。当到达该行时,它会让我回到 VS。完成后不要忘记删除该行。Debugger.Break()

更新:作为编译指示的替代方法,您还可以使用 attribute。#if DEBUGConditional("DEBUG_SERVICE")

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

在你的 上,只需调用这个方法:OnStart

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

在那里,代码将仅在调试生成期间启用。在执行此操作时,为服务调试创建单独的生成配置可能很有用。

评论

49赞 Omar Kooheji 9/24/2008
或者,您可以使用 Debugger.Launch(),您必须为 Systems.Diagnostics 命名空间包含一个 using 语句。
1赞 Bizhan 12/20/2011
您的博客文章运行良好,挽救了我的一天:)但是 Debugger.Break() 对我不起作用。似乎由于一些与优化相关的原因,.Net 跳过了 DebugMode 函数。
3赞 Oliver Bock 12/18/2014
当 Debugger.Break() 不起作用时,Debugger.Launch() 对我有用。(进程退出,代码为 255。
1赞 4thSpace 3/23/2016
你们是怎么做到的?什么也没发生。我尝试过 Break() 和 Launch()。
17赞 ffonz 4/13/2016
@4thSpace: 1.为服务创建安装程序,以便安装服务。2. 添加行 Debugger.Launch();在 Main() 的开头。3. 在调试模式下构建代码。4. 用debug-dll覆盖已安装的dll。5. 从 Windows 服务面板启动服务。现在,将显示一个弹出窗口,要求您附加到调试器。这种方式对我有用。希望对你也有好处。
15赞 rohancragg 9/24/2008 #10

更新

这种方法是迄今为止最简单的方法:

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

我把我原来的答案留给后人。


我的服务往往有一个封装计时器的类,因为我希望服务定期检查是否有任何工作要做。

我们新建该类,并在服务启动期间调用 StartEventLoop()。(此类也可以从控制台应用轻松使用。

这种设计的好副作用是,用于设置计时器的参数可用于在服务实际开始工作之前有延迟,以便您有时间手动附加调试器。

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] 标志来帮助避免它在发布版本中触发)。

我不再这样做,因为有时我们会忘记在 Release 中构建,并在客户端演示上运行的应用程序中出现调试器中断(令人尴尬!

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif

评论

0赞 Vinod Srivastav 10/22/2018
当完成时间超过 30 分钟时会发生什么?// do something
224赞 Christian.K 9/24/2008 #11

我还认为有一个单独的“版本”用于正常执行和作为服务是要走的路,但真的需要为此目的专用一个单独的命令行开关吗?

你不能做:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

这将有“好处”,你可以通过双击启动你的应用程序(好吧,如果你真的需要的话),你可以在Visual Studio中点击(不需要修改项目设置来包含该选项)。F5/console

从技术上讲,检查是否为当前窗口站设置了 Flag,但除了作为(非交互式)服务运行之外,还有其他原因会返回吗?Environment.UserInteractiveWSF_VISIBLEfalse

评论

0赞 Jonas 3/5/2010
伟大!我之前使用“if #debug”方法在调试时作为应用程序启动,否则作为服务启动。这会导致应用在想要调试时无法作为服务运行,但你的解决方案解决了这个问题,并让它可以在服务/应用和发布/调试的所有四种组合中运行。
32赞 Blorgbeard 5/17/2010
如果您不希望程序在双击时运行(用户可能会感到困惑并运行多个实例等),则可以使用 代替 .System.Diagnostics.Debugger.IsAttachedEnvironment.UserInteractive
5赞 Hogan 9/27/2011
但是,除了作为(非交互式)服务运行之外,还有其他原因会返回 false 吗?我能想到一个:不附加到控制台的计划任务。
7赞 Emir Akaydın 2/15/2012
在这种情况下,我使用命令行参数。--install 用于安装服务,--uninstall 用于卸载服务,--interactive 用于将服务作为应用程序运行。我将 --interactive 添加到项目选项(调试>命令参数)。所以我可以轻松地从 VS 进行调试。双击不会创建不需要的正在运行的实例,因为 --interactive 是必需的。只有我的 2 美分。
0赞 Christian.K 2/16/2012
@EmirAkayd是的,实际上我也有命令行参数作为“备份”。但是,我实际上希望在双击时有一个“交互式”实例,而不是关于无法以这种方式启动服务的错误消息。我猜有不同的目标;-)
2赞 janv8000 9/24/2008 #12

为了调试 Windows 服务,我将 GFlags 和 regedit 创建的 .reg 文件结合起来。

  1. 运行 GFlags,指定 exe-name 和 vsjitdebugger
  2. 运行 regedit 并转到 GFlags 设置其选项的位置
  3. 从文件菜单中选择“导出密钥”
  4. 将该文件保存在扩展名为 .reg 的某个位置
  5. 任何时候要调试服务:双击 .reg 文件
  6. 如果要停止调试,请双击第二个 .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"

评论

1赞 piers7 4/9/2010
这在 Win 7 / Win 2008 上仍然有效吗?这是 support.microsoft.com/kb/824344 的方法,但它依赖于交互式服务,我以为他们被扼杀了?它曾经是我的首选选项(因为在生产环境中可能会出现启动问题,在代码中插入 Debugger.Break() 可能不是一个选项)。
13赞 Thomas Bratt 9/30/2008 #13

static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}

评论

0赞 Thomas Bratt 9/30/2008
[对不起,代码没有解释 - markdown 问题]应该在调试版本中从 MS Visual Studio (F5) 正常运行。在发布版本中仍作为正常服务运行。
0赞 Ben Robbins 3/25/2010
将它与上面由 Christian K. 提出的解决方案相结合,使用“Environment.UserInteractive”属性,该解决方案非常干净和简单。
0赞 Eduard Luca 8/25/2013
OnStart是,则无法修改访问级别:(protected
1赞 Ervin Ter #14
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif
1赞 nh99 #15

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

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

我希望能够调试服务的各个方面,包括 OnStart() 中的任何初始化,同时仍然在 SCM 的框架内以完整的服务行为执行它......没有“控制台”或“应用程序”模式。

为此,我在同一项目中创建了第二个服务,用于调试。调试服务在照常启动时(即在服务 MMC 插件中)创建服务主机进程。这为你提供了一个将调试器附加到的过程,即使你尚未启动实际服务也是如此。将调试器附加到进程后,启动实际服务,可以在服务生命周期中的任何位置(包括 OnStart())中断它。

由于调试服务需要非常少的代码侵入,因此可以很容易地包含在服务设置项目中,并且可以通过注释掉一行代码并删除单个项目安装程序来轻松地从生产版本中删除。

详:

1)假设你正在实现,也创建.将两者添加到数组中,如下所示:MyServiceMyServiceDebugServiceBaseProgram.cs

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2) 将 real service 和 debug service 添加到服务项目的工程安装程序中:

enter image description here

将服务项目输出添加到服务的安装项目时,将包含这两个服务(real 和 debug)。安装后,这两个服务都将出现在 service.msc MMC 插件中。

3) 在 MMC 中启动调试服务。

4) 在 Visual Studio 中,将调试器附加到调试服务启动的进程。

5)启动实际服务,享受调试。

141赞 Anders Abel #17

几周前,当我建立一个新的服务项目时,我发现了这篇文章。虽然有很多很好的建议,但我仍然没有找到我想要的解决方案:可以在不对服务类进行任何修改的情况下调用服务类和方法。OnStartOnStop

我提出的解决方案使用选择运行模式,正如本文的其他答案所建议的那样。Environment.Interactive

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

帮助程序使用反射来调用受保护的方法:RunInteractiveOnStartOnStop

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

这是所需的所有代码,但我也编写了带有解释的演练

评论

5赞 Funbit 4/26/2013
确实是很好的解决方案。正如 David 建议的那样,我为 ServiceBase[] 创建了一个简单的扩展,它允许在一行代码中运行服务: pastebin.com/F0fhhG2R
4赞 sondergard 1/29/2015
+1 我的一位前同事创建了一个“EasyRunService”基类(它继承了 ServiceProcess),它做了几乎相同的事情,但不需要反射(因为 OnStart 现在在基类中)。它确实使调试 Windows 服务变得轻而易举。
3赞 Funbit 5/7/2016
@Chazt3n 确保项目输出类型设置为“控制台应用程序”。至于服务安装,无论选择哪种输出类型,行为都是一样的。
5赞 Arvo Bowen 7/12/2016
仍然是一个很好的解决方案!我唯一要添加的(如 所示)是确保在尝试编译和运行之前进入项目的属性并将输出类型更改为。在 找到它。此外,为了使它为我正常工作,我最终不得不使用命令运行应用程序。例如:对我不起作用。取而代之的是,我用了walk throughConsole ApplicationProject Properties -> Application -> Output type -> Console ApplicationstartC:\"my app name.exe" -serviceC:\start /wait "" "my app name.exe" -service
1赞 Connor Goddard 12/19/2016
+1 这是一个很好的解决方案(Windows 服务 - .NET 4.0)。根据他上面的评论,向@Funbit大声疾呼他的 ServiceBase 扩展方法!
60赞 Matt 10/8/2012 #18

有时,分析服务启动期间发生的情况很重要。附加到进程在这里没有帮助,因为在服务启动时,你不够快,无法附加调试器。


简短的回答是,我使用以下 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 7x64Visual Studio 2010 Team Edition,但也应该适用于其他(较新)环境。


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

由于代码的原因,这将导致出现一个对话框“服务名称 .exe 中发生未经处理的 Microsoft .NET Framework 异常”。单击Elevate是,调试服务名称 .exe”,如屏幕截图所示:Debugger.Launch
FrameworkException


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

UACPrompt

之后,将显示众所周知的 Visual Studio 实时调试器窗口。它会询问你是否要使用已选择的调试器进行调试。在单击“”之前,请选择不想打开新实例(第二个选项) - 新实例在此处没有帮助,因为不会显示源代码。因此,请改为选择之前打开的 Visual Studio 实例:
VSDebuggerPrompt

单击“”后,一段时间后,Visual Studio 将在语句所在的行中显示黄色箭头,并且你可以调试代码(方法,其中包含初始化)。Debugger.LaunchMyInitOnStartVSDebuggerBreakpoint

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

提示:若要使服务保持运行,请选择“调试”->“全部分离”。这样,你就可以在客户端正确启动并完成启动代码调试后运行与服务通信的客户端。如果按 Shift+F5(停止调试),这将终止服务。您应该使用服务控制面板来停止它,而不是这样做。



请注意

  • 如果生成 Release,则会自动删除调试代码并且服务将正常运行。

  • 我正在使用 Debugger.Launch(),启动并附加调试器。我也测试了 Debugger.Break(),不起作用,因为在服务启动时还没有附加调试器(导致“错误 1067:进程意外终止”)。

  • RequestAdditionalTime 为服务的启动设置更长的超时时间(它不会延迟代码本身,但会立即继续执行语句)。否则,启动服务的默认超时时间太短,如果从调试器调用的速度不够快,则启动服务将失败。实际上,10 分钟的超时可避免在调试器启动后立即看到消息“服务未响应...”。Debugger.Launchbase.Onstart(args)

  • 一旦你习惯了它,这个方法就非常简单了,因为它只需要你在现有的服务代码中添加 4 行,让你快速获得控制和调试。

评论

1赞 Shiv 11/25/2014
出于好奇,您知道用户与 Debugger.Launch() 用户提示符的交互是否超时吗?
1赞 Matt 11/25/2014
如前所述,如果服务控制未在该时间跨度内调用,则将阻止服务控制终止服务 10 分钟。除此之外,我记得如果您在一段时间后不输入管理员凭据,UAC 也会中止(我不知道具体有多少秒,但我认为您必须在一分钟内输入它,否则 UAC 中止),这将终止调试会话。base.RequestAdditionalTime(600000)base.OnStart(args)
2赞 Justin 7/19/2016
我发现这是调试 CustomCommand 消息的最佳方法。+1.
11赞 Misterhex #19

使用 TopShelf 库。

创建一个控制台应用程序,然后在 Main 中配置设置

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

若要调试服务,只需在 Visual Studio 中按 F5 即可。

要安装服务,请键入 cmd “console.exe install”

然后,您可以在 Windows 服务管理器中启动和停止服务。

评论

0赞 Alex Gordon 5/16/2016
他们的许可太混乱了,无法理解
0赞 robs 1/23/2018
他们使用 Apache 许可证 afaik。Topshelf 是我用来开发和调试 Windows 服务的最简单方法。超级好用。开发为控制台应用程序。使用一个命令行开关作为服务进行安装。强烈推荐。
0赞 L_7337 5/8/2019
TopShelf 为我节省了大量时间。感谢
0赞 kenny 11/17/2023
TopsShelf很棒,也是提供服务的唯一方法;)
32赞 3 revs, 2 users 81%Jason Miller #20

Fabio Scopel 的这个 YouTube 视频很好地解释了如何调试 Windows 服务......实际的方法从视频中的 4:45 开始......

这是视频中解释的代码...在 Program.cs 文件中,为“调试”部分添加内容...

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()
    {
    }

运作方式

基本上,您必须创建一个调用 因为它是受保护的,并且无法在外部访问。该程序添加了带有 的预处理器。public void OnDebug()OnStart(string[] args)void Main()#if#DEBUG

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

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

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

评论

0赞 Vinod Srivastav 10/22/2018
我一直在寻找这个答案,不知道为什么它的排名如此之低。解释了代码以帮助其他人或可能更多的评论;)
1赞 Yang You #21

要对现有 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

0赞 Sandaru #22

有两个选项可以进行调试。

  1. 创建日志文件:就我个人而言,我更喜欢单独的日志文件,如文本文件,而不是使用应用程序日志或事件日志。但这会花费你很多时间,因为仍然很难弄清楚我们的确切错误位置在哪里
  2. 将应用程序转换为控制台应用程序 :这将使您能够在 VS 中使用的所有调试工具。

请参阅我为该主题创建的这篇博文。

2赞 2 revs, 2 users 92%wotanii #23

只需将调试器启动放在任何位置,并在启动时附加 Visualstudio

#if DEBUG
    Debugger.Launch();
#endif

此外,您需要以管理员身份启动 VS,并且需要允许进程可以由其他用户自动调试(如此处所述):

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

使用 Windows 服务模板 C# 项目创建新的服务应用 https://github.com/HarpyWar/windows-service-template

有自动检测的控制台/服务模式,服务的自动安装程序/卸载程序以及几个最常用的功能。

2赞 2 revsMisterDr #25

这是我用来测试服务的简单方法,没有任何其他“调试”方法,并且集成了 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 });
}
1赞 2 revs, 2 users 77%Chutipong Roobklom #26

只需粘贴

Debugger.Break();

代码中的任何位置。

例如

internal static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    private static void Main()
    {
        Debugger.Break();
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

当您运行程序时,它会命中。Debugger.Break();

1赞 3 revs, 3 users 83%Mansoor #27
static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}

评论

0赞 Bahamut 8/30/2018
这更容易。只需将解决方案配置设置更改为“调试”,运行项目/解决方案,随时添加断点。
0赞 Amey P Naik #28

最好的选择是使用“System.Diagnostics”命名空间。

将代码包含在调试模式和发布模式的 if else 块中,如下所示,以便在 Visual Studio 中的调试和发布模式之间切换。

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif
0赞 2 revssingh_v #29

我能够按照 Microsoft - https://learn.microsoft.com/en-us/dotnet/framework/windows-services/how-to-debug-windows-service-applications#how-to-run-a-windows-service-as-a-console-application 的官方文档轻松调试 Windows 服务。

它指示将 Windows 服务作为控制台应用运行以进行调试。

0赞 sarawgeek #30

有时,您的服务无法启动,甚至在调用 OnStart 方法之前中断,如果您编写了自定义服务,则可能会发生这种情况。 在这种情况下,我们需要找出导致问题的原因。

在这种情况下,我们需要一些额外的时间将我们的 Visual Studio 附加到进程(应用程序),所以我们可以做的是在 Main 方法中添加延迟,然后附加服务。

您可能知道 Main 方法在 OnStart() 之前被调用。

只需添加

static void Main()
{
    // add this as first thing
    Task.Delay(10000).Wait()
}

这将在执行任何操作之前增加 10 秒的延迟。

有时,Windows服务在作为控制台应用程序运行时可能工作正常,但是在从服务运行时,它可能存在一些问题,我们可以通过这种方式找到这些问题。