WIN32汇编语言教程:第10章 内存管理和文件操作 · 10.2 文件操作(5)
● TRUNCATE_EXISTING——打开文件并将文件截断为零,当文件不存在时返回失败。
dwFlagsAndAttributes参数用来指定新建文件的属性,文件属性可以是下面这些值的组合:
● FILE_ATTRIBUTE_NORMAL——普通文件,设置这个属性时其他属性都不会生效。
● FILE_ATTRIBUTE_ARCHIVE——设置归档属性。
● FILE_ATTRIBUTE_HIDDEN——设置隐藏属性。
● FILE_ATTRIBUTE_READONLY——设置只读属性。
● FILE_ATTRIBUTE_SYSTEM——设置系统属性。
● FILE_ATTRIBUTE_TEMPORARY——临时文件,系统会尽量把所有的文件内容保持在内存中以加快存取速度,程序在不再使用文件的时候需尽快将它删除。
此外该参数还可同时指定对文件操作的方式,常用的方式有:
● FILE_FLAG_WRITE_THROUGH——使用WriteThrough模式,系统不会对文件使用缓存,文件的改变马上会被写入到磁盘中。
● FILE_FLAG_OVERLAPPED——使用异步文件操作模式。
● FILE_FLAG_DELETE_ON_CLOSE——文件被关闭后立即被系统自动删除。
● FILE_FLAG_RANDOM_ACCESS——对文件进行随机读写操作(操作系统对该文件的缓存进行优化)。
hTemplateFile指定了一个文件模板的句柄,该文件模板的所有属性都会被复制到当前创建的文件中。Windows 95不支持本参数,为了保持程序的兼容性,建议在参数中使用NULL。
当打开或创建文件成功的时候,函数返回一个文件句柄,失败的话,函数的返回值是INVALID_HANDLE_VALUE,注意:这个值被定义为−1而不是NULL。如果想再详细地了解失败的原因,可以继续调用GetLastError函数。
用不同参数的组合调用CreateFile可以完成不同的功能,比如在例子中打开输入的文本文件的时候,使用的是下面的代码:
invoke CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ,\ 0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 .if eax == INVALID_HANDLE_VALUE ;显示错误 .endif mov @hFile,eax
为什么要这样使用呢?因为如果文件不存在的话,就没有必要继续处理,所以要使用OPEN_EXISTING标志,而且程序仅需要读取文件的内容,不需要写入数据,所以存取方式使用GENERIC_READ 就可以了,为了让其他程序能同时使用这个文件,共享方式要指定为FILE_SHARE_READ。
而在_CountWord子程序中要写记录文件时就不同了,这次使用的代码如下:
invoke CreateFile,addr @szLogFile,GENERIC_WRITE,FILE_SHARE_READ,\ 0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0 .if eax != INVALID_HANDLE_VALUE mov @hFile,eax ... .endif
因为要写文件,所以存取方式使用GENERIC_WRITE,打开方式使用OPEN_ALWAYS,告诉函数如果记录文件已经存在则打开它并将它的内容清除,不存在的话则新建一个文件。
与打开或创建文件相比,关闭文件就简单得多了,关闭文件使用CloseHandle函数:
invoke CloseHandle,hFile
2. 移动文件指针
系统为每个打开的文件维护一个文件指针,指定在文件中下一个读操作或写操作在什么位置进行,随着数据的读出或写入,文件指针随之移动。当文件刚被打开的时候,文件指针处于文件的头部。
顺序读取或写入文件数据的时候,由于文件指针总是被调整到上一次操作以后的地方,所以循环进行读取或写入就能将全部的文件数据处理完毕。但使用中常常需要随机读取文件内容,这些就需要先调整文件指针,再进行读写操作。
调整文件指针使用SetFilePointer函数:
invoke SetFilePointer,hFile,lDistanceToMove,\ lpDistanceToMoveHigh,dwMoveMethod
hFile是用CreateFile函数返回的文件句柄。
lDistanceToMove指定要移动的距离。lpDistanceToMoveHigh是一个指针,指向一个32位的变量,变量存放有移动距离的高32位,它和lDistanceToMove中的32位一起组成一个64位的距离,使用64位距离的原因是某些平台上的Windows支持64位的文件长度,但在运行于80x86平台上的Windows版本中,文件长度不会超过4 GB,只需32位就能够覆盖全部的文件长度,所以lpDistanceToMoveHigh参数一般设置为NULL(其他的文件操作函数也使用两个参数来指定64位的文件长度,同样也可以只使用低32位)。当移动距离是正值的时候,文件指针向文件尾部移动;如果移动距离是负值,那么文件指针向文件头部方向移动。
dwMoveMethod指定了移动的模式,也就是指明从什么地方开始移动,它可以是以下的取值:
● FILE_BEGIN——不管文件指针当前位于什么地方,总是从文件头部开始移动,这时的位置参数相当于指定了一个绝对位置。
● FILE_CURRENT——从当前的文件指针处开始移动。
● FILE_END——从文件尾开始移动,如果要从文件尾往回移动一段位置,那么位置参数指定的就应该是负值。
Win32文件操作函数可以支持很多对象,有些对象并不支持文件指针,对它们就不能使用SetFilePointer函数,能想像对打开的串口进行移动指针操作是什么意思吗?
函数的返回值根据lpDistanceToMoveHigh参数的取值不同而不同,由于这个参数一般设置为NULL,这里仅讨论参数为NULL时的情况。这时如果函数执行失败,返回值是−1,否则函数返回新的文件指针位置。
既然文件指针可以设置,那么如何获取当前的文件指针呢?实际上并没有一个专用的函数可以完成这个功能(并没有GetFilePointer函数),但是既然SetFilePointer函数的返回值是新的文件指针位置,那么可以巧用该函数来获得当前的文件指针:
invoke SetFilePointer,hFile,0,NULL, FILE_CURRENT
从当前的文件指针处移动0字节,文件指针并没有真正移动,但是返回值就是当前的文件指针了!
文件指针也可以移动到文件所有数据的后面,比如现在文件的长度是100 B,但还是可以成功地把文件指针移到1 000 B的位置,这样的操作有什么用途呢?用途是可以将文件扩展到需要的长度,可以接着用WriteFile写入数据,系统会将文件从100 B扩展到1 000 B后再从1 000 B处写入数据。
使用SetEndOfFile函数也可以扩展文件长度,SetEndOfFile总是将文件的长度调整到当前的文件指针指向的长度,所以这个函数还有截断文件的功能,当文件指针位于文件中间的时候,函数将文件指针后面的内容截断,当文件指针位于文件尾以后位置的时候,函数将文件长度扩展。SetEndOfFile函数的用法是这样的:
invoke SetEndOfFile,hFile
当文件被扩展的时候,被扩展部分的内容是不确定的(不过这是MSDN说的,试验的结果好像这部分内容总是0)。
SetEndOfFile函数总是要和SetFilePointer函数配合使用,比如要写一个杀毒程序,需要将附在带毒软件尾部的病毒去掉,就可以先用SetFilePointer函数将文件指针移到文件原来长度的地方,再用SetEndOfFile函数将文件截断就可以了。另外,要建立一个内容为空的文件(并不是指文件长度为0),可以在创建文件后,马上将文件指针移到需要的地方,再用SetEndOfFile函数扩展文件就可以了。网络蚂蚁在下载前总是创建一个和目标文件同样尺寸的空文件,然后再逐步下载其中的数据,用这种方法就可以完成创建空文件的功能。
3. 读写文件
读写文件可以使用ReadFile/WriteFile函数,这两个函数读写的方式可以是同步的也可以是异步的;也可以使用ReadFileEx/WriteFileEx函数,这两个函数只用于异步读写文件。
读文件函数ReadFile的使用方法是:
invoke ReadFile,hFile,lpBuffer,nNumberOfBytesToRead,\ lpNumberOfBytesRead,lpOverlapped
其中hFile是文件句柄,用来指明要读取的文件;lpBuffer指向一个缓冲区,函数会将读出的数据传送到这里;nNumberOfBytesToRead参数指定需要读入的字节数,由于函数并不能总是读到用户要求的字节数(原因是多种多样的,比如遇到了文件尾),所以下面一个参数lpNumberOfBytesRead指向一个dword类型的变量,函数将在这里返回实际读入的字节数;lpOverlapped参数指向一个OVERLAPPED结构,供函数在异步读取文件时使用,在同步读写中这个参数设置为NULL。Windows 95的ReadFile函数并不支持对文件的异步读写,所以在Windows 95中使用时,这个参数总是设置为NULL。
上页:第10章 内存管理和文件操作 · 10.2 文件操作(4) 下页:第10章 内存管理和文件操作 · 10.2 文件操作(6)