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

评论

我创建了此用户语音票证。考虑投票:visualstudio.uservoice.com/forums/121579-visual-studio-ide/…

#1 楼

如果我想快速调试服务,只需在其中插入Debugger.Break()。当到达那条线时,它将使我回到VS。完成操作后,请不要忘记删除该行。
更新:作为#if DEBUG编译指示的替代方法,您还可以使用Conditional("DEBUG_SERVICE")属性。
[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

OnStart上,只需调用此方法:
public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

在那里,仅在Debug构建期间启用代码。在进行此操作时,为服务调试创建一个单独的构建配置可能会很有用。

评论


或者,您可以使用Debugger.Launch(),而必须在Systems.Diagnostics命名空间中包含using语句。

–奥马尔·库赫吉(Omar Kooheji)
08年9月24日在8:39



您的博客文章运行得很好,并节省了我的时间:)但是Debugger.Break()不适用于我。似乎由于某些与优化相关的原因,.Net跳过了DebugMode函数。

–Bizhan
2011年12月20日在1:09

当Debugger.Break()不起作用时,Debugger.Launch()对我有用。 (进程退出,代码为255。)

–奥利弗·博克(Oliver Bock)
2014年12月18日下午2:50

你们如何使它工作?没发生什么事。我已经尝试过Break()和Launch()。

– 4thSpace
16-3-22在17:05

@ 4thSpace:1.为您的服务创建一个安装程序,以便您可以安装服务。 2.添加一行Debugger.Launch();。在Main()的开头。 3.在调试模式下构建代码。 4.用debug-dll覆盖已安装的dll。 5.从Windows服务面板启动服务。现在出现一个弹出窗口,要求您附加到调试器。这种方式对我有用。希望对您也是如此。

–ffonz
16年4月13日在8:41

#2 楼

我还认为,为正常执行和作为服务使用单独的“版本”是可行的方法,但是是否真的需要为此目的专门使用单独的命令行开关?

您不能吗?只需执行以下操作即可:

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


具有“好处”,您可以通过双击启动应用程序(确定,如果确实需要),并且只需在Visual Studio中按F5键即可(无需修改项目设置以包含/console选项)。除了作为(非交互式)服务运行之外,它还会返回Environment.UserInteractive的任何其他原因?

评论


大!我之前使用“ if #debug”方法作为应用程序(如果进行调试)来启动,否则就作为服务来启动。如果您要调试应用程序,这将导致该应用程序不能作为服务运行,但是您的解决方案解决了这一问题,并使其可以在服务/应用程序和发行版/调试的所有四种组合中运行。

–乔纳斯(Jonas)
10 Mar 5 '10 at 13:39

如果您不希望该程序在双击时运行(用户可能会感到困惑并运行多个实例等),则可以使用System.Diagnostics.Debugger.IsAttached而不是Environment.UserInteractive。

–伯格胡子出局了
2010年5月17日15:22

但是除了作为(非交互式)服务运行之外,还有其他原因导致它返回false吗?我可以想到一个:不附在控制台上的计划任务。

– Hogan
2011-09-26 20:13



我在这种情况下使用命令行参数。 --install安装服务,--uninstall卸载服务,--interactive以应用程序运行服务。我在项目选项(调试>命令参数)中添加了--interactive。这样我就可以轻松地从VS进行调试。双击不会创建不必要的运行实例,因为需要--interactive。只是我的2美分。

–埃米尔·阿卡丁(EmirAkaydın)
2012-2-14在16:42

@EmirAkaydın是的,实际上我也具有命令行参数“ backup”。但是,我实际上希望在双击时有一个“交互式”实例,并且没有,并且出现有关无法以这种方式启动服务的错误消息。我猜目标是变化的;-)

– Christian.K
2012年2月16日上午9:36

#3 楼

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

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

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


这是所需的全部代码,但我还编写了演练并附有解释。

评论


这是ServiceBase []的良好扩展方法。我的解决方案中有多个服务,因此我没有为Program.cs提供通用的基类,而只是调用servicesToRun.RunInteractive(args)。不错的解决方案@Anders!

– David Keaveny
13年4月3日在23:13

确实是很好的解决方案。我按照David的建议为ServiceBase []创建了一个简单的扩展,该扩展允许只用一行代码运行服务:pastebin.com/F0fhhG2R

–有趣
13-4-26在3:41



+1我的一位前同事创建了一个“ EasyRunService”基类(该基类继承了ServiceProcess),该基类做了几乎相同的事情,但不需要反思(因为OnStart现在在基类中)。这确实使调试Windows服务变得轻而易举。

–sondergard
15年1月29日在13:40

@ Chazt3n确保您的项目输出类型设置为“控制台应用程序”。对于服务安装,选择哪种输出类型都没有关系,其行为相同。

–有趣
16年5月7日在1:52

仍然是一个很好的解决方案!我唯一要添加的内容(如本演练所示)是在尝试编译和运行之前,请确保您进入项目的属性并将输出类型更改为“控制台应用程序”。在项目属性->应用程序->输出类型->控制台应用程序中找到它。另外,为了使它对我来说正常工作,我最终不得不使用start命令运行该应用程序。例如:C:\“ my app name.exe” -service对我不起作用。相反,我使用C:\ start / wait“”“我的应用程序name.exe” -service

– Arvo Bowen
16年7月11日在17:43

#4 楼

有时候,分析服务启动过程中发生的事情很重要。附加到进程没有帮助,因为在启动服务时您不够快地附加调试器。

简单的答案是,我正在使用以下4行代码this:

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


这些被插入到服务的OnStart方法中,如下所示:那些以前没有做过的人,我在下面提供了详细的提示,因为您很容易陷入困境。以下提示引用的是Windows 7x64和Visual Studio 2010 Team Edition,但对其他环境也应有效。重要说明:以“手动”模式部署服务(使用VS命令提示符下的InstallUtil实用程序或运行您准备的服务安装程序项目)。在启动服务之前打开Visual Studio并加载包含服务源代码的解决方案-在Visual Studio中根据需要设置其他断点-然后通过“服务控制面板”启动该服务。

Debugger.Launch代码,这将导致对话框“ Servicename.exe中发生未处理的Microsoft .NET Framework异常”。出现。单击是,如屏幕快照所示调试Servicename.exe:

然后,尤其是在Windows 7 UAC中,可能会提示您输入管理员凭据。输入它们,然后选择是:



之后,将出现众所周知的Visual Studio实时调试器窗口。它询问您是否要使用指定的调试器进行调试。在单击“是”之前,请选择您不想打开新实例(第二个选项)-在这里新实例将无济于事,因为不会显示源代码。因此,请选择之前打开的Visual Studio实例:


单击“是”后,一段时间后,Visual Studio会在Debugger.Launch语句所在的行中向右显示黄色箭头,您可以调试代码(方法MyInitOnStart,其中包含初始化)。


按下F5会立即立即继续执行,直到到达您准备的下一个断点为止。所有。这样,您就可以在正确启动服务并完成调试启动代码之后,运行与服务进行通信的客户端。如果按Shift + F5(停止调试),将终止服务。而不是这样做,您应该使用服务控制面板将其停止。

请注意,如果您构建发行版,则调试代码会自动生成已删除,该服务正常运行。
我正在使用Debugger.Launch(),它将启动并附加调试器。我还测试了Debugger.Break(),该方法不起作用,因为在服务启动时还没有连接调试器(导致“错误1067:进程意外终止。”)。服务的启动(它不会延迟代码本身,但会立即继续执行RequestAdditionalTime语句)。否则,如果您没有从调试器足够快地调用Debugger.Launch,则启动服务的默认超时时间太短,并且启动服务将失败。实际上,超时10分钟可以避免在调试器启动后立即看到“服务未响应...”消息。
一旦习惯了,此方法非常简单,因为它只需要您可以在现有服务代码中添加4行,从而使您能够快速获得控制和调试。


评论


出于好奇,您是否知道与Debugger.Launch()用户提示的用户交互是否超时?

– Shiv
2014年11月25日下午4:54

如上所述,如果未在该时间段内调用base.OnStart(args),base.RequestAdditionalTime(600000)将阻止服务控件终止该服务10分钟。除此之外,我记得如果过一会儿不输入管理员凭据,UAC也会中止(我不知道确切的秒数,但是我认为您必须在一分钟内输入,否则UAC会中止) ,这将终止调试会话。

–马特
2014年11月25日9:02



我发现这是调试CustomCommand消息的最佳方法。 +1。

–贾斯汀
16年7月19日在13:36

#5 楼

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

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


评论


我也曾经使用过这种方法,但是我认为将其与上面的答案结合使用是一种享受。

–RobS
09年2月24日在4:55

#6 楼

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

此处是video ...在Program.cs文件中,添加Debug部分的内容...

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。这将允许在条件为true时执行下面的debug部分br />它将像控制台应用程序一样运行,一旦一切正常,您可以更改模式DEBUG,常规Release部分将触发逻辑

评论


我一直在寻找这个答案,不知道为什么它排名如此之低。解释了代码以帮助他人或可能有更多评论;)

– Vinod Srivastav
18-10-22在12:49

#7 楼

UPDATE

到目前为止,这种方法最简单:

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

为了后代,我在下面留下了原始答案。

我们重新创建了该类,并在服务启动期间调用StartEventLoop()。 (该类也可以很容易地从控制台应用程序中使用。)

此设计的一个不错的副作用是,用于设置Timer的参数可以用于延迟设置之前服务实际上开始工作,因此您有时间手动附加调试器。


ps如何手动将调试器附加到正在运行的进程...?有条件的编译器[#if]标志可帮助避免在Release版本中触发)。

我停止这样做是因为有时我们会忘记在Release中进行构建,而调试器在客户端演示上运行的应用程序(令人尴尬!)。

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");
    }
}


评论


//做某件事需要30分钟以上才能完成?

– Vinod Srivastav
18-10-22在12:55

#8 楼


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
}


评论


[对不起,代码没有解释-降价问题]应该在调试版本中从MS Visual Studio(F5)正常运行。仍在发行版中作为常规服务运行。

–托马斯·布拉特(Thomas Bratt)
08-09-29在21:48

将此与Christian K.的上述解决方案结合使用,以使用“ Environment.UserInteractive”属性,该解决方案确实干净简单。

–本·罗宾斯
10 Mar 25 '10在10:03

OnStart受保护,您不能修改访问级别:(

–爱德华·卢卡(Eduard Luca)
13年8月24日在20:20

#9 楼

您也可以通过命令提示符(sc.exe)启动服务。 ,更改为作为服务运行。

#10 楼

我曾经做过的事情是拥有一个命令行开关,它将以服务或常规应用程序的形式启动程序。然后,在我的IDE中设置开关,以便逐步浏览代码。

使用某些语言,您实际上可以检测它是否在IDE中运行,并自动执行此开关。 >
您使用什么语言?

#11 楼

使用TopShelf库。

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

studio。

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

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

评论


他们的许可过于混乱,难以理解

– Alex Gordon
16年5月15日在20:56

他们使用Apache License afaik。 Topshelf是我用来开发和调试Windows服务的最简单方法。超级好用。开发为控制台应用程序。使用一个命令行开关作为服务安装。强烈推荐。

–抢劫
18年1月23日在2:46

TopShelf节省了我很多时间。谢谢

– L_7337
19年5月7日在18:43

#12 楼

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

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


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

评论


GFlags +1。如果您无法修改源代码(或者您没有源代码),这尤其有用。

–克里斯·吉伦(Chris Gillum)
09年12月29日在3:09

#13 楼

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

我通过在同一项目中创建第二个服务来进行调试来实现此目的。调试服务如常启动时(即在服务MMC插件中),将创建服务主机进程。即使您尚未启动真正的服务,这也可以为您提供一个将调试器附加到其中的过程。将调试器附加到进程后,启动您的真实服务,您可以在服务生命周期中的任何位置进行破解,包括OnStart()。

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

详细信息:

1 )假设您要实现MyService,还请创建MyServiceDebug。将两者都添加到ServiceBase中的Program.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)将真实服务和调试服务添加到服务项目的项目安装程序中:

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

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

5)启动真正的服务并享受调试的乐趣。

#14 楼

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

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

我使用这种方法只是记得必须测试所有在真实服务中运行时的代码,而命令行工具是一个不错的调试工具,它是一个不同的环境,它的行为与真实服务并不完全相同。

#15 楼

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

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


评论


直到您必须调试服务特定的问题。

–嬉皮士
08年9月24日在8:19

是的,那么您必须将调试器附加到实际的服务过程。但是在大多数情况下,错误都会以两种方式出现,并且开发容易得多。

–莫里斯
08年9月24日在8:24

#16 楼

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

#17 楼

要调试Windows服务,我将GFlags和regedit创建的.reg文件结合在一起。


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


评论


在Win 7 / Win 2008上仍然可以使用吗?它是来自support.microsoft.com/kb/824344的方法,但它依赖于交互式服务,我认为它们被杀死了吗?一直以来,它一直是我的首选(因为启动问题可能会在生产中出现,因此在代码中插入Debugger.Break()可能不是一个选择)。

–piers7
2010-4-9 13:20

#18 楼

这是我用来测试服务的简单方法,没有任何其他“调试”方法,并且带有集成的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 });
}


#19 楼

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

在服务启动时,我检查命令行参数“ / debug”。如果使用此参数调用该服务,则不会执行通常的服务启动,而是启动所有侦听器,仅显示一个消息框“调试正在进行中,按ok结束”。如果我的服务以通常的方式启动,它将作为服务启动,如果它是使用命令行参数/ debug启动的,则它将像普通程序一样工作。

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

这样,我可以轻松调试大多数小型问题。当然,仍然需要将某些东西调试为服务,但对于99%的用户来说,这已经足够了。

#20 楼

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


#21 楼

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

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


#22 楼

要对现有Windows Service程序进行故障排除,请按照其他人的建议使用'Debugger.Break()'。

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

#23 楼

只需将调试器的午餐放在任何地方,然后在启动时附加Visualstudio即可。

不同的用户(如此处所述):

#if DEBUG
    Debugger.Launch();
#endif


#24 楼

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

自动检测到控制台/服务模式,自动安装/卸载您的服务以及一些最常用的功能。

#25 楼

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
    }
}


评论


这比较容易。只需将解决方案配置设置更改为调试,运行项目/解决方案,随时添加断点即可。

–巴哈马特
18年8月30日在3:59

#26 楼

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


创建日志文件:个人而言,我更喜欢像文本文件这样的单独日志文件,而不是使用应用程序日志或事件日志。但这会花费您大量时间代表很多时间,因为仍然很难弄清确切的错误位置在哪里
将应用程序转换为控制台应用程序:这将为您提供我们可以在VS中使用的所有调试工具。

,请参阅我为此主题创建的博客文章。

#27 楼

只需粘贴

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();

#28 楼

最好的选择是使用'System.Diagnostics'命名空间。 br />
#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