如何在C/ c++中获取磁盘驱动器序列号?

How do I get the disk drive serial number in C/C++

本文关键字:获取 磁盘驱动器 序列号 c++      更新时间:2023-10-16

这个问题已经有了答案,但这是一个c#解决方案。我如何在C或c++中做到这一点?

有几种方法可以做到这一点。你可以用系统打电话来获取信息。

Linux的

:

system("hdparm -i /dev/hda | grep -i serial");

不使用system:

static struct hd_driveid hd;
int fd;
if ((fd = open("/dev/hda", O_RDONLY | O_NONBLOCK)) < 0) {
    printf("ERROR opening /dev/hdan");
    exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
    printf("%.20sn", hd.serial_no);
} else if (errno == -ENOMSG) {
    printf("No serial number availablen");
} else {
    perror("ERROR: HDIO_GET_IDENTITY");
    exit(1);
}
Windows:

system("wmic path win32_physicalmedia get SerialNumber");

不使用系统(基于获取WMI数据):

hres = pSvc->ExecQuery(
    bstr_t("WQL"),
    bstr_t("SELECT SerialNumber FROM Win32_PhysicalMedia"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
    NULL,
    &pEnumerator);
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);

这是一个完整的解决方案,用于Windows封装WMI调用而不使用cmd或system。
所以你可以很容易地从WMI得到任何东西,包括硬盘序列号。

你所要做的就是调用:

getWmiQueryResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");

p。它基于官方文档,并且还做了一些更好的错误处理和清理。此外,您可能希望使用this或this来创建WQL查询。

#include "stdafx.h"
#define _WIN32_DCOM
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
#include <vector>
#include <string>
#pragma comment(lib, "wbemuuid.lib")
enum class WmiQueryError {
    None,
    BadQueryFailure,
    PropertyExtractionFailure,
    ComInitializationFailure,
    SecurityInitializationFailure,
    IWbemLocatorFailure,
    IWbemServiceConnectionFailure,
    BlanketProxySetFailure,
};
struct WmiQueryResult
{
    std::vector<std::wstring> ResultList;
    WmiQueryError Error = WmiQueryError::None;
    std::wstring ErrorDescription;
};
WmiQueryResult getWmiQueryResult(std::wstring wmiQuery, std::wstring propNameOfResultObject, bool allowEmptyItems = false) {
    WmiQueryResult retVal;
    retVal.Error = WmiQueryError::None;
    retVal.ErrorDescription = L"";
    HRESULT hres;

    IWbemLocator *pLoc = NULL;
    IWbemServices *pSvc = NULL;
    IEnumWbemClassObject* pEnumerator = NULL;
    IWbemClassObject *pclsObj = NULL;
    VARIANT vtProp;

    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        retVal.Error = WmiQueryError::ComInitializationFailure;
        retVal.ErrorDescription = L"Failed to initialize COM library. Error code : " + std::to_wstring(hres);
    }
    else
    {
        // Step 2: --------------------------------------------------
        // Set general COM security levels --------------------------
        // note: JUCE Framework users should comment this call out,
        // as this does not need to be initialized to run the query.
        // see https://social.msdn.microsoft.com/Forums/en-US/48b5626a-0f0f-4321-aecd-17871c7fa283/unable-to-call-coinitializesecurity?forum=windowscompatibility 
        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))
        {
            retVal.Error = WmiQueryError::SecurityInitializationFailure;
            retVal.ErrorDescription = L"Failed to initialize security. Error code : " + std::to_wstring(hres);
        }
        else
        {
            // Step 3: ---------------------------------------------------
            // Obtain the initial locator to WMI -------------------------
            pLoc = NULL;
            hres = CoCreateInstance(
                CLSID_WbemLocator,
                0,
                CLSCTX_INPROC_SERVER,
                IID_IWbemLocator, (LPVOID *)&pLoc);
            if (FAILED(hres))
            {
                retVal.Error = WmiQueryError::IWbemLocatorFailure;
                retVal.ErrorDescription = L"Failed to create IWbemLocator object. Error code : " + std::to_wstring(hres);
            }
            else
            {
                // Step 4: -----------------------------------------------------
                // Connect to WMI through the IWbemLocator::ConnectServer method
                pSvc = NULL;
                // Connect to the rootcimv2 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
                );
                // Connected to ROOT\CIMV2 WMI namespace
                if (FAILED(hres))
                {
                    retVal.Error = WmiQueryError::IWbemServiceConnectionFailure;
                    retVal.ErrorDescription = L"Could not connect to Wbem service.. Error code : " + std::to_wstring(hres);
                }
                else
                {
                    // 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
                        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))
                    {
                        retVal.Error = WmiQueryError::BlanketProxySetFailure;
                        retVal.ErrorDescription = L"Could not set proxy blanket. Error code : " + std::to_wstring(hres);
                    }
                    else
                    {
                        // Step 6: --------------------------------------------------
                        // Use the IWbemServices pointer to make requests of WMI ----
                        // For example, get the name of the operating system
                        pEnumerator = NULL;
                        hres = pSvc->ExecQuery(
                            bstr_t("WQL"),
                            bstr_t(wmiQuery.c_str()),
                            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                            NULL,
                            &pEnumerator);
                        if (FAILED(hres))
                        {
                            retVal.Error = WmiQueryError::BadQueryFailure;
                            retVal.ErrorDescription = L"Bad query. Error code : " + std::to_wstring(hres);
                        }
                        else
                        {
                            // Step 7: -------------------------------------------------
                            // Get the data from the query in step 6 -------------------
                            pclsObj = NULL;
                            ULONG uReturn = 0;
                            while (pEnumerator)
                            {
                                HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
                                    &pclsObj, &uReturn);
                                if (0 == uReturn)
                                {
                                    break;
                                }
                                // VARIANT vtProp;
                                // Get the value of desired property
                                hr = pclsObj->Get(propNameOfResultObject.c_str(), 0, &vtProp, 0, 0);
                                if (S_OK != hr) {
                                    retVal.Error = WmiQueryError::PropertyExtractionFailure;
                                    retVal.ErrorDescription = L"Couldn't extract property: " + propNameOfResultObject + L" from result of query. Error code : " + std::to_wstring(hr);
                                }
                                else {
                                    BSTR val = vtProp.bstrVal;
                                    // Sometimes val might be NULL even when result is S_OK
                                    // Convert NULL to empty string (otherwise "std::wstring(val)" would throw exception)
                                    if (NULL == val) {
                                        if (allowEmptyItems) {
                                            retVal.ResultList.push_back(std::wstring(L""));
                                        }
                                    }
                                    else {
                                        retVal.ResultList.push_back(std::wstring(val));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // Cleanup
    // ========
    VariantClear(&vtProp);
    if (pclsObj)
        pclsObj->Release();
    if (pSvc)
        pSvc->Release();
    if (pLoc)
        pLoc->Release();
    if (pEnumerator)
        pEnumerator->Release();
    CoUninitialize();
    return retVal;
}
void queryAndPrintResult(std::wstring query, std::wstring propNameOfResultObject)
{
    WmiQueryResult res;
    res = getWmiQueryResult(query, propNameOfResultObject);
    if (res.Error != WmiQueryError::None) {
        std::wcout << "Got this error while executing query: " << std::endl;
        std::wcout << res.ErrorDescription << std::endl;
        return; // Exitting function
    }
    for (const auto& item : res.ResultList) {
        std::wcout << item << std::endl;
    }
}
int main(int argc, char **argv)
{
    // Get OS and partition
    // queryAndPrintResult(L"SELECT * FROM Win32_OperatingSystem", L"Name");
    // Get list of running processes
    // queryAndPrintResult(L"Select * From Win32_Process", L"Name");
    // Get serial number of Hard Drive
    queryAndPrintResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
    // Get id of CPU
    queryAndPrintResult(L"SELECT ProcessorId FROM Win32_Processor", L"ProcessorId");
    // Get desktops
    queryAndPrintResult(L"SELECT * FROM Win32_DesktopMonitor ", L"DeviceId");

    system("pause");
}

注意:
@matkatmusic发现,如果您尝试CoInitializeSecurity,则在JUCE框架中的后台线程上使用此函数将失败。并建议通过删除CoInitializeSecurity()调用,该函数将正确运行WMI查询。

Windows:wmic path win32_physicalmedia get SerialNumber

下面是如何运行任何命令以字符串形式返回数据的示例。我们用_popen代替上面建议的system
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
exec("wmic path win32_physicalmedia get SerialNumber");
std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
    if (!pipe) throw std::runtime_error("_popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, pipe.get()) != NULL)
            result += buffer.data();
    }
    return result;
}

对于不需要root/sudo访问权限的Linux:

安装libudev-dev。使用如下代码:

#include <stdio.h>
#include <string.h>
#include <libudev.h>
#include <sys/stat.h>

int main()
{
    struct udev            *ud      = NULL;
    struct stat             statbuf;
    struct udev_device     *device  = NULL;
    struct udev_list_entry *entry   = NULL;
    ud = udev_new();
    if (NULL == ud) {
        fprintf(stderr, "Failed to create udev.n");
    } else {
        if (0 != stat("/dev/sda", &statbuf)) {
            fprintf(stderr, "Failed to stat /dev/sda.n");
        } else {
            device = udev_device_new_from_devnum(ud, 'b', statbuf.st_rdev);
            if (NULL == device) {
                fprintf(stderr, "Failed to open /dev/sda.n");
            } else {
                entry = udev_device_get_properties_list_entry(device);
                while (NULL != entry) {
                    if (0 == strcmp(udev_list_entry_get_name(entry),
                                    "ID_SERIAL")) {
                        break;
                    }
                    entry = udev_list_entry_get_next(entry);
                }
                printf("Serial ID: %sn", udev_list_entry_get_value(entry));
                udev_device_unref(device);
            }
        }
        (void)udev_unref(ud);
    }
    return 0;
} 

代码部分基于libdev文档,部分基于udevadm的源代码。

Linux:参见/sys/class/ata_device/dev*/id

在我的系统上有四个用户可读的文件,其中包含十六进制的设备信息转储;其中两个都是零,另外两个包含有关磁盘和DVD的信息;DVD没有序列号,磁盘序列号从偏移量0x20开始。

对于Windows,如果您不想要WMI,请使用DeviceIOControl()。这是我使用了多年的一个工作实现。

GetDiskSerialNumber("c:");
实现:

std::string GetDiskSerialNumber(const std::string& pDevicePath)
{
    // open the device
    HANDLE hDevice = ::CreateFileA(devicePath.c_str(), 0, 0, NULL, OPEN_EXISTING, NULL, NULL);
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        // unable to open disk
        M_LogT("GDSN - CF - FAILED - " << devicePath);
        return "";
    }
    // set the input data structure
    ::STORAGE_PROPERTY_QUERY storagePropertyQuery;
    ::ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY));
    storagePropertyQuery.PropertyId = StorageDeviceProperty;
    storagePropertyQuery.QueryType  = PropertyStandardQuery;
    // get the necessary output buffer size
    STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = {0};
    DWORD dwBytesReturned = 0;
    if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery,
            sizeof(STORAGE_PROPERTY_QUERY), &storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER),
            &dwBytesReturned, NULL))
    {
        DWORD error = ::GetLastError();
        ::CloseHandle(hDevice);
        M_LogWarnT("MGDSNV - FAILED - " << M_WinError(error));
        return "";
    }
    // has serial number?
    if (!storageDescriptorHeader.Size)
        return "";
    // alloc the output buffer
    const DWORD dwOutBufferSize = storageDescriptorHeader.Size;
    std::unique_ptr<BYTE[]> pOutBuffer(new BYTE[dwOutBufferSize]);
    ::ZeroMemory(pOutBuffer.get(), dwOutBufferSize);
    // het the storage device descriptor
    if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery,
            sizeof(STORAGE_PROPERTY_QUERY), pOutBuffer.get(), dwOutBufferSize, &dwBytesReturned, NULL))
    {
        DWORD error = ::GetLastError();
        ::CloseHandle(hDevice);
        LogWarnT("MGDSNV - FAILED - " << M_WinError(error));
        return "";
    }
    // cleanup
    ::CloseHandle(hDevice);
    std::string serial;
    // output buffer points to a STORAGE_DEVICE_DESCRIPTOR structure followed by additional
    // info like vendor ID, product ID, serial number, and so on.
    const STORAGE_DEVICE_DESCRIPTOR* pDeviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pOutBuffer.get();
    if (pDeviceDescriptor->SerialNumberOffset && *(pOutBuffer.get() + pDeviceDescriptor->SerialNumberOffset))
        // get the serial number
        serial = std::string(reinterpret_cast<char*>(pOutBuffer.get() + pDeviceDescriptor->SerialNumberOffset));
    return serial;
}