环境Win7,VS2015
CreateFile在各种地方都有很完全的说明,只不过我要做的事情稍微小众一些。我希望打开一个磁盘,而不是一个文件或者IO口之类。以下是在其他博客也有提到,经过无数次检验的代码:
fat16 = CreateFile("\\\\.\\I:",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL); //打开驱动器
特殊之处就在于那个所谓lpFileName参数。MSDN上提到了需要使用\\?\前缀等事宜,但并没有实例。我的理解是前面的\\是所谓的UNC前缀,后面的\\.\是Win32 Device Namespace的前缀,最后是一般的磁盘名\I:。另外我操作的是一个U盘,如果要对自己电脑上的磁盘操作,应该以管理员身份运行,否则报5号错误(拒绝访问)。
附文件和设备命名的说明
正常读写就填GENERIC_READ|GENERIC_WRITE即可,据说FILE_SHARE_READ | FILE_SHARE_WRITE是共享读写参数,然而我的程序调用WriteFile时还是不能同时用winHex查看I盘,原因后述。
ReadFile这个函数真是呵呵,比fread fget之流难用太多了。然而由于是CreateFile拿到的HANDLE句柄,不知道怎么用C的库函数操作,只好在WinAPI这棵树上吊死。
一般使用读磁盘的需求肯定是想读任意位置,任意长度的数据。然而在ReadFile这里不 存 在 的。如果使用同步方式读磁盘,一次只能读一个扇区,且只能从扇区起始位置开始。ReadFile的lpOverlapped参数不是有Offset成员吗?这个是异步读写才“起作用”的参数。想用也行,在CreateFile时加入异步读写的FLAG,接着读出的数据位置是对了,但总有那么几字节丢失。可能是异步读写时缓冲区被一边读一边写导致的?不是还有SetFilePointer函数吗?它也只能移动整数个扇区。。
下面对上面一段话详细说明。以下是读第base个扇区的内容放进buffer里的代码。
int sizeOfSector;//扇区大小
DWORD lpdwBytesRead = 0;
OVERLAPPED over = { 0 };
unsigned char buffer[sizeOfSector+1];//存放读出内容的数组(实际请填数字相信不用我说也知道)
over.Offset;//读位置的偏移量,扇区大小的整数倍
ReadFile(*p_fat16, buffer, sizeOfSector, &lpdwBytesRead, &over);
第三个参数就是读出内容的大小,可为sizeOfSector的整数倍,如果不是整数倍则超出部分按sizeOfSector算。这就需要你将buffer设置得足够大,否则会Duang地溢出。第四个参数最好填一个指针而不是NULL或者0。第五个参数就是上面提到的OVERLAPPED类型的参数。可能读者已经发现了矛盾,不是说要在CreateFile里加入一个FILE_FALG_OVERLAPPED参数才能让这个over结构体有效吗?但事实是,以上的代码实现了base*sizeOfSector字节的偏移,而且没有缺失的数据。不过也仅仅能偏移sizeOfSector的整数倍,如果需要读非base*sizeOfSector处的数据,另在buffer里偏移即可。
而且你不加这个LPOVERLAPPED指针还不行,置NULL会导致内存访问冲突wtf。
下面是前面提到的异步操作的CreateFile形式
fat16 = CreateFile("\\\\.\\I:",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FALG_OVERLAPPED,
NULL);
int sizeOfSector;//扇区大小
DWORD base;//写入位置的偏移量,扇区大小的整数倍
DWORD lpdwBytesRead = 0;
VOID* alterSec;//指向写入内容的指针
SetFilePointer(*p_fat16, base, NULL, FILE_BEGIN);
WriteFile(*p_fat16, alterSec, sizeOfSector, &lpdwBytesRead, NULL);
经过ReadFile的折腾,已经能平静面对WriteFile的诡奇无端了。
首先是SetFilePointer函数又好使了,作用是将文件指针(即写入的起始位置)偏移扇区大小的整数倍个字节。得亏是好使了,不然作业没法做了。
然后请看WriteFile最后那个NULL,那里的参数类型是LPOVERLAPPED。为什么要用SetFilePointer而不是像ReadFile一样用over.Offset呢?显然是OVERLAPPED在这里不好使啊,很正常,接受。
最大的一个问题是,如果直接这样运行,很可能会报5号拒绝访问错误。需要在CreateFile后“锁定磁盘”,代码:
BOOL bLOCK = DeviceIoControl(
fat16,
FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,
&dwByteReturned,
NULL);
正是这个操作使得Create时的FILE_SHARE_READ | FILE_SHARE_WRITE失效了。而且如果运行时磁盘被占用,例如被winHex查看,也是会报错的。