获得一个目录下的文件名
作者:LooL 于2007-10-15上传
--------------------------------------------------------------------------------
要获得一个目录下的全部文件名,通常使用 FindFirstFile 和 FindNextFile 这两个API。具体的做法可想而知:先用前者找到一下,然后再用后者找下一个直到找不到… …除此之外,还有通过DOS命令DIR获得指定目录下的目录列表,然后一个个的拆出来的方法,这种方法过于麻烦,今天要实现的是使用API的 ^_^
先对这些API做个简单的介绍:
FindFirstFile Function
Searches a directory for a file or subdirectory with a name that matches a specific name.
To specify additional attributes to use in a search, use the FindFirstFileEx function.
To perform this operation as a transacted operation, use the FindFirstFileTransacted function.
HANDLE WINAPI FindFirstFile(
__in LPCTSTR lpFileName,
__out LPWIN32_FIND_DATA lpFindFileData
);
Parameters
lpFileName 指向一个字符串,代表要寻找的文件名。
The directory or path, and the file name, which can include wildcard characters, for example, an asterisk (*) or a question mark (?).
If the string ends with a wildcard, period (.), or directory name, the user must have access to the root and all subdirectories on the path.
lpFindFileData 指向一个缓冲区,函数会在缓冲区中返回一个WIN32_FIND_DATA结构
A pointer to the WIN32_FIND_DATA structure that receives information about a found file or subdirectory.
Return Value
If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose.
如果成功,会返回一个handle 可以给FindNextFile或者 FindClose使用
If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
如果失败,返回INVALID_HANDLE_VALUE。
WIN32_FIND_DATA Structure
Contains information about the file that is found by the FindFirstFile, FindFirstFileEx, or FindNextFile function.
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; 找到的文件的属性
FILETIME ftCreationTime; 创建日期
FILETIME ftLastAccessTime;最后访问日期
FILETIME ftLastWriteTime; 最后写入日期
DWORD nFileSizeHigh; 文件长度的高32位
DWORD nFileSizeLow; 文件长度的低32位
DWORD dwReserved0; 扩展的文件标记
DWORD dwReserved1; 保留
TCHAR cFileName[MAX_PATH];本次找到的文件名
TCHAR cAlternateFileName[14]; 文件的8.3文件名
} WIN32_FIND_DATA,
*PWIN32_FIND_DATA,
*LPWIN32_FIND_DATA;
其中,文件的大小计算方法: (nFileSizeHigh * (MAXDWORD+1)) + nFileSizeLow.
DWORD dwReserved0; 扩展的文件标记,之前一直定义为未使用,而在目前的MSDN上说明为"If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute, this member specifies the reparse point tag."看起来好像是能否返回文件的所属类型。
cFileName 中只有文件名称,不包括路径。
FindNextFile Function
Continues a file search from a previous call to the FindFirstFile or FindFirstFileEx function.
BOOL WINAPI FindNextFile(
__in HANDLE hFindFile,
__out LPWIN32_FIND_DATA lpFindFileData
);
Parameters
hFindFile FindFirstFile或者FindFirstFileEx返回的handle
The search handle returned by a previous call to the FindFirstFile or FindFirstFileEx function.
lpFindFileData 和FindFirstFile的一样,用来返回找到的文件信息
A pointer to the WIN32_FIND_DATA structure that receives information about the found file or subdirectory.
The structure can be used in subsequent calls to FindNextFile to indicate from which file to continue the search.
Return Value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero (0). To get extended error information, call GetLastError.
If no matching files can be found, the GetLastError function returns ERROR_NO_MORE_FILES.
;#Mode=CON
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc
includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
include macro.asm
.data
szStartPath db 'c:\windows',0
szFilter db '*.exe',0
szEnter db 10,13,0
.data?
buffer db 100 dup(?)
dwCounter dd ?
.CODE
ProcessFile proc
ret
ProcessFile Endp
FindFile proc lpszPath
local stFindFile:WIN32_FIND_DATA
local hFindFile
;MAX_PATH在Windows.inc中有定义=260,注意大小写
local szPath[MAX_PATH] :byte ;当前要搜索的路径
local szSearch[MAX_PATH] :byte ;当前要搜索的路径\*.dll
local szFindFile[MAX_PATH]:byte ;路径\找到的文件
pushad
invoke lstrcpy,addr szPath,lpszPath ;生成当前要搜索的路径
@@:
invoke lstrlen,addr szPath
lea esi,szPath
add esi,eax ;指向 szPath 中最后一个字符
xor eax,eax
mov al,'\'
.if BYTE ptr [esi-1] != al ;判断最后一个字符的前一个字符是否为 \
mov word ptr [esi],ax ;不是,补全 szPath的形式就是 db 'xxxx\',0
.endif
invoke lstrcpy,addr szSearch,addr szPath ;复制到 szSearch
invoke lstrcat,addr szSearch,addr szFilter ;szSearch 的形式就是 db 'xxxx\*.dll',0
invoke FindFirstFile,addr szSearch,addr stFindFile
.if eax != INVALID_HANDLE_VALUE
mov hFindFile,eax ;保存查找的handle
.repeat
invoke lstrcpy,addr szFindFile,addr szPath ;因为找到的文件是不带路径
;所以,结果上要补全路径
invoke lstrcat,addr szFindFile,addr stFindFile.cFileName ;加上找到的文件名
.if stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
;找到的是目录
.if stFindFile.cFileName != '.' ;如果找到的目录不是"."
invoke FindFile,addr szFindFile ;递归,进入这个目录
.endif
.else ;找到的不是目录,就是文件
inc dwCounter ;文件计数器加一
invoke StdOut,addr szFindFile ;输出文件名
invoke StdOut,addr szEnter
.endif
;继续在这个目录中查找
invoke FindNextFile,hFindFile,addr stFindFile .until (eax==FALSE) ;直到找不到满足条件的文件
invoke FindClose,hFindFile
.endif
popad
ret
FindFile endp
START:
mov dwCounter,0
invoke FindFile,addr szStartPath ;起始路径
invoke wsprintf,addr buffer,CTXT("%s %d %s"),CTXT('一共找到'),dwCounter,CTXT('个文件')
invoke StdOut,addr buffer
;暂停显示,回车键关闭
invoke StdIn,addr buffer,sizeof buffer
invoke ExitProcess,0
end START
(编辑注:这段程序上的排版有些问题,建议读者拷贝到MasmPlus中查看,根据自动的逻辑划分体式可以很清晰的看懂结构)
上面的程序参考了《Windows环境下32位汇编语言程序设计》,其中的核心部分
invoke FindFirstFile,addr szSearch,addr stFindFile
.if eax != INVALID_HANDLE_VALUE
mov hFindFile,eax ;保存查找的handle
.repeat
invoke lstrcpy,addr szFindFile,addr szPath ;因为找到的文件是不带路径
;所以,结果上要补全路径
invoke lstrcat,addr szFindFile,addr stFindFile.cFileName ;加上找到的文件名
.if stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;找到的是目录
.if stFindFile.cFileName != '.' ;如果找到的目录不是"."
invoke FindFile,addr szFindFile ;递归,进入这个目录
.endif
.else ;找到的不是目录,就是文件
inc dwCounter ;文件计数器加一
invoke StdOut,addr szFindFile ;输出文件名
invoke StdOut,addr szEnter
.endif
;继续在这个目录中查找
invoke FindNextFile,hFindFile,addr stFindFile .until (eax==FALSE) ;直到找不到满足条件的文件
使用了递归,注意这个地方:
.if stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
用来判断找到的是否为目录,如果是,则进入递归,如果不是,则在下面按照文件的方式处理。但是这里面包含了一个小小的问题:如果我的目录名称满足通配符,会找到,但是因为它不是文件,可能会被漏掉。
举例说明,我在Windows目录下使用md lll.exe 建立一个名称为 "lll.exe"的目录。使用dir *.exe命令,结果如下:
而使用我们的程序,结果却是:
解决方法是
.if stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;找到的是目录?
.if stFindFile.cFileName != '.' ;如果找到的目录不是"."
****在这个地方或者下一跳代码的后面进行处理
invoke FindFile,addr szFindFile ;递归,进入这个目录
.endif
.else ;找到的不是目录,就是文件
...
.endif
另外,试验中发现:如果你使用非 "*.*"的通配符查找,是无法找到目录的。意思是如果你打算用这种方法"查找给定的路径下面的包括子目录的所有满足条件的文件",是不行的。
收稿 2007-10-12
--------------------------------------------------------------------------------