You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows平台下如何用C++识别系统所在的物理主驱动器

如何在Windows中定位系统所在的物理驱动器

问题背景

我需要在Windows系统中找出操作系统所在的物理驱动器。目前已通过WMI获取物理驱动器与型号的对应关系,但由于C:盘符并不总是对应PHYSICALDRIVE0,因此需要实现盘符到物理驱动器的准确匹配。

现有代码

#include <windows.h>
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>

int main() {
    HRESULT hres;

    // Initialize COM library
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) {
        std::cout << "Failed to initialize COM library. Error code = 0x" 
                  << std::hex << hres << std::endl;
        return 1;
    }

    // Initialize security
    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
    );

    if (FAILED(hres)) {
        std::cout << "Failed to initialize security. Error code = 0x" 
                  << std::hex << hres << std::endl;
        CoUninitialize();
        return 1;
    }

    // Obtain the initial locator to WMI 
    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
 
    if (FAILED(hres)) {
        std::cout << "Failed to create IWbemLocator object. Error code = 0x" 
                  << std::hex << hres << std::endl;
        CoUninitialize();
        return 1;
    }

    // Connect to WMI namespace
    IWbemServices *pSvc = NULL;
    hres = pLoc->ConnectServer(
         _bstr_t(L"ROOT\\CIMV2"), 
         NULL,                    // User name
         NULL,                    // Password
         0,                       // Locale             
         NULL,                    // Security flags
         0,                       // Authority        
         0,                       // Context object
         &pSvc                    // IWbemServices proxy
    );
    
    if (FAILED(hres)) {
        std::cout << "Could not connect. Error code = 0x" 
                  << std::hex << hres << std::endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;
    }

    // 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
       NULL,                        // Server principal name 
       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                        // client identity
       EOAC_NONE                    // proxy capabilities 
    );

    if (FAILED(hres)) {
        std::cout << "Could not set proxy blanket. Error code = 0x" 
                  << std::hex << hres << std::endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;
    }

    // Use the IWbemServices pointer to make requests of WMI
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"), 
        bstr_t("SELECT DeviceID, Model FROM Win32_DiskDrive"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
        NULL,
        &pEnumerator);

    if (FAILED(hres)) {
        std::cout << "Query for disk drives failed. Error code = 0x" 
                  << std::hex << hres << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Get the data from the query
    IWbemClassObject *pclsObj = NULL;
    ULONG uReturn = 0;
    
    while (pEnumerator) {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

        if(0 == uReturn) {
            break;
        }

        VARIANT vtDeviceID;
        VARIANT vtModel;

        // Get the value of the DeviceID and Model properties
        hr = pclsObj->Get(L"DeviceID", 0, &vtDeviceID, 0, 0);
        hr = pclsObj->Get(L"Model", 0, &vtModel, 0, 0);
        std::wcout << "Device ID : " << vtDeviceID.bstrVal << ", Model : " << vtModel.bstrVal << std::endl;
        VariantClear(&vtDeviceID);
        VariantClear(&vtModel);

        pclsObj->Release();
    }

    // Cleanup
    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    CoUninitialize();

    return 0;
}

当前输出

Device ID : \\.\PHYSICALDRIVE5, Model : Microsoft Virtual Disk
Device ID : \\.\PHYSICALDRIVE3, Model : NORELSYS 1081 SD USB Device
Device ID : \\.\PHYSICALDRIVE2, Model : ADATA SX8200PNP
Device ID : \\.\PHYSICALDRIVE0, Model : CT480BX500SSD1
Device ID : \\.\PHYSICALDRIVE4, Model : NORELSYS 1081 TF USB Device
Device ID : \\.\PHYSICALDRIVE1, Model : ADATA SP550

核心问题

我的系统实际位于PHYSICALDRIVE2,如何通过编程实现系统盘符(C:)与对应物理驱动器的匹配?

补充说明

  • 最终需求是读取磁盘序列号用于安全验证,但Win32_DiskDrive的查询顺序不确定(环境中存在无序列号的虚拟磁盘),因此无法依赖查询顺序定位系统盘。
  • 已获取目标磁盘的详细信息:

    Disk Information Dump:
    Model: ADATA SX8200PNP
    Serial Number: 2Jxxxxxxxxxx _00000001.
    Interface Type: SCSI
    Media Type: Fixed hard disk media
    Connected as: \.\PHYSICALDRIVE2


解决方案

核心思路是通过WMI的关联类建立系统盘符→磁盘分区→物理驱动器的映射关系,具体步骤如下:

  1. 查询Win32_OperatingSystem获取系统所在的盘符(如C:)
  2. 通过Win32_LogicalDiskToPartition关联逻辑盘符到对应的磁盘分区
  3. 通过Win32_DiskDriveToDiskPartition关联磁盘分区到对应的物理驱动器
  4. 最终匹配得到系统所在的PHYSICALDRIVE编号

修改后的完整代码

#include <windows.h>
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
#include <string>

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

// 辅助函数:从WMI对象中获取字符串属性值
std::wstring GetWmiStringProperty(IWbemClassObject* pObj, const wchar_t* propertyName) {
    VARIANT vtProp;
    VariantInit(&vtProp);
    HRESULT hr = pObj->Get(propertyName, 0, &vtProp, nullptr, nullptr);
    if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR && vtProp.bstrVal != nullptr) {
        std::wstring value(vtProp.bstrVal);
        VariantClear(&vtProp);
        return value;
    }
    VariantClear(&vtProp);
    return L"";
}

int main() {
    HRESULT hres;

    // 初始化COM库
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) {
        std::cout << "初始化COM库失败,错误码: 0x" << std::hex << hres << std::endl;
        return 1;
    }

    // 初始化安全设置
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
    );

    if (FAILED(hres) && hres != RPC_E_TOO_LATE) {
        std::cout << "初始化安全设置失败,错误码: 0x" << std::hex << hres << std::endl;
        CoUninitialize();
        return 1;
    }

    // 创建WMI定位器
    IWbemLocator* pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID*)&pLoc
    );

    if (FAILED(hres)) {
        std::cout << "创建IWbemLocator失败,错误码: 0x" << std::hex << hres << std::endl;
        CoUninitialize();
        return 1;
    }

    // 连接到WMI命名空间
    IWbemServices* pSvc = NULL;
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
    );

    if (FAILED(hres)) {
        std::cout << "连接WMI失败,错误码: 0x" << std::hex << hres << std::endl;
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // 设置代理安全 blanket
    hres = CoSetProxyBlanket(
        pSvc,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE
    );

    if (FAILED(hres)) {
        std::cout << "设置代理安全失败,错误码: 0x" << std::hex << hres << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // 步骤1:获取系统盘符(如C:)
    std::wstring systemDrive;
    IEnumWbemClassObject* pOsEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t("SELECT SystemDrive FROM Win32_OperatingSystem"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pOsEnumerator
    );

    if (SUCCEEDED(hres)) {
        IWbemClassObject* pOsObj = NULL;
        ULONG uReturn = 0;
        if (pOsEnumerator->Next(WBEM_INFINITE, 1, &pOsObj, &uReturn) == WBEM_S_NO_ERROR && uReturn > 0) {
            systemDrive = GetWmiStringProperty(pOsObj, L"SystemDrive");
            pOsObj->Release();
        }
        pOsEnumerator->Release();
    }

    if (systemDrive.empty()) {
        std::cout << "无法获取系统盘符" << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;
    }
    std::wcout << L"系统盘符: " << systemDrive << std::endl;

    // 步骤2:获取系统盘符对应的分区DeviceID
    std::wstring partitionDeviceID;
    IEnumWbemClassObject* pLogicalToPartEnumerator = NULL;
    std::wstring logicalQuery = L"ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"" + systemDrive + L"\"} WHERE AssocClass=Win32_LogicalDiskToPartition";
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t(logicalQuery.c_str()),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pLogicalToPartEnumerator
    );

    if (SUCCEEDED(hres)) {
        IWbemClassObject* pPartObj = NULL;
        ULONG uReturn = 0;
        if (pLogicalToPartEnumerator->Next(WBEM_INFINITE, 1, &pPartObj, &uReturn) == WBEM_S_NO_ERROR && uReturn > 0) {
            partitionDeviceID = GetWmiStringProperty(pPartObj, L"DeviceID");
            pPartObj->Release();
        }
        pLogicalToPartEnumerator->Release();
    }

    if (partitionDeviceID.empty()) {
        std::cout << "无法获取系统盘符对应的分区" << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;
    }
    std::wcout << L"对应分区: " << partitionDeviceID << std::endl;

    // 步骤3:获取分区对应的物理驱动器
    std::wstring systemPhysicalDrive;
    IEnumWbemClassObject* pPartToDriveEnumerator = NULL;
    std::wstring driveQuery = L"ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"" + partitionDeviceID + L"\"} WHERE AssocClass=Win32_DiskDriveToDiskPartition";
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t(driveQuery.c_str()),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pPartToDriveEnumerator
    );

    if (SUCCEEDED(hres)) {
        IWbemClassObject* pDriveObj = NULL;
        ULONG uReturn = 0;
        if (pPartToDriveEnumerator->Next(WBEM_INFINITE, 1, &pDriveObj, &uReturn) == WBEM_S_NO_ERROR && uReturn > 0) {
            systemPhysicalDrive = GetWmiStringProperty(pDriveObj, L"DeviceID");
            std::wstring model = GetWmiStringProperty(pDriveObj, L"Model");
            std::wstring serialNumber = GetWmiStringProperty(pDriveObj, L"SerialNumber");
            std::wcout << L"系统所在物理驱动器: " << systemPhysicalDrive << std::endl;
            std::wcout << L"驱动器型号: " << model << std::endl;
            std::wcout << L"驱动器序列号: " << serialNumber << std::endl;
            pDriveObj->Release();
        }
        pPartToDriveEnumerator->Release();
    }

    if (systemPhysicalDrive.empty()) {
        std::cout << "无法获取系统所在的物理驱动器" << std::endl;
    }

    // 清理资源
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();

    return 0;
}

代码说明

  1. 新增辅助函数:GetWmiStringProperty简化WMI属性的读取和内存清理
  2. 系统盘符获取:通过Win32_OperatingSystem的SystemDrive属性直接获取系统所在盘符,无需硬编码C:
  3. 两次关联查询:
    • 用Win32_LogicalDiskToPartition找到盘符对应的磁盘分区(如Disk #2, Partition #0)
    • 再用Win32_DiskDriveToDiskPartition找到分区对应的物理驱动器(如\\.\PHYSICALDRIVE2)
  4. 直接输出目标信息:同时获取并输出物理驱动器的型号、序列号,满足安全验证的需求

内容的提问来源于stack exchange,提问作者Markstar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:05:56