[自学][Windows C++]WMI 永久事件处理对象的命令、脚本与日志配置
WMI 永久事件处理对象的命令、脚本与日志配置相关功能已经加入KswordARK项目地址https://github.com/KSwordDEV/KSword本篇要完成的是只读枚举ROOT\subscription中的四类永久事件处理对象在控制台输出对象类型、名称及命令、脚本或日志字段。查询或枚举失败时输出 HRESULT属性不可读时输出占位文本。命令行对象提供命令模板和工作目录。脚本对象提供脚本文本与脚本引擎。日志对象只写文件或事件日志不能按启动命令解释。要想完成这项读取分为四步初始化 COM 并设置进程默认安全参数。连接ROOT\subscription并设置 WMI 服务代理安全参数。分别查询四类事件处理对象限制单次读取的等待时间。按VARIANT的真实类型转换属性并输出文本。这四步从连接环境开始到属性输出结束。类名、对象名称和每个属性的占位结果需要一同查看才能分清没有实例、查询失败和属性不可读这三种情况。一、先理解四步要读取的对象WMI 是 Windows 的管理对象系统。它把系统状态、配置与管理操作组织成可查询对象。客户端通过 COM 接口连接 WMI 服务再在指定命名空间中查询类和实例。永久事件订阅是其中一类配置涉及命名空间、类、实例、属性、提供程序、查询和对象引用。永久订阅的运行路径由三个对象连接。Filter 描述事件条件。Binding 保存 Filter 与 Consumer 的对象路径引用。Consumer 描述事件匹配后执行命令、运行脚本或写日志等处理方式。事件来源或系统状态变化 - __EventFilter 匹配 WQL 条件 - __FilterToConsumerBinding 关联对象 - Consumer 按自身类型处理命令、脚本或日志枚举程序只读取事件处理对象的实例及其属性。它不会触发事件也不能证明 Filter、Binding 已完整配置或Consumer已成功处理事件。初始化 COM - 连接 ROOT\subscription - 查询事件处理对象类 - 读取每个实例的 __CLASS 和属性 - 按 VARIANT 类型解释 - 保存对象路径、来源和 HRESULT命名空间是 WMI 对象的逻辑容器。ROOT\CIMV2保存大量常见的系统管理类例如进程、服务、磁盘和网络适配器。ROOT\subscription保存永久事件订阅相关的系统类和实例。命名空间路径决定查询范围同名类在不同命名空间可以表示不同的对象集合。ROOT CIMV2 Win32_Process 进程实例 Win32_Service 服务实例 其它系统管理类 subscription __EventFilter 永久订阅过滤器 __FilterToConsumerBinding CommandLineEventConsumer 其它永久事件处理对象类类描述对象具有哪些属性和方法实例是类的一条实际记录。CommandLineEventConsumer是命令行事件处理对象类名称为Example的一条配置是该类的实例。Name、CommandLineTemplate、ExecutablePath、WorkingDirectory是这个实例的属性。读取时必须保留类名和实例名称属性名相同并不代表来自同一类。提供程序负责把某些 WMI 类连接到真实数据或管理功能。WMI 服务接收查询后根据类所在命名空间调用相应提供程序或读取仓库中的永久对象。客户端无需直接调用驱动、注册表或服务控制器也不能假设所有类都由同一种底层来源提供。WQL 是 WMI 查询语言语法接近 SQL。SELECT Name FROM CommandLineEventConsumer表示从指定类的实例中读取Name属性。WQL 本身只描述要读取哪些对象和属性命名空间、连接权限和提供程序可用性仍由 WMI 环境决定。连接ROOT\subscription 查询SELECT Name, CommandLineTemplate FROM CommandLineEventConsumer 结果该类的零个或多个实例 属性Name、CommandLineTemplate 等带类型的值WMI 属性使用VARIANT返回。字符串通常为VT_BSTR整数、布尔值、数组和对象引用使用其它类型。属性缺失、空值和读取失败需要分开表示。把任何属性都当成 C 字符串会丢失类型信息也可能访问错误的联合成员。永久事件订阅由三种对象协作。__EventFilter保存事件条件和 WQL。Consumer类实例保存事件发生后的处理方式。__FilterToConsumerBinding保存两者之间的关联。这类处理对象只说明可执行、脚本或写日志等处理端。触发条件来自 Filter关联关系来自 Binding。本篇只读取这类对象实例。连接、查询和类型解释都保持只读。对象存在不代表其对应的 Filter、Binding 或实际事件触发已经成功三个层面需要分别查询。二、第一步初始化 COM 与进程安全1. COM 初始化和安全设置先于 WMI 查询初始化 COM 的 API 为当前线程建立 WMI 调用环境。S_OK与S_FALSE表示可继续使用 COM。RPC_E_CHANGED_MODE表示线程已采用另一种 COM 模型读取时需要避免重复清理。// 意义为当前线程初始化 COM。// 返回S_OK 或 S_FALSE 成功。RPC_E_CHANGED_MODE 表示已有不同模型。HRESULTCoInitializeEx(LPVOID pvReserved,DWORD dwCoInit);// 意义撤销当前线程一次成功的 COM 初始化。// 返回无。只能与 S_OK 或 S_FALSE 的 CoInitializeEx 对应。voidCoUninitialize(void);// 意义设置本进程默认 COM 安全级别。// 返回S_OK 成功。RPC_E_TOO_LATE 表示安全设置已由更早的调用完成。HRESULTCoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc,LONG cAuthSvc,SOLE_AUTHENTICATION_SERVICE*asAuthSvc,void*pReserved1,DWORD dwAuthnLevel,DWORD dwImpLevel,void*pAuthList,DWORD dwCapabilities,void*pReserved3);正确做法接受RPC_E_TOO_LATE并且只在CoInitializeEx成功时调用CoUninitialize。任务、WMI 和其它 COM 组件可能已经设置过同一进程的安全参数。constHRESULT initStatusCoInitializeEx(nullptr,COINIT_MULTITHREADED);constboolneedUninitializeSUCCEEDED(initStatus);if(FAILED(initStatus)initStatus!RPC_E_CHANGED_MODE){return;}constHRESULT securityStatusCoInitializeSecurity(nullptr,-1,nullptr,nullptr,RPC_C_AUTHN_LEVEL_DEFAULT,RPC_C_IMP_LEVEL_IMPERSONATE,nullptr,EOAC_NONE,nullptr);if(FAILED(securityStatus)securityStatus!RPC_E_TOO_LATE){if(needUninitialize)CoUninitialize();return;}错误示例把RPC_E_TOO_LATE当成连接失败。这个状态说明安全设置已存在可以继续使用已有设置。直接终止会遗漏本来可读的对象。完成第一步后当前线程已经具备调用 COM 的环境进程也已有可用的默认安全设置。接下来要连接保存永久订阅对象的命名空间才能取得实际执行查询的 WMI 服务接口。三、第二步连接 ROOT\subscription 并设置代理安全2. 连接 ROOT\subscription 并取得服务接口创建 COM 类的 API 根据 CLSID 返回指定接口。CLSID_WbemLocator用于创建IWbemLocator随后由 locator 连接 WMI 命名空间。接口指针需要调用Release。// 意义创建 WMI locator COM 对象。// 返回S_OK 成功。失败 HRESULT 表示类或接口不可用。HRESULTCoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter,DWORD dwClsContext,REFIID riid,LPVOID*ppv);// 意义连接 WMI 命名空间并得到服务接口。// 返回S_OK 成功。ppNamespace 接收 IWbemServices调用方 Release。HRESULTIWbemLocator::ConnectServer(BSTR strNetworkResource,BSTR strUser,BSTR strPassword,BSTR strLocale,LONG lSecurityFlags,BSTR strAuthority,IWbemContext*pCtx,IWbemServices**ppNamespace);// 意义创建一个由 OLE 自动化分配器管理的 BSTR。// 返回成功时为新 BSTR内存不足时为 nullptr。必须使用下方匹配的释放函数。BSTRSysAllocString(constOLECHAR*psz);// 意义释放 SysAllocString 或 COM getter 返回的 BSTR。// 返回无。传入 nullptr 安全。voidSysFreeString(BSTR bstrString);// 意义为已连接的 WMI 服务代理设置身份验证和模拟级别。// 返回S_OK 成功。失败 HRESULT 表示当前令牌或代理安全设置不可用。HRESULTCoSetProxyBlanket(IUnknown*pProxy,DWORD dwAuthnSvc,DWORD dwAuthzSvc,OLECHAR*pServerPrincName,DWORD dwAuthnLevel,DWORD dwImpLevel,RPC_AUTH_IDENTITY_HANDLE pAuthInfo,DWORD dwCapabilities);正确做法使用ROOT\subscription再给服务代理设置模拟级别。该命名空间保存永久订阅对象。ROOT\CIMV2中的普通系统类不能代替它。IWbemLocator*locatornullptr;IWbemServices*servicesnullptr;if(SUCCEEDED(CoCreateInstance(CLSID_WbemLocator,nullptr,CLSCTX_INPROC_SERVER,IID_IWbemLocator,reinterpret_castvoid**(locator)))){BSTR pathSysAllocString(LROOT\\subscription);constHRESULT connectedlocator-ConnectServer(path,nullptr,nullptr,nullptr,WBEM_FLAG_CONNECT_USE_MAX_WAIT,nullptr,nullptr,services);SysFreeString(path);if(SUCCEEDED(connected)){CoSetProxyBlanket(services,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,nullptr,RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,nullptr,EOAC_NONE);}locator-Release();}错误示例省略代理安全设置。服务接口已经返回时后续查询仍可能因当前令牌的模拟级别不足失败。连接和代理安全是两个独立步骤。完成第二步后已经取得了ROOT\subscription的服务接口并为这个代理设置了当前令牌所需的安全级别。接下来需要按对象类型执行独立查询才能避免把日志配置当成命令配置。四、第三步分别读取四类对象3. WQL 查询需要有限等待时间执行查询的方法返回枚举器。WBEM_FLAG_FORWARD_ONLY降低内存使用WBEM_FLAG_RETURN_IMMEDIATELY使 WMI 异步准备结果。枚举器的Next调用需要有限超时异常 WMI 提供者不能无限阻塞读取线程。// 意义执行只读 WQL 查询并返回结果枚举器。// 返回S_OK 成功。ppEnum 接收 IEnumWbemClassObject调用方 Release。HRESULTIWbemServices::ExecQuery(constBSTR strQueryLanguage,constBSTR strQuery,LONG lFlags,IWbemContext*pCtx,IEnumWbemClassObject**ppEnum);// 意义从 WMI 枚举器读取至多 lRequested 个对象。// 返回WBEM_S_NO_ERROR 成功。WBEM_S_TIMEDOUT 表示等待超时。HRESULTIEnumWbemClassObject::Next(LONG lTimeout,ULONG uCount,IWbemClassObject**apObjects,ULONG*puReturned);正确做法为每类事件处理对象使用独立 WQL。CommandLineEventConsumer读取CommandLineTemplate、ExecutablePath、WorkingDirectory。ActiveScriptEventConsumer读取ScriptText、ScriptingEngine。LogFileEventConsumer读取Filename、Text。NTEventLogEventConsumer读取SourceName、EventID、Category。constwchar_t*queryLSELECT Name, CommandLineTemplate, ExecutablePath, WorkingDirectory LFROM CommandLineEventConsumer;// 每次 Next 最多等待五秒。连续超时达到上限时记录类名并继续其它对象类。HRESULT statusenumerator-Next(5000,1,object,returned);if(statusWBEM_S_TIMEDOUTreturned0){// 记录超时不把它当作“该类没有对象”。}错误示例把日志处理对象的文件名当作启动映像。LogFileEventConsumer的Filename是写入目标NTEventLogEventConsumer的SourceName是事件来源。这两类对象记录日志不提供可执行命令。完成第三步后已经按类拿到对象实例和对应字段。实例中的每个属性仍是一段带类型的数据接下来需要检查VARIANT才能安全取得文本、数值或空值。五、第四步按属性类型转换并输出文本4. WMI 属性使用 VARIANT 返回读取属性的方法把值写入VARIANT。VT_BSTR保存文本整数和布尔值使用各自字段。每次读取后必须调用VariantClear因为属性可能携带 BSTR、接口或数组资源。// 意义读取 WMI 对象的一个命名属性。// 返回S_OK 成功。pVal 接收 VARIANT调用方必须 VariantClear。HRESULTIWbemClassObject::Get(LPCWSTR wszName,LONG lFlags,VARIANT*pVal,CIMTYPE*pType,LONG*plFlavor);// 意义把 VARIANT 初始化为空值供 COM 方法写入。// 返回无。每个初始化后的 VARIANT 最终都需要清理内部资源。voidVariantInit(VARIANTARG*pvarg);// 意义释放 VARIANT 内部的 BSTR、数组、接口等资源。// 返回S_OK 成功。应在不再读取属性后调用。HRESULTVariantClear(VARIANTARG*pvarg);正确做法初始化 VARIANT、读取属性、按vt分支解释再清理资源。空值、缺失字段和查询失败要保留不同状态不能都替换为空字符串。VARIANT value{};VariantInit(value);if(SUCCEEDED(object-Get(LScriptText,0,value,nullptr,nullptr))){if(value.vtVT_BSTRvalue.bstrVal!nullptr){// 保存 ActiveScriptEventConsumer 的原始脚本文本。}}VariantClear(value);错误示例把VARIANT强制转换为BSTR。EventID、Category等字段可能为整数直接访问bstrVal会读取错误的联合成员。完成第四步后读取结果已经按属性的真实类型转换为文本。最后还要把对象类别、触发条件和实际执行分别解释避免从一个静态对象推断事件已经触发。六、读取结果的解释边界5. 事件处理对象本身不说明触发条件对象类型要通过类名记录。命令行对象和脚本对象包含可执行或可解释内容。日志对象只表达记录动作。字段语义由类名决定。触发条件要与 Filter、Binding 分开读取。处理对象只定义处理方式。__EventFilter定义 WQL 条件。__FilterToConsumerBinding才把两者关联。仅看到处理对象不能推导它何时触发。结果已经能够说明每个对象属于哪一类、保存了哪些原始属性以及本次读取是否成功。Filter、Binding 和实际事件处理仍属于另外三类证据不能用这些对象的枚举结果替代。完整可运行程序在附件https://wangweicm.lanzouu.com/izqvX3xdeqxi

相关新闻

2026年7月2日星座运势与星象解析

2026年7月2日星座运势与星象解析

1. 星座运势解析:2026年7月2日星象概览2026年7月2日将迎来一次罕见的"木星-海王星三分相",这是每13年才出现一次的重要星象。当天太阳位于巨蟹座10,与位于双鱼座的天王星形成60和谐相位,同时月亮正在天蝎座运行。这种星…

2026/10/14 12:09:47 阅读更多 →
3步解锁数据主权:如何将微信聊天记录转化为个人AI训练素材

3步解锁数据主权:如何将微信聊天记录转化为个人AI训练素材

3步解锁数据主权:如何将微信聊天记录转化为个人AI训练素材 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…

2026/10/13 22:08:04 阅读更多 →
GEO优化规则重写:AI搜索进入信源审查时代,广拓时代解读企业数字身份建设

GEO优化规则重写:AI搜索进入信源审查时代,广拓时代解读企业数字身份建设

当企业还在讨论应该发布多少篇文章、覆盖多少个平台时,AI搜索的竞争规则已经悄然发生了改变。 2026年7月,豆包对全域信源采信体系进行调整。与此同时,《GEO红皮书(2026)》发布,中国信通院“铸基计划”启动首…

2026/10/9 19:31:13 阅读更多 →

最新新闻

AI读不懂你的技术文档?一场Rspress文档改造实录

AI读不懂你的技术文档?一场Rspress文档改造实录

前阵子我给自己长期维护的一个基于 Rspress 搭建的组件库文档站做了一次 AI 可读性测试,测试代号叫 Rspress-20。名字很直白:我从用户真实提问中筛出了 20 个高频问题,比如“某组件支持哪几种尺寸”“某个配置项的默认值是什么”“为什么某个…

2026/10/14 12:10:13 阅读更多 →
Spring源码编译踩坑指南:JDK、Gradle与依赖配置全解析

Spring源码编译踩坑指南:JDK、Gradle与依赖配置全解析

刚拿到Spring源码准备自己编译一份的时候,我其实是有点膨胀的。觉得无非就是clone代码、装个JDK、跑个Gradle,能有多难?结果一整天搭进去,光是在“编译不通过”和“下载依赖失败”之间来回折腾就花了大半时间。后来冷静下来&#…

2026/10/14 12:10:13 阅读更多 →
行波故障诊断原理与Simulink仿真复现实战指南

行波故障诊断原理与Simulink仿真复现实战指南

行波故障诊断,在输电线路保护领域算是一个“老资格”方向了。跟传统的阻抗法测距比起来,行波法不受过渡电阻、系统阻抗、线路不平衡等因素影响,定位精度高出一个数量级,所以这些年论文里、工程里讨论得都很多。最近我复现了一篇基…

2026/10/14 12:10:13 阅读更多 →
AD9834驱动代码详解:DDS信号源寄存器配置与SPI时序实现

AD9834驱动代码详解:DDS信号源寄存器配置与SPI时序实现

简介:面向嵌入式开发者的AD9834驱动代码包,专为需要快速生成高精度正弦波、方波与三角波的测试测量、通信系统和教育实验等场景设计,目标芯片为ADI的DDS数字频率合成器。压缩包仅7KB,共4个文件,包含2个C源文件与2个头文…

2026/10/14 12:10:13 阅读更多 →
C++数组从声明到越界:核心机制与避坑指南

C++数组从声明到越界:核心机制与避坑指南

1. 为什么数组值得单独拎出来讲C 学到数组这一章,很多人会觉得“这不就是一堆相同类型的数据放一起吗”,然后翻两页就过去了。但我带过的新人里,十个有八个在数组上栽过跟头,而且栽的方式五花八门:有人把数组长度写成变…

2026/10/14 12:10:13 阅读更多 →
从二进制到可编辑网格:owmdl模型在Blender中的导入解析

从二进制到可编辑网格:owmdl模型在Blender中的导入解析

简介:基于Blender的《守望先锋》owmdl模型导入插件源码包,主要面向需要在Blender中查看和编辑这一专有游戏格式的玩家、三维建模学习者和游戏资产研究者。插件通过io_scene_owm扩展Blender的导入能力,可解析模型网格、纹理、骨骼与动画数据&a…

2026/10/14 12:09:12 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →