C# 调试 Windows 服务的更简单方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/125964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Easier way to debug a Windows service
提问by Mats
Is there an easier way to step through the code than to start the service through the Windows Service Control Manager and then attaching the debugger to the thread? It's kind of cumbersome and I'm wondering if there is a more straightforward approach.
是否有比通过 Windows 服务控制管理器启动服务然后将调试器附加到线程更简单的方法来单步执行代码?这有点麻烦,我想知道是否有更直接的方法。
采纳答案by jop
If I want to quickly debug the service, I just drop in a Debugger.Break()
in there. When that line is reached, it will drop me back to VS. Don't forget to remove that line when you are done.
如果我想快速调试服务,我只需Debugger.Break()
在那里输入。当到达那条线时,它会让我回到 VS。完成后不要忘记删除该行。
UPDATE:As an alternative to #if DEBUG
pragmas, you can also use Conditional("DEBUG_SERVICE")
attribute.
更新:作为#if DEBUG
编译指示的替代,您还可以使用Conditional("DEBUG_SERVICE")
属性。
[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
Debugger.Break();
}
On your OnStart
, just call this method:
在您的 上OnStart
,只需调用此方法:
public override void OnStart()
{
DebugMode();
/* ... do the rest */
}
There, the code will only be enabled during Debug builds. While your at it, it might be useful to create a separate Build Configuration for service debugging.
在那里,代码只会在调试构建期间启用。虽然您正在使用它,但为服务调试创建单独的构建配置可能会很有用。
回答by akauppi
You can also start the service through the command prompt (sc.exe).
您还可以通过命令提示符 (sc.exe) 启动该服务。
Personally, I'd run the code as a stand-alone program in the debugging phase, and when most bugs are ironed out, change to running as service.
就个人而言,我会在调试阶段将代码作为独立程序运行,当大多数错误解决后,改为作为服务运行。
回答by RB.
What I used to do was to have a command line switch which would start the program either as a service or as a regular application. Then, in my IDE I would set the switch so that I could step through my code.
我过去所做的是有一个命令行开关,可以将程序作为服务或常规应用程序启动。然后,在我的 IDE 中,我会设置开关,以便我可以单步执行我的代码。
With some languages you can actually detect if it's running in an IDE, and perform this switch automatically.
使用某些语言,您实际上可以检测它是否在 IDE 中运行,并自动执行此切换。
What language are you using?
你使用什么语言?
回答by Paul van Brenk
What I usually do is encapsulate the logic of the service in a separate class and start that from a 'runner' class. This runner class can be the actual service or just a console application. So your solution has (atleast) 3 projects:
我通常做的是将服务的逻辑封装在一个单独的类中,并从一个 'runner' 类开始。这个运行器类可以是实际的服务,也可以只是一个控制台应用程序。所以你的解决方案有(至少)3个项目:
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
回答by RichS
I think it depends on what OS you are using, Vista is much harder to attach to Services, because of the separation between sessions.
我认为这取决于您使用的操作系统,由于会话之间的分离,Vista 更难附加到服务。
The two options I've used in the past are:
我过去使用的两个选项是:
- Use GFlags (in the Debugging Tools for Windows) to setup a permanent debugger for a process. This exists in the "Image File Execution Options" registry key and is incredibly useful. I think you'll need to tweak the Service settings to enable "Interact with Desktop". I use this for all types of debugging, not just services.
- The other option, is to separate the code a bit, so that the service part is interchangable with a normal app startup. That way, you can use a simple command line flag, and launch as a process (rather than a Service), which makes it much easier to debug.
- 使用 GFlags(在 Windows 调试工具中)为进程设置永久调试器。这存在于“Image File Execution Options”注册表项中,非常有用。我认为您需要调整服务设置以启用“与桌面交互”。我将它用于所有类型的调试,而不仅仅是服务。
- 另一种选择是将代码分开一点,以便服务部分可以与正常的应用程序启动互换。这样,您可以使用一个简单的命令行标志,并作为一个进程(而不是一个服务)启动,这使得调试更加容易。
Hope this helps.
希望这可以帮助。
回答by Sam
For routine small-stuff programming I've done a very simple trick to easily debug my service:
对于常规的小型编程,我做了一个非常简单的技巧来轻松调试我的服务:
On start of the service, I check for a command line parameter "/debug". If the service is called with this parameter, I don't do the usual service startup, but instead start all the listeners and just display a messagebox "Debug in progress, press ok to end".
在服务启动时,我检查命令行参数“/debug”。如果使用此参数调用服务,我不会执行通常的服务启动,而是启动所有侦听器并仅显示消息框“正在进行调试,按确定结束”。
So if my service is started the usual way, it will start as service, if it is started with the command line parameter /debug it will act like a normal program.
因此,如果我的服务以通常的方式启动,它将作为服务启动,如果它使用命令行参数 /debug 启动,它将像普通程序一样运行。
In VS I'll just add /debug as debugging parameter and start the service program directly.
在VS中,我将添加/debug作为调试参数并直接启动服务程序。
This way I can easily debug for most small kind problems. Of course, some stuff still will need to be debugged as service, but for 99% this is good enough.
这样我就可以轻松调试大多数小问题。当然,有些东西仍然需要作为服务进行调试,但对于 99% 来说,这已经足够了。
回答by Nir
When I write a service I put all the service logic in a dll project and create two "hosts" that call into this dll, one is a Windows service and the other is a command line application.
当我编写服务时,我将所有服务逻辑放在一个 dll 项目中,并创建两个调用此 dll 的“主机”,一个是 Windows 服务,另一个是命令行应用程序。
I use the command line application for debugging and attach the debugger to the real service only for bugs I can't reproduce in the command line application.
我使用命令行应用程序进行调试,并将调试器附加到实际服务中,仅针对无法在命令行应用程序中重现的错误。
I you use this approach just remember that you have to test all the code while running in a real service, while the command line tool is a nice debugging aid it's a different environment and it doesn't behave exactly like a real service.
我使用这种方法时请记住,您必须在实际服务中运行时测试所有代码,而命令行工具是一个很好的调试辅助工具,它是一个不同的环境,它的行为与实际服务并不完全相同。
回答by Maurice
When developing and debugging a Windows service I typically run it as a console application by adding a /console startup parameter and checking this. Makes life much easier.
在开发和调试 Windows 服务时,我通常通过添加 /console 启动参数并检查它来将它作为控制台应用程序运行。让生活更轻松。
static void Main(string[] args) {
if (Console.In != StreamReader.Null) {
if (args.Length > 0 && args[0] == "/console") {
// Start your service work.
}
}
}
回答by leppie
How about Debugger.Break() in the first line?
第一行中的 Debugger.Break() 怎么样?
回答by rohancragg
UPDATE
更新
This approach is by far the easiest:
这种方法是迄今为止最简单的:
http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx
http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx
I leave my original answer below for posterity.
我在下面留下我的原始答案以供后代使用。
My services tend to have a class that encapsulates a Timer as I want the service to check at regular intervals whether there is any work for it to do.
我的服务往往有一个封装 Timer 的类,因为我希望服务定期检查是否有任何工作要做。
We new up the class and call StartEventLoop() during the service start-up. (This class could easily be used from a console app too.)
我们新建类并在服务启动期间调用 StartEventLoop()。(这个类也可以很容易地从控制台应用程序中使用。)
The nice side-effect of this design is that the arguments with which you set up the Timer can be used to have a delay before the service actually starts working, so that you have time to attach a debugger manually.
这种设计的一个很好的副作用是,您设置 Timer 的参数可用于在服务实际开始工作之前延迟,以便您有时间手动附加调试器。
p.s. How to attach the debugger manuallyto a running process...?
ps如何手动将调试器附加到正在运行的进程...?
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");
}
}
Also I used to do the following (already mentioned in previous answers but with the conditional compiler [#if] flags to help avoid it firing in a Release build).
我也曾经执行以下操作(在之前的答案中已经提到,但使用条件编译器 [#if] 标志来帮助避免它在发布版本中触发)。
I stopped doing it this way because sometimes we'd forget to build in Release and have a debugger break in an app running on a client demo (embarrasing!).
我停止这样做是因为有时我们会忘记在 Release 中构建并且在客户端演示上运行的应用程序中出现调试器中断(令人尴尬!)。
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
#endif