首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

實戰 DeviceIoControl 系列之二:全方位獲取軟碟、硬碟與光碟幾何參數

     編輯:組合語言

問題背景:為什麼 MSDN 範例會失敗?

許多開發者在嘗試用 IOCTL_DISK_GET_DRIVE_GEOMETRY 取得光碟機參數時發現:換片後失效、光碟機完全無法取得資料。根本原因在於:

  • 軟碟機必須實際讀取媒體才能回報格式化後的幾何資訊,故 CreateFile 必須指定 GENERIC_READ
  • IOCTL_DISK_GET_DRIVE_GEOMETRY 僅支援磁性媒體(軟碟/硬碟),不支援 CD/DVD-ROM、磁帶機等可移動儲存裝置

解決方案:雙控制碼回退機制

採用「優先嘗試標準磁碟控制碼,失敗再改用儲存裝置控制碼」的策略:

  1. 先嘗試 IOCTL_DISK_GET_DRIVE_GEOMETRY → 適用於硬碟、軟碟
  2. 若失敗,改用 IOCTL_STORAGE_GET_MEDIA_TYPES_EX → 支援光碟、磁帶、MO 等所有儲存類別

關鍵資料結構解析

DISK_GEOMETRY(標準磁碟)

typedef struct _DISK_GEOMETRY {
    LARGE_INTEGER Cylinders;      // 磁柱數
    MEDIA_TYPE MediaType;         // 媒體類型
    DWORD TracksPerCylinder;      // 每柱面磁道數
    DWORD SectorsPerTrack;        // 每磁道扇區數
    DWORD BytesPerSector;         // 每扇區位元組數
} DISK_GEOMETRY;

GET_MEDIA_TYPES / DEVICE_MEDIA_INFO(通用儲存裝置)

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;
}

MFC 測試程式範例

在 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,光碟機用 \\.\CdromX
  • 權限問題:存取實體磁碟需系統管理員權限,否則 CreateFile 回傳 ERROR_ACCESS_DENIED

延伸應用

此技術可用於:

  • 開機磁碟偵測與備份軟體的裝置枚舉
  • 光碟燒錄軟體自動偵測媒體容量與類型
  • 磁碟陣列管理工具識別成員磁碟幾何一致性
  • 虛擬機器軟體掛載實體磁碟前的參數檢查
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved