robertjohns 0 Posted May 25, 2023 2 hours ago, programmerdelphi2k said: procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; const AListBox: TListBox); var LSearchRecord: TSearchRec; LIsDirectory : boolean; LContinue : boolean; begin for var LPattern in APatternToSearch do begin LContinue := FindFirst(IncludeTrailingPathDelimiter(AFolderRoot) + LPattern, faAnyFile, LSearchRecord) = 0; // if LContinue then try while LContinue do begin LIsDirectory := ((LSearchRecord.Attr and faDirectory) = faDirectory); // if not((LSearchRecord.Name = '.') or (LSearchRecord.Name = '..')) then begin if LIsDirectory then AListBox.Items.Add('Dir: ' + LSearchRecord.Name) else AListBox.Items.Add('File: ' + LSearchRecord.Name); end; // LContinue := FindNext(LSearchRecord) = 0; end; finally FindClose(LSearchRecord); end; end; end; procedure TForm1.Button1Click(Sender: TObject); begin MyFindFilePattern('d:\MyZip', ['*.zip', '*.txt'], ListBox1); end; In Delphi Tokyo for var LPattern in APatternToSearch do does not work and what is LPattern ? , Undeclared identifier: 'LPattern' Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 2 minutes ago, robertjohns said: Delphi Tokyo for var LPattern in APatternToSearch do does not work and what is LPattern ? you can use default-way: Quote var i:integer; begin for i := 0 to ( length(APatternToSearch) -1) do .... APatternToSearch[ i ] .... Share this post Link to post
robertjohns 0 Posted May 25, 2023 2 hours ago, programmerdelphi2k said: procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; const AListBox: TListBox); var LSearchRecord: TSearchRec; LIsDirectory : boolean; LContinue : boolean; begin for var LPattern in APatternToSearch do begin LContinue := FindFirst(IncludeTrailingPathDelimiter(AFolderRoot) + LPattern, faAnyFile, LSearchRecord) = 0; // if LContinue then try while LContinue do begin LIsDirectory := ((LSearchRecord.Attr and faDirectory) = faDirectory); // if not((LSearchRecord.Name = '.') or (LSearchRecord.Name = '..')) then begin if LIsDirectory then AListBox.Items.Add('Dir: ' + LSearchRecord.Name) else AListBox.Items.Add('File: ' + LSearchRecord.Name); end; // LContinue := FindNext(LSearchRecord) = 0; end; finally FindClose(LSearchRecord); end; end; end; procedure TForm1.Button1Click(Sender: TObject); begin MyFindFilePattern('d:\MyZip', ['*.zip', '*.txt'], ListBox1); end; oh! I tested this code .. this does the same task what the codes does I posted in my first post. Actually I am trying to get all the files from Drive C:\ from its all directories and sub-directories even from special and hidden folders and this function fails for it Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 (edited) @robertjohns first, try read all files/dirs would can be "very slowly" and "consume so much memory", etc... for that exists functions and library for this tasks. In RAD Studio you can try use "System.IOUtils.pas" and the "TDirectory" class for example. now, said this, you can try this way: but remember in "c:\" you can have zillions of files/dirs!!!! do the little test with a folder contained a little files/subdirs!! // when using "Recursive function, like this try dont use visual components like ListBox or any other" type TMyArrToStoreTheResulted = array of string; // procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; const AListBox: TListBox); procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; var AArrToStoreTheResulted: TMyArrToStoreTheResulted); var LSearchRecord: TSearchRec; LIsDirectory : boolean; LContinue : boolean; begin // for var LPattern in APatternToSearch do // ....."LPattern" will receive each element in "APatternToSearch" array!!! // for var i: integer := 0 to high(APatternToSearch) do begin AFolderRoot := IncludeTrailingPathDelimiter(AFolderRoot); // LContinue := FindFirst(AFolderRoot + APatternToSearch[i], faAnyFile, LSearchRecord) = 0; // if LContinue then try while LContinue do begin LIsDirectory := ((LSearchRecord.Attr and faDirectory) = faDirectory); // if not((LSearchRecord.Name = '.') or (LSearchRecord.Name = '..')) then begin if LIsDirectory then begin // AListBox.Items.Add('....... subDir (just for text on screen, delete this line): ' + AFolderRoot + LSearchRecord.Name); // MyFindFilePattern(AFolderRoot + LSearchRecord.Name, APatternToSearch, AArrToStoreTheResulted) end else // AListBox.Items.Add('File: ' + AFolderRoot + LSearchRecord.Name); AArrToStoreTheResulted := AArrToStoreTheResulted + [AFolderRoot + LSearchRecord.Name]; end; // LContinue := FindNext(LSearchRecord) = 0; end; finally FindClose(LSearchRecord); end; end; end; procedure TForm1.Button1Click(Sender: TObject); var LMyResult: TMyArrToStoreTheResulted; // of course, more elements, more memory it's necessary! then, dont abuse! begin // MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], ListBox1); // MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], LMyResult); // ListBox1.Items.AddStrings(LMyResult); end; Edited May 25, 2023 by programmerdelphi2k Share this post Link to post
robertjohns 0 Posted May 25, 2023 43 minutes ago, programmerdelphi2k said: @robertjohns first, try read all files/dirs would can be "very slowly" and "consume so much memory", etc... for that exists functions and library for this tasks. In RAD Studio you can try use "System.IOUtils.pas" and the "TDirectory" class for example. now, said this, you can try this way: but remember in "c:\" you can have zillions of files/dirs!!!! do the little test with a folder contained a little files/subdirs!! // when using "Recursive function, like this try dont use visual components like ListBox or any other" type TMyArrToStoreTheResulted = array of string; // procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; const AListBox: TListBox); procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: array of string; var AArrToStoreTheResulted: TMyArrToStoreTheResulted); var LSearchRecord: TSearchRec; LIsDirectory : boolean; LContinue : boolean; begin // for var LPattern in APatternToSearch do // ....."LPattern" will receive each element in "APatternToSearch" array!!! // for var i: integer := 0 to high(APatternToSearch) do begin AFolderRoot := IncludeTrailingPathDelimiter(AFolderRoot); // LContinue := FindFirst(AFolderRoot + APatternToSearch[i], faAnyFile, LSearchRecord) = 0; // if LContinue then try while LContinue do begin LIsDirectory := ((LSearchRecord.Attr and faDirectory) = faDirectory); // if not((LSearchRecord.Name = '.') or (LSearchRecord.Name = '..')) then begin if LIsDirectory then begin // AListBox.Items.Add('....... subDir (just for text on screen, delete this line): ' + AFolderRoot + LSearchRecord.Name); // MyFindFilePattern(AFolderRoot + LSearchRecord.Name, APatternToSearch, AArrToStoreTheResulted) end else // AListBox.Items.Add('File: ' + AFolderRoot + LSearchRecord.Name); AArrToStoreTheResulted := AArrToStoreTheResulted + [AFolderRoot + LSearchRecord.Name]; end; // LContinue := FindNext(LSearchRecord) = 0; end; finally FindClose(LSearchRecord); end; end; end; procedure TForm1.Button1Click(Sender: TObject); var LMyResult: TMyArrToStoreTheResulted; // of course, more elements, more memory it's necessary! then, dont abuse! begin // MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], ListBox1); // MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], LMyResult); // ListBox1.Items.AddStrings(LMyResult); end; This line does not work in Tokyo MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], LMyResult); ListBox1 .Items.AddStrings(LMyResult); There is no overloaded version of 'AddStrings' that can be called with these arguments Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 (edited) @robertjohns First, YOU DONT need quote the old post all time, ok! just "quote" the line in question ... not all!!! Second, I think that "ITEMS" should have it, because is a TStrings used in many classes, like: TListBox, TMemo, TStringList, etc.. any way, try this //ListBox1.Items.AddStrings(LMyResult); // for var i: integer := 0 to high(LMyResult) do // high(xxxx) = ( length(xxxx) -1 ) ListBox1.Items.Add(LMyResult[i]); // or for var ElementX in LMyResult do ListBox1.Items.Add(ElementX); Edited May 25, 2023 by programmerdelphi2k Share this post Link to post
Remy Lebeau 1396 Posted May 25, 2023 (edited) 52 minutes ago, robertjohns said: This line does not work in Tokyo MyFindFilePattern('d:\MyZip', ['*.*', '*.txt', '*.zip'], LMyResult); ListBox1 .Items.AddStrings(LMyResult); There is no overloaded version of 'AddStrings' that can be called with these arguments AddStrings() has an overload that takes a TArray<string> instead of a dynamic "array of string" (even in 10.2 Tokyo), eg: type //TMyArrToStoreTheResulted = array of string; TMyArrToStoreTheResulted = TArray<string>; Edited May 25, 2023 by Remy Lebeau Share this post Link to post
robertjohns 0 Posted May 25, 2023 23 minutes ago, programmerdelphi2k said: @robertjohns First, YOU DONT need quote the old post all time, ok! just "quote" the line in question ... not all!!! Second, I think that "ITEMS" should have it, because is a TString used in many classes, like: TListBox, TMemo, TStringList, etc.. any way, try this //ListBox1.Items.AddStrings(LMyResult); // for var i: integer := 0 to high(LMyResult) do ListBox1.Items.Add(LMyResult[i]); // or for var ElementX in LMyResult do ListBox1.Items.Add(ElementX); Thanks it keeps repeating the search in folders again and again and never ends I tried C:\ '*.*' Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 (edited) 9 minutes ago, robertjohns said: and never ends I tried C:\ '*. I said that "dont abuse"... c:\ have a zillions objects... using my code it works for sure! using "c:\" , [ '*.*', '*.txt' ] = duplicating many results because a ".TXT" is contained in a "*.*" Edited May 25, 2023 by programmerdelphi2k Share this post Link to post
robertjohns 0 Posted May 25, 2023 3 minutes ago, programmerdelphi2k said: I said that "dont abuse"... c:\ have a zillions objects... using my code it works for sure! using "c:\" , [ '*.*', '*.txt' ] = duplicating many results because a ".TXT" is contained in a "*.*" But I am using MyFindFilePattern('C:', ['*.*'], LMyResult); It still keep repeating search files in the directories and sub-directors again and again and never ends like dir A dir B dir C sub dir a sub dir b sub dir c .... dir A dir B dir C sub dir a sub dir b sub dir c .... and never ends keeps going on again and again Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 (edited) 5 minutes ago, robertjohns said: keep repeating search files in the directories and sub-directors again and again and never ends for sure! if A contains B, and B contains C... what happens here? read A... find B... read B.... find C... read C... and go back to B... and go on.... until the end and go back to A... etc... Edited May 25, 2023 by programmerdelphi2k Share this post Link to post
robertjohns 0 Posted May 28, 2023 On 5/26/2023 at 12:18 AM, programmerdelphi2k said: for sure! if A contains B, and B contains C... what happens here? read A... find B... read B.... find C... read C... and go back to B... and go on.... until the end and go back to A... etc... Is there any way to search C:\ Driver with all sub-directories but skip Windows directory ? Share this post Link to post
KodeZwerg 54 Posted May 28, 2023 Here is my way on Windows to search for files or folders by using fastest method. There is still space for optimizations. Written and tested with FreePascal. unit uMain; {$mode objfpc}{$H+} interface uses Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; type { TfrmMain } TfrmMain = class(TForm) btnStart: TButton; cbSubFolder: TCheckBox; edtBase: TEdit; edtFolder: TEdit; edtMask: TEdit; lblResult: TLabel; lbFiles: TListBox; pnlOptions: TPanel; pnlResult: TPanel; procedure btnStartClick(Sender: TObject); private public end; var frmMain: TfrmMain; implementation {$R *.lfm} type // My variant of an "StringList" TStrArr = array of WideString; const // missing FindEx flags FIND_FIRST_EX_CASE_SENSITIVE = $00000001; FIND_FIRST_EX_LARGE_FETCH = $00000002; FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY = $00000004; // Small helper to add strings in my "StringList" procedure AddStrArr(var AArr: TStrArr; const AString: WideString); inline; var i: Integer; begin i := Length(AArr); SetLength(AArr, Succ(i)); AArr[i] := AString; end; // This method will crawl thru a folder and collect their names // IncludeSubFolders switch will get every folder, False by default // Based upon very fast FindEx Api (Windows) // The result will contain full path function FindExFolders(const BasePath: WideString; const IncludeSubFolders: Boolean = False): TStrArr; var FindExHandle : THandle; Win32FindData : TWin32FindDataW; FindExInfoLevels: TFINDEX_INFO_LEVELS; FindExSearchOps : TFINDEX_SEARCH_OPS; AdditionalFlags : DWORD; tmp : TStrArr; i, ii : Integer; begin SetLength(Result{%H-}, 0); FindExInfoLevels := FindExInfoBasic; FindExSearchOps := FindExSearchLimitToDirectories; AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY; FindExHandle := Windows.FindFirstFileExW( PWideChar(IncludeTrailingBackslash(BasePath) + '*.*') ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil ,AdditionalFlags); if (FindExHandle <> INVALID_HANDLE_VALUE) then repeat if ((Win32FindData.cFileName <> '.') and (Win32FindData.cFileName <> '..') and (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then AddStrArr(Result{%H-}, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); until not Windows.FindNextFileW(FindExHandle, Win32FindData); Windows.FindClose(FindExHandle); if IncludeSubFolders then for i := Low(Result) to High(Result) do begin tmp := FindExFolders(Result[i], IncludeSubFolders); for ii := Low(tmp) to High(tmp) do AddStrArr(Result, tmp[ii]); end; SetLength(tmp, 0); end; // This method will crawl thru a folder and collect their filenames // IncludeSubFolders switch will get every filename, False by default // Based upon very fast FindEx Api (Windows) // The result will contain full path + filename function FindExFiles(const BasePath: WideString; const FileMask: WideString = '*.*'; const IncludeSubFolders: Boolean = False): TStrArr; var FindExHandle : THandle; Win32FindData : TWin32FindDataW; FindExInfoLevels: TFINDEX_INFO_LEVELS; FindExSearchOps : TFINDEX_SEARCH_OPS; AdditionalFlags : DWORD; tmp, Folders : TStrArr; i, ii : Integer; begin SetLength(Result{%H-}, 0); SetLength(Folders{%H-}, 0); SetLength(tmp{%H-}, 0); FindExInfoLevels := FindExInfoBasic; FindExSearchOps := FindExSearchLimitToDirectories; AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY; FindExHandle := Windows.FindFirstFileExW( PWideChar(IncludeTrailingBackslash(BasePath) + FileMask) ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil ,AdditionalFlags); if (FindExHandle <> INVALID_HANDLE_VALUE) then repeat if ((Win32FindData.cFileName <> '.') and (Win32FindData.cFileName <> '..')) then begin if (0 = (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)) then AddStrArr(Result, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); if (IncludeSubFolders and (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then AddStrArr(Folders, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); end; until not Windows.FindNextFileW(FindExHandle, Win32FindData); Windows.FindClose(FindExHandle); if IncludeSubFolders then for i := Low(Folders) to High(Folders) do begin tmp := FindExFiles(Folders[i], FileMask, IncludeSubFolders); for ii := Low(tmp) to High(tmp) do AddStrArr(Result, tmp[ii]); end; SetLength(Folders, 0); SetLength(tmp, 0); end; // My variant of how a file search method can be done for windows systems // BasePath = where do we start at? eg "C:\Users" // BaseFolder = what foldername is a must for results? eg "Documents", can be left empty for all // FileMask = what files you hunt for? eg "*.pas" // IncludeSubFolders = yes or no, you choose. False by default // based upon my "FindExFolders" and "FindExFiles" methods function FindEx(const BasePath: WideString; const BaseFolder: WideString = ''; const FileMask: WideString = '*.*'; const IncludeSubFolders: Boolean = False): TStrArr; var tmp, Folders, Files: TStrArr; i, ii: Integer; begin SetLength(tmp{%H-}, 0); SetLength(Folders{%H-}, 0); SetLength(Files{%H-}, 0); // collect folder(s) to work on if IncludeSubFolders then tmp := FindExFolders(BasePath, IncludeSubFolders) else AddStrArr(tmp, BasePath); // sieve out folders that match criteria if (BaseFolder <> '') then begin for i := Low(tmp) to High(tmp) do if (0 <> Pos(UpperCase(BaseFolder), UpperCase(tmp[i]))) then AddStrArr(Folders, tmp[i]); end else Folders := tmp; SetLength(tmp, 0); // get files that matching the criteria for i := Low(Folders) to High(Folders) do begin tmp := FindExFiles(Folders[i], FileMask); // do not enable the IncludeSubFolders switch here (!) for ii := Low(tmp) to High(tmp) do AddStrArr(Files, tmp[ii]); end; Result := Files; SetLength(tmp, 0); SetLength(Folders, 0); SetLength(Files, 0); end; { TfrmMain } procedure TfrmMain.btnStartClick(Sender: TObject); var i: Integer; List: TStrArr; begin lblResult.Caption := 'Search in progress, program may freeze, please wait...'; lblResult.Refresh; List := FindEx(WideString(edtBase.Text), WideString(edtFolder.Text), WideString(edtMask.Text), cbSubFolder.Checked); lbFiles.Items.BeginUpdate; lbFiles.Clear; for i := Low(List) to High(List) do lbFiles.Items.Add(AnsiString(List[i])); lbFiles.Items.EndUpdate; lblResult.Caption := 'Found ' + IntToStr(lbFiles.Count) + ' file(s).'; SetLength(List, 0); end; end. It should be easy to adapt any different kind of specifications, like excluding a defined folder. Enjoy! Share this post Link to post
robertjohns 0 Posted May 28, 2023 3 hours ago, KodeZwerg said: Here is my way on Windows to search for files or folders by using fastest method. There is still space for optimizations. Written and tested with FreePascal. unit uMain; {$mode objfpc}{$H+} interface uses Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; type { TfrmMain } TfrmMain = class(TForm) btnStart: TButton; cbSubFolder: TCheckBox; edtBase: TEdit; edtFolder: TEdit; edtMask: TEdit; lblResult: TLabel; lbFiles: TListBox; pnlOptions: TPanel; pnlResult: TPanel; procedure btnStartClick(Sender: TObject); private public end; var frmMain: TfrmMain; implementation {$R *.lfm} type // My variant of an "StringList" TStrArr = array of WideString; const // missing FindEx flags FIND_FIRST_EX_CASE_SENSITIVE = $00000001; FIND_FIRST_EX_LARGE_FETCH = $00000002; FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY = $00000004; // Small helper to add strings in my "StringList" procedure AddStrArr(var AArr: TStrArr; const AString: WideString); inline; var i: Integer; begin i := Length(AArr); SetLength(AArr, Succ(i)); AArr[i] := AString; end; // This method will crawl thru a folder and collect their names // IncludeSubFolders switch will get every folder, False by default // Based upon very fast FindEx Api (Windows) // The result will contain full path function FindExFolders(const BasePath: WideString; const IncludeSubFolders: Boolean = False): TStrArr; var FindExHandle : THandle; Win32FindData : TWin32FindDataW; FindExInfoLevels: TFINDEX_INFO_LEVELS; FindExSearchOps : TFINDEX_SEARCH_OPS; AdditionalFlags : DWORD; tmp : TStrArr; i, ii : Integer; begin SetLength(Result{%H-}, 0); FindExInfoLevels := FindExInfoBasic; FindExSearchOps := FindExSearchLimitToDirectories; AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY; FindExHandle := Windows.FindFirstFileExW( PWideChar(IncludeTrailingBackslash(BasePath) + '*.*') ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil ,AdditionalFlags); if (FindExHandle <> INVALID_HANDLE_VALUE) then repeat if ((Win32FindData.cFileName <> '.') and (Win32FindData.cFileName <> '..') and (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then AddStrArr(Result{%H-}, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); until not Windows.FindNextFileW(FindExHandle, Win32FindData); Windows.FindClose(FindExHandle); if IncludeSubFolders then for i := Low(Result) to High(Result) do begin tmp := FindExFolders(Result[i], IncludeSubFolders); for ii := Low(tmp) to High(tmp) do AddStrArr(Result, tmp[ii]); end; SetLength(tmp, 0); end; // This method will crawl thru a folder and collect their filenames // IncludeSubFolders switch will get every filename, False by default // Based upon very fast FindEx Api (Windows) // The result will contain full path + filename function FindExFiles(const BasePath: WideString; const FileMask: WideString = '*.*'; const IncludeSubFolders: Boolean = False): TStrArr; var FindExHandle : THandle; Win32FindData : TWin32FindDataW; FindExInfoLevels: TFINDEX_INFO_LEVELS; FindExSearchOps : TFINDEX_SEARCH_OPS; AdditionalFlags : DWORD; tmp, Folders : TStrArr; i, ii : Integer; begin SetLength(Result{%H-}, 0); SetLength(Folders{%H-}, 0); SetLength(tmp{%H-}, 0); FindExInfoLevels := FindExInfoBasic; FindExSearchOps := FindExSearchLimitToDirectories; AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY; FindExHandle := Windows.FindFirstFileExW( PWideChar(IncludeTrailingBackslash(BasePath) + FileMask) ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil ,AdditionalFlags); if (FindExHandle <> INVALID_HANDLE_VALUE) then repeat if ((Win32FindData.cFileName <> '.') and (Win32FindData.cFileName <> '..')) then begin if (0 = (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)) then AddStrArr(Result, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); if (IncludeSubFolders and (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then AddStrArr(Folders, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName); end; until not Windows.FindNextFileW(FindExHandle, Win32FindData); Windows.FindClose(FindExHandle); if IncludeSubFolders then for i := Low(Folders) to High(Folders) do begin tmp := FindExFiles(Folders[i], FileMask, IncludeSubFolders); for ii := Low(tmp) to High(tmp) do AddStrArr(Result, tmp[ii]); end; SetLength(Folders, 0); SetLength(tmp, 0); end; // My variant of how a file search method can be done for windows systems // BasePath = where do we start at? eg "C:\Users" // BaseFolder = what foldername is a must for results? eg "Documents", can be left empty for all // FileMask = what files you hunt for? eg "*.pas" // IncludeSubFolders = yes or no, you choose. False by default // based upon my "FindExFolders" and "FindExFiles" methods function FindEx(const BasePath: WideString; const BaseFolder: WideString = ''; const FileMask: WideString = '*.*'; const IncludeSubFolders: Boolean = False): TStrArr; var tmp, Folders, Files: TStrArr; i, ii: Integer; begin SetLength(tmp{%H-}, 0); SetLength(Folders{%H-}, 0); SetLength(Files{%H-}, 0); // collect folder(s) to work on if IncludeSubFolders then tmp := FindExFolders(BasePath, IncludeSubFolders) else AddStrArr(tmp, BasePath); // sieve out folders that match criteria if (BaseFolder <> '') then begin for i := Low(tmp) to High(tmp) do if (0 <> Pos(UpperCase(BaseFolder), UpperCase(tmp[i]))) then AddStrArr(Folders, tmp[i]); end else Folders := tmp; SetLength(tmp, 0); // get files that matching the criteria for i := Low(Folders) to High(Folders) do begin tmp := FindExFiles(Folders[i], FileMask); // do not enable the IncludeSubFolders switch here (!) for ii := Low(tmp) to High(tmp) do AddStrArr(Files, tmp[ii]); end; Result := Files; SetLength(tmp, 0); SetLength(Folders, 0); SetLength(Files, 0); end; { TfrmMain } procedure TfrmMain.btnStartClick(Sender: TObject); var i: Integer; List: TStrArr; begin lblResult.Caption := 'Search in progress, program may freeze, please wait...'; lblResult.Refresh; List := FindEx(WideString(edtBase.Text), WideString(edtFolder.Text), WideString(edtMask.Text), cbSubFolder.Checked); lbFiles.Items.BeginUpdate; lbFiles.Clear; for i := Low(List) to High(List) do lbFiles.Items.Add(AnsiString(List[i])); lbFiles.Items.EndUpdate; lblResult.Caption := 'Found ' + IntToStr(lbFiles.Count) + ' file(s).'; SetLength(List, 0); end; end. It should be easy to adapt any different kind of specifications, like excluding a defined folder. Enjoy! Undeclared identifier in Delphi 10.02 FindExInfoLevels: TFINDEX_INFO_LEVELS; FindExSearchOps : TFINDEX_SEARCH_OPS; Undeclared identifier: 'TFINDEX_INFO_LEVELS' Undeclared identifier: 'TFINDEX_SEARCH_OPS' Share this post Link to post
KodeZwerg 54 Posted May 28, 2023 3 hours ago, robertjohns said: type _FINDEX_INFO_LEVELS = (FindExInfoStandard, FindExInfoBasic, FindExInfoMaxInfoLevel); TFINDEX_INFO_LEVELS = _FINDEX_INFO_LEVELS; _FINDEX_SEARCH_OPS = (FindExSearchNameMatch, FindExSearchLimitToDirectories, FindExSearchLimitToDevices, FindExSearchMaxSearchOp); TFINDEX_SEARCH_OPS = _FINDEX_SEARCH_OPS; Share this post Link to post
robertjohns 0 Posted May 28, 2023 1 hour ago, KodeZwerg said: type _FINDEX_INFO_LEVELS = (FindExInfoStandard, FindExInfoBasic, FindExInfoMaxInfoLevel); TFINDEX_INFO_LEVELS = _FINDEX_INFO_LEVELS; _FINDEX_SEARCH_OPS = (FindExSearchNameMatch, FindExSearchLimitToDirectories, FindExSearchLimitToDevices, FindExSearchMaxSearchOp); TFINDEX_SEARCH_OPS = _FINDEX_SEARCH_OPS; Thanks but actually not working in Delphi E2010 Incompatible types: 'Winapi.Windows._FINDEX_INFO_LEVELS' and 'Unit1._FINDEX_INFO_LEVELS' E2010 Incompatible types: 'Winapi.Windows._FINDEX_SEARCH_OPS' and 'Unit1._FINDEX_SEARCH_OPS' Share this post Link to post
programmerdelphi2k 237 Posted May 28, 2023 (edited) // when using "Recursive function, like this try dont use visual components like ListBox or any other" type TMyArrToStoreTheResulted = array of string; TMyPatternToSearch = array of string; TMyFoldersToSkip = array of string; function MyFoldersToSkip(AFoldersToSkip: TMyFoldersToSkip; ACurrentFolderOnSearch: string): boolean; begin result := false; // if ACurrentFolderOnSearch.IsEmpty then { or (length(AFoldersToSkip) = 0) if not verifyed before on MyFindFilePattern() } exit; // for var F in AFoldersToSkip do if (F = ACurrentFolderOnSearch) then exit(true); end; procedure MyFindFilePattern(AFolderRoot: string; APatternToSearch: TMyPatternToSearch; AFoldersToSkip: TMyFoldersToSkip; var AArrToStoreTheResulted: TMyArrToStoreTheResulted); var LSearchRecord : TSearchRec; LIsDirectory : boolean; LContinue : boolean; LLookFolderToSkip: boolean; begin LLookFolderToSkip := (length(AFoldersToSkip) > 0); // to avoid verify all time... // for var i: integer := 0 to high(APatternToSearch) do begin AFolderRoot := IncludeTrailingPathDelimiter(AFolderRoot); // LContinue := FindFirst(AFolderRoot + APatternToSearch[i], faAnyFile, LSearchRecord) = 0; // if LContinue then try while LContinue do begin LIsDirectory := ((LSearchRecord.Attr and faDirectory) = faDirectory); // if not((LSearchRecord.Name = '.') or (LSearchRecord.Name = '..')) then begin if LIsDirectory then begin if not(LLookFolderToSkip and MyFoldersToSkip(AFoldersToSkip, LSearchRecord.Name)) then MyFindFilePattern(AFolderRoot + LSearchRecord.Name, APatternToSearch, AFoldersToSkip, AArrToStoreTheResulted); end else AArrToStoreTheResulted := AArrToStoreTheResulted + [AFolderRoot + LSearchRecord.Name]; end; // LContinue := FindNext(LSearchRecord) = 0; end; finally FindClose(LSearchRecord); end; end; end; procedure TForm1.Button1Click(Sender: TObject); var LMyResult : TMyArrToStoreTheResulted; // of course, more elements, more memory it's necessary! then, dont abuse! LTickCount: UInt64; begin LTickCount := GetTickCount64; // // MyFindFilePattern('c:\', ['*.*'], [], LMyResult); MyFindFilePattern('D:\Downloads', ['*.*'], ['DontLookThisFolder'], LMyResult); // LTickCount := GetTickCount64 - LTickCount; // ListBox1.Items.AddStrings(LMyResult); // ListBox1.Items.Add('Items: ' + ListBox1.Items.Count.ToString + ', Time total: ' + LTickCount.ToString); // { for var i: integer := 0 to high(LMyResult) do ListBox1.Items.Add(LMyResult[i]); // or for var ElementX in LMyResult do ListBox1.Items.Add(ElementX); } end; Edited May 28, 2023 by programmerdelphi2k Share this post Link to post
dummzeuch 1505 Posted May 28, 2023 1 hour ago, programmerdelphi2k said: then JUMP it" I think the word you're looking for is "skip". But yes that's exactly how to implement this. Share this post Link to post
programmerdelphi2k 237 Posted May 28, 2023 7 minutes ago, dummzeuch said: then JUMP it" sorry, my english is not so good 😂 Share this post Link to post
robertjohns 0 Posted May 29, 2023 @programmerdelphi2k Actually I am after the Delphi Search File function which will search all the directories and sub-directories in Drive C:\ excluding Windows Folder [need to skip Windows folder from search] Like the normal cmd command does dir /S /B /A:-D *.exe | findstr /V /I /C:"\\Windows\\" But None of the Delphi function does the same , when Search starts it search in Windows folder too Share this post Link to post
dummzeuch 1505 Posted May 29, 2023 4 hours ago, robertjohns said: Like the normal cmd command does dir /S /B /A:-D *.exe | findstr /V /I /C:"\\Windows\\" But None of the Delphi function does the same , when Search starts it search in Windows folder too This command also searches all subdirectories and only later applies a filter removing all entries that start with c:\Windows\. The function @programmerdelphi2k gave you is actually more efficient because it doesn't descend into the windows directory at all. 1 Share this post Link to post
KodeZwerg 54 Posted May 29, 2023 21 hours ago, robertjohns said: Thanks but actually not working in Delphi E2010 Incompatible types: 'Winapi.Windows._FINDEX_INFO_LEVELS' and 'Unit1._FINDEX_INFO_LEVELS' E2010 Incompatible types: 'Winapi.Windows._FINDEX_SEARCH_OPS' and 'Unit1._FINDEX_SEARCH_OPS' Yes, it was very complicated to fix. Uploaded a fully working Demo application based on my above mentioned code, added "Exclude" capabilities as you wanted. FindEx.zip Share this post Link to post
programmerdelphi2k 237 Posted May 29, 2023 (edited) 5 hours ago, robertjohns said: But None of the Delphi function does the same , when Search starts it search in Windows folder too I think you may be wrong, because as @dummzeuch said, my proposal above is precisely "ignore access to the indicated directory, in this case "Windows". This way, when receiving the name of the directory called "Windows", the function simply move to the next one, so it won't even be accessed! But as I said at the beginning, there are many libraries and functions ready to be used in Delphi! One of them is in the "System.IOUtils.pas" unit, that is, TDirectory, among others! Edited May 29, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted May 29, 2023 (edited) Here a more simple way using "TDirectory.GETFILES(...)", but it was slow than my function above ( +20 seconds ) procedure TForm1.Button2Click(Sender: TObject); var LRootPath : string; LPatternSrc : string; LExcludeFolder: string; LFoundFiles : TArray<string>; LTickCounter : UInt64; begin { NOTE: The "GetFiles" function will only return a value if there is at least one file within the search directory! Otherwise, nothing will be returned. So, there must be at least one file inside a directory for the search to show any result. This is a consequence of the "GetFiles" function of the "TDirectory" record. } LRootPath := 'C:\'; // where start? LPatternSrc := '*.*'; // what see? LExcludeFolder := '\Windows'; // any "object" like this, will be "excluded from search" // LTickCounter := GetTickCount64; // try // GetFiles raise a exception if any error on syntax! LFoundFiles := TDirectory.GetFiles(LRootPath, LPatternSrc, TSearchOption.soAllDirectories, { } function(const Path: string; const SearchRec: TSearchRec): boolean { } begin { } result := not Path.Contains(LExcludeFolder); // unfortunatelly, ALL object will be compared! = more time consumed end); // LTickCounter := GetTickCount64 - LTickCounter; // ListBox1.Items.Clear; ListBox1.Items.AddStrings(LFoundFiles); // ShowMessage(Format('Time: %dms', [LTickCounter])); except on E: Exception do ShowMessage(E.Message); end; end; Edited May 29, 2023 by programmerdelphi2k Share this post Link to post
KodeZwerg 54 Posted May 29, 2023 41 minutes ago, programmerdelphi2k said: Please compare with my approach, I posted earlier a full demo app that works straight out of the box, would like to see time results and if my way would also benefit of a full exclusion from giving names. Share this post Link to post