<< 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 (;;)
{
hRes = CoCreateInstance(__uuidof(WbemLocator), NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IWbemLocator),
(PVOID *)&pLocator);
if (FAILED(hRes))
break;
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
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;
DWORDLONG dwlKernelTime;
DWORDLONG dwlUserTime;
DWORD dwProcessId;
WCHAR szPathName[MAX_PATH];
} PROCINFO;
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 (;;)
{
hRes = pServer->CreateInstanceEnum(bstrName,
WBEM_FLAG_SHALLOW|WBEM_FLAG_FORWARD_ONLY,
NULL, &pEnum);
if (FAILED(hRes))
break;
for (;;)
{
if (pObject != NULL)
{
pObject->Release();
pObject = NULL;
}
if (pInfo != NULL)
{
CoTaskMemFree(pInfo);
pInfo = NULL;
}
if (pEnum->Next(WBEM_INFINITE, 1,
&pObject, &ulCount) != S_OK)
{
hRes = S_OK;
break;
}
pInfo = (PPROCINFO)CoTaskMemAlloc(sizeof(PROCINFO));
if (pInfo == NULL)
{
hRes = E_OUTOFMEMORY;
break;
}
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);
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);
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));
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));
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.