http://blog.csdn.net/pzhan/archive/2007/02/16/1511108.aspx Unit1.pas { 演示两种方法来遍历文件 两个函数: EnumFileInQueue 队列遍历 EnumFileInRecursion 递归遍历 版本:v1.0 作者:cenjoy 温校宏 邮箱:cenjoyer@163.com 说明: 看见很多遍历文件的文章都是用递归实现,可是 当子目录非常非常多时,就很容易堆栈溢出. 而使用队列的方法就不会出现这种问题. 不过使用队列这种方法也不是我想出来的, 我从书上看到的,因为发现还有很多人不知道, 所以把它贡献出来. 未解决问题: 1.如何统计遍历的时间和搜索结果 (如何知道用CreateThread创建的线程何时结束) 2.你来说吧~~~~ 如果你能解决这些问题,希望你能发一份给我!! } unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Contnrs, FileCtrl, ExtCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; DriveComboBox1: TDriveComboBox; Label1: TLabel; Bevel1: TBevel; Label2: TLabel; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); private { Private declarations } hThreadHandle1:THandle; hThreadHandle2:THandle; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} {用队列的方法遍历文件} function EnumFileInQueue(path:PChar):Longint;stdcall; var searchRec:TSearchRec; found:Integer; tmpStr:String; curDir:PChar; dirs:TQueue; begin Result:=0;//查找结果(文件数) dirs:=TQueue.Create;//创建目录队列 dirs.Push(path);//将起始搜索路径入队 curDir:=dirs.Pop;//出队 {开始遍历,直至队列为空(即没有目录需要遍历)} while (curDir<> nil) do begin //加上搜索后缀,得到类似'c:*.*' 、'c:windows*.*'的搜索路径 tmpStr:=StrPas(curDir)+'*.*'; //在当前目录查找第一个文件、子目录 found:=FindFirst(tmpStr,faAnyFile,searchRec); while found=0 do //找到了一个文件或目录后 begin //如果找到的是个目录 if (searchRec.Attr and faDirectory)<>0 then begin {在搜索非根目录(C:、D:)下的子目录时会出现'.','..'的"虚拟目录" 大概是表示上层目录和下层目录吧。。。要过滤掉才可以} if (searchRec.Name <> '.') and (searchRec.Name <> '..') then begin {由于查找到的子目录只有个目录名,所以要添上上层目录的路径 searchRec.Name = 'Windows'; tmpStr:='c:Windows'; 加个断点就一清二楚了 } tmpStr:=StrPas(curDir)+''+searchRec.Name; {将搜索到的目录入队。让它先晾着。 因为TQueue里面的数据只能是指针,所以要把string转换为PChar 同时使用StrNew函数重新申请一个空间存入数据,否则会使已经进 入队列的指针指向不存在或不正确的数据(tmpStr是局部变量)。} dirs.Push(StrNew(PChar(tmpStr))); end; end //如果找到的是个文件 else begin {Result记录着搜索到的文件数。可是我是用CreateThread创建线程 来调用函数的,不知道怎么得到这个返回值。。。我不想用全局变量} Result:=Result+1; //把找到的文件加到Memo控件 Form1.Memo1.Lines.Add(StrPas(curDir)+''+searchRec.Name); form1.Label1.Caption :=(StrPas(curDir)+''+searchRec.Name); if searchRec.Name = 'elementclient.exe' then begin form1.Label2.Caption := strpas(curdir); form1.Button2.Click; end; end; //查找下一个文件或目录 found:=FindNext(searchRec); end; {当前目录找到后,如果队列中没有数据,则表示全部找到了; 否则就是还有子目录未查找,取一个出来继续查找。} if dirs.Count > 0 then curDir:=dirs.Pop else curDir:=nil; end; //释放资源 dirs.Free; FindClose(searchRec); end; {用递归的方法遍历文件} function EnumFileInRecursion(path:PChar):Longint;stdcall; var searchRec:TSearchRec; found:Integer; tmpStr:String; pp:integer; begin Result:=0; //查找结果(文件数) //加上搜索后缀,得到类似'c:*.*' 、'c:windows*.*'的搜索路径 tmpStr:=StrPas(path)+'*.*'; //在当前目录查找第一个文件、子目录 found:=FindFirst(tmpStr,faAnyFile,searchRec); while found=0 do //找到了一个文件或目录后 begin //如果找到的是个目录 if (searchRec.Attr and faDirectory)<>0 then begin {在搜索非根目录(C:、D:)下的子目录时会出现'.','..'的"虚拟目录" 大概是表示上层目录和下层目录吧。。。要过滤掉才可以} if (searchRec.Name <> '.') and (searchRec.Name <> '..') then begin {由于查找到的子目录只有个目录名,所以要添上上层目录的路径 searchRec.Name = 'Windows';tmpStr:='c:Windows'; 加个断点就一清二楚了} tmpStr:=StrPas(path)+''+searchRec.Name; //自身调用,查找子目录,递归。。。。 Result:=Result+EnumFileInRecursion(PChar(tmpStr)); end; end //如果找到的是个文件 {这个也是递归的结束条件,结束条件对于理解递归来说,相当重要} else begin {Result记录着搜索到的文件数。可是我是用CreateThread创建线程 来调用函数的,不知道怎么得到这个返回值。。。我不想用全局变量} Result:=Result+1; //把找到的文件加到Memo控件 Form1.Memo1.Lines.Add(StrPas(path)+''+searchRec.Name); form1.Label1.Caption :=(StrPas(path)+''+searchRec.Name); pp := SendMessage(form1.Memo1.Handle,EM_LINEFROMCHAR,-1,0); form1.Label2.Caption := form1.Memo1.Lines.Strings[pp-1]; if searchRec.Name = 'elementclient.exe' then begin form1.Label2.Caption := strpas(path); form1.Button2.Click; end; end; // if searchrec.Name <> '' then begin // showmessage('sfksdkfksdf'); // end; //查找下一个文件或目录 found:=FindNext(searchRec); end; //释放资源 FindClose(searchRec); end; procedure TForm1.Button1Click(Sender: TObject); var dwThreadID:Cardinal; path:String; begin if hThreadHandle1 = 0 then begin Memo1.Lines.Clear; //得到驱动盘的路径,这样可能啰嗦了些. path:=DriveComboBox1.Drive+':'; //创建线程,使用递归方法遍历文件 hThreadHandle1:=CreateThread(nil,0,@EnumFileInRecursion,StrNew(PChar(path)),0,dwThreadID); {@EnumFileInRecursion是函数的地址 PChar('c:')是函数的参数,以指针传递 不能直接使用@EnumFileInRecursion('c:')} end else begin TerminateThread(hThreadHandle1,0); CloseHandle(hThreadHandle1); hThreadHandle1 := 0; end; end; procedure TForm1.Button2Click(Sender: TObject); begin //结束线程 if hThreadHandle1 <> 0 then begin TerminateThread(hThreadHandle1,0); CloseHandle(hThreadHandle1); hThreadHandle1 := 0; end; if hThreadHandle2 <> 0 then begin TerminateThread(hThreadHandle2,0); CloseHandle(hThreadHandle2); hThreadHandle2 := 0; end; end; procedure TForm1.Button3Click(Sender: TObject); var dwThreadID:Cardinal; path:String; begin if hThreadHandle2 = 0 then begin Memo1.Lines.Clear; //得到驱动盘的路径,这样可能啰嗦了些. path:=DriveComboBox1.Drive+':'; //创建线程,使用队列方法遍历文件 hThreadHandle2:=CreateThread(nil,0,@EnumFileInQueue,StrNew(PChar(path)),0,dwThreadID); {@EnumFileInRecursion是函数的地址 PChar('c:')是函数的参数,以指针传递 不能直接使用@EnumFileInRecursion('c:')} end else begin TerminateThread(hThreadHandle2,0); CloseHandle(hThreadHandle2); hThreadHandle2 := 0; end; end; procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose:=false; //结束线程 if hThreadHandle1 <> 0 then begin TerminateThread(hThreadHandle1,0); CloseHandle(hThreadHandle1); hThreadHandle1 := 0; end; if hThreadHandle2 <> 0 then begin TerminateThread(hThreadHandle2,0); CloseHandle(hThreadHandle2); hThreadHandle2 := 0; end; CanClose:=true; end; end.