监听进程事件时WmiPrvSE.exe CPU占用过高问题排查
WMI进程监听导致WmiPrvSE高CPU占用及资源泄漏问题
我用C++编写了以下代码,通过WMI的NotificationQuery和ExecQuery监听指定进程的创建/删除事件,获取进程名、ID和命令行参数。但发现程序空闲时WmiPrvSE.exe的CPU占用率达1.5-2%,关闭我的程序后该进程CPU占用仍居高不下,必须重启电脑才能恢复。同时在事件查看器的「Application and Services Logs > Windows WMI-Activity > Operational」中发现相关错误,想确认是否存在资源泄漏问题。
代码实现
#include <iostream> #include <string> #include <vector> #include <map> #include <algorithm> #include <comdef.h> #include <Wbemidl.h> #include <windows.h> #pragma comment(lib, "wbemuuid.lib") enum EventType { QUERY, PROCESS_CREATION, PROCESS_DELETION }; class EventSink : public IWbemObjectSink { LONG m_lRef = 0; public: EventSink(){} virtual ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&m_lRef); } virtual ULONG STDMETHODCALLTYPE Release() { LONG lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; } void event(EventType eventType, const std::vector<IWbemClassObject*>& objList) { for (IWbemClassObject* obj : objList) { DWORD processId = 0; std::wstring processName = L""; std::wstring commandLine = L""; VARIANT vtProp; HRESULT hr = obj->Get(L"Name", 0, &vtProp, 0, 0); if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) processName = vtProp.bstrVal; VariantClear(&vtProp); hr = obj->Get(L"ProcessId", 0, &vtProp, 0, 0); if (SUCCEEDED(hr) && vtProp.vt == VT_I4) processId = vtProp.uintVal; VariantClear(&vtProp); if (eventType == QUERY || eventType == PROCESS_CREATION) { hr = obj->Get(L"CommandLine", 0, &vtProp, 0, 0); if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) commandLine = vtProp.bstrVal; VariantClear(&vtProp); //... } else if (eventType == PROCESS_DELETION) { //... } obj->Release(); } } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { if (riid == IID_IUnknown || riid == IID_IWbemObjectSink) { *ppv = (IWbemObjectSink *) this; AddRef(); return WBEM_S_NO_ERROR; } else return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE Indicate(LONG lObjectCount, IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray) { std::map<EventType, std::vector<IWbemClassObject*>> eventMap; for (int i = 0; i < lObjectCount; i++) { VARIANT vtClass; HRESULT hr = apObjArray[i]->Get(L"__CLASS", 0, &vtClass, 0, 0); if (!SUCCEEDED(hr)) continue; bool creationEvent = (wcscmp(vtClass.bstrVal, L"__InstanceCreationEvent") == 0); VariantClear(&vtClass); VARIANT vtTargetInstance; hr = apObjArray[i]->Get(L"TargetInstance", 0, &vtTargetInstance, 0, 0); if (!SUCCEEDED(hr) || vtTargetInstance.vt != VT_UNKNOWN) continue; IWbemClassObject* pTargetInstance = NULL; hr = vtTargetInstance.punkVal->QueryInterface(IID_IWbemClassObject, (void**)&pTargetInstance); if (!SUCCEEDED(hr)) continue; VariantClear(&vtTargetInstance); eventMap[creationEvent ? PROCESS_CREATION : PROCESS_DELETION].emplace_back(pTargetInstance); } for (auto it = eventMap.begin(); it != eventMap.end(); ++it) { event(it->first, it->second); } return WBEM_S_NO_ERROR; } HRESULT STDMETHODCALLTYPE SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) { return WBEM_S_NO_ERROR; } }; class ProcessMonitor { public: ProcessMonitor() { HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); IWbemLocator* pLoc = NULL; hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc); if (FAILED(hres)) { std::cout << "Failed to create IWbemLocator object. Err code = 0x" << std::hex << hres << std::endl; CoUninitialize(); return; } pSvc = NULL; hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // WMI namespace NULL, // User name NULL, // User password 0, // Locale NULL, // Security flags 0, // Authority 0, // Context object &pSvc // IWbemServices proxy ); if (FAILED(hres) || !pSvc || !pLoc) { std::cout << "Could not connect. Error code = 0x" << std::hex << hres << std::endl; CoUninitialize(); return; } hres = CoSetProxyBlanket( pSvc, // the proxy to set RPC_C_AUTHN_WINNT, // authentication service RPC_C_AUTHZ_NONE, // authorization service NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // authentication level RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { std::cout << "Could not set proxy blanket. Error code = 0x" << std::hex << hres << std::endl; CoUninitialize(); return; } pSink = new EventSink; pSink->AddRef(); } void query() { IEnumWbemClassObject* pEnumerator = NULL; pSvc->ExecQuery(bstr_t("WQL"), bstr_t(wmiQuery.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; std::vector<IWbemClassObject*> objList; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) break; objList.emplace_back(pclsObj); } pSink->event(QUERY, objList); pEnumerator->Release(); } void updateQuery(const std::wstring& processName) { processList.push_back(processName); wmiQuery = L"SELECT Name, CommandLine, ProcessId FROM Win32_Process WHERE NAME = '"; processCreationQuery = L"SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA " L"'Win32_Process' AND (TargetInstance.Name = '"; // notepad.exe' OR TargetInstance.Name = 'CalculatorApp.exe')"; processDeletionQuery = L"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA " L"'Win32_Process' AND (TargetInstance.Name = '"; // notepad.exe' OR TargetInstance.Name = 'CalculatorApp.exe')"; for (size_t i = 0; i < processList.size(); ++i) { processCreationQuery += processList[i] + L"'"; processDeletionQuery += processList[i] + L"'"; wmiQuery += processList[i] + L"'"; if (i < processList.size() - 1) { wmiQuery += L" OR NAME = '"; processCreationQuery += L" OR TargetInstance.Name = '"; processDeletionQuery += L" OR TargetInstance.Name = '"; } else if (i == processList.size() - 1) { processCreationQuery += L")"; processDeletionQuery += L")"; } } pSvc->CancelAsyncCall(pSink); BSTR queryLanguage = SysAllocString(L"WQL"); // Query for process creation HRESULT hres = pSvc->ExecNotificationQueryAsync( queryLanguage, bstr_t(processCreationQuery.c_str()), WBEM_FLAG_SEND_STATUS, NULL, pSink); if (FAILED(hres)) { std::wcout << L"ExecNotificationQueryAsync failed with = 0x" << std::hex << hres << std::endl; CoUninitialize(); return; } // Query for process deletion hres = pSvc->ExecNotificationQueryAsync( queryLanguage, bstr_t(processDeletionQuery.c_str()), WBEM_FLAG_SEND_STATUS, NULL, pSink); if (FAILED(hres)) { std::wcout << L"ExecNotificationQueryAsync failed with = 0x" << std::hex << hres << std::endl; CoUninitialize(); return; } } private: IWbemServices* pSvc; EventSink* pSink; std::wstring wmiQuery; std::wstring processCreationQuery; std::wstring processDeletionQuery; std::vector<std::wstring> processList; }; int main() { ProcessMonitor pm; pm.updateQuery(L"notepad.exe"); // At this point its already listening to create/deletion events pm.query(); // The call to query will be used to collect the processes running before the application started std::cin.get(); // Idle, but WmiPrvSE continues using high cpu }
事件查看器错误日志
Id = {00000000-0000-0000-0000-000000000000}; ClientMachine = DESKTOP-...; User = DESKTOP-...; ClientProcessId = 20240; Component = Unknown; Operation = Start IWbemServices::ExecNotificationQuery - ROOT\CIMV2 : SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND (TargetInstance.Name = 'notepad.exe'); ResultCode = 0x80041032; PossibleCause = Unknown
Id = {00000000-0000-0000-0000-000000000000}; ClientMachine = DESKTOP-...; User = DESKTOP-...; ClientProcessId = 20240; Component = Core; Operation = Start IWbemServices::ExecNotificationQuery - ROOT\CIMV2 : SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND (TargetInstance.Name = 'notepad.exe'); ResultCode = 0x800706BA; PossibleCause = Could not send status to client
问题分析与解决
错误码解析
0x80041032:WBEM_E_INVALID_QUERY,说明进程删除事件的WQL查询逻辑错误——进程删除后TargetInstance已不存在,无法直接读取其属性0x800706BA:RPC_S_SERVER_UNAVAILABLE,WMI服务无法向客户端发送状态,根源是客户端未正确释放COM资源,导致连接残留
资源泄漏问题
- COM对象未释放:
ProcessMonitor构造函数中创建的IWbemLocator* pLoc,仅在失败时释放,成功后未调用pLoc->Release()- 类中未定义析构函数,
pSvc、pSink等COM对象在程序退出时未被释放,WMI服务仍维持异步查询连接
- BSTR内存泄漏:
updateQuery中调用SysAllocString(L"WQL")创建的BSTR未调用SysFreeString释放 ExecQuery返回值未检查:query方法中直接使用pEnumerator,未判断ExecQuery是否成功,可能导致空指针访问
修复方案
添加析构函数释放资源
在ProcessMonitor类中添加析构函数:~ProcessMonitor() { if (pSink) { pSvc->CancelAsyncCall(pSink); pSink->Release(); } if (pSvc) { pSvc->Release(); } CoUninitialize(); }同时在构造函数连接成功后释放
pLoc:// 连接成功后添加 pLoc->Release();修复BSTR内存泄漏
在updateQuery中执行完异步查询后释放BSTR:SysFreeString(queryLanguage);修正进程删除事件查询逻辑
进程删除事件应读取PreviousInstance而非TargetInstance,修改EventSink::Indicate中的相关逻辑:// 替换原TargetInstance获取代码 VARIANT vtPreviousInstance; HRESULT hr = apObjArray[i]->Get(L"PreviousInstance", 0, &vtPreviousInstance, 0, 0); if (!SUCCEEDED(hr) || vtPreviousInstance.vt != VT_UNKNOWN) continue; IWbemClassObject* pPreviousInstance = NULL; hr = vtPreviousInstance.punkVal->QueryInterface(IID_IWbemClassObject, (void**)&pPreviousInstance); if (!SUCCEEDED(hr)) continue; VariantClear(&vtPreviousInstance); eventMap[PROCESS_DELETION].emplace_back(pPreviousInstance);优化WMI轮询间隔
将查询中的WITHIN 1调整为更大的值(如WITHIN 5),降低WMI服务轮询频率,减少CPU占用检查
ExecQuery返回值
在query方法中先判断ExecQuery是否成功:HRESULT hr = pSvc->ExecQuery(bstr_t("WQL"), bstr_t(wmiQuery.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hr) || !pEnumerator) { std::cout << "ExecQuery failed. Error code = 0x" << std::hex << hr << std::endl; return; }
内容的提问来源于stack exchange,提问作者Katt
相关产品推荐
相关产品推荐

