This has been already answered but it's a C# solution. How do I do this in C or C++?
- 
                    I believe that this article on MSDN might be helpful to you: http://social.msdn.microsoft.com/Forums/vstudio/en-US/fa0f6225-33f5-4e0c-b909-1130e6058c02/how-to-get-drive-info-like-vendor-model-name-serial-number-firmware-version-etc?forum=vclanguage – IrishGeek82 Jun 04 '14 at 23:44
- 
                    Well, the accepted answer points to some projects on codeproject where all them are C# solutions... – The Mask Jun 05 '14 at 00:21
- 
                    Does [this question](http://stackoverflow.com/q/3396216) (particularly the link in the accepted answer) help? – Ken White Jun 05 '14 at 00:32
6 Answers
There are a few ways to do this. You could make calls using system to get the information.
For Linux:
system("hdparm -i /dev/hda | grep -i serial");
Without using system:
static struct hd_driveid hd;
int fd;
if ((fd = open("/dev/hda", O_RDONLY | O_NONBLOCK)) < 0) {
    printf("ERROR opening /dev/hda\n");
    exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
    printf("%.20s\n", hd.serial_no);
} else if (errno == -ENOMSG) {
    printf("No serial number available\n");
} else {
    perror("ERROR: HDIO_GET_IDENTITY");
    exit(1);
}
For Windows:
system("wmic path win32_physicalmedia get SerialNumber");
Without using system (Based on Getting WMI Data ):
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);
 
    
    - 924
- 7
- 8
- 
                    
- 
                    
- 
                    3The Linux proposed method doesn't work if you don't have root privileges. – piponazo May 22 '15 at 12:53
- 
                    1I got white spaces too. I'm not sure how to handle them. Until a complete answer, I'd rather considering them as a part of the serial number. Of course, for display only, it's better to crop them. – Spiralwise Nov 30 '15 at 14:30
- 
                    
Here is a complete solution for Windows which wraps WMI calls without using cmd or system .
So you can easily get anything from WMI including hard drive serial number.  
All you have to do is to call :
getWmiQueryResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
PS. It's based on official documentation and additionally does some better error handling and cleanup. Also you might want to use this or this for creating WQL queries.
#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 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
                );
                // 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");
}
Note:
@matkatmusic discovered that using this function on a background thread in the JUCE framework will fail if you try to CoInitializeSecurity. And suggests that by removing the CoInitializeSecurity() call, this function will correctly run the WMI query.
 
    
    - 10,860
- 6
- 57
- 75
For Windows:
wmic path win32_physicalmedia get SerialNumber
Here is example how to get back the data as a string running any command. we're using _popen instead of system suggested above
#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;
}
 
    
    - 19,004
- 11
- 72
- 86
For Linux without requiring root/sudo access:
Install libudev-dev. Use code like that below:
#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: %s\n", udev_list_entry_get_value(entry));
                udev_device_unref(device);
            }
        }
        (void)udev_unref(ud);
    }
    return 0;
} 
The code is partly based on libudev documentation and partly on the source code for udevadm.
 
    
    - 4,119
- 5
- 26
- 41
Linux: see /sys/class/ata_device/dev*/id
On my system there are four user-readable files containing hex dumps of device info; two of them are all zeros, two other contain info about disk and DVD; DVD has no serial number and disk serial starts at offset 0x20.
 
    
    - 1
For Windows, if you don't want WMI, use DeviceIOControl(). Here is a working implementation that I have used for years.
GetDiskSerialNumber("c:");
Implementation:
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;
}
 
    
    - 558
- 4
- 10
- 
                    When i use "C:" i get ERROR_ACCESS_DENIED = 5 on CreateFile. You need to use the "\\\\.\\PhysicalDrive0". Mapping the 2 is not trivial. – Andrew Robinson Mar 13 '23 at 18:50
 
    