Answers:
如果我想快速调试该服务,只需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构建期间启用代码。当您在使用它时,为服务调试创建一个单独的构建配置可能会很有用。
我还认为,为正常执行和作为服务使用单独的“版本”是可行的方法,但是是否真的需要为此目的专门使用单独的命令行开关?
你不能只是做:
public static int Main(string[] args)
{
if (!Environment.UserInteractive)
{
// Startup as service.
}
else
{
// Startup as application
}
}
那将具有“好处”,您可以通过双击来启动您的应用程序(如果确实需要,可以单击确定),并且只需F5在Visual Studio中单击即可(无需修改项目设置以包括该/console
Option)。
从技术上讲,Environment.UserInteractive
检查是否WSF_VISIBLE
为当前窗口站设置了Flag,但是false
除了作为(非交互式)服务运行之外,还有其他原因会返回它吗?
System.Diagnostics.Debugger.IsAttached
代替Environment.UserInteractive
。
几周前,当我建立一个新的服务项目时,我发现了这篇文章。尽管有很多不错的建议,但我仍然没有找到我想要的解决方案:无需修改服务类即可调用服务类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);
}
这是所需的全部代码,但我还编写了演练并附有解释。
walk through
)是确保Console Application
在尝试编译和运行之前,进入项目的属性并将输出类型更改为。在找到它Project Properties -> Application -> Output type -> Console Application
。另外,为了使它对我来说正常工作,我最终不得不使用start
命令运行该应用程序。例如:C:\"my app name.exe" -service
对我不起作用。相反,我使用了C:\start /wait "" "my app name.exe" -service
有时,重要的是分析服务启动期间发生的事情。附加到进程没有帮助,因为在启动服务时您不够快地附加调试器。
简短的答案是,我正在使用以下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 7x64和Visual Studio 2010 Team Edition,但对其他环境也应有效。
重要:以“手动”模式部署服务(使用InstallUtil
VS命令提示符下的实用程序或运行您准备的服务安装程序项目)。启动服务之前,请先打开Visual Studio,然后加载包含服务源代码的解决方案-在Visual Studio中根据需要设置其他断点-然后通过“ 服务控制面板”启动该服务。
由于该Debugger.Launch
代码,这将导致对话框“ Servicename.exe中发生未处理的Microsoft .NET Framework异常”。出现。单击,如屏幕截图所示: Yes, debug Servicename.exe
之后,尤其是在Windows 7 UAC中,可能会提示您输入管理员凭据。输入它们并继续Yes:
此后,将出现众所周知的Visual Studio即时调试器窗口。它询问您是否要使用指定的调试器进行调试。单击之前Yes,选择您不想打开新实例(第二个选项)-在这里新实例将无济于事,因为不会显示源代码。因此,您选择之前打开的Visual Studio实例:
点击之后Yes,经过一段时间的Visual Studio将显示在该行的黄色箭头向右,其中Debugger.Launch
语句,你能调试代码(方法MyInitOnStart
,其中包含你的初始化)。
按F5立即继续执行,直到到达您准备的下一个断点。
提示:要保持服务运行,请选择Debug-> Detach all。这样,您可以在正确启动服务并完成调试启动代码后,运行与该服务进行通信的客户端。如果按Shift+F5(停止调试),将终止服务。而不是这样做,您应该使用服务控制面板将其停止。
注意的是
如果构建发行版,则调试代码将自动删除,并且该服务将正常运行。
我正在使用Debugger.Launch()
,它将启动并附加一个调试器。我也进行了测试Debugger.Break()
,但没有成功,因为在服务启动时还没有连接调试器(导致“错误1067:进程意外终止。”)。
RequestAdditionalTime
为服务启动设置更长的超时时间(它不会延迟代码本身,但会立即继续执行该Debugger.Launch
语句)。否则,如果您没有base.Onstart(args)
足够快地从调试器调用,则启动服务的默认超时时间将太短,并且启动服务将失败。实际上,超时10分钟可避免在调试器启动后立即看到消息“ 服务未响应...”。
一旦习惯了,此方法非常简单,因为它只需要在现有服务代码上添加4行,即可快速获得控制权和调试权。
base.RequestAdditionalTime(600000)
如果base.OnStart(args)
在该时间段内未调用服务控件,它将阻止该服务控件终止10分钟)。除此之外,我记得如果过一会儿不输入管理员凭据,UAC也会中止(我不知道确切的秒数,但是我认为您必须在一分钟内输入,否则UAC会中止) ,这将终止调试会话。
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
项目是否以Debug模式编译。这将在条件为true时执行下面的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的参数可以在服务真正开始工作之前有一个延迟,这样您就有时间手动附加调试器。
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");
}
}
我也曾经做过以下事情(已经在前面的答案中提到过,但是使用条件编译器[#if]标志来帮助避免在Release版本中触发)。
我停止这样做是因为有时我们会忘记在Release中进行构建,而在客户端演示上运行的应用程序中出现调试器中断(令人尴尬!)。
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
#endif
// do something
需要30分钟以上才能完成?
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
}
OnStart
是protected
,您无法修改访问级别:(
使用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服务管理器中启动和停止服务。
我认为这取决于您所使用的操作系统,由于会话之间的分隔,Vista很难附加到服务上。
我过去使用的两个选项是:
希望这可以帮助。
我希望能够调试服务的各个方面,包括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)将真实服务和调试服务添加到服务项目的项目安装程序中:
当您将服务项目输出添加到该服务的安装项目时,将同时包含这两个服务(真实和调试)。安装后,这两个服务都将出现在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.
}
}
}
为了调试Windows服务,我将GFlags和regedit创建的.reg文件结合在一起。
或保存以下代码片段,并将servicename.exe替换为所需的可执行文件名称。
debugon.reg:
Windows注册表编辑器版本5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe] “ GlobalFlag” =“ 0x00000000” “调试器” =“ vsjitdebugger.exe”
debugoff.reg:
Windows注册表编辑器版本5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe] “ GlobalFlag” =“ 0x00000000”
要对现有Windows Service程序进行故障排除,请使用其他人建议的'Debugger.Break()'。
对于新的Windows Service程序,我建议使用James Michael Hare的方法http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-self-installable-windows-service-template- redux.aspx
使用Windows服务模板C#项目创建一个新的服务应用https://github.com/HarpyWar/windows-service-template
有自动检测到的控制台/服务模式,服务的自动安装程序/卸载程序以及一些最常用的功能。
这是我用来测试服务的简单方法,没有任何其他“调试”方法,并且集成了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
}
}
只是粘贴
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