排雷Windows CreateFile、ReadFile、WriteFile API

【经历了一次编写FAT16读写磁盘的作业,对三个文件操作的API有一些体会,如果MSDN和其他博客没有解决你的问题,不妨试试这篇】

环境Win7,VS2015

CreateFile

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

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);

 

WriteFile

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查看,也是会报错的。

 

 

【以上是非专业人员使用这三个函数时遇到的问题和他的土味解决方法,很可能存在更正常,主流,漂亮,体面的解决方法,不过管他呢,作业做完完事。】

你可能感兴趣的:(操作系统)