1.什么是WMI?
WMI全称Windows Management Instrumentation,是Microsoft基于Web的企业管理(WBEM)的实现,WBEM是一项行业倡议,旨在开发用于在企业环境中访问管理信息的标准技术….其他不再赘述,详情见MSDN - About WMI
The CIMWin32 WMI providers支持CimWin32.dll中支持的类,由核心的CIM WMI类和电源管理事件,以下用到了Win32_DiskDrive, Win32_DiskDriveToDiskPartition, Win32_LogicalDiskToPartition等类。相关链接WMI providers
2. 开始
如果系统只有一个硬盘,那其实就可以直接通过命令行直接获取结果,例如:
wmic diskdrive where index=0 get serialnumber
倘若不只只有一个硬盘呢?有以下思路:
1.首先获取程序所在逻辑硬盘位置
2.通过逻辑硬盘位置和硬盘的序列号关联表查询序列号
3.获取到当前硬盘序列号
其中的难点在于第二步,如何获取硬盘的逻辑分区情况,这时刚好可以通过WQL查询获得,以下是通过MSDN连接WMI例子而加工的代码:
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
// note: 此处步骤1,2 如果已经初始化过,第二次会false,所以视情况调用
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator* pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices* pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
nullptr, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
nullptr, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = nullptr;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_DiskDrive"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
// note:
// 1. 这里通过查询得到DEVICEID = \\\\.\\PHYSICALDRIVE0, 然而还需要将\ 替换为 \\
// 2. WQL语句中“=”左右不能有空格,否则ExecQuery之后会查询失败
// 如:ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\" xxx \"}就会失败
// 3. gcc环境中 bstr_t("xxx") 可能会编译报错, 使用 "BSTR xxx = SysAllocString(L"sql .. ") " 和 SysFreeString(xxx) 组合代替
// 如:bstr_t("WQL") ——> BSTR strQueryLanguage = SysAllocString(L"WQL"); SysFreeString(strQueryLanguage)
//hres = pSvc->ExecQuery(
// bstr_t("WQL"),
// bstr_t("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE0\"} WHERE AssocClass=Win32_DiskDriveToDiskPartition"),
// //bstr_t("ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"C:\"}"),
// WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
// NULL,
// &pEnumerator);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj = nullptr;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"DeviceID", 0, &vtProp, 0, 0);
//wcout << "DeviceID : " << vtProp.bstrVal << endl;
wstring deviceID = vtProp.bstrVal;
wcout << deviceID << endl;
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
wstring serialNumber = vtProp.bstrVal;
wcout << "SerialNumber : " << serialNumber << endl;
VariantClear(&vtProp);
pclsObj->Release();
replace(deviceID, L"\\", L"\\\\");
wstring query;
query.append(L"ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"");
query.append(deviceID);
query.append(L"\"} WHERE AssocClass=Win32_DiskDriveToDiskPartition");
IEnumWbemClassObject* partitionEnumerator = nullptr;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t(query.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
nullptr,
&partitionEnumerator);
IWbemClassObject* diskPartition = nullptr;
while (partitionEnumerator)
{
HRESULT hr = partitionEnumerator->Next(WBEM_INFINITE, 1,
&diskPartition, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = diskPartition->Get(L"DeviceID", 0, &vtProp, 0, 0);
wstring diskPartitionDeviceID = vtProp.bstrVal;
wcout << "diskPartitionDeviceID : " << diskPartitionDeviceID << endl;
VariantClear(&vtProp);
diskPartition->Release();
diskPartition = nullptr;
wstring query;
query.append(L"ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"");
query.append(diskPartitionDeviceID);
query.append(L"\"} WHERE AssocClass=Win32_LogicalDiskToPartition");
IEnumWbemClassObject* logicalDiskEnumerator = nullptr;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t(query.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
nullptr,
&logicalDiskEnumerator);
IWbemClassObject* logicalPartition = nullptr;
while (logicalDiskEnumerator) {
HRESULT hr = logicalDiskEnumerator->Next(WBEM_INFINITE, 1,
&logicalPartition, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
hr = logicalPartition->Get(L"DeviceID", 0, &vtProp, 0, 0);
wstring logicalDiskDeviceID = vtProp.bstrVal;
wcout << "logicalDiskDeviceID : " << logicalDiskDeviceID << endl; // 通过上方得到的SerialNumber和此处的logicalDiskDeviceID建立map就完成了步骤2
VariantClear(&vtProp);
logicalPartition->Release();
logicalPartition = nullptr;
}
}
}
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
return 0; // Program successfully completed.
代码中踩坑的地方做了注释,记录这些以备不时之需。
其中WQL查询参考文章How Can I Correlate Logical Drives and Physical Disks?。