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; }
に変更して完了です。