許多開發者在嘗試用 IOCTL_DISK_GET_DRIVE_GEOMETRY 取得光碟機參數時發現:換片後失效、光碟機完全無法取得資料。根本原因在於:
CreateFile 必須指定 GENERIC_READIOCTL_DISK_GET_DRIVE_GEOMETRY 僅支援磁性媒體(軟碟/硬碟),不支援 CD/DVD-ROM、磁帶機等可移動儲存裝置採用「優先嘗試標準磁碟控制碼,失敗再改用儲存裝置控制碼」的策略:
IOCTL_DISK_GET_DRIVE_GEOMETRY → 適用於硬碟、軟碟IOCTL_STORAGE_GET_MEDIA_TYPES_EX → 支援光碟、磁帶、MO 等所有儲存類別typedef struct _DISK_GEOMETRY {
LARGE_INTEGER Cylinders; // 磁柱數
MEDIA_TYPE MediaType; // 媒體類型
DWORD TracksPerCylinder; // 每柱面磁道數
DWORD SectorsPerTrack; // 每磁道扇區數
DWORD BytesPerSector; // 每扇區位元組數
} DISK_GEOMETRY;
typedef struct _GET_MEDIA_TYPES {
DWORD DeviceType; // 裝置類型
DWORD MediaInfoCount; // 媒體資訊筆數
DEVICE_MEDIA_INFO MediaInfo[1]; // 彈性陣列
} GET_MEDIA_TYPES;
typedef struct _DEVICE_MEDIA_INFO {
union {
struct { /* DiskInfo */ } DiskInfo;
struct { /* RemovableDiskInfo */ } RemovableDiskInfo; // 光碟歸屬此類
struct { /* TapeInfo */ } TapeInfo;
} DeviceSpecific;
} DEVICE_MEDIA_INFO;
注意:GET_MEDIA_TYPES 結構僅預留一筆 DEVICE_MEDIA_INFO,實際陣列長度由 MediaInfoCount 決定,呼叫時必須配置足夠緩衝區(建議預留 15 筆以上)。
#define MEDIA_INFO_SIZE (sizeof(GET_MEDIA_TYPES) + 15 * sizeof(DEVICE_MEDIA_INFO))
BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY* pdg)
{
HANDLE hDevice = ::CreateFile(filename, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE) return FALSE;
DWORD dwOutBytes;
BOOL bResult = ::DeviceIoControl(hDevice,
IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
pdg, sizeof(DISK_GEOMETRY), &dwOutBytes, NULL);
if (!bResult) {
GET_MEDIA_TYPES* pmt = (GET_MEDIA_TYPES*)new BYTE[MEDIA_INFO_SIZE];
bResult = ::DeviceIoControl(hDevice,
IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0,
pmt, MEDIA_INFO_SIZE, &dwOutBytes, NULL);
if (bResult) {
// DEVICE_MEDIA_INFO.RemovableDiskInfo 前五欄與 DISK_GEOMETRY 記憶體配置相容
::memcpy(pdg, &pmt->MediaInfo[0].DeviceSpecific.RemovableDiskInfo,
sizeof(DISK_GEOMETRY));
}
delete[] pmt;
}
::CloseHandle(hDevice);
return bResult;
}
在 CEditView 專案中加入工具列按鈕,對應 ID_GET_DISK_GEOMETRY,處理函數中遍歷常見裝置路徑:
const char* szDevName[] = {
"\\\\.\\A:", "\\\\.\\B:",
"\\\\.\\PhysicalDrive0", "\\\\.\\PhysicalDrive1",
"\\\\.\\PhysicalDrive2", "\\\\.\\PhysicalDrive3",
"\\\\.\\Cdrom0", "\\\\.\\Cdrom1"
};
DISK_GEOMETRY dg;
ULONGLONG DiskSize;
CString strMsg;
for (int i = 0; i < _countof(szDevName); ++i) {
BOOL bResult = GetDriveGeometry(szDevName[i], &dg);
strMsg.AppendFormat("\r\n%s result = %s\r\n",
szDevName[i], bResult ? "success" : "failure");
if (!bResult) continue;
strMsg.AppendFormat(" Media Type = %d\r\n", dg.MediaType);
strMsg.AppendFormat(" Cylinders = %I64d\r\n", dg.Cylinders.QuadPart);
strMsg.AppendFormat(" Tracks per cylinder = %lu\r\n", dg.TracksPerCylinder);
strMsg.AppendFormat(" Sectors per track = %lu\r\n", dg.SectorsPerTrack);
strMsg.AppendFormat(" Bytes per sector = %lu\r\n", dg.BytesPerSector);
DiskSize = dg.Cylinders.QuadPart *
(ULONGLONG)dg.TracksPerCylinder *
(ULONGLONG)dg.SectorsPerTrack *
(ULONGLONG)dg.BytesPerSector;
strMsg.AppendFormat(" Disk size = %I64d Bytes = %I64d MB\r\n",
DiskSize, DiskSize / (1024 * 1024));
}
GetEditCtrl().SetWindowText(strMsg);
\\.\C:,實體硬碟用 \\.\PhysicalDriveX,光碟機用 \\.\CdromXCreateFile 回傳 ERROR_ACCESS_DENIED此技術可用於: