65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MOF 创建 WMI 永久事件订阅

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (18投票s)

2008年7月29日

CPOL

17分钟阅读

viewsIcon

100367

downloadIcon

759

本文展示了使用MOF设置永久事件订阅和始终接收WMI事件的一些示例。

目录

  1. 引言
  2. WQL事件查询
  3. 事件辅助类
  4. WITHIN和GROUP子句
  5. 临时事件使用者
  6. 永久事件订阅
  7. 使用WMI工具
  8. ActiveScriptEventConsumer类
  9. 关于字符串的说明
  10. TargetEvent和TargetInstance
  11. SMTPEventConsumer类
  12. LogFileEventConsumer类
  13. CommandLineEventConsumer类
  14. Win32_LocalTime类
  15. 最后说明
  16. 有用链接

1. 引言

WMI事件子系统允许您订阅WMI事件。WMI事件代表WMI数据的更改:如果您启动记事本,将创建一个Win32_Process WMI类的实例,并创建一个实例创建WMI事件。如果您从磁盘删除文件,将删除CIM_DataFile类的实例,并创建一个实例删除WMI事件。事实上,WMI数据的任何更改都可以用于创建事件,因此很容易看出WMI事件在系统管理中的作用。

除非您订阅它们,否则WMI不会为您创建事件。注册对WMI事件感兴趣的应用程序称为事件使用者。

事件使用者有两种类型:临时和永久。临时事件使用者通常是使用.NET Framework及其System.Management命名空间或WMI脚本库接收WMI事件的应用程序,这意味着它们仅在用户启动时接收事件。永久事件使用者有所不同 - 它们被设计为始终接收事件。临时和永久事件使用者都使用WMI事件查询来订阅它们感兴趣的事件。

2. WQL事件查询

与其他WMI查询一样,WMI事件查询使用WQL(WMI查询语言)发出。事件查询和其他查询类型之间存在一些差异,但最重要的是WMI事件查询使用WMI事件类。如果WMI类派生自__Event系统类,则该WMI类是事件类。因此,为了查看可以使用WMI事件完成哪些任务,您首先需要检查WMI事件类。但是,您如何做到这一点?由于所有事件类都派生自__Event系统类,因此您可以使用这样的查询

Select * From Meta_Class Where __This Isa "__Event"

尽管此查询包含对__Event类的引用,但它不是事件查询。实际上,它是一个WMI架构查询 - 它使用Meta_Class,一个代表WMI命名空间中所有类的特殊类。由于您不想要所有类,而只想__Event派生的类,因此您还需要添加WHERE子句。执行时,查询将返回一个WMI类列表,如下所示

. . . 
MSFT_WMI_GenericNonCOMEvent
MSFT_WmiSelfEvent
Msft_WmiProvider_OperationEvent
Msft_WmiProvider_ComServerLoadOperationEvent
Msft_WmiProvider_InitializationOperationFailureEvent
Msft_WmiProvider_LoadOperationEvent
Msft_WmiProvider_OperationEvent_Pre
Msft_WmiProvider_DeleteClassAsyncEvent_Pre
Msft_WmiProvider_GetObjectAsyncEvent_Pre
Msft_WmiProvider_AccessCheck_Pre
Msft_WmiProvider_CreateClassEnumAsyncEvent_Pre
Msft_WmiProvider_ExecQueryAsyncEvent_Pre
Msft_WmiProvider_CreateInstanceEnumAsyncEvent_Pre
Msft_WmiProvider_NewQuery_Pre
Msft_WmiProvider_DeleteInstanceAsyncEvent_Pre
Msft_WmiProvider_CancelQuery_Pre
Msft_WmiProvider_PutInstanceAsyncEvent_Pre
. . .

在我的测试Windows XP SP2机器上,该查询总共返回136个类。您计算机上的数字可能不同,但如果您仔细检查列表,您会注意到最常用的WMI类,如Win32_ProcessWin32_Service,并不在其中。

3. 事件辅助类

因此,您真正感兴趣的类并非派生自__Event类,但仍可在WMI事件查询中使用它们。您可以将所有WMI类用于事件查询,只是不能直接使用。为了在事件查询中使用非__Event派生的类,您需要使用其中一个辅助类,例如

  • __InstanceCreationEvent
  • __InstanceModificationEvent
  • __InstanceDeletionEvent

上述所有类都派生自__InstanceOperationEvent,并具有TargetInstance属性,该属性是对您希望接收事件通知的类的实例的引用。因此,如果您使用这样的查询

Select * From __InstanceCreationEvent
Where TargetInstance Isa "Win32_Process"

返回事件的TargetInstance属性将包含一个指向已创建的Win32_Process实例的引用。如果要引用Win32_Process.ExecutablePath属性,请使用__InstanceCreationEvent.TargetInstance.ExecutablePath属性。此外,__InstanceModificationEvent类具有PreviousInstance属性,该属性包含在WMI类实例修改前的副本的引用。派生自__InstanceOperationEvent的类及其TargetInstance属性使您可以在事件查询中使用所有WMI类。

4. WITHIN和GROUP子句

WMI事件子系统使用轮询机制进行事件传递。要指定轮询间隔,请使用WITHIN关键字,后跟轮询间隔(以秒为单位)

Select * From Win32_Process
Within 10
Where TargetInstance Isa "Win32_Process"

在此示例中,WMI最初枚举所有Win32_Process实例,并每十秒轮询一次更改。这意味着可能会错过一些事件:如果一个进程在不到十秒的时间内创建和销毁,它将不会引发事件。

Group子句导致WMI仅创建一个事件通知来表示一组事件。例如,此查询

Select * From __InstanceModificationEvent
Within 10
Where TargetInstance Isa
"Win32_PerfFormattedData_PerfOS_Processor"
Group Within 5
Having NumberOfEvents > 3

将创建一个事件,该事件表示在5秒内发生的所有Win32_PerfFormattedData_PerfOS_Processor的修改事件,但前提是事件数量大于3。

总结:

  • WQL事件查询使用派生自__Event系统类的类。
  • WQL事件查询使用WithinGroup子句。
  • WQL事件查询使用Isa关键字指定要接收事件的类。
  • WQL事件查询使用TargetInstancePreviousInstance访问感兴趣的WMI数据。

5. 临时事件使用者

临时事件使用者是任何请求WMI事件通知的应用程序。在大多数情况下,它是VBScript或使用System.Management命名空间的代码。这是一个订阅Win32_Process创建事件的VBScript示例

' VBScript source code

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "\\" & strComputer & "\root\cimv2")

Set colMonitoredProcesses = objWMIService. _        
ExecNotificationQuery("select * from __InstanceCreationEvent " _ 
& " Within 1 Where TargetInstance isa 'Win32_Process'")

Do
Set objLatestProcess = colMonitoredProcesses.NextEvent
Wscript.Echo objLatestProcess.TargetInstance.Name
Loop

尽管此代码有效,但至少存在三个缺点

  • 脚本进程需要一直运行。上面的脚本仅在运行时接收WMI事件,并且在运行时会消耗系统资源。如果您同时运行多个脚本,这可能会成为一个问题。
  • 进程容易被中断。像上面这样的脚本通常从命令提示符运行,因此它们很容易被中断,无论是通过关闭命令提示符窗口还是按Ctrl + C。
  • 即使CScript进程被中断,事件通知也不会被取消。这可能是最严重的缺点。

这是第三种情况的示例

' VBScript source code

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "\\" & strComputer & "\root\cimv2")

Set colEvents =  objWMIService.ExecNotificationQuery _
("Select * From __InstanceCreationEvent Within 2" _
& "Where TargetInstance Isa 'Win32_Directory' " _
& "And TargetInstance.Path = '\\Scripts\\'")

Do
Set objEvent = colEvents.NextEvent()
WScript.Echo objEvent.TargetInstance.Name
Loop

像这样的脚本通常从命令提示符运行。但是,即使您停止脚本,事件通知也不会被取消 - 您可以轻易地观察到这一点,因为软盘驱动器仍然每两秒闪烁一次(我称之为“FDD灯光秀”)。这不仅适用于文件系统监视脚本,也适用于所有其他脚本。在这种情况下,取消事件通知的唯一方法是停止Winmgmt服务本身,使用

net stop winmgmt

Windows防火墙服务依赖于Winmgmt,因此很容易想象这种情况可能会成为一个问题。

6. 永久事件订阅

WMI永久事件订阅可以解决所有这些问题。它不依赖于正在运行的进程(除了托管Winmgmt服务的svchost.exe)。要中断它,您需要了解WMI,因此不容易意外停止它,并且您可以随时取消它,而无需重新启动Winmgmt服务。其基础是,永久事件订阅是一组存储在CIM存储库中的静态WMI类。当然,您可以使用VBScript或.NET Framework的System.Management类来创建这些实例并设置永久事件订阅,但最简单的方法是(至少在我看来)使用MOF。这是一个MOF示例,您可以将其用作创建永久事件订阅的模板

// 1. Change the context to Root\Subscription namespace
//    All standard consumer classes are
//    registered there.

#pragma namespace("\\\\.\\root\\subscription")


// 2. Create an instance of __EventFilter class
//    and use it's Query property to store
//    your WQL event query.

instance of __EventFilter as $EventFilter
{
    Name  = "Event Filter Instance Name";
    EventNamespace = "Root\\Cimv2";
    Query = "WQL Event query text";
    QueryLanguage = "WQL";
};


// 3. Create an instance of __EventConsumer
//    derived class. (ActiveScriptEventConsumer
//    SMTPEventConsumer etc...) 

instance of __EventConsumer derived class as $Consumer
{
    Name = "Event Consumer Instance";
    // Specify any other relevant properties.
};


// 4. Join the two instances by creating
//    an instance of __FilterToConsumerBinding
//    class.

instance of __FilterToConsumerBinding
{
    Filter = $EventFilter;
    Consumer   = $Consumer;
}; 

要创建永久WMI事件订阅,您需要执行以下步骤

  • 将WMI上下文更改为Root\Subscription命名空间。从Windows XP开始,Microsoft建议将所有永久事件订阅存储在那里。
  • 创建__EventFilter类的实例。__EventFilter类派生自__IndicationRelated系统类,其主要目的是保存WQL事件查询。要创建__EventFilter的实例,请使用‘instance of’关键字。首先,使用其Name属性为类实例指定一个唯一名称。其次,指定其EventNamespace属性 - 通常,这将是Root\Cimv2命名空间,因为大多数有用类都位于那里。第三,将其Query属性设置为您想使用的WQL事件查询。
  • 创建__EventConsumer派生类的实例。这是代表事件使用者的实例。虽然可以创建自定义的__EventConsumer派生类,但这需要创建一个响应指定事件的COM对象,这对于大多数系统管理员来说可能不是一个可行的解决方案。幸运的是,Microsoft提供了一组标准的事件使用者类,如ActiveScriptEventConsumerLogFileEventConsumer等。在本文中,我将只使用标准的事件使用者类。
  • 通过创建__FilterToConsumerBinding类的实例来关联__EventFilter__EventConsumer派生类的实例。其Filter属性是对先前创建的__EventFilter类实例的引用,其Consumer属性是对标准事件使用者类实例之一的引用。

在本文的其余部分,我将尝试通过几个使用标准事件使用者类的永久事件订阅示例来指导您。

7. 使用WMI工具

WMI工具中包含一个名为WMI事件注册的工具,在处理永久订阅时非常有用:它允许您使用用户友好的界面浏览现有过滤器、使用者或计时器,并创建新的。您也可以使用此工具取消事件订阅。

首次打开此工具时,系统会提示您连接到Root\Cimv2命名空间,但请连接到Root\Subscription - 这里将创建大多数永久事件订阅。连接后,如果从最左侧的下拉列表中选择“Consumers”,您将在左侧窗格中看到所有可用的标准事件使用者类列表,因为它们已在此注册。

如果任何标准事件使用者类已存在实例,通过选择它,您可以在右侧窗格中查看可用的__EventFilter实例。如果任何__EventFilter实例与选定的使用者实例关联,则会选中它,因此,绿色的复选标记实际上代表__FilterToConsumerBinding类的实例。

本文中提供的所有永久事件订阅示例都是使用MOF创建的 - 您需要一个名为mofcomp.exe的工具将MOF文件中包含的实例定义存储到CIM存储库中。Mofcomp.exe存储在Windows目录中(通常是C:\Windows\System32\Wbem\),其基本语法是

mofcomp FileName.mof

8. ActiveScriptEventConsumer类

ActiveScriptEventConsumer类是标准事件使用者类之一:它允许您在事件传递给它时运行ActiveX脚本代码。要创建ActiveScriptEventConsumer类的实例,您需要为其属性赋值

  • Name:为ActiveScriptEventConsumer的实例提供一个唯一名称。
  • ScriptingEngine:这是将使用的脚本引擎的名称。尽管文档说明这可以是任何任意脚本引擎,但我使用的只有“VBScript”和“JScript”。
  • ScriptText:这是一个字符串属性,包含在事件传递给ActiveScriptEventConsumer实例时要执行的VBScript或JScript代码。
  • ScriptFileName:此属性保存要执行的VBScript或JScript文件的完整路径,在事件到达时执行。ScriptTextScriptFileName属性是互斥的。

作为测试,您可以创建一个事件使用者,当Win32_Process实例名为“notepad.exe”的实例创建时,该使用者会执行任意VBScript代码。要创建使用ActiveScriptEventConsumer的永久事件订阅

  • 将当前WMI命名空间更改为Root\Subscription
    #pragma namespace("\\\\.\\root\\subscription")
  • 创建__EventFilter类的实例以监视Win32_Process的创建
    instance of __EventFilter as $EventFilter
    {
        EventNamespace = "Root\\Cimv2";
        Name  = "New Process Instance Filter";
        Query = "Select * From __InstanceCreationEvent Within 2" 
                "Where TargetInstance Isa \"Win32_Process\" "
                "And Targetinstance.Name = \"notepad.exe\" ";
        QueryLanguage = "WQL";
    };

    在这种情况下,您将收到一个__InstanceCreationEvent类实例,但来自Root\Cimv2命名空间,因为Win32_Process类位于那里。

  • 创建ActiveScriptEventConsumer类的实例
    instance of ActiveScriptEventConsumer as $Consumer
    {
        Name = "TestConsumer";
        ScriptingEngine = "VBScript";
        ScriptText = 
        "Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n"
        "Set objFile = objFSO.OpenTextFile(\"c:\\log.txt\", 8, True)\n"
        "objFile.WriteLine Time & \" \" & \" Notepad started\"\n"
        "objFile.Close\n";
    };

    分配给其ScriptText属性的VBScript代码只需将notepad.exe进程创建时间记录到文本文件中。

  • 使用__FilterToConsumerBinding类绑定前面创建的两个实例
    instance of __FilterToConsumerBinding
    {
        Consumer   = $Consumer;
        Filter = $EventFilter;
    }

当您使用mofcomp.exe编译上面的MOF时,每次打开记事本时,notepad.exe进程的创建时间都会记录到c:\log.txt文件中。如果文件尚不存在,它将在收到第一个事件通知时创建。

您也可以使用ScriptFileName属性代替ScriptText

instance of ActiveScriptEventConsumer as $Consumer
{
    Name = "ExternalScriptConsumer";
    ScriptingEngine = "VBScript";
    ScriptFileName = "C:\\Consumer.vbs";
};

在这种情况下,您还需要一个外部脚本文件:c:\Consumer.vbs

在使用ActiveScriptEventCosumer创建VBScript或JScript脚本时,您需要注意一些限制

  • ActiveScriptEventConsumer不使用Windows脚本主机(WSH),而WSH在系统管理脚本中被广泛使用。这意味着您不能使用WScript对象或其任何属性和方法(如WScript.CreateObjectWScript.Sleep等)。
  • 脚本不能产生任何屏幕输出,这意味着您不能使用VBScript的MsgBox函数。
  • 脚本没有网络访问权限。
  • 脚本无法使用任何用户特定数据,例如环境变量或网络共享。

9. 关于字符串的说明

设置永久事件订阅时,您很可能需要使用字符串,因此快速说明如下

MOF字符串是用双引号括起来的字符序列。连续的字符串会连接在一起,所以这个

"Select * From __InstanceCreationEvent "
"Within 30 "

变成

"Select * From __InstanceCreationEvent Within 30 "

您还可以使用以下转义序列

\b   backspace 
\t   horizontal 
\n   linefeed 
\f   form feed 
\r   carriage return 
\"   double quote 
\'   single quote 
\\   backslash

10. TargetEvent和TargetInstance

ActiveScriptEventConsumer实例执行的脚本可以访问名为TargetEvent的环境变量,该变量保存对事件类的引用

instance of ActiveScriptEventConsumer as $Consumer
{
    Name = "TargetEventConsumer";
    ScriptingEngine = "VBScript";
    ScriptText = 
    "Const ForReading = 1\n"
    "Const ForWriting = 2\n"
    "\n"
    "Set objFso = CreateObject(\"Scripting.FileSystemobject\")\n"
    "Set objStream = objFso.OpenTextFile( _\n"
    "    TargetEvent.TargetInstance.Name, ForReading, False)\n"
    "\n"
    "strContent = objStream.ReadAll()\n"
    "objStream.Close\n"
    "\n"
    "Set objStream = objFso.OpenTextFile( _\n"
    "    TargetEvent.TargetInstance.Name, ForWriting, False)\n"
    "\n"
    "objStream.Write( _\n"
    "    Replace(strContent, \"127.0.0.1\", \"Localhost\"))\n"
    "objStream.Close\n";
};

事件类通常是各种__InstanceOperationEvent派生类之一,其TargetInstance属性反过来是对已创建对象的实际类的实例的引用。如果该类是,例如,CIM_DataFile,则需要使用以下方法来访问其Name属性

TargetEvent.TargetInstance.Name

11. SMTPEventConsumer类

每次事件传递给它时,此类都会发送一封电子邮件。要创建SMTPEventConsumer类的实例,请为其属性赋值

  • Name:为实例提供一个唯一名称。
  • SMTPServer:将通过其发送消息的SMTP服务器的名称。
  • ToLine:电子邮件的收件人行。
  • FromLine:电子邮件的发件人行。
  • Subject:邮件的主题行。
  • Message:电子邮件的正文。

例如,设置一个使用SMTPEventConsumer类在每次打印机状态更改时发送电子邮件的永久事件订阅。要在永久事件订阅中使用SMTPEventConsumer

  • 将上下文更改为Root\Subscription命名空间
    #pragma namespace("\\\\.\\root\\subscription")
  • 创建__EventFilter类的实例
    instance of __EventFilter as $EventFilter
    {
        EventNamespace = "Root\\Cimv2";
        Name  = "SMTPEventFilter";
        Query = "Select * From __InstanceModificationEvent "
                "Within 2 " 
                "Where TargetInstance Isa \"Win32_Printer\" "
                "And (TargetInstance.PrinterStatus = 1 "
                "Or TargetInstance.PrinterStatus = 2) "
                "And Not (PreviousInstance.PrinterStatus = 1 "
                "Or PreviousInstance.PrinterStatus = 2)";
        QueryLanguage = "WQL";
    };

    使用上面的WQL查询,您可以订阅Win32_Printer类实例的修改事件。注意__InstanceModificationEvent.PreviousInstance属性的使用,该属性包含在Win32Printer实例更改之前的副本。这对于比较实例属性更改前后很有用。在本例中,我们只关心Win32_Printer.PrinterStatus值从其他值更改为1或2的事件。

  • 创建SMTPEventConsumer类的实例
    instance of SMTPEventConsumer as $Consumer
    {
        Name = "Printer Error Event Consumer";
        SMTPServer = "SMTPServerName";
        ToLine = "Recipient@nn.com";
        FromLine = "Sender@nn.com";
        Subject = "Printer Error!";
        Message = "An error is detected in one of the printers!\n"
                  "Printer Name: %TargetInstance.Name%\n"
                  "Server:       %TargetInstance.__Server%\n"
                  "Event Date:   %TIME_CREATED%";
    };

    如果您查看SMTPEventConsumer类MOF代码,您会发现其大多数属性都用Template限定符标记。这意味着您可以使用WMI标准字符串模板来设置其值。使用标准字符串模板,您可以访问事件类的属性,就像您可以使用TargetEvent环境变量与ActiveScriptEventConsumer一样。例如,如果TargetInstanceWin32_Printer,则此

    "Printer Name: %TargetInstance.Name%"

    将被翻译成类似

    "Printer Name: HP LaserJet III PostScript Plus v2010.118"

    同样,这个

    "Event Date:   %TIME_CREATED%"

    将变成

    "Event Date:    128611400690000000"

    __InstanceModificationEvent.Time_Created是自1601年1月1日以来100纳秒间隔的数量,因此如果您想将其转换为可读格式,可能需要做一些工作。

  • 通过创建__FilterToConsumerBinding类的实例来绑定两个实例
    instance of __FilterToConsumerBinding
    {
        Consumer   = $Consumer;
        Filter = $EventFilter;
    };

12. LogFileEventConsumer类

LogFileEventConsumer类在每次事件传递给它时,将自定义字符串写入文本文件。重要属性

  • Name:为实例提供一个唯一名称。
  • Text:包含事件到达时要写入日志文件的文本。您可以使用标准字符串模板来构建它。
  • Filename:要将Text属性的值写入的文本文件的路径。

LogFileEventConsumer的一个示例用法可以是记录Windows服务状态的更改。要在永久事件订阅中使用LogFileEventConsumer

  • 将上下文更改为Root\Subscription命名空间
    #pragma namespace("\\\\.\\root\\subscription")
  • 创建__EventFilter类的实例,该实例监视Win32_Service修改事件
    instance of __EventFilter as $EventFilter
    {
        EventNamespace = "Root\\Cimv2";
        Name  = "Service State Event Filter";
        Query = "Select * From __InstanceModificationEvent "
                "Within 2 " 
                "Where TargetInstance Isa \"Win32_Service\" "
                "And TargetInstance.State <> "
                "PreviousInstance.State";
        QueryLanguage = "WQL";
    };
  • 创建LogFileEventConsumer的实例
    instance of LogFileEventConsumer as $Consumer
    {
        Name = "Service State Log Consumer";
        Filename = "c:\\scripts\\ServiceStateLog.csv";
        IsUnicode = true;
        Text = "\"%TargetInstance.Name%\","
               "\"%PreviousInstance.State%\","
               "\"%TargetInstance.State%\","
               "\"%TIME_CREATED%\"";
    };
  • 使用__FilterToConsumerBinding类绑定两个实例
    instance of __FilterToConsumerBinding
    {
        Consumer   = $Consumer;
        Filter = $EventFilter;
    };

在为LogFileEventConsumer.Text属性赋值时,请使用WMI标准字符串模板访问与事件相关的数据。

13. CommandLineEventConsumer类

CommandLineEventConsumer类在事件传递给它时启动任意进程。重要属性是

  • Name:为实例提供一个唯一名称。
  • ExecutablePath:可执行文件的路径。此属性可以为Null,但即使您为此属性赋值,仍需要在CommandLineTemplate属性的开头包含可执行文件的路径。
  • CommandLineTemplate:标准字符串模板,指定要启动的可执行文件,后跟任何命令行参数。创建它时可以使用标准字符串模板。

这是一个监视PNP设备更改的CommandLineEventConsumer MOF示例。要创建使用CommandLineEventConsumer的永久事件订阅

  • 将WMI上下文更改为Root\Subscription命名空间
    #pragma namespace("\\\\.\\root\\subscription")
  • 创建__EventFilter类的实例,该实例检测Win32_PNPEntity实例的创建
    instance of __EventFilter as $EventFilter
    {
        EventNamespace = "Root\\Cimv2";
        Name  = "Test Command Line Event Filter";
        Query = "Select * From __InstanceCreationEvent "
                "Within 2 " 
                "Where TargetInstance Isa \"Win32_PNPEntity\" ";
        QueryLanguage = "WQL";
    };
  • 创建CommandLineEventConsumer类的实例
    instance of CommandLineEventConsumer as $Consumer
    {
        Name = "Test CommandLine Event Consumer";
        RunInteractively = false;
        CommandLineTemplate = "cmd /c "
            "WMIC /Output:"
            "C:\\HWLogs\\PNPDeviceLog%TIME_CREATED%.html "
            "Path Win32_PNPEntity "
            "Get Caption, DeviceId, PNPDeviceId "
            "/Format:HTable.xsl";
    };

    CommandLineEventConsumer实例使用WMI命令行实用程序(WMIC)创建一个简单的HTML文件,该文件包含每次创建新的Win32_PNPEntity实例时所有Win32_PNPEntity实例的列表。

  • 使用__FilterToConsumerBinding绑定实例
    instance of __FilterToConsumerBinding
    {
        Consumer   = $Consumer;
        Filter = $EventFilter;
    };

14. Win32_LocalTime类

Win32_LocalTime类是一个例外:它不派生自__Event类,但您仍可以在WQL事件查询中使用它,这意味着您也可以使用它来设置永久事件订阅。Win32_LocalTime类的一个有趣用途是模仿Windows计划程序服务。要创建订阅Win32_LocalTime事件的永久事件订阅

  • 将上下文更改为Root\Subscription命名空间
    #pragma namespace("\\\\.\\root\\subscription")
  • 创建__EventFilter类的实例
    instance of __EventFilter as $EventFilter
    {
        EventNamespace = "Root\\Cimv2";
        Name  = "Sample Timer Event Filter";
        Query = "Select * From __InstanceModificationEvent "
                "Where TargetInstance Isa \"Win32_LocalTime\" "
                "And TargetInstance.Hour = 18 "
                "And TargetInstance.Minute = 10 "
                "And TargetInstance.Second = 30";
        QueryLanguage = "WQL";
    };

    使用此过滤器订阅Win32_LocalTime修改事件。在查询中,您可以使用任何Win32_LocalTime属性的组合:DayDayOfWeekHourMillisecondsMinuteMonthQuarterSecondWeekInMonthYear

  • 创建__EventConsumer派生类的实例
    instance of CommandLineEventConsumer as $Consumer
    {
        Name = "Test CommandLine Event Consumer";
        RunInteractively = false;
        CommandLineTemplate = "cmd /c "
            "C:\\Backup\\LocalBackup.bat";
    };

    在这种情况下,它是CommandLineEventConsumer类的实例,但它可以是任何标准使用者类。

  • 通过创建__FilterToConsumerBinding类的实例来绑定__EventFilterCommandLineEventConsumer实例
    instance of __FilterToConsumerBinding
    {
        Consumer   = $Consumer;
        Filter = $EventFilter;
    };

15. 最终说明

与临时事件订阅相比,永久事件订阅有几个优点,但也有一个缺点:临时事件订阅更容易调试。如果您使用System.Management命名空间创建订阅WMI事件的应用程序,您将拥有所有Visual Studio调试工具。如果您使用VBScript,您可以将WQL事件查询与其余代码分开测试,并且可以从WMI获得有意义的(至少有时)错误消息。在测试永久事件订阅时,调试信息的唯一来源是WMI事件子系统日志文件,名为Wbemess.log(通常位于C:\Windows\System32\Wbem\Logs\目录) - 事件过滤器和事件使用者实例中检测到的所有错误都会在此记录,并且消息并不总是容易理解。因此,最好先使用System.Management或VBScript测试您想用于永久事件订阅的WQL查询。

永久事件订阅可能很有用,但如果您不小心使用它,它可能会消耗过多的系统资源并变得效率低下。有两种方法可以解决这个问题

  • 使用Within子句增加轮询间隔。在本文示例中,我使用了非常短的轮询间隔,但根据您的要求,您可以随意增加它。
  • 使用Where子句使WQL查询更具选择性。例如,此查询
    Select * From __InstanceCreationEvent
    Where TargetInstance Isa "CIM_DataFile"
    And TargetInstance.Path = "\\Logs\\"

    将不如这个有效

    Select * From __instanceCreationEvent
    Where TargetInstance Isa "CIM_DataFile"
    And TargetInstance.Drive = "C:"
    And TargetInstance.Path = "\\Logs\\"
    And TargetInstance.Extension = "Log"

包含文件系统类(如CIM_DataFileWin32_Directory)的查询通常非常消耗资源:监视几百个文件的查询可能会明显降低系统的速度。

WQL是SQL的一个版本,对于SQL查询,通常建议不要选择表中的所有字段(使用'*'),除非您确实需要它们。我没有用WQL查询测试过这个建议,但我不认为这个建议适用于WQL。

16. 有用链接

关于永久事件订阅的文档不多,但您可以在MSDN中找到一些

© . All rights reserved.