更简单的方法来debuggingWindows服务
有没有更简单的方法来通过代码,而不是通过Windows服务控制pipe理器启动服务,然后将debugging器附加到线程? 这有点麻烦,我想知道是否有一个更直接的方法。
如果我想快速debugging服务,那么我只需要放入一个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 */ }
在那里,代码将只在debugging版本中被启用。 尽pipe如此,为服务debugging创build单独的构buildconfiguration可能会很有用。
我也认为有一个单独的“版本”正常执行和作为服务的路要走,但是真的需要为此目的专用一个单独的命令行开关?
你不能只是做:
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
?
几周前,当我build立一个新的服务项目时,我发现了这个post。 虽然有很多很棒的build议,但我仍然没有find我想要的解决scheme:可以调用服务类的OnStart
和OnStop
方法,而不需要对服务类进行任何修改。
我提出的解决scheme使用Environment.Interactive
select运行模式,正如本文的其他答案所build议的。
static void Main() { ServiceBase[] servicesToRun; servicesToRun = new ServiceBase[] { new MyService() }; if (Environment.UserInteractive) { RunInteractive(servicesToRun); } else { ServiceBase.Run(servicesToRun); } }
RunInteractive
helper使用reflection来调用受保护的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); }
这是所有需要的代码,但我也写了演练与解释。
我通常做的是将服务的逻辑封装在一个单独的类中,并从“跑步者”类开始。 这个跑步者类可以是实际的服务或只是一个控制台应用程序。 所以你的解决scheme有(至less)3个项目:
/ConsoleRunner /.... /ServiceRunner /.... /ApplicationLogic /....
有时分析服务启动过程中发生的事情很重要。 附加到进程在这里没有帮助,因为在服务启动的时候,你不够快速地连接debugging器。
简短的回答是,我正在使用以下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 7×64和Visual Studio 2010 Team Edition ,但也应该适用于其他环境。
重要提示:以“手动”模式部署服务(使用VS命令提示符中的InstallUtil
实用程序或运行已准备好的服务安装程序项目)。 在启动服务之前打开Visual Studio并加载包含服务源代码的解决scheme – 在Visual Studio中根据需要设置其他断点 – 然后通过服务控制面板启动服务。
由于Debugger.Launch
代码,这将导致一个对话框“ Servicename.exe中出现未处理的Microsoft .NET Frameworkexception”。 出现。 点击 是的,debuggingServicename.exe如屏幕截图所示:
之后,尤其是在Windows 7中,UAC可能会提示您inputpipe理员凭据。 input他们并继续是 :
之后,出现着名的Visual Studio Just-In-Time Debugger窗口。 它询问您是否要使用精选的debugging器进行debugging。 在你点击Yes之前,select你不想打开一个新的实例(第二个选项) – 一个新的实例在这里没有帮助,因为源代码不会被显示。 所以你select你之前打开的Visual Studio实例:
点击Yes之后,Visual Studio会在Debugger.Launch
语句所在行的右边显示黄色箭头,并且可以debugging代码( MyInitOnStart
,它包含您的初始化)。
按F5将立即继续执行,直到达到您准备好的下一个断点。
提示:要使服务保持运行,请selectDebug – > Detach all 。 这使您可以在启动正确后运行与服务通信的客户端,并完成debugging启动代码。 如果您按Shift + F5 (停止debugging),这将终止服务。 而不是这样做,你应该使用服务控制面板来停止它。
请注意
-
如果您构build一个版本,则debugging代码将自动删除,并且服务正常运行。
-
我正在使用
Debugger.Launch()
,它启动并附加一个debugging器 。 我也testing了Debugger.Break()
,这是行不通的 ,因为在启动服务时没有附加debugging器(导致“错误1067:进程意外终止” )。 -
RequestAdditionalTime
为服务的启动设置了一个较长的超时(它不会延迟代码本身,但会立即继续执行Debugger.Launch
语句)。 否则,启动服务的默认超时太短,如果不从debugging器中调用base.Onstart(args)
足够快,则启动服务将失败。 实际上,10分钟的超时可以避免在debugging器启动后立即看到消息“ 服务没有响应…” 。 -
一旦你习惯了,这种方法非常简单,因为它只需要你在现有的服务代码中增加4行 ,让你快速获得控制和debugging。
Fabio Scopel的这个YouTubevideo解释了如何很好地debuggingWindows服务…实际的做法是从video的4:45开始。
这是在video中解释的代码…在你的Program.cs文件中,添加debugging部分的东西…
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() { }
UPDATE
这种方法是迄今为止最简单的:
http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx
为了后人,我留下了原来的答案。
我的服务往往有一个类封装一个计时器,因为我希望服务定期检查是否有任何工作要做。
我们新build了一个类,并在服务启动期间调用StartEventLoop()。 (这个类也可以从控制台应用程序中轻松使用。)
这个devise的好处在于,在服务实际开始工作之前,可以使用设置Timer的参数来延迟,以便您有时间手动连接debugging器。
ps 如何将debugging器手动附加到正在运行的进程…?
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中构build,并在运行在客户端演示(尴尬!)上的应用程序中有debugging器中断。
#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)启动该服务。
就个人而言,我会在debugging阶段将代码作为独立程序运行,当大多数错误被消除时,请改为以服务方式运行。
我曾经做过的事情是有一个命令行开关,可以启动程序作为服务或作为一个普通的应用程序。 然后,在我的IDE中,我将设置开关,以便我可以通过我的代码。
使用某些语言,您可以实际检测它是否在IDE中运行,并自动执行此切换。
你使用什么语言?
我认为这取决于你使用的操作系统,因为会话之间的分离,Vista很难附加到服务上。
我过去使用的两个选项是:
- 使用GFlags(在Windowsdebugging工具中)为进程设置永久debugging器。 这存在于“图像文件执行选项”registry项中,非常有用。 我认为您需要调整服务设置以启用“与桌面互动”。 我用它来进行所有types的debugging,而不仅仅是服务。
- 另一种select是将代码分离一点,这样服务部分就可以与一个正常的应用程序启动互换。 这样,你可以使用一个简单的命令行标志,并启动一个进程(而不是一个服务),这使得它更容易debugging。
希望这可以帮助。
当我编写一个服务时,我将所有的服务逻辑放在一个dll项目中,并创build两个调用这个dll的“主机”,一个是Windows服务,另一个是命令行应用程序。
我使用命令行应用程序进行debugging,并将debugging器附加到实际服务中,仅用于在命令行应用程序中无法再现的错误。
我使用这种方法只记得在真正的服务中运行时必须testing所有的代码,而命令行工具是一个很好的debugging工具,它是一个不同的环境,它不像一个真正的服务。
使用TopShelf库。
创build一个控制台应用程序,然后configuration您的主设置
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() { } }
要debugging您的服务,只需在Visual Studio中点击F5。
要安装服务,请inputcmd“console.exe install”
然后,您可以在Windows服务pipe理器中启动和停止服务。
在开发和debuggingWindows服务时,我通常会通过添加/ console启动参数并将其检查来将其作为控制台应用程序运行。 让生活变得更轻松。
static void Main(string[] args) { if (Console.In != StreamReader.Null) { if (args.Length > 0 && args[0] == "/console") { // Start your service work. } } }
如何在第一行Debugger.Break()?
我喜欢能够debugging我的服务的每个方面,包括在OnStart()中的任何初始化,同时仍然在SCM的框架内执行完整的服务行为…没有“控制台”或“应用程序”模式。
我通过在同一个项目中创build第二个服务来进行debugging。 debugging服务,像往常一样启动(即在服务MMC插件),创build服务主机进程。 这给你一个附加debugging器的过程,即使你还没有开始真正的服务。 将debugging器附加到进程后,启动真实的服务,并在服务生命周期的任何地方打开它,包括OnStart()。
因为它只需要很less的代码入侵,debugging服务可以很容易地包含在你的服务设置项目中,通过注释掉一行代码并删除一个项目安装程序,很容易从生产版本中删除。
细节:
1)假设你正在实现MyService
,也创buildMyServiceDebug
。 将两个添加到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)将服务和debugging服务添加到服务项目的项目安装程序中:
将服务项目输出添加到服务的安装项目时,将包括这两种服务(实际和debugging)。 安装完成后,两个服务都将出现在service.msc MMC插件中。
3)在MMC中启动debugging服务。
4)在Visual Studio中,将debugging器附加到由debugging服务启动的进程。
5)开始真正的服务,享受debugging。
要debuggingWindows服务,我将GFlags和由regedit创build的.reg文件组合在一起。
- 运行GFlags,指定exe-name和vsjitdebugger
- 运行regedit并转到GFlags设置其选项的位置
- 从文件菜单中select“导出密钥”
- 用.reg扩展名保存该文件
- 任何时候你想debugging服务:双击.reg文件
- 如果要停止debugging,请双击第二个.reg文件
或者保存下面的代码片段,并用所需的可执行文件名replaceservicename.exe。
debugon.reg:
Windowsregistry编辑器版本5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe] “的GlobalFlag”= “0×00000000” “debugging器”= “vsjitdebugger.exe”
debugoff.reg:
Windowsregistry编辑器版本5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe] “的GlobalFlag”= “0×00000000”
对于例行的小东西编程,我做了一个非常简单的窍门来轻松地debugging我的服务:
在服务启动时,我检查命令行参数“/ debug”。 如果使用此参数调用该服务,则不会执行通常的服务启动,而是启动所有侦听器,并只显示一个消息框“debugging进行中,按ok结束”。
因此,如果我的服务是以通常的方式启动的,那么它将作为服务启动,如果它是以命令行参数/debugging启动的,它就像一个正常的程序一样。
在VS我只是添加/debugging作为debugging参数,并直接启动服务程序。
这样我可以轻松地debugging大多数小类问题。 当然,有些东西仍然需要作为服务来debugging,但99%这是够好的。
#if DEBUG System.Diagnostics.Debugger.Break(); #endif
我使用了JOP答案的变体。 使用命令行参数,您可以在IDE中使用项目属性或通过Windows服务pipe理器来设置debugging模式。
protected override void OnStart(string[] args) { if (args.Contains<string>("DEBUG_SERVICE")) { Debugger.Break(); } ... }
在这个问题之后这是相当多的,但可能对未来的参考有用。
如果可以,我build议使用优秀的TopShelf项目。 它使得Windows服务的开发和debugging变得更加容易,并且增加了部署也变得更容易。
检查出来: http : //topshelf-project.com/
对于现有的Windows服务程序的故障排除,使用“Debugger.Break()”作为其他人build议。
对于新的Windows服务程序,我build议使用James Michael Hare的方法http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template- redux.aspx
只要把你的debugging器午餐放在任何地方,并在启动时附加Visualstudio
#if DEBUG Debugger.Launch(); #endif
你也需要启动VS作为pipe理员,你需要允许一个进程可以被不同的用户自动debugging(如这里所解释的):
reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f
使用Windows服务模板C#项目来创build一个新的服务应用程序https://github.com/HarpyWar/windows-service-template
有控制台/服务模式自动检测,自动安装/卸载您的服务和几个最常用的function包括在内。
这里是我用来testing服务的简单方法,没有任何额外的“debugging”方法和集成VSunit testing。
[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 }); }
您需要考虑简化Windows服务
你有两个选项来进行debugging。
- 创build一个日志文件:个人而言,我更喜欢单独的日志文件,如文本文件,而不是使用应用程序日志或事件日志。但这会花费你很多代表时间,因为它仍然很难find我们的确切的错误位置是
- 将应用程序转换为控制台应用程序:这将使您可以在VS中使用所有的debugging工具。
请参考我为该主题创build的博客文章。
只需粘贴
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();
当你运行你的程序。