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:
- Initialize COM.
1HRESULT hr = ::CoInitializeEx(0, COINIT_MULTITHREADED); - Set the default process security level.
12345678910hr = ::CoInitializeSecurity(NULL, // Security descriptor-1, // COM negotiates authentication serviceNULL, // Authentication servicesNULL, // ReservedRPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication level for proxiesRPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation level for proxiesNULL, // Authentication infoEOAC_NONE, // Additional capabilities of the client or serverNULL); // Reserved - Create a connection to WMI namespace.
12345678910// Initialize the IWbemLocator interfaceCComPtr<IWbemLocator> pIWbemLocator;hr = ::CoCreateInstance(CLSID_WbemLocator, 0,CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pIWbemLocator);// ...// Call IWbemLocator::ConnectServer for connecting to WMICComPtr<IWbemServices> pIWbemServices;hr = pIWbemLocator->ConnectServer(L"ROOT\\CIMV2",NULL, NULL, 0, NULL, 0, 0, &pIWbemServices); - Set the security levels on WMI connection.
123456789hr = ::CoSetProxyBlanket(pIWbemServices,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE); - 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.
123456789const BSTR szQueryLanguage = L"WQL";const BSTR szQuery = L"SELECT Tag, SerialNumber FROM Win32_PhysicalMedia";CComPtr<IEnumWbemClassObject> pIEnumWbemClassObject;hr = pIWbemServices->ExecQuery(szQueryLanguage, // Query languageszQuery, // QueryWBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY, // FlagsNULL, // Context&pIEnumWbemClassObject); // Enumerator - 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
// 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
- Codexpert blog: Get Physical Drive Serial Number – Part 1
Resources
- MSDN: Win32_PhysicalMedia class
- MSDN: Getting WMI Data from the Local Computer
- MSDN: WQL (SQL for WMI)
Later edit
We can simplify a little bit the above example by adding WHERE clause in the WQL query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
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); } } |
Frumos, foarte frumos. Bina ca mai sunt oameni care stiu ATL, ca sa nu mai zic ca impresionant de mic este si numarul celor care stiu ce este ala WMI.
Apropo WMI, apelurile merg bine din scripting si din C++, ca din .NET sunt multe dude pe zona marshaling la care iese cu virgula si crapa.
I followed your code. On my machine, the serial number is coming out to be some weird long string “2020202057202d….30337”. Also, the serial number returned by WMI is the same for both the hard disk and CD drive. My OS: Windows 7.