检测/侦听服务启动和停止状态更改

时间:2012-07-30 19:35:47

标签: c# asp.net service event-handling

我有一个C#应用程序,它使用的Windows服务并不总是打开,我希望能够在服务启动和关闭时发送电子邮件通知。我编写了电子邮件脚本,但我似乎无法弄清楚如何检测服务状态的变化。

我一直在阅读ServiceController课程,我认为WaitForStatus()方法可能就是我需要的方法,但我无法找到一个例子。用于尚未启动的服务。编辑:由于WaitForStatus()方法忙等待,我需要执行服务运行的其余程序,同时监听服务启动/停止,我不认为这是我的方法,除非有人有一个解决方案,使用这种方法结合多线程,并且干净,高效。

更多:

  • 应用程序不会启动该服务 - 应用程序用户将直接从“管理工具”的“服务”窗口启动该服务。
  • 所使用的服务不是默认的Windows服务 - 它是专为此应用程序设计的自定义服务

感谢您的帮助!

P.S。请注意,我是C#的新手,在我去的时候正在学习。

更新:

我设法在每次服务启动时收到警报电子邮件:当我继续阅读我所拥有的代码时(遗憾的是,我不能在这里发布),我注意到用于创建service正在扩展ServiceBase类,并且有人创建了一个自定义OnStart()方法来覆盖默认方法。我向新的OnStart()方法添加了必要的方法调用,并成功发送了通知。

我试图对OnStop()方法做同样的事情,但这对我来说效果不好 - 在继续之前,我想补充说我已经用Java编程了好几年了,我非常熟悉Java设计模式。

我试图在Java中使用的方法是使用调用电子邮件通知的方法覆盖ServiceBase类的OnStop()方法,将MyService强制转换为{ {1}}然后重新调用ServiceBase类的ServiceBase方法(注意:Stop()是受保护的方法,因此无法直接调用 - OnStop()方法调用Stop()然后继续使用必要的代码来停止服务)。我认为转换为OnStop()类型会强制调用默认的ServiceBase方法,而不是我的自定义方法。

正如您可能想象的那样,在我成功将计算机强行关机之前,我最终只有不到10,000封电子邮件成功发送到我的收件箱。

我现在需要的是使用我重写的OnStop()方法,然后让它调用默认方法或另一种解决此问题的方法。任何和所有的帮助非常感谢。非常感谢。

对于那些有多种解决方案的人:

OnStop()

另外,请记住调用此方法的类,我们称之为protected override void OnStart(string[] args) { string subject = "Notice: Service Started"; string body = "This message is to notify you that the service " + "has been started. This message was generated automatically."; EmailNotification em = new EmailNotification(subject, body); em.SendNotification(); ...INITIALIZE LISTENER FOR SERVICE STOPPING HERE... ...custom stuff to be run on start... } ,扩展Service类。

更新二:

关于我使用ServiceBase的建议,我了解到由于各种原因,解决方案不允许使用系统功能。为了澄清,只有纯粹属于C#和.NET范围的解决方案才是可行的。再次感谢您的帮助!

6 个答案:

答案 0 :(得分:3)

以下是解决方案以及我之前找不到的原因:正如我之前所说,我的类扩展了ServiceBase类。在我的第一次更新中,我发布了我尝试解决这个问题,就像我用Java解决它一样:通过强制转换。但是,在C#中,如果在派生类中覆盖它,显然不允许调用基本方法。当我第一次发布这个问题时,我不知道的事情之一和这个更新(显然是其中一个没有人想到的事情)是C#包含可用于调用方法的base构造函数派生类的基类。由于base构造函数可用于C#中的任何类,因此它不会出现在ServiceBase Class documentation中。

一旦我学会了这个,我就可以使用我原来的解决方案并修改它以使用基类:

    protected override void OnStop() {
        string subject = "Notice: Service Stopped";
        string body = "This message is to notify you that the service has " +
            "been stopped. This message was generated automatically.";
        EmailNotification em = new EmailNotification(subject, body);
        em.SendNotification();
        base.OnStop();
    }

当我在Visual Studio中使用代码并在IntelliSense中注意到base时,我想到了这一点。我点击进入它的定义并将它发送到ServiceBase(显然没有定义)。在注意到我的代码中没有定义base并且它是ServiceBase类的一个实例后,我意识到它一定是某种构造函数。快速谷歌搜索后,我找到了我想要的东西。走智能感!

感谢大家的帮助!

答案 1 :(得分:1)

ServiceController类有WaitForStatus方法。但是在内部它会进行轮询。

答案 2 :(得分:1)

您可以使用wmi监控事件:technet.microsoft.com/en-us/library/ff730927.aspx

答案 3 :(得分:1)

如果你不能PInvoke NotifyServiceStatusChange,那么你将不得不轮询该服务。例如:

ServiceController sc = new ServiceController("Some Service");
Console.WriteLine("Status = " + sc.Status);

答案 4 :(得分:0)

使用NotifyServiceStatusChange()非常小心,因为它仅在Windows Vista / Windows 2008(及更高版本)上受支持。如果您定位到以下任何平台,则无法使用该API。 (那里还有很多XP / Windows 2000-2003系统。)

更糟糕的是,在服务重新启动的情况下,轮询并不总是可靠的,因为如果您在非常快的系统上轮询服务(SSD驱动器或虚拟机上的预缓存I / O),服务可能会在两次民意调查之间重启。

答案 5 :(得分:0)

如果您想要一个没有 win32 api 的 .NET 解决方案,请查看下面我的解决方案。我从 ServiceController 类继承并在 Task 中使用 WaitForStatus() 使其非阻塞,然后在状态更改时引发事件。也许它需要更多测试但对我有用:

类定义

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.ServiceProcess; // not referenced by default

public class ExtendedServiceController: ServiceController
{
    public event EventHandler<ServiceStatusEventArgs> StatusChanged;
    private Dictionary<ServiceControllerStatus, Task> _tasks = new Dictionary<ServiceControllerStatus, Task>();

    new public ServiceControllerStatus Status
    {
        get
        {
            base.Refresh();
            return base.Status;
        }
    }

    public ExtendedServiceController(string ServiceName): base(ServiceName)
    {
        foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
        {
            _tasks.Add(status, null);
        }
        StartListening();
    }

    private void StartListening()
    {
        foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
        {
            if (this.Status != status && (_tasks[status] == null || _tasks[status].IsCompleted))
            {
                _tasks[status] = Task.Run(() =>
                {
                    try
                    {
                        base.WaitForStatus(status);
                        OnStatusChanged(new ServiceStatusEventArgs(status));
                        StartListening();
                    }
                    catch
                    {
                        // You can either raise another event here with the exception or ignore it since it most likely means the service was uninstalled/lost communication
                    }
                });
            }
        }
    }

    protected virtual void OnStatusChanged(ServiceStatusEventArgs e)
    {
        EventHandler<ServiceStatusEventArgs> handler = StatusChanged;
        handler?.Invoke(this, e);
    }
}

public class ServiceStatusEventArgs : EventArgs
{
    public ServiceControllerStatus Status { get; private set; }
    public ServiceStatusEventArgs(ServiceControllerStatus Status)
    {
        this.Status = Status;
    }
}

用法

static void Main(string[] args)
{
    ExtendedServiceController xServiceController = new ExtendedServiceController("myService");
    xServiceController.StatusChanged += xServiceController_StatusChanged;
    Console.Read();

    // Added bonus since the class inherits from ServiceController, you can use it to control the service as well.
}

// This event handler will catch service status changes externally as well
private static void xServiceController_StatusChanged(object sender, ServiceStatusEventArgs e)
{
    Console.WriteLine("Status Changed: " + e.Status);
}