SerialNumberの取得(3)

色々やっていると欲が出てくるもので、VistaとXPに関しては昨日のロジックでなんら問題は無かったのですが、今度はWindows2000で表示できないというのが発覚しました。
最近ではWindows2000なんて殆ど見ることもなかったのですが、仕事の関係でWindows2000のサポートは無視するわけにはいかず、とにかくネットで検索して
http://www.usefullcode.net/2007/02/if.html
なんかを参考にして必要最低限まで削った関数を作っちゃいました。

#include <winioctl.h>
#include <ata.h>
enum    IO_CONTROL_CODE {
    DFP_GET_VERSION        = 0x00074080,
    DFP_RECEIVE_DRIVE_DATA = 0x0007c088,
};

enum    ATAPI_COMMAND_CODE {
    ATAPI_IDENTIFY_DEVICE   = 0xA1,
    ATA_IDENTIFY_DEVICE = 0xEC,
};

typedef GETVERSIONINPARAMS GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;

#pragma pack(push,1)
typedef struct _IDENTIFY_DEVICE_OUTDATA {
    SENDCMDOUTPARAMS    sSendCmdOutParam;
    BYTE    pData[IDENTIFY_BUFFER_SIZE-1];
} IDENTIFY_DEVICE_OUTDATA, *PIDENTIFY_DEVICE_OUTDATA;
#pragma pack(pop)

String getDiskSerial(int drive, _TCHAR *device_id) {
    BOOL    status = FALSE;
    DWORD   ret_size = 0;
    String  serialNumber = String(_T("N/A"));
    /*
     *  取得したい物理デバイスをオープンする
     *  return  : I/Oハンドル
     */
    HANDLE  hIoCtrl = CreateFile(device_id,
                                GENERIC_READ | GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                0,
                                NULL);
    if (hIoCtrl != INVALID_HANDLE_VALUE) {
        GETVERSIONOUTPARAMS     ver_param;
        memset((void*)&ver_param, 0, sizeof(ver_param));
        /*
         *  デバイスからDFPのバージョンを取得する
         *  return  : 処理ステータス
         *            0以外 = 成功()
         *            0 = 失敗
         *  参照:http://msdn.microsoft.com/en-us/library/aa363216.aspx
         */
        status = DeviceIoControl(hIoCtrl,
                                DFP_GET_VERSION,
                                NULL,
                                0,
                                &ver_param,
                                sizeof(ver_param),
                                &ret_size,
                                NULL);
        if (status != 0) {
            SENDCMDINPARAMS             send_cmd;
            IDENTIFY_DEVICE_OUTDATA     device_data;

            memset(&send_cmd, 0, sizeof(SENDCMDINPARAMS));
            send_cmd.cBufferSize = IDENTIFY_BUFFER_SIZE;
            send_cmd.irDriveRegs.bCommandReg = (ver_param.bIDEDeviceMap >> drive & 0x10) ? ATAPI_IDENTIFY_DEVICE : ATA_IDENTIFY_DEVICE;
            send_cmd.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
            send_cmd.bDriveNumber = drive;

            ret_size = 0;
            memset(&device_data, 0, sizeof(IDENTIFY_DEVICE_OUTDATA));
            /*
             *  デバイスからDFPのバージョンを取得する
             *  return  : 処理ステータス
             *            0以外 = 成功
             *            0 = 失敗
             */
            status = DeviceIoControl(hIoCtrl,
                                    DFP_RECEIVE_DRIVE_DATA,
                                    &send_cmd,
                                    sizeof(SENDCMDINPARAMS),
                                    &device_data,
                                    sizeof(IDENTIFY_DEVICE_OUTDATA),
                                    &ret_size,
                                    NULL);
            if (status != 0) {
                PIDENTIFY_DEVICE_DATA   id_device;
                id_device = (PIDENTIFY_DEVICE_DATA)device_data.sSendCmdOutParam.bBuffer;
                size_t len = sizeof(id_device->SerialNumber);
                unsigned char *serial = (unsigned char *)calloc(len+1, sizeof(char));
                for(size_t i = 0; i < len; i += 2) {
                    serial[i] = id_device->SerialNumber[i+1];
                    serial[i+1] = id_device->SerialNumber[i];
                }
                _bstr_t buff = _bstr_t((char *)serial);
                serialNumber = String((_TCHAR*)buff);
                free(serial);
            }
        }
        else {
            STORAGE_PROPERTY_QUERY      query;
            STORAGE_DESCRIPTOR_HEADER   header;

            memset(&query, 0, sizeof(STORAGE_PROPERTY_QUERY));
            query.PropertyId = StorageDeviceProperty;
            query.QueryType = PropertyStandardQuery;

            /*
             *  デバイスのプロパティを取得する(但しここではプロパティを格納するエリアのサイズだけ取得する)
             *  return  : 処理ステータス
             *            0以外 = 成功
             *            0 = 失敗
             *  参照:http://msdn.microsoft.com/en-us/library/ms803642.aspx
             */
            status = DeviceIoControl(hIoCtrl,
                                    IOCTL_STORAGE_QUERY_PROPERTY,
                                    &query,
                                    sizeof(STORAGE_PROPERTY_QUERY),
                                    &header,
                                    sizeof(STORAGE_DESCRIPTOR_HEADER),
                                    &ret_size,
                                    NULL);
            if (status != 0) {
                PSTORAGE_DEVICE_DESCRIPTOR  device_desc;
                device_desc = (PSTORAGE_DEVICE_DESCRIPTOR)calloc(header.Size+1, sizeof(char));
                ret_size = 0;
                /*
                 *  デバイスのプロパティを取得する
                 *  return  : 処理ステータス
                 *            0以外 = 成功
                 *            0 = 失敗
                 *  参照:http://msdn.microsoft.com/en-us/library/ms803642.aspx
                 */
                status = DeviceIoControl(hIoCtrl,
                                        IOCTL_STORAGE_QUERY_PROPERTY,
                                        &query,
                                        sizeof(STORAGE_PROPERTY_QUERY),
                                        device_desc,
                                        header.Size,
                                        &ret_size,
                                        NULL);
                if (status != 0) {
                    if ((int)device_desc->SerialNumberOffset > 0) {
                        char *serial = (char *)device_desc;
                        serial[header.Size] = 0;
                        _bstr_t buff = _bstr_t((char *)&serial[device_desc->SerialNumberOffset]);
                        serialNumber = String((_TCHAR*)buff);
                    }
                }
                free(device_desc);
            }
        }
        CloseHandle(hIoCtrl);
    }
    return serialNumber;
}

DDKを使っているので、これは
http://www.microsoft.com/japan/whdc/DevTools/WDK/WDKpkg.mspx
からリンクを辿ってダウンロードしてきました。
それで、呼び出しプログラム部分は、

    WmiResult disk_drive = wmi.access(_T("SELECT * FROM Win32_DiskDrive"));
    if (disk_drive.items() == 0) {
        String msg = wmi.getErrorMessage();
        return;
    }
    COUT << disk_drive.print();

    for (size_t i = 0; i < disk_drive.size(); i++) {
        String serial = getDiskSerial(disk_drive.getLong(_T("Index"), i), (_TCHAR *)disk_drive.getString(_T("DeviceID"), i).c_str());
        COUT << _T("SerialNumber = ") << serial << endl;
    }

に変更して完了です。