WIN32汇编语言教程:第10章 内存管理和文件操作 · 10.2 文件操作(9)

在_FindFile子程序中,使用了前面推荐的循环格式:

invoke FindFirstFile,addr @szSearch,addr @stFindFile
.if    eax != INVALID_HANDLE_VALUE
          mov    @hFindFile,eax
          .repeat
                  invoke lstrcpy,addr @szFindFile,addr @szPath
                  invoke lstrcat,addr @szFindFile,addr @stFindFile.cFileName
                  .if    @stFindFile.dwFileAttributes &\
                           FILE_ATTRIBUTE_DIRECTORY
                          .if    @stFindFile.cFileName != '.'
                                  inc    dwFolderCount
                                  invoke _FindFile,addr @szFindFile
                          .endif
                  .else
                          invoke _ProcessFile,addr @szFindFile
                  .endif
                  invoke FindNextFile,@hFindFile,addr @stFindFile
          .until (eax == FALSE) || (dwOption & F_STOP)
          invoke FindClose,@hFindFile
.endif

注意:这个子程序是递归调用的,这样不管下层子目录的深度有多少,程序都不需要去考虑它。如果不使用递归调用的方法,那么程序必须将找到的子目录先保存起来,等处理完当前目录的所有文件后再继续处理保存的目录。

由于WIN32_FIND_DATA结构中的cFileName字段不包括路径,所以在找到一个文件后,程序用lstrcpy和lstrcat函数将保存的路径和文件名连接到一起组成一个全路径的文件名,然后,程序测试dwFileAttributes中的属性。如果找到的是目录,则递归调用_FindFile子程序寻找下一层目录;如果不是目录,则调用_ProcessFile子程序处理找到的文件。

在_ProcessFile子程序中打开文件并获取文件长度进行累计。如果要把这个全盘搜索程序用在别的地方(比如编写杀毒程序的时候),只要将_ProcessFile子程序替换成对文件进行病毒检查的子程序就可以了。

当找到一个目录以后,还要判断目录名是否是“.”和“..”,如果是这两个名称则不进行处理,因为“.”目录代表本目录,“..”目录代表上一层目录,如果对它们进行递归查找,那么这个循环可就真的出不来了。

.repeat循环中多了一个对dwOption是否是F_STOP的测试,这是为了随时终止查找过程。因为找完整个磁盘可能需要很长的时间,让用户有机会终止查找过程是程序人性化的表现。

对这个程序进行简单的修改,读者就可以将它用在任何需要全盘查找文件的地方,惟一需要注意的地方是:如果要查找的是“*.exe”之类的文件而不是“*.*”的话,在输入查找文件名的时候不能使用“*.exe”,因为这样会漏过子目录,正确的做法是使用“*.*”当做要查找的文件名并在处理找到的文件时再对文件名进行判别,对扩展名不是.exe的文件直接忽略掉就可以了。

10.2.4 文件属性

一个文件有多种相关属性,如文件的类型、长度、日期、只读的或隐含的等,Win32中有一些函数可以对这些属性进行操作,在本节中将对此进行讨论。

1. 获取文件类型

Win32的文件函数可以操作多种对象,如果需要知道一个文件句柄究竟对应什么对象,可以使用GetFileType函数:

   invoke GetFileType,hFile

函数的返回值可能是下面的一种:FILE_TYPE_UNKNOWN,FILE_TYPE_DISK,FILE_TYPE_CHAR或者FILE_TYPE_PIPE,分别代表文件类型未知,文件句柄对应磁盘文件,文件句柄对应字符设备(如控制台或并行口等)和对应管道这4种情况。

2. 获取文件长度

如果需要得知文件当前的长度,可以使用GetFileSize函数:

      invoke GetFileSize,hFile,lpFileSizeHigh

lpFileSizeHigh指向一个用来接收高32位长度的变量,一般设置为NULL,长度的低32位在返回值中返回,用GetFileSize函数得到的文件长度是“当前”长度,也就是说,假如现在文件长度是100字节,我们又在文件尾写了100字节,那么再一次调用函数得到的文件长度就是200字节。

3. 获取和修改文件日期

如果要获取文件的日期,可以使用GetFileTime函数:

invoke GetFileTime,hFile,lpCreationTime,lpLastAccessTime,lpLastWriteTime

其中,最后的3个参数指向3个缓冲区,函数会将创建日期、最后存取日期与最后写入日期分别返回到3个缓冲区中,如果不需要某个日期数据,可以把对应的输入参数设置为NULL。返回的日期数据是个FILETIME结构,结构的定义为:

FILETIME STRUCT
 dwLowDateTime    DWORD     ? ;文件日期低32位
 dwHighDateTime   DWORD     ? ;文件日期高32位
FILETIME ENDS

可以看到,在这个结构中无法直接得到日期的年、月、日、时、分与秒等数据,为了将它转换成我们熟悉的SYSTEMTIME结构,需要调用FileTimeToSystemTime函数进行转换:

invoke FileTimeToSystemTime,lpFileTime,lpSystemTime

与GetFileTime函数相对应,也可以使用SetFileTime函数把文件日期设置成希望的日期,同样,输入的参数指向3个FILETIME结构,结构中预先填写好期望的日期参数,不需要修改的日期属性可以在参数中使用NULL。函数用法如下:

invoke GetFileTime,hFile,lpCreationTime,lpLastAccessTime,lpLastWriteTime

同样,填写FILETIME结构的时候,最简便的方法是使用SystemTimeToFileTime函数将预先填写好的SYSTEMTIME结构转换为FILETIME结构:

   invoke SystemTimeToFileTime,lpSystemTime,lpFileTime

4. 获取和修改文件属性

在创建文件的时候,可以在CreateFile的dwFlagsAndAttributes参数中指定文件属性,除了用这种方法设置文件属性外,也可以在以后使用SetFileAttributes参数修改文件属性:

invoke SetFileAttributes,lpFileName,dwFileAttributes

调用SetFileAttributes函数的时候不需要打开文件,只需要指定全路径的文件名就可以了,dwFileAttributes参数的定义与CreateFile函数中的dwFileAttributes参数是一样的。

要获取文件的只读、隐含与系统等属性的话,可以使用GetFileAttributes函数:

   invoke GetFileAttributes,lpFileName

如果函数执行失败,返回值是−1,否则返回文件的属性。

10.2.5 其他文件操作

在DOS操作系统中,并没有一个中断功能对应拷贝文件和移动文件功能,如果要实现这些功能,需要自己编写代码读取一个文件的内容并写入另一个文件,而在Win32中实现这些功能很简单,因为系统提供了对应的API函数。

在这一小节中,将讨论这些函数。

1. 拷贝文件

拷贝文件使用CopyFile或CopyFileEx函数,CopyFile函数的用法如下:

   invoke CopyFile,lpExistingFileName,lpNewFileName,bFailIfExists

函数将lpExistingFileName指定的文件拷贝为lpNewFileName指定的文件,最后一个参数bFailIfExists指定目标文件存在时的动作,如果指定为TRUE,则拷贝失败,指定为FALSE,则函数继续拷贝并覆盖掉原来的文件,拷贝出来的新文件的属性(如只读或隐含等属性)与原来文件的属性一样。

CopyFileEx函数可以完成同样的功能,只不过函数可以指定一个回调函数,在拷贝的过程中,函数在每拷贝完一部分数据以后会调用回调函数,在回调函数中程序可以指定继续拷贝还是停止,或者显示一个进度条来指示拷贝的进度,所以CopyFileEx函数只在拷贝特大型文件的时候比较有用。

2. 移动文件

移动文件使用MoveFile或MoveFileEx函数。MoveFile函数的用法如下:

   invoke MoveFile,lpExistingFileName,lpNewFileName

函数将lpExistingFileName指定的文件移动到lpNewFileName指定的目标位置,文件名可以同时改变或者不改变,目标文件必须不存在,否则函数调用失败。当函数执行成功的时候返回非0值,否则函数返回0。

MoveFile函数可以移动文件或者目录。当移动一个文件的时候,源文件和目标文件既可以处于相同的驱动器上也可以处于不同的驱动器上,函数会自动决定是否需要进行拷贝工作(如果处在相同的驱动器中,函数仅移动了一下目录项,并不进行数据拷贝);当移动的对象是一个目录时,那么目标目录和源目录必须处在同一个驱动器中,函数会将目录中的所有文件包括所有的下层子目录一并移动到新的位置上。

上页:第10章 内存管理和文件操作 · 10.2 文件操作(8) 下页:第10章 内存管理和文件操作 · 10.2 文件操作(10)

第10章 内存管理和文件操作

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号