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

實戰 DeviceIoControl 系列之一:Windows 核心裝置存取三步曲

     編輯:組合語言

核心概念:DeviceIoControl 是什麼?

在 Windows NT 核心架構(NT/2000/XP/Server 2003/Vista/7/10/11)中,應用程式無法直接存取硬體,必須透過核心模式驅動程式。DeviceIoControl 正是使用者模式與驅動程式溝通的標準介面,透過發送控制碼與資料緩衝區,實現資訊查詢、命令下發、資料交換等功能。

函數原型完整解析

BOOL DeviceIoControl(
    HANDLE hDevice,           // 裝置控制代碼
    DWORD dwIoControlCode,    // 控制碼
    LPVOID lpInBuffer,        // 輸入緩衝區
    DWORD nInBufferSize,      // 輸入緩衝區大小
    LPVOID lpOutBuffer,       // 輸出緩衝區
    DWORD nOutBufferSize,     // 輸出緩衝區大小
    LPDWORD lpBytesReturned,  // 實際回傳位元組數
    LPOVERLAPPED lpOverlapped // 非同步結構,NULL 表同步
);

關鍵參數說明

參數用途備註
hDevice識別目標裝置由 CreateFile 取得
dwIoControlCode指定操作類型定義於 winioctl.h,前綴 IOCTL_ 或 FSCTL_
lpInBuffer/OutBuffer輸入/輸出資料結構由控制碼決定,無需時填 NULL/0
lpOverlapped非同步控制設 NULL 為同步阻塞呼叫

常用控制碼分類

  • 磁碟管理:IOCTL_DISK_GET_DRIVE_GEOMETRY、IOCTL_DISK_GET_LENGTH_INFO、IOCTL_DISK_FORMAT_TRACKS
  • 磁碟區管理:FSCTL_LOCK_VOLUME、FSCTL_DISMOUNT_VOLUME、FSCTL_UNLOCK_VOLUME
  • 儲存裝置通用:IOCTL_STORAGE_GET_MEDIA_TYPES_EX、IOCTL_STORAGE_QUERY_PROPERTY
  • 光碟機專用:IOCTL_CDROM_READ_TOC、IOCTL_CDROM_GET_DRIVE_SPEED

三步曲:取得裝置控制代碼

CreateFile 不僅開啟檔案,也是「打開裝置驅動程式」的關鍵:

HANDLE hDevice = CreateFile(
    "\\\\.\\PhysicalDrive0",   // 裝置名稱格式:\\.\DeviceName
    GENERIC_READ | GENERIC_WRITE, // 或 0 僅查詢
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,                     // 預設安全性
    OPEN_EXISTING,            // 必須存在
    0,                        // 無特殊屬性
    NULL);                    // 無模板
if (hDevice == INVALID_HANDLE_VALUE) {
    // 處理錯誤:GetLastError()
}

標準裝置命名規則

裝置類型命名格式範例
軟碟機\\.\A:、\\.\B:\\.\A:
邏輯磁碟區\\.\C:、\\.\D: ...\\.\C:
實體硬碟\\.\PhysicalDriveX\\.\PhysicalDrive0
光碟機\\.\CdromX\\.\Cdrom0
磁帶機\\.\TapeX\\.\Tape0

注意:實體硬碟不包含軟碟機與光碟機;邏輯磁碟區可為硬碟分區、光碟、USB 隨身碟、虛擬磁碟等。

MSDN 經典範例:取得硬碟基本參數

BOOL GetDriveGeometry(DISK_GEOMETRY* pdg)
{
    HANDLE hDevice = CreateFile("\\\\.\\PhysicalDrive0", 0,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_EXISTING, 0, NULL);
    if (hDevice == INVALID_HANDLE_VALUE) return FALSE;

    DWORD junk;
    BOOL bResult = DeviceIoControl(hDevice,
        IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
        pdg, sizeof(*pdg), &junk, NULL);
    CloseHandle(hDevice);
    return bResult;
}

int main()
{
    DISK_GEOMETRY pdg;
    if (GetDriveGeometry(&pdg)) {
        ULONGLONG DiskSize = pdg.Cylinders.QuadPart *
            pdg.TracksPerCylinder * pdg.SectorsPerTrack * pdg.BytesPerSector;
        printf("Cylinders = %I64d\n", pdg.Cylinders.QuadPart);
        printf("Tracks/cylinder = %lu\n", pdg.TracksPerCylinder);
        printf("Sectors/track = %lu\n", pdg.SectorsPerTrack);
        printf("Bytes/sector = %lu\n", pdg.BytesPerSector);
        printf("Disk size = %I64d Bytes = %I64d MB\n",
            DiskSize, DiskSize / (1024*1024));
    } else {
        printf("Failed. Error %lu\n", GetLastError());
    }
    return 0;
}

常見陷阱與最佳實務

  1. 權限不足:存取實體磁碟需管理員權限,建議在 Manifest 中宣告 requireAdministrator
  2. 裝置名稱錯誤:C/C++ 字串中反斜線需雙重轉義 \\\\.\\PhysicalDrive0
  3. 緩衝區大小不足:輸出緩衝區必須 ≥ 結構大小,IOCTL_STORAGE_GET_MEDIA_TYPES_EX 需預留陣列空間
  4. 非同步 vs 同步:簡單查詢用同步(lpOverlapped=NULL),大量資料傳輸建議用非同步 + GetOverlappedResult
  5. 資源釋放:必須呼叫 CloseHandle 關閉裝置代碼,否則導致控制代碼洩漏

總結:DeviceIoControl 開發三步曲

  1. CreateFile → 取得裝置控制代碼
  2. DeviceIoControl → 發送控制碼與資料進行 I/O 操作
  3. CloseHandle → 釋放裝置控制代碼

掌握此核心流程,即可開發磁碟工具、光碟燒錄、硬體監控、效能測試等系統級應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved