我有一个C#应用程序,它使用的Windows服务并不总是打开的,我希望能够在服务启动和关闭时发送电子邮件通知。我已经写好了电子邮件脚本,但我似乎不知道如何检测服务状态的变化。
我一直在阅读EDIT:由于ServiceController
类,我认为WaitForStatus()
方法可能是我所需要的,但我还没有找到一个在尚未启动的服务上使用它的示例WaitForStatus()
方法繁忙等待,我需要在侦听服务启动/停止时执行服务运行的其余程序,我不认为这是适合我的方法,除非有人有一个将此方法与多线程相结合的干净高效的解决方案。
更多:
- 该服务将不会由应用程序启动-应用程序用户将直接从"管理工具"中的"服务"窗口启动该服务
- 所使用的服务不是默认的Windows服务,而是为与此应用程序一起使用而设计的自定义服务
谢谢你的帮助!
附言:请注意,我是C#的新手,在这里学习。
更新:
我已经设法在每次服务启动时发送警报电子邮件:当我继续阅读我的代码(不幸的是,我无法在这里发布)时,我注意到用于创建服务的类正在扩展ServiceBase
类,并且有人制作了一个自定义OnStart()
方法来覆盖默认方法。我向新的OnStart()
方法添加了必要的方法调用,它成功地发送了通知。
我试图对OnStop()
方法做同样的事情,但对我来说效果不太好——在继续之前,我想补充一点,我已经用Java编程好几年了,我非常熟悉Java设计模式。
我试图用一个调用电子邮件通知的方法覆盖ServiceBase
类的OnStop()
方法,这在Java中是可行的,将MyService
强制转换为ServiceBase
类型,然后重新调用ServiceBase
类的Stop()
方法(注意:OnStop()
是受保护的方法,因此不能直接调用它-Stop()
方法调用OnStop()
,然后继续执行必要的代码以停止服务)。我认为转换为类型ServiceBase
会强制调用默认的OnStop()
方法,而不是我的自定义方法。
正如你所想象的,在我设法迫使我的电脑硬关机之前,我的收件箱里成功发送了不到10000封电子邮件。
我现在需要的是一种方法,要么使用我重写的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
类。
更新二:
关于我使用NotifyServerStatusChange
的建议,我了解到由于各种原因,解决方案不允许使用系统功能。需要澄清的是,只有完全在C#和.NET范围内的解决方案才是可行的。再次感谢您的帮助!
以下是解决方案以及我以前找不到它的原因:正如我前面所说,我的类扩展了ServiceBase类。在我的第一次更新中,我发布了我试图用与Java相同的方法来解决这个问题:通过强制转换。然而,在C#中,如果在派生类中重写基方法,那么强制转换显然不允许调用它。当我第一次发布这个问题和这个更新时,我不知道的一件事(显然也是没有人想到的一件事情)是C#包含了base
构造函数,该构造函数可用于从派生类调用基类的方法。由于base
构造函数可用于C#中的任何类,因此它不会出现在ServiceBase class文档中。
一旦我学会了这一点,我就可以采用我的原始解决方案,并将其修改为使用基类:
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类的一个实例后,我意识到它一定是某种构造函数。在谷歌上快速搜索后,我找到了我要找的东西。IntelliSense的发展之路!
感谢大家的帮助!
如果您想要一个没有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);
}
ServiceController类有一个WaitForStatus
方法。不过,它在内部进行民意调查。
如果不能PInvoke NotifyServiceStatusChange,则必须轮询该服务。例如:
ServiceController sc = new ServiceController("Some Service");
Console.WriteLine("Status = " + sc.Status);
您可以使用wmi监视事件:technet.microsoft.com/en-us/library/ff730927.aspx
请非常小心地使用NotifyServiceStatusChange(),因为它仅在Windows Vista/Windows 2008(及更高版本)上受支持。如果您针对以下任何平台,则不能使用该API。(仍然有很多XP/Windows 2000-2003系统。)
更糟糕的是,在服务重新启动的情况下,轮询并不总是可靠的,因为如果您在速度非常快的系统(SSD驱动器或虚拟机上的预缓冲I/O)上轮询服务,该服务可能会在两次轮询之间重新启动。