目录

转载自:https://www.codeproject.com/Articles/28226/Creating-WMI-Permanent-Event-Subscriptions-Using-M#7.UsingWMITools6

使用mof创建wmi永久事件

目录

  1. 内容
  2. 介绍
  3. WQL事件查询
  4. 事件助手类
  5. WITHIN和GROUP子句
  6. 临时事件消费者
  7. 永久事件订阅
  8. 使用WMI工具
  9. ActiveScriptEventConsumer类
  10. 关于字符串的说明
  11. TargetEvent和TargetInstance
  12. SMTPEventConsumer类
  13. LogFileEventConsumer类
  14. CommandLineEventConsumer类
  15. Win32_LocalTime类
  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查询一样,使用WQL(WMI查询语言)发布WMI事件查询。事件查询和其他查询类型之间有几个区别,但最重要的是WMI事件查询使用WMI事件类。如果WMI类是从__Event系统类派生的,则它是一个事件类。因此,要查看使用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_Process或Win32_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

将创建一个事件,该事件代表Win32_PerfFormattedData_PerfOS_Processor在5秒内发生的所有修改事件,但仅当事件数量大于3时才会发生。
所以:

5.临时活动消费者

临时事件使用者是从WMI请求事件通知的任何应用程序。在大多数情况下,它是使用System.Management名称空间的VBScript或代码。以下是订阅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

尽管此代码有效,但它至少有三个缺点:

  1. 脚本进程需要一直运行。上述脚本仅在运行时才会收到WMI事件,并且在运行时会占用系统资源。如果您有多个脚本同时运行,这可能会成为问题。
  2. 这个过程很容易中断。上述脚本通常从命令提示符运行,因此可以通过关闭命令提示框或按Ctrl + C轻松中断它们。
  3. 即使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 Light Show')。这不仅适用于文件系统监控脚本,还适用于其他方面。在这种情况下取​​消事件通知的唯一方法是使用以下命令停止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事件订阅,您需要执行以下步骤:

  1. 将WMI上下文更改为Root\Subscription命名空间。从Windows XP开始,微软建议在那里存储所有永久事件订阅。
  2. 创建__EventFilter类的一个实例。本__EventFilter类是从派生__IndicationRelated系统类,其主要目的是举行WQL事件查询。要创建一个实例__EventFilter,请使用' instance of'关键字。
  3. 首先,您使用其Name属性为类实例指定一个唯一的名称。
  4. 其次,指定它的EventNamespace属性 - 通常,这将是Root\Cimv2命名空间,因为大多数有用的类都位于那里。
  5. 第三,将其Query属性设置为要使用的WQL事件查询。

创建__EventConsumer派生类的实例。这是表示事件使用者的实例。虽然可以创建自定义__EventConsumer派生类,但这需要创建一个对特定事件有反应的COM对象,这对大多数系统管理员来说可能不是一个可行的解决方案。幸运的是,微软提供了一组标准的事件消费类,像ActiveScriptEventConsumer,LogFileEventConsumer等在这篇文章中,我将仅使用标准事件消费类。
关联的实例__EventFilter,并__EventConsumer通过创建的实例派生类__FilterToConsumerBinding的类。其Filter属性是对以前创建的__EventFilter类实例的Consumer引用,其属性是对标准事件使用者类实例之一的引用。
在本文的其余部分,我将尝试引导您使用标准事件消费者类的几个永久事件订阅样本。

7.使用WMI工具

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

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

如果任何标准事件消费者类的实例已经存在,那么通过选择它,您可以__EventFilter在右侧窗格中查看可用实例。如果任何__EventFilter实例与所选消费者实例连接,则会进行检查,因此,绿色复选标记实际上代表__FilterToConsumerBinding该类的一个实例。

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

mofcomp FileName.mof

8.ActiveScriptEventConsumer类

该ActiveScriptEventConsumer班是标准的事件消费者的一类:它可以让你无论何时事件被传递到它运行ActiveX脚本代码。要创建ActiveScriptEventConsumer类的实例,您需要为其属性指定值:

作为测试,您可以创建一个事件使用者,只要创建了一个Win32_Process名为' notepad.exe ' 的实例,就会执行一些任意的VBScript代码。要创建使用以下内容的永久事件订阅ActiveScriptEventConsumer:将当前的WMI名称空间更改为Root\Subscription:

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

创建一个EventFilter类的实例来监视Win32_Process创建: <code>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";

}; </code>

在这种情况下,您将收到一个__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文件中。如果文件尚不存在,则会在收到第一个事件通知时创建。而不是ScriptText,您也可以使用该ScriptFileName属性:

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

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

创建VBScript或JScript脚本以供使用时ActiveScriptEventCosumer,您需要了解一些限制:

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类的实例,请为其属性指定值:

例如,设置一个永久事件订阅,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 = "[email protected]";
    FromLine = "[email protected]";
    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。举例来说,如果TargetInstance是Win32_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每次将事件传送到文本文件时,该类都将自定义字符串写入文本文件。重要属性:

示例用法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当一个事件传递给它时,该类将启动一个任意进程。重要的属性是:

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

将WMI上下文更改为Root\Subscription名称空间:

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

创建一个EventFilter检测Win32_PNPEntity实例创建的类的实例: <code> 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";

}; </code>

创建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 Scheduler服务。要创建订阅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属性:Day,DayOfWeek,Hour,Milliseconds,Minute,Month,Quarter,Second,WeekInMonth,和Year。

创建__EventConsumer派生类的实例:

instance of CommandLineEventConsumer as $Consumer
{
    Name = "Test CommandLine Event Consumer";
    RunInteractively = false;
    CommandLineTemplate = "cmd /c "
        "C:\\Backup\\LocalBackup.bat";
};

在这种情况下,它是CommandLineEventConsumer类的一个实例,但它可以是任何标准的消费者类。

通过创建类的实例来绑定__EventFilter和CommandLineEventConsumer实例__FilterToConsumerBinding:

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

15.最终说明

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

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

  1. 使用该Within子句增加轮询间隔。在这里提供的示例中,我使用了非常短的轮询间隔,但根据您的要求,您可以根据自己的意愿增加它。
  2. 使用该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_DataFile或Win32_Directory可以是非常消耗资源,一般来说:监视一对夫妇数百个文件,可以显着降低系统性能的查询。

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