實戰DeviceIoControl 共7篇,這是我在兩年前看到的文章,很不錯,現在想把它轉載過來。
原作者和其部落格的位址是:http://blog.csdn.net/bhw98
他部落格裡的這些序列文章的格式更好看,如果你覺得這些文章好,不妨去他的部落格看,格式比我的随便粘貼好多了 :)
在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 -- 功能号7305H
DS:BX -- 讀寫扇區的資訊結構
CX -- 必須為-1
DL -- 驅動器号: 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 -- 功能号440DH
BH -- 鎖的級别,0-3級,直接寫扇區用1
BL -- 驅動器号: 1=A:, 2=B:, 3=C:, ...
CH -- 0x08
CL -- 0x4A
DX -- 0
AX -- 功能号440DH
BL -- 驅動器号: 1=A:, 2=B:, 3=C:, ...
CH -- 0x08
CL -- 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 |
Other/Unknown. | |||
1 | FAT data. | ||
1 | Directory data. | ||
1 | 1 | Normal file data. | |
1 | Reserved. |
如果不按照上述值操作,盡管能夠寫成功,但系統無法自動完成相關功能,可能會導緻FAT資料備份、驅動器資料壓縮等方面的問題。
[相關資源]
- bhw98的專欄:http://www.csdn.net/develop/author/netauthor/bhw98/