Windows服务失败的正确方法是什么?

时间:2010-11-16 17:26:11

标签: c# windows-services

我继承了用C#编写的Windows服务。在极少数情况下,它会严重失败。但是,如何彻底失败并不清楚。 Ross Bennett在bytes.com处优雅地陈述了这个问题。为了简单起见,我将在这里引用他。

  啊,伙计!

     

我一直在寻找这个,   但我似乎无法动摇任何   来自MSDN或来自的文档   谷歌。我已经回顾了每一个.NET   有关开发Windows服务的文章   在我找到的MSDN中。

     

我正在开发Windows服务   应用。这项服务读取它   来自系统的配置数据   登记处(HKLM)存放的地方   由另一个“经理”应用程序。没有   那里的问题。

     

该服务使用工作线程来完成   这是工作。线程是在。中创建的   OnStart()并发信号/加入/处置   在OnStop()中。再一次,没有问题。

     

在以下情况下,一切都很美妙:

     
      
  1. 系统管理员已正确设置所有内容,
  2.   
  3. 外部网络资源都可以访问。
  4.         

    但当然,我们只是开发人员   不能依赖:

         
        
    1. 系统管理员已正确设置所有内容,或
    2.   
    3. 可以访问外部网络资源。
    4.         

      真的,我们需要的是   服务应用程序有一些方法   自己死亡。如果是网络   资源下降,我们需要   服务停止。但更多的是   我们需要SCM知道它有   自愿停止了。 SCM需要   知道该服务有   “失败了”......并没有被关闭   被某人打倒。

           

      呼叫“返回”或投掷   “OnStart()”方法中的异常   对服务甚至没有帮助   在启动过程中...... SCM去了   欢快地,过程保持   但是,在任务管理器中运行   从那以后它实际上没有做任何事情   工作线程从未创建过   然后开始了。

           

      使用ServiceController实例   也不这样做。那似乎是   SCM作为正常关机 - 不是   服务失败。所以没有   恢复操作或重新启动发生。   (另外,还有MSDNful文档   关于a的危险的警告   ServiceBase后代使用了   ServiceController制作东西   发生在自己身上。)

           

      我读过人们的文章   搞乱PInvoking调用   本机代码只是为了设置   SCM中的“已停止”状态标志。但   那不会关闭过程的   服务正在运行。

           

      我真的很想知道预期的   方式:

           
          
      1. 从服务中关闭服务,
      2.   
      3. SCM被适当地通知该服务已“停止”,并且
      4.   
      5. 该过程从任务管理器中消失。
      6.         

        涉及ServiceControllers的解决方案   如果只是,似乎不合适   因为2不满意。 (那个   特别是框架文档   这样做是禁忌的   顺便说一句,体重很重。)

             

        我很感激任何建议,   指向文档,甚至是   有充分理由的推测。 :-)哦!和   我非常乐意接受这一点   我错过了这一点。

             

        最诚挚的,

             

        Ross Bennett

5 个答案:

答案 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。