在控制台应用程序中包装C#服务以进行调试
我想调试用C#编写的服务,而且老式的方式太长了。我必须停止服务,以调试模式启动我的应用程序(Visual Studio 2008),启动服务,附加到服务进程,然后在我的Asp.Net应用程序中导航以触发服务。在控制台应用程序中包装C#服务以进行调试
我基本上有服务在后台运行,等待任务。 Web应用程序将触发一项任务,由服务提取。
我想要做的是有一个控制台应用程序,为我尝试调试服务。是否有任何人知道的简单演示?
谢谢 杰克
我已经使用单元测试,调试困难的设置过去,只需编写一个单元测试,用任何参数调用任何服务方法,并在单元测试中设置调试断点。
使用testdriven.net或jetbrains testrunner使其更容易。
我倾向于有一张配置设置或使用指令的调试版本:
#if DEBUG
Debugger.Break();
#endif
或
if(Settings.DebugBreak)
Debugger.Break();
我提出,在服务组件的OnStart方法。然后,系统会自动提示您并将其附加到流程中。
你可以做这样的事情在主入口点:
static void Main()
{
#if DEBUG
Service1 s = new Service1();
s.Init(); // Init() is pretty much any code you would have in OnStart().
#else
ServiceBase[] ServicesToRun;
ServicesToRun=new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
#endif
}
,并在你的OnStart事件处理程序:
protected override void OnStart(string[] args)
{
Init();
}
的方法,我总是采取隔离所有的应用程序重刑的逻辑到类库中。这使得您的服务项目真的只是一个将您的类库作为服务托管的shell。
通过这样做,您可以轻松地对代码进行单元测试和调试,而无需处理通过附加到进程来调试服务的头痛问题。我当然会推荐单元测试,但如果你没有这样做,那么添加一个调用与您的服务相同的入口点的控制台应用程序是最好的选择。
为了避免使用全局定义,我通常在运行时测试我是否是通过Environment.UserInteractive属性的服务或常规应用程序。
[MTAThread()]
private static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] aServicesToRun;
// More than one NT Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase() {New ServiceMain, New MySecondUserService}
//
aServicesToRun = new ServiceBase[] {new ServiceMain()};
Run(aServicesToRun);
}
else
{
var oService = new ServiceMain();
oService.OnStart(null);
}
}
如果您希望显示控制台,请不要忘记在项目属性中将输出类型设置为控制台应用程序。该程序仍然可以作为Windows服务运行,无论它是控制台应用程序还是Windows应用程序,所以不用担心。 – 2016-03-15 06:48:47
我用它来检查我的进程是否作为服务运行或不运行。
public class ServiceDiagnostics
{
readonly bool _isUserService;
readonly bool _isLocalSystem;
readonly bool _isInteractive;
public ServiceDiagnostics()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null);
var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null);
this._isUserService = wp.IsInRole(serviceSid);
// Neither Interactive or Service was present in the current user's token, This implies
// that the process is running as a service, most likely running as LocalSystem.
this._isLocalSystem = wp.IsInRole(localSystemSid);
// This process has the Interactive SID in its token. This means that the process is
// running as a console process.
this._isInteractive = wp.IsInRole(interactiveSid);
}
public bool IsService
{
get { return this.IsUserService || this.IsLocalSystem || !this.IsInteractive; }
}
public bool IsConsole
{
get { return !this.IsService; }
}
/// <summary>
/// This process has the Service SID in its token. This means that the process is running
/// as a service running in a user account (not local system).
/// </summary>
public bool IsUserService
{
get { return this._isUserService; }
}
/// <summary>
/// Neither Interactive or Service was present in the current user's token, This implies
/// that the process is running as a service, most likely running as LocalSystem.
/// </summary>
public bool IsLocalSystem
{
get { return this._isLocalSystem; }
}
/// <summary>
/// This process has the Interactive SID in its token. This means that the process is
/// running as a console process.
/// </summary>
public bool IsInteractive
{
get { return this._isInteractive; }
}
}
您可以通过反射调用服务方法,如下所示。
使用Environment.UserInteractive
可以让我们知道我们是作为控制台应用程序运行还是作为服务运行。
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
if (!Environment.UserInteractive)
{
// This is what normally happens when the service is run.
ServiceBase.Run(ServicesToRun);
}
else
{
// Here we call the services OnStart via reflection.
Type type = typeof(ServiceBase);
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo method = type.GetMethod("OnStart", flags);
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStart()");
// Your Main method might not have (string[] args) but you could add that to be able to send arguments in.
method.Invoke(service, new object[] { args });
}
Console.WriteLine("Finished running all OnStart Methods.");
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStop()");
service.Stop();
}
}
+1 - 我已经写了几个服务几乎完全相同的代码。 – 37Stars 2010-03-25 19:01:20