是有一个简单的方法来步骤通过代码于启动服务过的Windows服务的控制管理中心,然后附的调试器螺纹?这是一种烦琐的和我想知道,如果有一个更简单的方法。

有帮助吗?

解决方案

如果我想快速调试服务,我只需在其中输入 Debugger.Break()。当达到那条线时,它会让我回到VS.完成后别忘了删除该行。

UPDATE:作为 #if DEBUG pragma的替代方法,您还可以使用条件(“DEBUG_SERVICE”)属性。

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

OnStart 上,只需调用此方法:

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

在那里,代码只会在Debug构建期间启用。在使用它时,为服务调试创建单独的Build Configuration可能很有用。

其他提示

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

你不能这样做:

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

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

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

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

我提出的解决方案使用 Environment.Interactive 选择运行模式,正如此帖的其他答案所示。

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

RunInteractive 帮助器使用反射来调用受保护的 OnStart OnStop 方法:

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

这是所有必需的代码,但我也写了演练和解释。

有时候,重要的是要分析什么 在开始的服务。 附加的过程不会帮助在这里,因为你不够快将调试器,而服务的开始。

简短的回答是,我用下 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);
}

对于那些还没有这样做之前,我已经包含了 详细的提示下, 因为你可以很容易地得到坚持。下面的提示,请参阅 Windows7x64Visual Studio2010年版团队, 但应该是有效的,对于其他环境,也是。


重要的是: 部署该服务 "手册"模式 (使用 InstallUtil 用从VS示命令或运行服务安装项目中,你已经准备).打开Visual Studio 之前 你开始服务,并负荷的解决方案包含服务的源代码设立额外的断点因为你需要他们在Visual Studio-然后开始的服务通过 业务控制面板。

Debugger.Launch 代码,这将导致对话"未经处理的微软。净框架中出现异常在 Servicename.exe."出现。点击 Elevate 是的,调试 Servicename.exe 如图所示:
FrameworkException

之后,escpecially在Windows7UAC可能促使你进入管理的全权证书。进入他们继续 是的:

UACPrompt

在这之后,众所周知的 Visual Studio只是时间的调试器窗口 出现。它要求你如果你想要调试使用delected调试器。 你点击之前 是的, 选择你 不要打开一个新的实例 (2项)-一个新的实例就不会有用的这里,因为来源的代码不会显示出来。所以你选择Visual Studio实例,你打开前面而不是:VSDebuggerPrompt

之后你有点击 是的, 一段时间后Visual Studio将显示黄色的箭头正在线 Debugger.Launch 声明的是,你能够调试你的代码(方法 MyInitOnStart, ,其中包含你的初始化)。VSDebuggerBreakpoint

按压 F5 继续执行立即, 直到下一个断点你已经准备就达到了。

提示: 保持服务的运行,选择 调试->所有分离.这让你跑的一个客户与服务后,它开始是否正确和完调试启动代码。如果你按 移位+F5 (停止调试),这将终止服务。而不是做这个,你应该使用 业务控制面板 停止它。

注意到

  • 如果你建立一个 释放, 那么 "调试"的代码被自动删除, 和服务运行正常。

  • 我使用 Debugger.Launch(), , 开始重视一个调试器.我已经测试 Debugger.Break() 好,这 没有工作, 因为没有附加调试器上启动的服务(引起的 "错误1067:该流程意外终止。").

  • RequestAdditionalTime 组长 超时启动的服务 (它是 拖延的代码本身,而是将立即继续 Debugger.Launch 发言).否则默认的超时开始的服务是太短,并开始服务失败如果你不打电话 base.Onstart(args) 足够快的速度从调试器。实际上,一个超时10分钟,避免了,你看到的消息"服务没有回应..." 后立即程序开始。

  • 一旦你得到使用,这种方法是非常容易的,因为它只是需要你 增加4行 到现有的服务代码,允许你很快获得控制和调试。

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

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

这个 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);

它将像控制台应用程序一样运行,一旦事情变好,你可以改变模式 Release ,而常规 else 部分将触发逻辑

<强>更新

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

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

我将下面的原始答案留给后人。


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

我们新上课并在服务启动期间调用StartEventLoop()。 (这个类也可以很容易地从控制台应用程序中使用。)

这种设计的好处是,设置Timer的参数可以用来在服务实际开始工作之前有一个延迟,这样你就有时间手动附加一个调试器。

  

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版本中触发)。

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

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

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
}

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

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

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

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

您使用的是哪种语言?

使用 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服务管理器中启动和停止服务。

我想这取决于什么操作系统使用的,都是很难附加服务,由于分离之间的会议。

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

  • 使用GFlags(在调试工具,用于Windows)设置一个永久性的调试器用一个过程。这一存在的"图像文件的执行选项"注册的关键,是非常有用的。我想你会需要调整服务的设置,以便"进行交互与桌面"。我使用这个用于所有类型的调试,不能只是服务。
  • 其他选项,是独立代码一点,使得该服务的部分是互换正常程序的启动。这种方式,可以使用一个简单的命令行标记,并启动一个进程(而不是一个服务),这使它更容易调试。

希望这会有所帮助。

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

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

我使用这种方法只记得你必须在真实服务中运行时测试所有代码,而命令行工具是一个很好的调试辅助工具,它是一个不同的环境,它的行为不像真正的服务

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

我通过在同一个项目中创建第二个服务来进行调试。调试服务,像往常一样启动(即在服务MMC插件中),创建服务主机进程。即使您尚未启动实际服务,这也为您提供了附加调试器的过程。将调试器附加到进程后,启动您的实际服务,您可以在服务生命周期的任何地方进入,包括OnStart()。

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

<强>详细信息:

1)假设您正在实现 MyService ,还要创建 MyServiceDebug 。将它们添加到 Program.cs 中的 ServiceBase 数组中,如下所示:

    /// <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)将实际服务和调试服务添加到服务项目的项目安装程序:

将服务项目输出添加到服务的安装项目时,将包括两个服务(实际和调试)。安装后,两个服务都将出现在service.msc MMC插件中。

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

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

5)启动真实服务并享受调试。

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

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

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

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

  1. 运行GFlags,指定exe-name和vsjitdebugger
  2. 运行regedit并前往GFlags设置选项的位置
  3. 选择“导出密钥”来自文件菜单
  4. 使用.reg扩展名保存该文件
  5. 您想要调试服务的任何时候:双击.reg文件
  6. 如果要停止调试,请双击第二个.reg文件
  7. 或保存以下代码段并将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"

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

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

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

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

这样我就可以轻松调试大多数小问题。当然,有些东西仍然需要作为服务进行调试,但99%这样就足够了。

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

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

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

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

只需将调试器午餐放在任何地方,并在启动时附加Visualstudio

#if DEBUG
    Debugger.Launch();
#endif

此外,您需要启动VS作为Administatrator,您需要允许,一个进程可以由不同的用户自动调试(如这里):

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

使用Windows服务模板C#项目创建新的服务应用 https://github.com/ HarpyWar /窗口服务模板

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

这是我用来测试服务的简单方法,没有任何额外的“调试”功能。方法和集成的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 });
}
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
    }
}

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

  1. 创建一个日志文件:我个人更喜欢单独的日志文件,比如文本文件,而不是使用应用程序日志或事件日志。但这会花费你很多代表时间,因为它仍然很难确定我们在哪里确切的错误位置
  2. 将应用程序转换为控制台应用程序:这将启用我们可以在VS中使用的所有调试工具。
  3. 请参阅我创建的博文为主题。

只需粘贴

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

最佳选择是使用“ 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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top