Unit1.dfm
object Form1: TForm1 Left = 248 Top = 107 BorderStyle = bsDialog Caption = # 25991 # 20214 # 36941 # 21382 # 30340 # 20363 # 23376 ClientHeight = 364 ClientWidth = 478 Color = clBtnFace Font.Charset = ANSI_CHARSET Font.Color = clWindowText Font.Height = - 12 Font.Name = ' Arial' Font.Style = [] OldCreateOrder = False OnCloseQuery = FormCloseQuery PixelsPerInch = 96 TextHeight = 15 object Label1: TLabel Left = 3 Top = 343 Width = 173 Height = 15 Caption = ' by rockhan irochan@163.com' Font.Charset = ANSI_CHARSET Font.Color = clWindowText Font.Height = - 12 Font.Name = ' Arial' Font.Style = [fsBold] ParentFont = False end object Bevel1: TBevel Left = 0 Top = 320 Width = 481 Height = 18 Shape = bsBottomLine end object Label2: TLabel Left = 8 Top = 280 Width = 225 Height = 15 Caption = # 27809 # 26377 # 25214 # 21040 # 27494 # 26519 # 22806 # 20256 # 30340 # 30446 # 24405 ' ....'#35831#25442#30424#21518#20877#25214'...' end object Memo1: TMemo Left = 0 Top = 0 Width = 473 Height = 273 ScrollBars = ssVertical TabOrder = 0 end object Button1: TButton Left = 296 Top = 304 Width = 75 Height = 25 Caption = # 36882 # 24402 # 36941 # 21382 TabOrder = 1 OnClick = Button1Click end object Button2: TButton Left = 384 Top = 304 Width = 75 Height = 25 Caption = # 20572 # 27490 TabOrder = 2 OnClick = Button2Click end object Button3: TButton Left = 208 Top = 304 Width = 75 Height = 25 Caption = # 38431 # 21015 # 36941 # 21382 TabOrder = 3 OnClick = Button3Click end object DriveComboBox1: TDriveComboBox Left = 48 Top = 304 Width = 145 Height = 21 BevelInner = bvNone BevelOuter = bvNone TabOrder = 4 end endUnit1.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.