Katt
Katt

Reputation: 49

High WMI cpu usage when listening to events

I have written the code below to listen to creation and deletion events of when specific applications are created/deleted, and also retrieve their process name, id, command line, using WMI, NotificationQuery, ExecQuery.

I noted that WmiPrvSE.exe is using "too much" CPU when my program is "idling", it's using like 1.5-2%.

My current code is:

#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
}

After pm.query() the process is "idling" but the CPU usage of WmiPrvSE.exe continues high, and when i close my process it also continues high, i need to reboot the pc to it "reset".

It's calling the query only once and then listening to the events, am I leaking something?

I also noted on Event Viewer > Application and Services Logs > Windows WMI-Activity > Operational that it is storing the following errors relative to my process:

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

Upvotes: 1

Views: 102

Answers (0)

Related Questions