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;

        }

    }

}