在Windows NT/2K/XP中,直接用CreateFile開啟名稱類似於"\\.\A:"的”檔案”,就可以與裝置驅動打交道,通過ReadFile/WriteFile以絕對位址方式訪問磁碟了。但Windows 9X不支援這樣的簡單方法。本文介紹一種在Windows 9X中實現磁碟直接存取的方法:利用系統的vwin32.vxd,通過DeviceIoControl調用DOS INT21 7305H與440DH功能來完成。該調用支援FAT12、FAT16和FAT32,適用於Windows 95 SR2以及更高版本。
先來瞭解一下DOS INT21 7305H功能的入口參數:
AX -- 功能號7305HDS:BX -- 讀寫扇區的資訊結構CX -- 必須為-1DL -- 磁碟機代號: 1=A:, 2=B:, 3=C:, ...SI -- 讀寫標誌: 最低位0=讀, 1=寫
若調用成功,清除C標誌;否則設定C標誌。
DS:BX指向一個結構,此結構定義如下:
DISKIO STRUC dwStartSector dd ? ; 起始扇區 wSector dw ? ; 扇區數 lpBuffer dd ? ; 資料緩衝區地址DISKIO ENDS
在寫操作下,需要“鎖定”磁碟機。DOS INT21 440DH的4AH/6AH功能可實現邏輯磁碟機的加鎖/解鎖。其入口參數為:
AX -- 功能號440DHBH -- 鎖的層級,0-3級,直接寫扇區用1BL -- 磁碟機代號: 1=A:, 2=B:, 3=C:, ...CH -- 0x08CL -- 0x4ADX -- 0
AX -- 功能號440DHBL -- 磁碟機代號: 1=A:, 2=B:, 3=C:, ...CH -- 0x08CL -- 0x6A
以上兩個調用,若調用成功,清除C標誌;否則設定C標誌。
通過IOCTL碼VWIN32_DIOC_DOS_DRIVEINFO等調用上述中斷。實現絕對磁碟讀寫的關鍵代碼如下:
// INT21的IOCTL碼#define VWIN32_DIOC_DOS_IOCTL 1#define VWIN32_DIOC_DOS_DRIVEINFO 6 // 寄存器組typedef struct _DIOC_REGISTERS { DWORD reg_EBX; DWORD reg_EDX; DWORD reg_ECX; DWORD reg_EAX; DWORD reg_EDI; DWORD reg_ESI; DWORD reg_Flags;} DIOC_REGISTERS, *PDIOC_REGISTERS; // IO參數(注意位元組對齊)#pragma pack(1)typedef struct _DISKIO { DWORD dwStartSector; // 起始扇區 WORD wSectors; // 扇區數 void* pBuffer; // 緩衝區指標} DISKIO, *PDISKIO;#pragma pack() BOOL AbsDiskRead( BYTE nDiskNumber, // 盤號, 1=A:, 2=B:, 3= C:, ... DWORD dwStartSector, // 起始扇區 WORD wSectors, // 扇區數 void* pBuffer) // 資料緩衝區指標{ HANDLE hDevice; DIOC_REGISTERS regs; DISKIO dio; DWORD dwOutBytes; BOOL bResult; // 開啟裝置,獲得VxD控制代碼 hDevice = CreateFile("\\\\.\\vwin32", // 裝置路徑 GENERIC_READ | GENERIC_WRITE, // 讀寫方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共用方式 NULL, // 預設的安全性描述元 OPEN_EXISTING, // 建立方式 FILE_ATTRIBUTE_NORMAL, // 檔案屬性 NULL); // 不需參照模板檔案 if(hDevice == INVALID_HANDLE_VALUE) { return FALSE; } // 填充DISKIO參數結構 dio.dwStartSector = dwStartSector; dio.wSectors = wSectors; dio.pBuffer = pBuffer; // 填充寄存器組--中斷入口參數 memset(®s, 0, sizeof(DIOC_REGISTERS)); regs.reg_EAX = 0x7305; // AX=0x7305 regs.reg_EBX = (DWORD)&dio; // EBX=DS:BX=參數指標 regs.reg_ECX = 0xffff; // CX=-1 regs.reg_EDX = nDiskNumber; // DL=盤號 regs.reg_ESI = 0; // SI=0 -- 讀操作 // 用VWIN32_DIOC_DOS_DRIVEINFO讀磁碟 dwOutBytes = 0; bResult = DeviceIoControl(hDevice, // 裝置控制代碼 VWIN32_DIOC_DOS_DRIVEINFO, // INT21 ®s, sizeof(regs), // 輸出資料緩衝區與長度 ®s, sizeof(regs), // 輸出資料緩衝區與長度 &dwOutBytes, // 輸出資料長度 NULL); // 用同步I/O // 確定DeviceIoControl與INT21都無錯誤 bResult = bResult && !(regs.reg_Flags & 1); CloseHandle(hDevice); return bResult;} BOOL AbsDiskWrite( BYTE nDiskNumber, // 盤號, 1=A:, 2=B:, 3= C:, ... DWORD dwStartSector, // 起始扇區 WORD wSectors, // 扇區數 void* pBuffer) // 資料緩衝區指標{ HANDLE hDevice; DIOC_REGISTERS regs; DISKIO dio; DWORD dwOutBytes; BOOL bResult; // 開啟裝置,獲得VxD控制代碼 hDevice = CreateFile("\\\\.\\vwin32", // 裝置路徑 GENERIC_READ | GENERIC_WRITE, // 讀寫方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共用方式 NULL, // 預設的安全性描述元 OPEN_EXISTING, // 建立方式 FILE_ATTRIBUTE_NORMAL, // 檔案屬性 NULL); // 不需參照模板檔案 if(hDevice == INVALID_HANDLE_VALUE) { return FALSE; } // 填充DISKIO參數結構 dio.dwStartSector = dwStartSector; dio.wSectors = wSectors; dio.pBuffer = pBuffer; // 填充寄存器組--中斷入口參數 memset(®s, 0, sizeof(DIOC_REGISTERS)); regs.reg_EAX = 0x7305; // AX=0x7305 regs.reg_EBX = (DWORD)&dio; // EBX=DS:BX=參數指標 regs.reg_ECX = 0xffff; // CX=-1 regs.reg_EDX = nDiskNumber; // DL=盤號 regs.reg_ESI = 0x6001; // SI=0x6001 -- 普通寫操作 // 用VWIN32_DIOC_DOS_DRIVEINFO寫磁碟 dwOutBytes = 0; bResult = DeviceIoControl(hDevice, // 裝置控制代碼 VWIN32_DIOC_DOS_DRIVEINFO, // INT21 ®s, sizeof(regs), // 輸出資料緩衝區與長度 ®s, sizeof(regs), // 輸出資料緩衝區與長度 &dwOutBytes, // 輸出資料長度 NULL); // 用同步I/O // 確定DeviceIoControl與INT21都無錯誤 bResult = bResult && !(regs.reg_Flags & 1); CloseHandle(hDevice); return bResult;} BOOL LockVolume( BYTE nDiskNumber) // 盤號, 1=A:, 2=B:, 3=C:, ... { HANDLE hDevice; DIOC_REGISTERS regs; DWORD dwOutBytes; BOOL bResult; // 開啟裝置,獲得VxD控制代碼 hDevice = CreateFile("\\\\.\\vwin32", // 裝置路徑 GENERIC_READ | GENERIC_WRITE, // 讀寫方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共用方式 NULL, // 預設的安全性描述元 OPEN_EXISTING, // 建立方式 FILE_ATTRIBUTE_NORMAL, // 檔案屬性 NULL); // 不需參照模板檔案 if(hDevice == INVALID_HANDLE_VALUE) { return FALSE; } // 填充寄存器組--中斷入口參數 memset(®s, 0, sizeof(DIOC_REGISTERS)); regs.reg_EAX = 0x440D; // AX=0x440D regs.reg_EBX = 0x0100 | nDiskNumber; // BH=鎖的層級,BL=盤號 regs.reg_ECX = 0x084A; regs.reg_EDX = 0; // 用VWIN32_DIOC_DOS_DRIVEINFO讀磁碟 dwOutBytes = 0; bResult = DeviceIoControl(hDevice, // 裝置控制代碼 VWIN32_DIOC_DOS_IOCTL, // INT21 ®s, sizeof(regs), // 輸入資料緩衝區與長度 ®s, sizeof(regs), // 輸出資料緩衝區與長度 &dwOutBytes, // 輸出資料長度 NULL); // 用同步I/O // 確定DeviceIoControl與INT21都無錯誤 bResult = bResult && !(regs.reg_Flags & 1); CloseHandle(hDevice); return bResult;} BOOL UnlockVolume( BYTE nDiskNumber) // 盤號, 1=A:, 2=B:, 3=C:, ... { HANDLE hDevice; DIOC_REGISTERS regs; DWORD dwOutBytes; BOOL bResult; // 開啟裝置,獲得VxD控制代碼 hDevice = CreateFile("\\\\.\\vwin32", // 裝置路徑 GENERIC_READ | GENERIC_WRITE, // 讀寫方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共用方式 NULL, // 預設的安全性描述元 OPEN_EXISTING, // 建立方式 FILE_ATTRIBUTE_NORMAL, // 檔案屬性 NULL); // 不需參照模板檔案 if(hDevice == INVALID_HANDLE_VALUE) { return FALSE; } // 填充寄存器組--中斷入口參數 memset(®s, 0, sizeof(DIOC_REGISTERS)); regs.reg_EAX = 0x440D; // AX=0x440D regs.reg_EBX = nDiskNumber; // BL=盤號 regs.reg_ECX = 0x086A; // 用VWIN32_DIOC_DOS_DRIVEINFO讀磁碟 dwOutBytes = 0; bResult = DeviceIoControl(hDevice, // 裝置控制代碼 VWIN32_DIOC_DOS_IOCTL, // INT21 ®s, sizeof(regs), // 輸入資料緩衝區與長度 ®s, sizeof(regs), // 輸出資料緩衝區與長度 &dwOutBytes, // 輸出資料長度 NULL); // 用同步I/O // 確定DeviceIoControl與INT21都無錯誤 bResult = bResult && !(regs.reg_Flags & 1); CloseHandle(hDevice); return bResult;}
下面的例子,從A盤的0扇區開始,讀取10個扇區的資料,並儲存在檔案中:
unsigned char buf[512 * 10]; if (AbsDiskRead(1, 0, 10, buf)) { FILE* fp = fopen("a.dat", "w+b"); fwrite(buf, 512, 10, fp); fclose(fp); }
下面的例子,讀取D磁碟機的第8888扇區,然後寫回去:
unsigned char buf[512]; LockVolume(4); if (AbsDiskRead(4, 8888, 1, buf)) { ... ... if (AbsDiskWrite(4, 8888, 1, buf)) { ... ... } } UnlockVolume(4);
在寫方式下,SI寄存器的位0設定為1,位15-13在磁碟的不同地區需要有不同的值:
Bit 15 |
Bit 14 |
Bit 13 |
Description |
0 |
0 |
0 |
Other/Unknown. |
0 |
0 |
1 |
FAT data. |
0 |
1 |
0 |
Directory data. |
0 |
1 |
1 |
Normal file data. |
1 |
0 |
0 |
Reserved. |
如果不按照上述值操作,儘管能夠寫成功,但系統無法自動完成相關功能,可能會導致FAT資料備份、磁碟機資料壓縮等方面的問題。
[相關資源]
bhw98的專欄:http://www.csdn.net/develop/author/netauthor/bhw98/