Show / Hide Table of Contents

MSDN.WhiteKnight - Stack Overflow answers

Ответ на "Получение серийного номера и типа шины диска"

Answer 1099919

Link

Не знаю насчет универсального способа, но для любого жесткого диска, поддерживающего набор команд ATA-2, должна работать команда IDENTIFY DEVICE, которая возвращает серийный номер. Разная интерпретация байт тут невозможна, так как там просто ANSI-символы по сути. Хотя конечно, команда возвращает только то, что зашито в устройство. Там может быть просто мусор вместо уникального номера, как со знаменитыми "to be filled by O.E.M." в таблице SMBIOS. По некорректным результатам для одного устройства нельзя сделать вывод, что способ нерабочий. Нужно иметь несколько разных устройств и с ними экспериментировать.

Вот пример кода для получения серийного номера (требуются права администратора):

#include <windows.h>
#include <stdio.h>

typedef struct
{
    WORD wGenConfig;
    WORD wNumCyls;
    WORD wReserved;
    WORD wNumHeads;
    WORD wBytesPerTrack;
    WORD wBytesPerSector;
    WORD wSectorsPerTrack;
    WORD wVendorUnique[3];
    BYTE sSerialNumber[20];
    WORD wBufferType;
    WORD wBufferSize;
    WORD wECCSize;
    BYTE sFirmwareRev[8];
    BYTE sModelNumber[39];
    WORD wMoreVendorUnique;
    WORD wDoubleWordIO;
    WORD wCapabilities;
    WORD wReserved1;
    WORD wPIOTiming;
    WORD wDMATiming;
    WORD wBS;
    WORD wNumCurrentCycles;
    WORD wNumCurrentHeads;
    WORD wNumCurrentSectorsPerTrack;
    WORD ulCurrentSectorCapacity;
    WORD wMultiSectorStuff;
    DWORD ulTotalAddressableSectors;
    WORD wSingleWordDMA;
    WORD wMultiWordDMA;
    BYTE bReserved[127];
}ST_IDSECTOR; //ATA 7.17.7.1 - IDENTIFY DEVICE data

typedef struct
{
    BYTE m_ucAttribIndex;
    DWORD m_dwAttribValue;
    BYTE m_ucValue;
    BYTE m_ucWorst;
    DWORD m_dwThreshold;
}ST_SMART_INFO;

typedef struct
{
    GETVERSIONINPARAMS m_stGVIP;
    ST_IDSECTOR m_stInfo;
    ST_SMART_INFO m_stSmartInfo[256];
    BYTE m_ucSmartValues;
    BYTE m_ucDriveIndex;
    char m_csErrorString[1000];
}ST_DRIVE_INFO;

#define DRIVE_HEAD_REG  0xA0
#define OUT_BUFFER_SIZE IDENTIFY_BUFFER_SIZE+16

void SwapBytes(char* p, size_t len) {

    if (len % 2 != 0)len--;

    char t;
    for (int i = 0; i < len - 1; i += 2) {
        t = p[i];
        p[i] = p[i + 1];
        p[i + 1] = t;
    }
}

BOOL CollectDriveInfo(HANDLE hDevice, UCHAR ucDriveIndex, ST_IDSECTOR* pInfo)
{
    BOOL bRet = FALSE;
    SENDCMDINPARAMS stCIP = { 0 };
    DWORD dwRet = 0;

    char szOutput[OUT_BUFFER_SIZE] = { 0 };

    stCIP.cBufferSize = IDENTIFY_BUFFER_SIZE;
    stCIP.bDriveNumber = ucDriveIndex;
    stCIP.irDriveRegs.bFeaturesReg = 0;
    stCIP.irDriveRegs.bSectorCountReg = 1;
    stCIP.irDriveRegs.bSectorNumberReg = 1;
    stCIP.irDriveRegs.bCylLowReg = 0;
    stCIP.irDriveRegs.bCylHighReg = 0;
    stCIP.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
    stCIP.irDriveRegs.bCommandReg = ID_CMD; //ATA 7.17 - IDENTIFY DEVICE

    bRet = DeviceIoControl(hDevice, SMART_RCV_DRIVE_DATA, &stCIP, sizeof(stCIP), szOutput, OUT_BUFFER_SIZE, &dwRet, NULL);
    if (bRet)
    {
        CopyMemory(pInfo, szOutput + 16, sizeof(ST_IDSECTOR));      
    }
    else
        dwRet = GetLastError();
    return bRet;
}

BOOL GetDriveInfo(BYTE ucDriveIndex, ST_DRIVE_INFO* pInfo)
{
    HANDLE hDevice = NULL;
    char szT1[MAX_PATH] = { 0 };
    BOOL bRet = FALSE;
    DWORD dwRet = 0;    

    sprintf_s(szT1, sizeof(szT1), "\\\\.\\PHYSICALDRIVE%d", ucDriveIndex);
    hDevice = CreateFileA(szT1, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
    if (hDevice != INVALID_HANDLE_VALUE)
    {
        bRet = DeviceIoControl(hDevice, SMART_GET_VERSION, NULL, 0, &pInfo->m_stGVIP, sizeof(GETVERSIONINPARAMS), &dwRet, NULL);
        if (bRet)
        {
            if ((pInfo->m_stGVIP.fCapabilities & CAP_ATA_ID_CMD) == CAP_ATA_ID_CMD)
            {               
                bRet = CollectDriveInfo(hDevice, ucDriveIndex,&pInfo->m_stInfo);
            }
            else printf("ATA IDENTIFY DEVICE command is not supported");
        }
        else printf("S.M.A.R.T error");
        CloseHandle(hDevice);
    }
    else printf("CreateFile error %d\n",GetLastError());
    return bRet;
}

int main()
{   
    ST_DRIVE_INFO info;
    BOOL res = GetDriveInfo(0,&info);

    if (res == FALSE) {
        printf("GetDriveInfo Error\n");
        getchar();
        return 1;
    }

    char sbuf[50] = "";
    char* pch = NULL;   

    ZeroMemory(sbuf, sizeof(sbuf));
    strncpy_s(sbuf, sizeof(sbuf), (char*)info.m_stInfo.sSerialNumber, 20);
    SwapBytes(sbuf, 20);

    pch = &(sbuf[0]);
    while (true) {
        if (*pch != ' ')break;
        if (pch >= &(sbuf[18]))break;
        pch++;
    }

    printf("Serial number: %s\n", pch);
    getchar();

    return 0;
}

У дисков NVM Express драйвер может работать в режиме эмуляции ATA, тогда устройство может отвечать на команду IDENTIFY DEVICE, на при этом не возвращать флаг CAP_ATA_ID_CMD при проверке возможностей. (У NVM Express есть своя система команд, в которой тоже есть команда IDENTIFY, но другого формата.) Если нужно обрабатывать такие случаи, лучше без предварительной проверки сразу слать команду и смотреть на коды ошибок.

Источники

  • SMART_RCV_DRIVE_DATA
  • Hard drive information using S.M.A.R.T.
  • ATA/ATAPI Command Set 2

Content is retrieved from StackExchange API.

Auto-generated by ruso-archive tools.

Back to top Stack Overflow answers (published from sources in GitHub repository). Copyright (c) 2020, MSDN.WhiteKnight. Content licensed under BSD 3-Clause License.
Generated by DocFX