【问题标题】:What is the proper way for a Windows service to fail?Windows 服务失败的正确方法是什么?
【发布时间】:2010-11-16 17:26:10
【问题描述】:

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

嘿,伙计们!

我一直在寻找这个, 但我似乎无法动摇 来自 MSDN 或来自 谷歌。我审查了每个 .NET 关于开发 Windows 服务的文章 在我找到的 MSDN 中。

我正在开发 Windows 服务 应用。该服务读取其 来自系统的配置数据 登记处 (HKLM) 由另一个“经理”应用程序。不 有问题。

服务使用工作线程来做 是工作。线程是在 OnStart() 和信号/加入/处置 在 OnStop() 中。同样,没有问题。

在以下情况下一切都很好:

  1. 系统管理员已正确设置所有内容,并且
  2. 外网资源均可达。

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

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

真的,我们需要的是 服务申请有办法 自己死去。如果一个网络 资源下降,我们需要 服务停止。但更多的是 点,我们需要 SCM 知道它有 自行停止。单片机需求 知道该服务有 “失败”……而且不只是被关闭 被某人击倒。

调用“return”或抛出一个 “OnStart()”方法中的异常 甚至对服务都没有帮助 在启动过程中..单片机去 快乐地进行,过程不断 在任务管理器中运行——尽管 它实际上并没有做任何事情,因为 从未创建工作线程 并开始了。

使用 ServiceController 实例 也不这样做。这似乎 将 SCM 作为正常关机——而不是 服务失败。所以没有一个 恢复操作或重新启动发生。 (另外,还有 MSDNful 文档 警告 ServiceBase 后代使用 ServiceController 做事 自己发生。)

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

我真的很想知道 方式:

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

涉及 ServiceController 的解决方案 似乎不合适,如果只是 因为2不满足。 (那个 专门的框架文档 禁忌这样做 顺便说一句,重量很大。)

如果有任何建议,我将不胜感激, 指向文档的指针,甚至 有道理的猜想。 :-) 哦!和 我很高兴能接受 我没有抓住重点。

最诚挚的,

罗斯·贝内特

【问题讨论】:

    标签: c# windows-services


    【解决方案1】:

    本机代码的最佳做法是使用非零退出代码调用 SetServiceStatus,以指示 1) 它已停止和 2) 出现问题。

    在托管代码中,您可以通过the ServiceBase.ServiceHandle Property 获取SCM 句柄并P/Invoke-ing Win32 API 来达到相同的效果。

    我不明白为什么 SCM 会以不同于将 ServiceBase.ExitCode 属性设置为非零然后调用 ServiceBase.Stop 来处理这个问题。如果服务处于恐慌模式,P/Invoke 可能会更直接一些。


    如 cmets 中所述(另请参阅 https://serverfault.com/questions/72318/set-up-recovery-actions-to-take-place-when-a-service-fails),如果进程使用非零退出代码调用 SetServiceStatus(SERVICE_STOPPED),则服务的恢复操作在选项“为出现错误的停止启用操作”(sc.exe failureflag) 已打勾。 -> 系统事件 ID 7024

    如果服务进程退出 (Env.Exit()) 或在未咨询 SCM 的情况下崩溃,则恢复操作将始终运行。 -> 系统事件 ID 7031

    【讨论】:

    • 我正在寻找的答案包含在这个答案中。简洁的答案是:将 ServiceBase.ExitCode 属性设置为非零,然后调用 ServiceBase.Stop。
    • 试过这个(通过托管 API 和 SetServiceStatus 的 PInvoke),如果配置为这样做,这种方法不允许服务自动重启。它绝对会停止服务。调用 Environment.Exit() 或抛出异常而不捕获它会导致操作系统似乎正在寻找的“异常”终止,用于自动重启失败。
    • @jglouie:请参阅serverfault.com/q/72318/206739 了解您的问题的解决方案。
    • @JEdwardEllis,唯一的问题是事件日志告诉“服务已成功停止”。 (带有信息,而不是错误标签)。你知道如何解决吗?
    【解决方案2】:

    我发现 Environment.Exit(1) 适合我。我通常将它放在一个捕获未处理异常的方法中,并在我停止它之前记录问题。它完全破坏了服务,但 SCM 也知道它已关闭。您可以将 SCM 设置为在服务中断 x 次时自动重新启动您的服务。我发现这比编写自己的重启/关闭代码有用得多。

    【讨论】:

    • Environment.Exit() 可能大部分时间都有效,但您实际上是在将椅子从您的流程中踢出。例如,如果您正在写入一个文件,如果您只是硬停止该进程,则缓冲区不会刷新到磁盘。
    【解决方案3】:

    我不知道是否有(非P/Invoke)等价物,但WinAPI的方式似乎是调用SetServiceStatus,值为SERVICE_STOPPED,然后等待SCM关闭你失望。作为一个积极的副作用,它会将您的服务失败记录到事件日志中。

    以下是the relevant part of the documentation的一些引用:

    如果服务调用 SetServiceStatus 并将 dwCurrentState 成员设置为 SERVICE_STOPPED 并且 dwWin32ExitCode 成员设置为非零值,则以下条目将写入系统事件日志:

    [...] 因以下错误而终止: [...]

    以下是调用此函数时的最佳做法:

    [...]

    • 如果状态为 SERVICE_STOPPED,则执行所有必要的清理并仅调用一次 SetServiceStatus。此函数对 SCM 进行 LRPC 调用。第一次调用处于 SERVICE_STOPPED 状态的函数会关闭 RPC 上下文句柄,任何后续调用都可能导致进程崩溃。
    • 在使用 SERVICE_STOPPED 调用 SetServiceStatus 后不要尝试执行任何其他工作,因为服务进程可以随时终止。

    PS:在我看来,如果网络资源不可用,服务不应该停止而是继续运行,等待资源可用。可能会发生暂时的网络中断,一旦网络备份,它们不应需要系统管理员的手动干预。

    【讨论】:

    • +1 PS 正是我想要的。假设关键是找到一些适当的时间间隔来检查可用性
    【解决方案4】:

    经过一些测试,我发现在您调用 Stop 可能会导致其他问题的情况下,以下工作:

            ExitCode = 1;
            Environment.Exit(1);
    

    仅调用 Environment.Exit 不会使 SCM 进行故障处理,但首先设置 ServiceBase ExitCode 会。

    【讨论】:

      【解决方案5】:

      您可以获得正确的 ExitCode,如 here 所述。 所以 Windows 服务管理器会给出正确的错误文本。

      我在 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();
          }
      }
      

      【讨论】:

      猜你喜欢
      • 2013-04-03
      • 2018-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-14
      相关资源
      最近更新 更多