我继承了用C#编写的Windows服务。在极少数情况下,它会严重失败。但是,如何彻底失败并不清楚。 Ross Bennett在bytes.com处优雅地陈述了这个问题。为了简单起见,我将在这里引用他。
啊,伙计!我一直在寻找这个, 但我似乎无法动摇任何 来自MSDN或来自的文档 谷歌。我已经回顾了每一个.NET 有关开发Windows服务的文章 在我找到的MSDN中。
我正在开发Windows服务 应用。这项服务读取它 来自系统的配置数据 登记处(HKLM)存放的地方 由另一个“经理”应用程序。没有 那里的问题。
该服务使用工作线程来完成 这是工作。线程是在。中创建的 OnStart()并发信号/加入/处置 在OnStop()中。再一次,没有问题。
在以下情况下,一切都很美妙:
- 系统管理员已正确设置所有内容,
- 外部网络资源都可以访问。
醇>但当然,我们只是开发人员 不能依赖:
- 系统管理员已正确设置所有内容,或
- 可以访问外部网络资源。
醇>真的,我们需要的是 服务应用程序有一些方法 自己死亡。如果是网络 资源下降,我们需要 服务停止。但更多的是 我们需要SCM知道它有 自愿停止了。 SCM需要 知道该服务有 “失败了”......并没有被关闭 被某人打倒。
呼叫“返回”或投掷 “OnStart()”方法中的异常 对服务甚至没有帮助 在启动过程中...... SCM去了 欢快地,过程保持 但是,在任务管理器中运行 从那以后它实际上没有做任何事情 工作线程从未创建过 然后开始了。
使用ServiceController实例 也不这样做。那似乎是 SCM作为正常关机 - 不是 服务失败。所以没有 恢复操作或重新启动发生。 (另外,还有MSDNful文档 关于a的危险的警告 ServiceBase后代使用了 ServiceController制作东西 发生在自己身上。)
我读过人们的文章 搞乱PInvoking调用 本机代码只是为了设置 SCM中的“已停止”状态标志。但 那不会关闭过程的 服务正在运行。
我真的很想知道预期的 方式:
- 从服务中关闭服务,
- SCM被适当地通知该服务已“停止”,并且
- 该过程从任务管理器中消失。
醇>涉及ServiceControllers的解决方案 如果只是,似乎不合适 因为2不满意。 (那个 特别是框架文档 这样做是禁忌的 顺便说一句,体重很重。)
我很感激任何建议, 指向文档,甚至是 有充分理由的推测。 :-)哦!和 我非常乐意接受这一点 我错过了这一点。
最诚挚的,
Ross Bennett
答案 0 :(得分:30)
本机代码的最佳做法是使用非零退出代码调用SetServiceStatus以指示1)它已停止且2)出现问题。
在托管代码中,通过the ServiceBase.ServiceHandle Property和P / Invoke-Win32 API获取SCM句柄可以达到同样的效果。
我不明白SCM为什么会这样做,只是将ServiceBase.ExitCode
属性设置为非零然后再调用ServiceBase.Stop
。如果服务处于恐慌模式,P / Invoke可能更直接。
答案 1 :(得分:5)
我发现Environment.Exit(1)对我来说很好。我通常把它放在一个捕获未处理的异常的方法中,并在我停止之前记录问题。它完全破坏了服务,但SCM也知道它已关闭。您可以将SCM设置为在服务次数下降x次时自动重新启动服务。我发现这比编写自己的重启/关闭代码更有用。
答案 2 :(得分:2)
我不知道是否存在(非P / Invoke)等价物,但WinAPI方式似乎是调用SetServiceStatus
值为SERVICE_STOPPED
然后等待SCM让你失望。作为积极的副作用,它会将您的服务失败记录到事件日志中。
以下是the relevant part of the documentation引用的一些内容:
如果服务调用SetServiceStatus并将dwCurrentState成员设置为SERVICE_STOPPED并且将dwWin32ExitCode成员设置为非零值,则将以下条目写入系统事件日志:
[...]< ServiceName>因以下错误而终止:< ExitCode> [...]
以下是调用此函数时的最佳做法:
[...]
- 如果状态为SERVICE_STOPPED,请执行所有必要的清理并仅调用一次SetServiceStatus。此函数对SCM进行LRPC调用。第一次调用SERVICE_STOPPED状态的函数会关闭RPC上下文句柄,任何后续调用都可能导致进程崩溃。
- 使用SERVICE_STOPPED调用SetServiceStatus后,请勿尝试执行任何其他工作,因为服务进程可以随时终止。
PS:在我看来,如果网络资源不可用,服务不应该停止,而是继续运行,等待资源可用。可能会发生临时网络中断,一旦网络恢复,它们就不需要系统管理员的手动干预。
答案 3 :(得分:0)
您可以按照here所述获得正确的ExitCode。 因此,Windows服务管理器将提供正确的错误文本。
ServiceBase中的My 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();
}
}
答案 4 :(得分:0)
经过一些测试后,我发现以下情况适用于您调用Stop可能会导致其他问题:
ExitCode = 1;
Environment.Exit(1);
只是调用Environment.Exit不会使SCM进行故障处理,但首先设置ServiceBase ExitCode。