引言
由于Windows 操作系统在很大程度上采取了访问安全保护机制(例如,在Windows操作系统下不能直接访问物理内存、不能使用各种DOS、BIOS中断等等),使得广大程序设计人员在长时间的开发过程中不知不觉地逐渐养成了这样的潜意识——在Windows操作系统下直接操纵硬件设备是极端困难和非常烦琐的,并将其看作Windows编程的一个禁区。尽管在大多数场合下这样的论断还算是贴切,但也并非对所有的硬件设备访问都那么困难。其实Windows在采取“实保护”措施的同时也提供了另外的一种有别于在DOS下访问硬件设备的方法,即把所有的硬件设备全部看做“文件”,并允许按照对文件的读写方式来对其进行数据存取访问。撰写本文的另外一个目的也就是帮助读者打消在Windows环境下对硬件编程的恐惧心理。
对磁盘扇区数据的访问
前面已经提过,在Windows 下把所有的设备当作文件进行操作。如果对串口进行编程或许不少读者还比较熟悉:对于串行端口1、2,可以用”COM1”、”COM2”作为参数调用CreateFile()函数,这里的”COM1”、”COM2”即以文件存放路径的方式指出了要操作的硬件设备。但是如果需要对磁盘的某个扇区进行读写,可能不少读者不会想到使用CreateFile()函数或是不知如何使用。其实,与对串行端口的访问类似,需要用与文件存放路径相类似的方式指出要操作的硬件设备(硬盘)。但是这里并不是用“DISK1”、“DISK2”等去标识某一块物理存在的硬盘。由于逻辑扇区是存在于逻辑分区上的,因此这里需要以某种特定的格式来指定需要访问的磁盘逻辑分区。对于逻辑分区X,其格式为”\\.\X:”。
HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); |
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); |
BOOL CDirectAccessHDDlg::WriteSectors(BYTE bDrive, DWORD dwStartSector, WORD wSectors, LPBYTE lpSectBuff) // 对磁盘扇区数据的写入 { if (bDrive == 0) return 0; char devName[] = "\\\\.\\A:"; devName[4] =@#A@# + bDrive - 1; HANDLE hDev = CreateFile(devName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDev == INVALID_HANDLE_VALUE) return 0; SetFilePointer(hDev, 512 * dwStartSector, 0, FILE_BEGIN); DWORD dwCB; BOOL bRet = WriteFile(hDev, lpSectBuff, 512 * wSectors, &dwCB, NULL); CloseHandle(hDev); return bRet; } BOOL CDirectAccessHDDlg::ReadSectors(BYTE bDrive, DWORD dwStartSector, WORD wSectors, LPBYTE lpSectBuff) // 对磁盘扇区数据的读取 { if (bDrive == 0) return 0; char devName[] = "\\\\.\\A:"; devName[4] =@#A@# + bDrive - 1; HANDLE hDev = CreateFile(devName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDev == INVALID_HANDLE_VALUE) return 0; SetFilePointer(hDev, 512 * dwStartSector, 0, FILE_BEGIN); DWORD dwCB; BOOL bRet = ReadFile(hDev, lpSectBuff, 512 * wSectors, &dwCB, NULL); CloseHandle(hDev); return bRet; } |
if (ReadSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分区不存在!", "错误", MB_OK | MB_ICONERROR); return; } |
for (DWORD i = 0; i < dwSectorNum * 512; i++) { sprintf(cBuf, "%s%02X ", cBuf, bBuf[i]); if ((i % 512) == 511) sprintf(cBuf, "%s\r\n第%d扇区\r\n", cBuf, (int)(i / 512) + m_uFrom); if ((i % 16) == 15) sprintf(cBuf, "%s\r\n", cBuf); else if ((i % 16) == 7) sprintf(cBuf, "%s- ", cBuf); } |
显示结果如上图所示。另外一种应用与之类似,即对磁盘扇区内容的备份与恢复处理。不少防病毒软件都提供这样的功能:对硬盘引导区内容的备份,一旦硬盘引导扇区被病毒破坏后能够通过对备份数据的写入实现恢复。备份操作与前面的数据显示操作类似,只是把读取的内容不经格式处理而直接保存到指定的文件中即可:
file.Open(fileDlg.GetPathName(), CFile::modeCreate | CFile::modeReadWrite); …… if (ReadSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分区不存在!", "错误", MB_OK | MB_ICONERROR); return; } file.Write(bBuf, dwSectorNum * 512); file.Close(); |
file.Open(fileDlg.GetPathName(), CFile::modeReadWrite); DWORD dwSectorNum = file.GetLength(); if (dwSectorNum % 512 != 0) return; dwSectorNum /= 512; unsigned char* bBuf = new unsigned char[dwSectorNum * 512]; file.Read(bBuf, dwSectorNum * 512); if (WriteSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分区不存在!", "错误", MB_OK | MB_ICONERROR); return; } file.Close(); delete[] bBuf; |
unsigned char bBuf[512]; UINT i = 0; BOOL bRet = TRUE; while (m_bAllDisk){ memset(bBuf, 0xFF, sizeof(bBuf)); bRet = WriteSectors(uDiskID, i, 1, bBuf); memset(bBuf, 0, sizeof(bBuf)); bRet = WriteSectors(uDiskID, i, 1, bBuf); if (bRet == FALSE){ if (i == 0) MessageBox("所选磁盘分区不存在!", "错误", MB_OK | MB_ICONERROR); else MessageBox("磁盘数据擦除完毕!", "错误", MB_OK | MB_ICONERROR); return; } i++; } |