Listing Processes – Using Windows Management Instrumentation (WMI)

Using Win32_Process WMI class

A previous article shows how to use Win32_PhysicalMedia WMI class to get physical drive info. We can write something similar for getting a list of running processes. All we have to do is to replace the WQL query and get specific properties for Win32_Process. However, to simplify the things, I wrote few C++ wrapper classes over the WMI stuff.
Here is a brief description for each one:

  • CWMIConnection – opens and keeps a connection to WMI namespace.
  • CWMIQuery – executes WQL (SQL for WMI) queries and navigates through results.
  • CWMIWin32_Process – is derived from CWMIQuery and is specialized for Win32_Process.
  • CWMIValue – a class that gets user readable strings from different CIM types.

The implementation details can be found in the attached demo project.
Let’s show now just a usage example, that fills a list-view control with info about running processes.

void CDemoDlg::_FillProcessesList()
{
    // clear listview control
    m_listProcesses.DeleteAllItems();

    try
    {
        // Open WMI connection
        CWMIConnection wmiConnection;
        wmiConnection.Open(L"ROOT\\CIMV2");

        // Query Win32_Process
        CWMIWin32_Process wmiQuery(wmiConnection);
        wmiQuery.Open();

        // Fill the list
        int nItem = 0;
        while(wmiQuery.MoveNext())
        {
            m_listProcesses.InsertItem(nItem, NULL);

            m_listProcesses.SetItemText(nItem, ePID, WMI_GETPROPERTYSTR(wmiQuery, Handle));
            m_listProcesses.SetItemText(nItem, eSessionID, WMI_GETPROPERTYSTR(wmiQuery, SessionId));
            m_listProcesses.SetItemText(nItem, eImageName, WMI_GETPROPERTYSTR(wmiQuery, Caption));
            m_listProcesses.SetItemText(nItem, eCommandLine, WMI_GETPROPERTYSTR(wmiQuery, CommandLine));
            // ...

            // NOTE: This is just for demo purpose and can be completed. 
            // For a compelte list of Win32_Process properties, see MSDN documentation.
            // https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process
            nItem++;
        }
    }
    catch(COleException* e)
    {
        e->ReportError();
        e->Delete();
    }
}

Demo project

Download: Listing_Processes_Using_WMI.zip

The demo project is a simple MFC dialog-based application that uses WMI wrapper classes to list and get info about running processes.

Using WMI - Demo Application

Resources

Get Physical Drive Serial Number – Part 2

The previous article shows how to get manufacturer-provided serial number for a physical drive by calling DeviceIoControl function.
Now, let’s see how can it be made by using WMI (Windows Management Instrumentation).

Get serial number by using Win32_PhysicalMedia WMI class

To get the physical drive serial number by using Win32_PhysicalMedia class, follow these steps:

  1. Initialize COM.
        HRESULT hr = ::CoInitializeEx(0, COINIT_MULTITHREADED);
  2. Set the default process security level.
        hr =  ::CoInitializeSecurity(
            NULL,                        // Security descriptor    
            -1,                          // COM negotiates authentication service
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication level for proxies
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation level for proxies
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities of the client or server
            NULL);                       // Reserved
  3. Create a connection to WMI namespace.
        // Initialize the IWbemLocator interface
        CComPtr<IWbemLocator> pIWbemLocator;
        hr = ::CoCreateInstance(CLSID_WbemLocator, 0, 
            CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pIWbemLocator);
        // ...
    
        // Call IWbemLocator::ConnectServer for connecting to WMI 
        CComPtr<IWbemServices> pIWbemServices;
        hr = pIWbemLocator->ConnectServer(L"ROOT\\CIMV2",
            NULL, NULL, 0, NULL, 0, 0, &pIWbemServices);
  4. Set the security levels on WMI connection.
        hr = ::CoSetProxyBlanket(
            pIWbemServices, 
            RPC_C_AUTHN_WINNT,
            RPC_C_AUTHZ_NONE, 
            NULL, 
            RPC_C_AUTHN_LEVEL_CALL,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL,
            EOAC_NONE);
  5. Execute a WQL (WMI Query Language) query to get a list of physical media. Each list element contains a tag as unique identifier (e.g. PHYSICALDRIVE0)  and the manufacturer-provided serial number.
        const BSTR szQueryLanguage = L"WQL";
        const BSTR szQuery =  L"SELECT Tag, SerialNumber FROM Win32_PhysicalMedia";
        CComPtr<IEnumWbemClassObject> pIEnumWbemClassObject;
        hr = pIWbemServices->ExecQuery(
            szQueryLanguage,                                       // Query language
            szQuery,                                               // Query
            WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,   // Flags
            NULL,                                                  // Context
            &pIEnumWbemClassObject);                               // Enumerator
  6. Get each enumerator element until find the desired physical drive. For detailed code, see the complete demo application, below.

Putting all together with some helpful ATL stuff, we can make now a simple console demo application.

// ConsoleWMI.cpp : Defines the entry point for the console application.
//
#include <atlbase.h>
#include <atlstr.h>
#include <comutil.h>
#include <wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

void GetPhysicalDriveSerialNumber(UINT nDriveNumber IN, CString& strSerialNumber OUT);

int _tmain(int argc, _TCHAR* argv[])
{
    CString strResult;
    try
    {
        // 1. Initialize COM 
        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa390885(v=vs.85).aspx
        HRESULT hr = ::CoInitializeEx(0, COINIT_MULTITHREADED); 

        ATLENSURE_SUCCEEDED(hr);

        CString strSerialNumber;
        UINT nDriveNumber = 0;
        GetPhysicalDriveSerialNumber(nDriveNumber, strSerialNumber);
        strResult.Format(_T("Serial number for drive #%u is %s"), 
            nDriveNumber, strSerialNumber);
    }
    catch(CAtlException& e)
    {
        strResult.Format(_T("Get serial number failure. Error code: 0x%08X"), 
            (HRESULT)e);
    }

    // Show result
    ::MessageBox(NULL, strResult, _T("Serial number demo"), MB_OK);

    // Uninitialize COM
    ::CoUninitialize();
    return 0;
}

void GetPhysicalDriveSerialNumber(UINT nDriveNumber IN, CString& strSerialNumber OUT)
{
    strSerialNumber.Empty();

    // Format physical drive path (may be '\\.\PhysicalDrive0', '\\.\PhysicalDrive1' and so on).
    CString strDrivePath;
    strDrivePath.Format(_T("\\\\.\\PhysicalDrive%u"), nDriveNumber);

    // 2. Set the default process security level 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa393617(v=vs.85).aspx
    HRESULT hr =  ::CoInitializeSecurity(
        NULL,                        // Security descriptor    
        -1,                          // COM negotiates authentication service
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication level for proxies
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation level for proxies
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities of the client or server
        NULL);                       // Reserved

    ATLENSURE_SUCCEEDED(hr);

    // 3. Create a connection to WMI namespace
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa389749(v=vs.85).aspx

    // 3.1. Initialize the IWbemLocator interface
    CComPtr<IWbemLocator> pIWbemLocator;
    hr = ::CoCreateInstance(CLSID_WbemLocator, 0, 
        CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pIWbemLocator);

    ATLENSURE_SUCCEEDED(hr);

    // 3.2. Call IWbemLocator::ConnectServer for connecting to WMI 
    CComPtr<IWbemServices> pIWbemServices;
    hr = pIWbemLocator->ConnectServer(L"ROOT\\CIMV2",
        NULL, NULL, 0, NULL, 0, 0, &pIWbemServices);

    ATLENSURE_SUCCEEDED(hr);

    // 4. Set the security levels on WMI connection
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa393619(v=vs.85).aspx
    hr = ::CoSetProxyBlanket(
        pIWbemServices, 
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE, 
        NULL, 
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE);

    ATLENSURE_SUCCEEDED(hr);

    // 5. Execute a WQL (WMI Query Language) query to get physical media info
    const BSTR szQueryLanguage = L"WQL";
    const BSTR szQuery =  L"SELECT Tag, SerialNumber FROM Win32_PhysicalMedia";
    CComPtr<IEnumWbemClassObject> pIEnumWbemClassObject;
    hr = pIWbemServices->ExecQuery(
        szQueryLanguage,                                       // Query language
        szQuery,                                               // Query
        WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,   // Flags
        NULL,                                                  // Context
        &pIEnumWbemClassObject);                               // Enumerator

    ATLENSURE_SUCCEEDED(hr);

    // 6. Get each enumerator element until find the desired physical drive 
    ULONG uReturn = 0;
    while(pIEnumWbemClassObject)
    {
        CComPtr<IWbemClassObject> pIWbemClassObject;
        hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturn);
        if(0 == uReturn || FAILED(hr))
            break;

        variant_t vtTag;           // unique tag, e.g. '\\.\PHYSICALDRIVE0'
        variant_t vtSerialNumber;  // manufacturer-provided serial number

        hr = pIWbemClassObject->Get(L"Tag", 0, &vtTag, NULL, NULL);
        ATLENSURE_SUCCEEDED(hr);

        CString strTag(vtTag.bstrVal);
        if(!strTag.CompareNoCase(strDrivePath)) // physical drive found
        {
            hr = pIWbemClassObject->Get(L"SerialNumber", 0, &vtSerialNumber, NULL, NULL);
            ATLENSURE_SUCCEEDED(hr);
            strSerialNumber = vtSerialNumber.bstrVal; // get the serial number
            break;
        }
    }
}

See also

Resources

Later edit

We can simplify a little bit the above example by adding WHERE clause in the WQL query.

void GetPhysicalDriveSerialNumber(UINT nDriveNumber IN, CString& strSerialNumber OUT)
{
    strSerialNumber.Empty();

    // Format physical drive path (may be '\\\\.\\PhysicalDrive0', '\\\\.\\PhysicalDrive1' and so on). 
    // Note: backslash is used as escape in WQL, so we need to double each one.
    CStringW strDrivePath;
    strDrivePath.Format(_T("\\\\\\\\.\\\\PhysicalDrive%u"), nDriveNumber);

    // ...
    // ... [the same code as in previous example]
    // ...

    // 5. Execute a WQL (WMI Query Language) query to get the wanted phisical drive serial number
    const BSTR szQueryLanguage = L"WQL";
    CStringW strQuery;
    strQuery.Format(L"SELECT SerialNumber FROM Win32_PhysicalMedia WHERE Tag=\"%s\"", 
        strDrivePath);

    CComPtr<IEnumWbemClassObject> pIEnumWbemClassObject;
    hr = pIWbemServices->ExecQuery(
        szQueryLanguage,                                       // Query language
        (BSTR)strQuery.GetString(),                            // Query
        WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,   // Flags
        NULL,                                                  // Context
        &pIEnumWbemClassObject);                               // Enumerator

    ATLENSURE_SUCCEEDED(hr);

   // 6. Get first enumerator element. If exists, get the serial number.
    ULONG uReturn = 0;
    CComPtr<IWbemClassObject> pIWbemClassObject;
    hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturn);

    if(WBEM_S_NO_ERROR == hr)
    {
        variant_t vtSerialNumber;  // manufacturer-provided serial number
        hr = pIWbemClassObject->Get(L"SerialNumber", 0, &vtSerialNumber, NULL, NULL);
        ATLENSURE_SUCCEEDED(hr);

        strSerialNumber = vtSerialNumber.bstrVal; // assign serial number to output parameter
    }
    else
    {
        AtlThrow(hr);
    }
}