天天看點

通過API通路裝置驅動程式之一: 實戰DeviceIoControl

NO.1

Q :在 NT/2000/XP 中,我想用 VC 編寫應用程式通路硬體裝置,如擷取磁盤參數、讀寫絕對扇區資料、測試光驅實際速度等,該從哪裡入手呢?

A :在 NT/2000/XP 中,應用程式可以通過 API 函數 DeviceIoControl 來實作對裝置的通路—擷取資訊,發送指令,交換資料等。利用該接口函數向指定的裝置驅動發送正确的控制碼及資料,然後分析它的響應,就可以達到我們的目的。

 

DeviceIoControl 的函數原型為 BOOL DeviceIoControl( HANDLE hDevice, // 裝置句柄 DWORD dwIoControlCode, // 控制碼 LPVOID lpInBuffer, // 輸入資料緩沖區指針 DWORD nInBufferSize, // 輸入資料緩沖區長度 LPVOID lpOutBuffer, // 輸出資料緩沖區指針 DWORD nOutBufferSize, // 輸出資料緩沖區長度 LPDWORD lpBytesReturned, // 輸出資料實際長度單元長度 LPOVERLAPPED lpOverlapped // 重疊操作結構指針 );

      裝置句柄用來辨別你所通路的裝置。

  發送不同的控制碼,可以調用裝置驅動程式的不同類型的功能。在頭檔案winioctl.h 中,預定義的标準裝置控制碼,都以 IOCTL 或 FSCTL 開頭。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY 是對實體驅動器取結構參數(媒體類型、柱面數、每柱面磁道數、每磁道扇區數等)的控制碼,FSCTL_LOCK_VOLUME 是對邏輯驅動器的卷加鎖的控制碼。輸入輸出資料緩沖區是否需要,是何種結構,以及占多少位元組空間,完全由不同裝置的不同操作類型決定。在頭檔案 winioctl.h 中,已經為标準裝置預定義了一些輸入輸出資料結構。重疊操作結構指針設定為 NULL,DeviceIoControl 将進行阻塞調用;否則,應在程式設計時按異步操作設計。

NO.2

Q:裝置句柄是從哪裡獲得的?

A :裝置句柄可以用 API 函數 CreateFile 獲得。它的原型為

HANDLE CreateFile( LPCTSTR lpFileName, // 檔案名 DWORD dwDesiredAccess, // 通路方式 DWORD dwShareMode, // 共享方式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指針 DWORD dwCreationDisposition, // 建立方式 DWORD dwFlagsAndAttributes, // 檔案屬性及标志 HANDLE hTemplateFile // 模闆檔案的句柄 );

CreateFile 這個函數用處很多,這裡我們用它“打開”裝置驅動程式,得到裝置的句柄。操作完成後用 CloseHandle 關閉裝置句柄。與普通檔案名有所不同,裝置驅動的“檔案名”形式固定為“.DeviceName”(注意在 C 程式中該字元串寫法為“.DeviceName”),DeviceName 必須與裝置驅動程式内規定的裝置名稱一緻。一般地,調用 CreateFile 獲得裝置句柄時,通路方式參數設定為 0 或GENERIC_READ|GENERIC_WRITE,共享方式參數設定為FILE_SHARE_READ|FILE_SHARE_WRITE,建立方式參數設定為 OPEN_EXISTING,其它參數設定為 0 或 NULL。

NO.3

Q :可是,我怎麼知道裝置名稱是什麼呢?

A :一些儲存設備的名稱是微軟規定好的,不可能有什麼變化。大體列出如下

  軟碟驅動器 A:, B:

  邏輯驅動器 C:, D:, E:, ……

  實體驅動器 PHYSICALDRIVEx

  CD-ROM, DVD/ROM CDROMx

  錄音帶機 TAPEx

  其中,實體驅動器不包括軟驅和光驅。邏輯驅動器可以是 IDE/SCSI/PCMCIA/USB接口的硬碟分區(卷)、光驅、MO、CF 卡等,甚至是虛拟盤。x=0,1,2 ……

  其它的裝置名稱需通過驅動接口的 GUID 調用裝置管理函數族取得,這裡暫不讨論。

NO.4

Q :請舉一個簡單的例子說明如何通過DeviceIoControl 通路裝置驅動程式。

A :這裡有一個從 MSDN 上摘抄來的 demo 程式,示範在 NT/2000/XP 中如何通過DeviceIoControl 擷取硬碟的基本參數。

#include  #include BOOL GetDriveGeometry(DISK_GEOMETRY *pdg) { HANDLE hDevice; // handle to the drive to be examined  BOOL bResult; // results flag DWORD junk; // discard results hDevice = CreateFile(.PhysicalDrive0, // drive to open 0, // no access to the drive FILE_SHARE_READ | // share mode FILE_SHARE_WRITE,  NULL, // default security attributes OPEN_EXISTING, // disposition 0, // file attributes NULL); // do not copy file attributes if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive { return (FALSE); } bResult = DeviceIoControl(hDevice, // device to be queried IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform NULL, 0, // no input buffer pdg, sizeof(*pdg), // output buffer &junk, // # bytes returned (LPOVERLAPPED) NULL); // synchronous I/O CloseHandle(hDevice); return (bResult); } int main(int argc, char *argv[]) { DISK_GEOMETRY pdg; // disk drive geometry structure BOOL bResult; // generic results flag ULONGLONG DiskSize; // size of the drive, in bytes bResult = GetDriveGeometry (&pdg); if (bResult)  { printf(Cylinders = %I64d , pdg.Cylinders); printf(Tracks per cylinder = %ld , (ULONG) pdg.TracksPerCylinder); printf(Sectors per track = %ld , (ULONG) pdg.SectorsPerTrack); printf(Bytes per sector = %ld , (ULONG) pdg.BytesPerSector); DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder * (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector; printf(Disk size = %I64d (Bytes) = %I64d (Mb) , DiskSize, DiskSize / (1024 * 1024)); }  else  { printf (GetDriveGeometry failed. Error %ld. , GetLastError ()); } return ((int)bResult); }

NO.5

現在我們總結一下通過 DeviceIoControl通路裝置驅動程式的“三步曲”:首先用 CreateFile 取得裝置句柄,然後用DeviceIoControl 與裝置進行 I/O,最後别忘記用 CloseHandle 關閉裝置句柄。

繼續閱讀