Alex Fedotov.com 

Requirements

Development Environment Requirements

Microsoft Visual C++ 6.0
Microsoft Platform SDK

Run-Time Requirements

Microsoft Windows 2000 or later

This sample can also run on Windows NT 4.0 and Windows 9x/Me if WMI runtime is installed.

<< Hide left pane Leave feedback

WMI Top Sample

WMI provides per-process processor usage information via the Win32_Process class. The KernelModeTime and UserModeTime properties of this class show the amount of time spent by a process in kernel and user modes respectively. These values show the time spent by the process from its beginning, and therefore cannot be used as a processor usage indicator directly. We have to make two snapshots of these values and then use some simple arithmetic to get processor usage.

First, let me introduce the following definitions:

  • T – time interval, with which measurements are made;
  • N – total number of processes;
  • U0[i] – UserModeTime value of the process i at the beginning of the interval;
  • U1[i] – UserModeTime value of the process i at the end of the interval;
  • K0[i] – KernelModeTime value of the process i at the beginning of the interval;
  • K1[i] – KernelModeTime value of the process i at the end of the interval.

Using these definitions, processor usage by a particular process in user mode can be expressed as

Similarly, processor usage in kernel mode is

and overall processor usage by this process is

If we sum up P[i] values for all processes in the system, we should get 100%, because the system always executes some process. If there is no other task to do, the system runs the so-called System Idle Process. Therefore, if we take the total of all processes except the System Idle Process, we will get overall processor usage in the system:

Well, enough mathematics, let's go to the implementation. The first thing we have to do is to connect with the WMI provider on the machine of interest. Since WMI works transparently across the network, we get remote monitoring ability as a free bonus. The following function connects to the WMI provider:

HRESULT DoConnect(
    IN PCTSTR pszMachineName,
    OUT IWbemServices ** ppServer
    )
{
    _ASSERTE(ppServer != NULL);
    *ppServer = NULL;

    HRESULT hRes;
    IWbemLocator * pLocator = NULL;

    TCHAR szServer[256];
    BSTR bstrServer = NULL;

    for (;;)
    {
        // create a WBEM locator object
        hRes = CoCreateInstance(__uuidof(WbemLocator), NULL,
                            CLSCTX_INPROC_SERVER, 
                            __uuidof(IWbemLocator),
                            (PVOID *)&pLocator);
        if (FAILED(hRes))
            break;

        // prepare server name
        if (pszMachineName == NULL)
            lstrcpy(szServer, _T("root\\cimv2"));
        else
        {
            wsprintf(szServer, _T("%s\\root\\cimv2"), 
                     pszMachineName);
        }

#ifdef _UNICODE
        bstrServer = SysAllocString(szServer);
        if (bstrServer == NULL)
        {
            hRes = E_OUTOFMEMORY;
            break;
        }
#else
        int len = lstrlen(szServer);
        bstrServer = SysAllocStringLen(NULL, len);
        if (bstrServer == NULL)
        {
            hRes = E_OUTOFMEMORY;
            break;
        }
        MultiByteToWideChar(CP_ACP, 0, szServer, -1,
                            bstrServer, len + 1);
#endif

        // connect to the services object on the specified machine
        hRes = pLocator->ConnectServer(bstrServer, NULL,
                                       NULL, NULL, 0, NULL, NULL, 
                                       ppServer);
        break;
    }

    if (bstrServer != NULL)
        SysFreeString(bstrServer);
    if (pLocator != NULL)
        pLocator->Release();

    return hRes;
}

This function receives machine name on input, which may be specified as NULL for the local machine, and returns a pointer to the IWbemServices interface, which represents the WMI service provider.

Having connected with the service provider, the program starts making snapshots of process times every second. It uses the formulas discussed above to convert this information into percentage values understandable by the end user. The following function is used to make a snapshot of process time information.

typedef struct _PROCINFO * PPROCINFO;
typedef struct _PROCINFO {
    PPROCINFO pNext;                // pointer to the next process
    DWORDLONG dwlKernelTime;        // kernel time (*)
    DWORDLONG dwlUserTime;          // user time (*)
    DWORD     dwProcessId;          // process identifier
    WCHAR     szPathName[MAX_PATH]; // executable path
} PROCINFO;
// (*) expressed in 100-nanosecond intervals

HRESULT GetStatistics(
    IN IWbemServices * pServer,
    OUT PPROCINFO * ppInfo
    )
{
    _ASSERTE(pServer != NULL);
    _ASSERTE(ppInfo != NULL);

    *ppInfo = NULL;

    HRESULT hRes;
    IEnumWbemClassObject * pEnum = NULL;
    IWbemClassObject * pObject = NULL;

    PPROCINFO pInfo = NULL;
    PPROCINFO pList = NULL;
    ULONG ulCount;
    
    VARIANT var;
    VariantInit(&var);

    BSTR bstrName = SysAllocString(L"Win32_Process");
    if (bstrName == NULL)
        return E_OUTOFMEMORY;

    for (;;)
    {
        // create an enumerator of processes
        hRes = pServer->CreateInstanceEnum(bstrName,
                             WBEM_FLAG_SHALLOW|WBEM_FLAG_FORWARD_ONLY,
                             NULL, &pEnum);
        if (FAILED(hRes))
            break;

        // walk through all processes
        for (;;)
        {
            if (pObject != NULL)
            {
                pObject->Release();
                pObject = NULL;
            }

            if (pInfo != NULL)
            {
                CoTaskMemFree(pInfo);
                pInfo = NULL;
            }

            // get next process instance
            if (pEnum->Next(WBEM_INFINITE, 1,
                            &pObject, &ulCount) != S_OK)
            {
                hRes = S_OK;
                break;
            }

            // allocate new process information structure
            pInfo = (PPROCINFO)CoTaskMemAlloc(sizeof(PROCINFO));
            if (pInfo == NULL)
            {
                hRes = E_OUTOFMEMORY;
                break;
            }

            // retrieve process identifier
            VariantClear(&var);
            hRes = pObject->Get(L"ProcessId", 0, &var, NULL, NULL);
            if (FAILED(hRes))
                continue;

            _ASSERTE(V_VT(&var) == VT_I4);
            pInfo->dwProcessId = V_I4(&var);

            // retrieve process executable path
            VariantClear(&var);
            hRes = pObject->Get(L"ExecutablePath", 0, &var, NULL, NULL);
            if (FAILED(hRes))
                continue;
            
            if (V_VT(&var) == VT_NULL)
            {
                hRes = pObject->Get(L"Name", 0, &var, NULL, NULL);
                if (FAILED(hRes))
                    continue;
            }

            _ASSERTE(V_VT(&var) == VT_BSTR);
            lstrcpynW(pInfo->szPathName, V_BSTR(&var), MAX_PATH);

            // retrieve process kernel time
            VariantClear(&var);
            hRes = pObject->Get(L"KernelModeTime", 0, &var, NULL, NULL);
            if (FAILED(hRes))
                continue;

            _ASSERTE(V_VT(&var) == VT_BSTR);
            pInfo->dwlKernelTime = _wtoi64(V_BSTR(&var));

            // retrieve process user time
            VariantClear(&var);
            hRes = pObject->Get(L"UserModeTime", 0, &var, NULL, NULL);
            if (FAILED(hRes))
                continue;

            _ASSERTE(V_VT(&var) == VT_BSTR);
            pInfo->dwlUserTime = _wtoi64(V_BSTR(&var));

            // insert this item into the list
            pInfo->pNext = pList;
            pList = pInfo;
            pInfo = NULL;
        }

        break;
    }

    VariantClear(&var);
    SysFreeString(bstrName);

    if (FAILED(hRes) && pList != NULL)
    {
        FreeProcInfoList(pList);
        pList = NULL;
    }

    if (pObject != NULL)
        pObject->Release();
    if (pEnum != NULL)
        pEnum->Release();

    *ppInfo = pList;
    return hRes;
}

The function creates a list of PROCINFO structures, where each structure represents one process. The structure contains process name, process identifier along with user mode and kernel mode times of the process.

The main function uses lists returned by GetStatistics to calculate per-process and overall processor usage in user and kernel mode. Finally, it displays the list of processes in a manner similar to the Unix top utility, sorting most consuming processes to the top of the list.

I would like to thank Serge Kholodilov who spotted a few bugs in the original sample code.

Leave your comment

From:
Subject:
Comment:
 
 
Best viewed with Microsoft Internet Explorer 4.0+Send feedback to: me@alexfedotov.com
Last modified on Fri, Feb 10 2006

HotLog