情報を取得したドライブに対してDeviceIoControlによりIOCTL_STORAGE_QUERY_PROPERTYコマンドを実行すると、そのドライブの情報を取得できる。
とは言うものの実際に利用してみると、情報を取得できないドライブが少なからずある。また取得できても例えばシリアル番号などは値の格納され方がドライブによって異なっている。図でCとDドライブのシリアル番号は本来0x20 0x20 ... 0x45 0x32というバイト配列(文字列)のシリアル番号だが、二重にアスキーエンコードされている。逆にFドライブの場合はシリアル番号がアスキー文字列になっていない。情報がきちんと取得できない原因はドライブ側のためどうしようもないのだが...
結局、信頼できる情報はリムーバブルメディアかどうかと接続インターフェースぐらいだろうか?
#include "winioctl.h" #include "atlstr.h" class CDnpStorageProperty { public: // // 使用中のドライブレター取得 // //ドライブ名が文字列として列挙されて返る。例:AとC、Dドライブが使われているなら"ACD" // bool GetUsedDriveLetter(TCHAR* pszLetter,int nSize) { int i; int nIndex; TCHAR cbDrive; DWORD dwDrives; if(pszLetter == NULL || nSize <= 0) return false; dwDrives = ::GetLogicalDrives(); ZeroMemory(pszLetter,sizeof(TCHAR) * nSize); nIndex = 0; cbDrive = _T('A'); for(i = 0; i <= 'Z' - 'A'; i++) { if(! ((1 << i) & dwDrives)) continue; pszLetter[nIndex++] = cbDrive + i; if(nIndex < nSize) continue; return (dwDrives << (i+1)) ? false : true; } return true; } class CInfo { public: CAtlString _strSerialNo; CAtlString _strProductID; CAtlString _strVendorID; CAtlString _strRevision; bool _bRemovableMedia; STORAGE_BUS_TYPE _enumBusType; CInfo() { _bRemovableMedia = false; _enumBusType = BusTypeUnknown; } }; // // ドライブのプロパティ情報を取得する // bool GetStorageProperty(TCHAR cbDrive,CInfo* pInfo) { BOOL ret; HANDLE hDrive; DWORD dwRet; DWORD dwLen; BYTE* pcbData; CAtlString strDrive; STORAGE_DEVICE_DESCRIPTOR* pDescriptor; STORAGE_PROPERTY_QUERY sQuery; if(pInfo == NULL) return false; *pInfo = CInfo(); strDrive.Format(_T("\\\\.\\%c:"),cbDrive); hDrive = CreateFile(strDrive,GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDrive == INVALID_HANDLE_VALUE) return false; dwLen = 4096; //十分なバッファー領域を確保 pcbData = new BYTE[dwLen]; if(pcbData == NULL) { ::CloseHandle(hDrive); return false; } ZeroMemory(pcbData,dwLen); sQuery.PropertyId = StorageDeviceProperty; sQuery.QueryType = PropertyStandardQuery; sQuery.AdditionalParameters[0] = NULL; ret = ::DeviceIoControl(hDrive,IOCTL_STORAGE_QUERY_PROPERTY,&sQuery,sizeof(STORAGE_PROPERTY_QUERY),pcbData,dwLen,&dwRet,NULL); ::CloseHandle(hDrive); if(ret == FALSE) { delete pcbData; return false; } pDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pcbData; if(pDescriptor->SerialNumberOffset) pInfo->_strSerialNo = (char*)pDescriptor + pDescriptor->SerialNumberOffset; if(pDescriptor->ProductIdOffset) pInfo->_strProductID = (char*)pDescriptor + pDescriptor->ProductIdOffset; if(pDescriptor->ProductRevisionOffset) pInfo->_strRevision = (char*)pDescriptor + pDescriptor->ProductRevisionOffset; if(pDescriptor->VendorIdOffset) pInfo->_strVendorID = (char*)pDescriptor + pDescriptor->VendorIdOffset; pInfo->_enumBusType = pDescriptor->BusType; pInfo->_bRemovableMedia = pDescriptor->RemovableMedia ? true : false; delete pcbData; return true; } }; bool Test() { bool ret; size_t i; size_t nSize; TCHAR pszDrives[256]; CAtlString strBuff; CAtlString strMessage; CDnpStorageProperty cStorage; CDnpStorageProperty::CInfo cInfo; ret = cStorage.GetUsedDriveLetter(pszDrives,256); if(ret == false) return false; nSize = ::_tcslen(pszDrives); for(i = 0; i < nSize; i++) { ret = cStorage.GetStorageProperty(pszDrives[i],&cInfo); if(ret == false) continue; strBuff.Format(_T("\nドライブ:%c"),pszDrives[i]); strMessage += strBuff; strMessage += _T("\nベンダーID:"); strMessage += cInfo._strVendorID; strMessage += _T("\nプロダクトID:"); strMessage += cInfo._strProductID; strMessage += _T("\nシリアル番号:"); strMessage += cInfo._strSerialNo; strMessage += _T("\nリビジョン番号:"); strMessage += cInfo._strRevision; strMessage += _T("\nメディアタイプ:"); strMessage += (cInfo._bRemovableMedia) ? _T("リムーバブルメディア") : _T("固定メディア"); strMessage += _T("\n接続インターフェース:"); switch(cInfo._enumBusType) { case BusTypeScsi: strMessage += _T("SCSI"); break; case BusTypeAtapi: strMessage += _T("ATAPI"); break; case BusTypeAta: strMessage += _T("ATA"); break; case BusType1394: strMessage += _T("IEEE1394"); break; case BusTypeSsa: strMessage += _T("BusTypeSsa"); break; case BusTypeFibre: strMessage += _T("BusTypeFibre"); break; case BusTypeUsb: strMessage += _T("USB"); break; case BusTypeRAID: strMessage += _T("RAID"); break; case BusTypeiScsi: strMessage += _T("iSCSI"); break; case BusTypeSas: strMessage += _T("SAS"); break; case BusTypeSata: strMessage += _T("SerialATA"); break; case BusTypeSd: strMessage += _T("SD"); break; case BusTypeMmc: strMessage += _T("MMC"); break; default: case BusTypeUnknown: strMessage += _T("不明"); break; } strMessage += _T("\n"); } ::MessageBox(NULL,strMessage,_T(""),MB_OK); return true; }