什么是Windows服务失败的正确方法?
我inheritance了用C#编写的Windows服务。 在罕见的情况下,它失败了。 但是,如何很好地完成还不是很清楚。 Ross Bennett在bytes.com上陈述了这个问题。 为了简单起见,我会在这里引用他的话。
嗨,伙计!
我一直在寻找这个,但我似乎无法动摇任何MSDN或谷歌文件。 我已经在我find的MSDN中查看了关于开发Windows服务的每篇.NET文章。
我正在开发一个Windows服务应用程序。 该服务从系统registry(HKLM)读取其configuration数据,由另一个“pipe理”应用程序存放。 那里没有问题。
该服务使用工作线程来完成其工作。 该线程在OnStart()中创build,并在OnStop()中发送信号/连接/放置。 再次,没有问题。
以下情况下,一切都很好用
- 系统pipe理员已经正确设置了一切,并且
- 外网资源全部可达。
但是当然,我们作为开发者根本就不能依靠:
- 系统pipe理员已经正确设置了一切,或者
- 外部networking资源可达。
真的,我们需要的是服务应用程序有自己的死亡方式。 如果networking资源瘫痪,我们需要停止服务。 但更重要的是,我们需要SCM知道它已经自行停止了。 供应链pipe理需要知道服务“失败”……并不只是被某个人closures。
在“OnStart()”方法中调用“return”或抛出exception对于仍处于启动过程中的服务甚至没有帮助。SCM快乐地开启并且进程在任务pipe理器中保持运行 – 尽pipe它实际上没有做任何事情,因为工作线程从未被创build和启动。
使用ServiceController实例也不这样做。 SCM看起来像是正常关机 – 而不是服务故障。 因此,没有发生恢复操作或重新启动。 (另外,有一个MSDNful文档警告使用ServiceController来使事情与自身发生有关的ServiceBase后代的危险。)
我已经阅读过文章,人们正在使用PInvoking调用本地代码,只是在SCM中设置“停止”状态标志。 但是这并没有closures服务运行的过程。
我真的很想知道的意图:
- 在服务内closures服务,在哪里
- SCM被专门通知服务已经“停止”了
- 该过程从任务pipe理器中消失。
涉及ServiceControllers的解决scheme似乎并不合适,如果只是因为2不满意。 (顺便说一下,框架文档明确禁止这样做会带来很大的分量。)
我会很感激任何build议,文档的指针,甚至是合理的猜测。 :-)哦! 我非常乐意接受我错过了这个观点。
最诚挚的,
Ross Bennett
本机代码中的最佳做法是使用非零的退出代码调用SetServiceStatus ,以指示1)已停止,并且2)出错。
在托pipe代码中,您可以通过ServiceBase.ServiceHandle属性和P / Invoke-Win32 API获取SCM句柄来获得相同的效果。
我不明白为什么SCM会对此进行任何处理,而不是将ServiceBase.ExitCode
属性设置为非零值,然后实际调用ServiceBase.Stop
。 如果服务处于恐慌模式,P / Invoke可能更直接一点。
我发现Environment.Exit(1)对我来说工作正常。 我通常把它放在一个方法中,捕获未处理的exception,并在停止之前logging下问题。 它完全破坏了服务,但SCM也知道它是closures的。 您可以设置SCM在下降x次时自动重新启动您的服务。 我发现这比编写你自己的重启/关机代码更有用。
我不知道是否有这样的(非P / Invoke)等价物,但WinAPI方式似乎是调用SetServiceStatus
值为SERVICE_STOPPED
,然后等待SCMclosures。 作为一个积极的副作用,它将您的服务失败logging到事件日志中。
以下是文档相关部分的一些引用:
如果服务调用SetServiceStatus与dwCurrentState成员设置为SERVICE_STOPPED和dwWin32ExitCode成员设置为非零值,以下条目被写入到系统事件日志中:
[…] <ServiceName>以下列错误终止:<ExitCode> […]
以下是调用此函数的最佳实践:
[…]
- 如果状态为SERVICE_STOPPED,则执行所有必要的清理并仅调用一次SetServiceStatus。 这个函数对SCM进行LRPC调用。 对SERVICE_STOPPED状态中的函数的第一个调用将closuresRPC上下文句柄,任何后续调用都可能导致进程崩溃。
- 在使用SERVICE_STOPPED调用SetServiceStatus之后,不要试图执行任何额外的工作,因为服务进程可以在任何时候终止。
PS:在我看来,如果networking资源不可用,服务不应停止,而是继续运行,等待资源可用。 临时networking中断可能发生,一旦networking备份,系统pipe理员就不需要手动干预。
您可以按照此处所述获取正确的ExitCode。 所以Windows服务pipe理器会给出正确的错误文本。
在ServiceBase中我的OnStart如下所示:
protected override void OnStart(string[] args) { try { DoStart(); } catch (Exception exp) { Win32Exception w32ex = exp as Win32Exception; if (w32ex == null) { w32ex = exp.InnerException as Win32Exception; } if (w32ex != null) { ExitCode = w32ex.ErrorCode; } Stop(); } }
经过一些testing后,我发现以下几种情况可能会导致其他问题:
ExitCode = 1; Environment.Exit(1);
只要调用Environment.Exit不会使SCM做error handling,但首先设置ServiceBase ExitCode。