WMI Series :事件预订和处理
WMI事件概述
对于从事Winows编程的开发人员来说,事件驱动的应用程序设计是再熟悉不过了,但是WMI中的事件又是一个什么样的概念呢?对于宝贵的内存和CPU资源,管理员需要不断的监视其性能;对于磁盘而言,我们需要随时知道它的使用情况,包括I/O性能,剩余空间等……操作系统中如此之多的管理对象需要管理员一刻不停的监视其运行的状态,这样的工作负担是十分庞大的。因此,能不能当管理对象的性能数据接近某个我们事先设定的临界值时,发出警报或者Email通知给管理员,希望他能来处理。这种情形就是WMI中的事件处理机制,这种机制使得开发人员能够发布、订阅、传递事件的消息。
对于WMI事件处理机制来说,它必须解决三个方面的问题:
n 事件发布:能够找到事件的源;
n 事件订阅:能够订阅我感兴趣的事件;
n 事件传送:保证事件准确无误的传递到事件订阅者。
所幸的是WMI架构规范定义了完善的、强大的事件处理框架,使得上面的问题轻松的解决。您可以在编程时给出一个事件查询,来过滤数据,获取您感兴趣的事件。
WMI事件分为三类:
n 内部事件:当类实例被创建、修改、删除时,WMI响应这些内部的数据变化,这些信息来自CIM储存库。
n 外部事件:这类事件是用户自定义的事件。
n 计时器事件:这种事件是在特定的事件或者每个指定的事件发生的事件,它是由计时器来发布的,并且是订阅的用户自己建立的计时器。
WMI事件查询
WMI事件查询语句与关系数据库的SQL查询语句很相似,下面给出一例:
SELECT *
FROM __InstanceModificationEvent
WITHIN 10
WHERE TargetInstance isa 'Win32_Service' AND
TargetInstance.StartMode = 'Manual' AND
TargetInstance.Started = FALSE AND
PreviousInstance.Started = TRUE
上面的例子查询的事件条件(WHERE字句):事件源为Win32_Service(即windows服务)中的服务,服务启动的模式为手动,而且是刚被停止的服务事件;WITHIN字句指定事件轮询间隔为10秒钟;FROM子句指定事件类型,即为实例被修改,包括服务启动、暂停、终止等。(有关更加详细的信息请参考WMI SDK文档)
Microsoft公司新发布的Visual Studio 2003增强了服务器资源管理器的功能,对于WMI来说,我们可以通过服务器资源管理器查看管理类和管理事件,并且能够做事件查询,简化了编程前WMI查询的测试,也可以将这些管理对象做拖放到设计器,简化了编程。为了使您的服务器资源管理器能够增建WMI的功能,您需要到Microsoft公司的官方网站下载最新的WMI Extensions for Visual Studio Windows Server 2003 Explorer,并将其安装。
安装好了WMI Extensions后,展开服务器资源管理器您就可以看到Management Classes和Management Events两个节点。通过WMI Extensions,您可以轻松的完成下列任务:
n 查询WMI类的命名空间。您只需要右键单击Management Classes,选择Add Classes,在Find class containg文本框输入类名,然后查找就可以找到这个类所在的命名空间;
n 编译您的WMI查询语句,从而获取您感兴趣的事件数据。右键单击Management Events,选择Add Event Query即打开了编译WMI查询语句的窗口,在输入框中输入查询语句,并且选择事件源的类型,就可以执行WMI查询语句。这里还有一个高级选项,自动生成查询语句,查询的结果在Visual Studio的输出窗口中。
编程实现事件预定和处理
在这里我们将看到两个事件预定与处理的例子,一个是关于Win32_Service的事件预定与处理,一个是关于计时器事件的预定与处理,前者是同步的,后者是异步的。在给出例程之前,我们先看看有关事件预定与处理会涉及到的几个类,它们均位于System.Management命名空间下。
WqlEventQuery类:它代表的是一个WMI事件查询,它的构造函数有8个,用户不同的情况。我们将只介绍其中的两个构造函数。
public WqlEventQuery(string):输入参数为WMI的SQL查询语句,如给出的查询语句:
SELECT *
FROM __InstanceModificationEvent
WITHIN 10
WHERE TargetInstance isa 'Win32_Service'
public WqlEventQuery(string, TimeSpan, string):输入参数依次为事件类型,轮询间隔,过滤条件。对应上面的WMI查询语句,三个输入参数分别为:”__InstanceModificationEvent”、 new TimeSpan(0,0,10)、” TargetInstance isa 'Win32_Service'”。上述的两种方式使用构造函数得到的实例是一样的。
ManagementEventWatcher类:根据指定的事件查询预订临时事件通知,指定的事件指的是WqlEventQuery类的实例。ManagementEventWatcher的构造函数常使用的是带一个WqlEventQuery对象的输入参数。它还有另外一个重要的方法WaitForNextEvent()方法,用于等待下一个与指定查询匹配的事件,然后返回该事件。Start()方法用于启动查询并等待事件通知(发生),Stop()方法停止预定事件通知。
有了这些准备后,我们可以看看程序了。
*****************************************************
//同步的事件预定和处理
public static void syncEvent()
{
//创建事件查询,每隔一秒轮询一次
string Query="SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance isa 'Win32_Service' AND TargetInstance.StartMode = 'Manual' AND TargetInstance.Started = FALSE AND PreviousInstance.Started = TRUE";
WqlEventQuery query=new WqlEventQuery(Query);
ManagementEventWatcher watcher=new ManagementEventWatcher(query);
// 直到下一次事件发生
ManagementBaseObject e = watcher.WaitForNextEvent();
//显示返回事件的信息
Console.WriteLine(
"服务{0}刚被停止, 现在的状态为: {1},服务描述为: {2}",
((ManagementBaseObject)e["TargetInstance"])["Name"], ((ManagementBaseObject)e["TargetInstance"])["State"],((ManagementBaseObject)e["TargetInstance"])["Description"]);
//退出事件预定
watcher.Stop();
}
*****************************************************
应该说来上面的程序是比较简单的,为了测试结果,您可以将你的服务管理器中的任何一个手动启动的服务(或将其中一个服务改为手动启动)从启动状态改为停止,上面的程序就会有输出结果了。如果您选定的服务不是手动启动的,后者服务不是从启动状态改为停止,上面的事件查询将不会有任何结果,一直等待一个符合WQL查询条件的事件发生。
下面要讲的一个例子是计时器对象的事件预定和处理,有关计时器的知识,您可以阅读本书的计数器技术章节获取更为详细的信息。这个程序依照下列步骤:
1) 创建计时器事件的管理类对象;
2) 获取计时器的实例;
3) 设定计时器的参数;
4) 计时器开始定时;
5) 创建计时器事件查询的WqlEventQuery类型实例,并预定事件;
6) 挂接计时器事件处理方法;
7) 开始侦听计时器事件;
8) 停止预定计时器事件。
首先我们定义计时器事件的处理方法:
*****************************************************
//定义事件处理方法
public class EventHandler
{
public void HandleEvent(object sender, EventArrivedEventArgs e)
{
Console.WriteLine("Event arrived !");
}
}
*****************************************************
接下来的代码为计时器事件预定与处理的全部代码:
*****************************************************
//计时器事件预定与处理
public static void asyncEvent()
{
ManagementClass timerClass =
new ManagementClass("__IntervalTimerInstruction");
ManagementObject timer = timerClass.CreateInstance();
timer["TimerId"] = "Timer1";
timer["IntervalBetweenEvents"] = 1000;
timer.Put();
// 预定计时器事件
// 创建计时器事件查询
WqlEventQuery query =
new WqlEventQuery("__TimerEvent", "TimerId=\"Timer1\"");
// 初始化watcher,并预定query定义的事件
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
// 挂接事件的处理方法
watcher.EventArrived +=
new EventArrivedEventHandler((new EventHandler()).HandleEvent);
// 开始侦停事件
watcher.Start();
// 暂停线程执行,以使watcher侦听事件
System.Threading.Thread.Sleep(10000);
// 停止事件预定
watcher.Stop();
}
*****************************************************
正如我在前面所讲述的,计时器的实例是由订阅者自己定义的。