Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.
Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...
Было дело, надо было создать компонент, котрый производит поиск файлов. Он был создан и в периодически дополнялся новыми возможностями. Вот и получился компонент с огромными возможностями. Единственное "но" - он был опробован только на Delphi 5 + WinNT 4.0 SP6. Но !должен! без проблем работать и в других средах....
Краткие характеристики
Компонент позволет производить поиск как на локальных дисках так и в локаольной сети.
Компонент использует многопотоковость.
Это усовершенствование должно заметно если не сказать "КОНКРЕТНО" повышает скорость сканирования.
Фильтрование файлов. Гарантируется, что один и тот же файл не будет дважды и более возвращен. Это может случиться при поиске файлов по нескольким маскам (Например поиск ведется по маскам [some*.*] и [*.txt] в этом случае файл somebody.txt попадает в две котегории)
Компонент ведет статистику:
Описание
Имя: TCustomFileFinder.
procedure DoFindFile(var FileInfo: TFileInfo); virtual; protected;
Вызывает OnFindFile. Может быть отменена в производных классах.
procedure DoScanDir(const Dir: string); virtual; protected;
Вызывает OnScanDirectory. Может быть отменена в производных классах.
property Dirs: TStrings; protected;
Содержит список директорий в которых будет производиться посик.
Понимает следующие выражения:
[Drive:][][Dir[]] - Поиск в каталоге на локальном диске \ - Поиск во всех ресурсах каждого компьютера в сети \[Computer][] - Поиск во всех ресурсах определенного компьютера в сети \[Computer][Share][] - Поиск в данном ресурсе определенного компьютера в сети
Комментарий:
Список используется только при ScanDirs равном sdOther.
Замечание:
Если указываются подкаталоги то при в включеной рекурсии они игнорируются.
Пример:
Указан поиск в c: emp
\ \server <== (*) d:win95 d:win95 emp <== (*)
Каталоги (*) будут игнориорваться т.к. [\server] входит в множество [\], а [d:win95 emp] входит в [d:win95]
property ScanDirs: TScanDirs; protected;
Указывает, где будет производиться поиск.
но не где находится исполняемый файл)
property Wildcards: TStrings; protected;
Содержит список масок по которым будет производиться поиск файлов.
Например: Поиск всех файлов с расширением WAV и MP3:
*.wav *.mp3
property Recurse: Boolean; protected;
Если True, то поиск также будет производиться в поддиректориях.
property Attributes: TFileAttributes; protected;
Указываются атрибуты искомых файлов.
Например:
[faArchive, faReadOnly] - будут найдены файлы у которых нет установленных атрибутов и файлы у которых установлены аттрибуты faArchive или faReadOnly или оба вместе.
property MaxThreads: Cardinal; protected;
Указывает максимальное количество одновременно работающих потоков. 0 - нет ограничений.
Комментарий:
Используется при поиске в локальной сети. Оптимальное значение не найдено. Но при малом значениии снижается скорость поиска, а при большом наблюдается большая загрузка ресурсов компьютера. Для поиска на локальных дисках используется один поток, т. к. использование нескольких потоков сколь нибудь заметного прироста производительности не дадут.
property OnFindFile(Sender: TObject; var FileInfo: TFileInfo); protected; event;
Вызывается если файл отвечающий условиям поиска найден.
Информация о файле содержиться в структуре FileInfo;
Время обработки этого события старайтесь сделать как можно меньше, т. к. поиск файлов вызывающий поток возобонвит только после возврата из из события.
property OnScanDirectory(Sender: TObject; const Dir: string); protected; event;
Вызывается перед поиском файлов в директории Dir.
Не вижу сколь нибудь пользы от этого обработчика, кроме информационной. Можно пользователю показать, где в данные момент производиться поиск.
property OnEndScan(Sender: TObject; Terminated: Boolean); protected; event;
Вызывается после того как все потоки завершили свою работу.
procedure Start(Wait: Boolean = False); public;
Собственно дает команду начать поиск.
Если Wait = True, то процедура вернет управление только когда полностью закончиться поиск. Иначе функция сразу вернет управление. Если уже идет поиск, то выбрасывается исклчение.
procedure Terminate; public;
Прерывавает поиск. Если поиск не происходит, то выбрасывается исклчение.
function Scaning: Boolean; public;
Если возвращает True, то компонент осуществляет поиск.
property Pause: Boolean; public;
Присваивание этому свойству True, приостанавливает поиск.
Присваивание этому свойству False, возобновляет поиск.
Статистика
property Stat_DateTimeBegin: TDateTime; public; - время начала поиска (*) property Stat_DateTimeEnd: TDateTime; public; - время окончания поиска (**) property Stat_ScaningTime: TDateTime; public; - время сканирования (**) property Stat_ScanedFiles: Integer; public; - количество найденных файлов property Stat_ScanedDirs: Integer; public; - количество просмотренных директорий
(*) статистическая переменная доступны после начала поиска (**) статистические переменные доступны после окончания поиска
unit FileFinder; interface uses Windows, SysUtils, Classes; type EFileFinderError = class(Exception); TFileAttribute = (faArchive, faReadOnly, faHidden, faSystem, faCompressed, faOffline, faTemporary); TFileAttributes = set of TFileAttribute; TScanDirs = (sdOther, sdCurrentDir, sdCurrentDrive, sdFixedDrives, sdAllDrives, sdAllNetwork); PFileInfo = ^TFileInfo; TFileInfo = record FileName: string; FileSize: Longword; Attributes: TFileAttributes; CreationTime: TDateTime; ModifyTime: TDateTime; LastAccessTime: TDateTime; end; TFindFileEvent = procedure(Sender: TObject; var FileInfo: TFileInfo) of object; TScanDirEvent = procedure(Sender: TObject; const Dir: string) of object; TEndScanEvent = procedure(Sender: TObject; Terminated: Boolean) of object; TCustomFileFinder = class(TComponent) private FThrManager: Pointer; FScanDirs: TScanDirs; FDirs: TStrings; FWildcards: TStrings; FRecurse: Boolean; FAttributes: TFileAttributes; FMaxThreads: Cardinal; FOnFindFile: TFindFileEvent; FOnScanDir: TScanDirEvent; FOnEndScan: TEndScanEvent; FStat_BeginTime: TDateTime; FStat_EndTime: TDateTime; FStat_IncTime: TDateTime; FStat_BegScan: TDateTime; FStat_NumFiles: Integer; FStat_NumDirs: Integer; function GetPause: Boolean; procedure SetPause(Value: Boolean); procedure SetDirs(Value: TStrings); procedure SetScanDirs(Value: TScanDirs); procedure SetWildcards(Value: TStrings); procedure SetRecurse(Value: Boolean); procedure SetAttributes(Value: TFileAttributes); procedure SetMaxThreads(Value: Cardinal); procedure FindFileCB(var FileInfo: TFileInfo); procedure ScanDirCB(const Dir: string); procedure TMTerminated; function GetStat_DateTimeBegin: TDateTime; function GetStat_DateTimeEnd: TDateTime; function GetStat_ScaningTime: TDateTime; protected procedure DoFindFile(var FileInfo: TFileInfo); virtual; procedure DoScanDir(const Dir: string); virtual; property Dirs: TStrings read FDirs write SetDirs; property ScanDirs: TScanDirs read FScanDirs write SetScanDirs; property Wildcards: TStrings read FWildcards write SetWildcards; property Recurse: Boolean read FRecurse write SetRecurse default TRUE; property Attributes: TFileAttributes read FAttributes write SetAttributes; property MaxThreads: Cardinal read FMaxThreads write SetMaxThreads; property OnFindFile: TFindFileEvent read FOnFindFile write FOnFindFile; property OnScanDirectory: TScanDirEvent read FOnScanDir write FOnScanDir; property OnEndScan: TEndScanEvent read FOnEndScan write FOnEndScan; public constructor Create(Owner: TComponent); override; destructor Destroy; override; procedure Start(Wait: Boolean = False); procedure Terminate; function Scaning: Boolean; property Pause: Boolean read GetPause write SetPause; property Stat_DateTimeBegin: TDateTime read GetStat_DateTimeBegin; property Stat_DateTimeEnd: TDateTime read GetStat_DateTimeEnd; property Stat_ScaningTime: TDateTime read GetStat_ScaningTime; property Stat_ScanedFiles: Integer read FStat_NumFiles; property Stat_ScanedDirs: Integer read FStat_NumDirs; end; TFileFinder = class(TCustomFileFinder) published property Dirs; property ScanDirs; property Wildcards; property Recurse; property Attributes; property MaxThreads; property OnFindFile; property OnScanDirectory; property OnEndScan; end; procedure register; implementation type PQueueRecord = ^TQueueRecord; TQueueRecord = record Dir: string; Thread: Pointer; end; TThreadManager = class private FWildcards: array of string; FTerminated: Boolean; FFF: TCustomFileFinder; ThreadList: TThreadList; TermEvent: THandle; FQueue: TThreadList; constructor Create(AFF: TCustomFileFinder); destructor Destroy; override; function GetDir(Sender: TObject): string; procedure AddDir(const Dir: string); procedure ExamineAndStart; procedure Terminate; procedure Suspend; procedure Resume; procedure WaitForAll; function GetSuspended: Boolean; procedure FFTTerminated(Sender: TObject); end; TFileFinderThread = class(TThread) private ThrManager: TThreadManager; FilesInfo: array of TFileInfo; Bounds: array of Integer; FilesCount: Integer; CurFileInfo: PFileInfo; CurrentDir: string; ProcFileName: string; ProcFileAttr: Cardinal; NetRes: TNetResource; ServerProc: string; procedure EnumNetRes(Ptr: PNetResource); function PartNetworkPath(const Dir: string): Boolean; function TestFile(var ft: TFileAttributes): Boolean; procedure WildcardProc(const Wildcard: string); procedure DirProc(const Dir: string); function SubSearch(Low, High: Integer): Boolean; function SearchFile: Boolean; procedure IncFilesCount; procedure SafeCallFind; procedure SafeCallNotify; protected procedure DoTerminate; override; procedure Execute; override; public constructor Create(ATM: TThreadManager); end; resourcestring NamePalette = 'Tadex''s Components'; ScaningProcessError = 'Scaning in progress. Can not change this parameter.'; ProcThreadError = 'Scaning don''t started'; BeginScaningError = 'Scaning already in progress.'; StatNotCollected = 'This statistic information isn''t collected yet'; function DrivePath(Letter: char): string; begin Result := Letter + ':'; end; function MakePath(const Path, FileName: string): string; begin if Path[Length(Path)] = '' then Result := Concat(Path, FileName) else Result := Concat(Path, '', FileName); end; function ExtractServerName(const UNCPath: string): string; var DelimPos: Integer; begin Result := '.'; if (UNCPath[1] <> '') or (UNCPath[2] <> '') then Exit; Result := Copy(UNCPath, 3, Length(UNCPath) - 2); DelimPos := Pos('', Result); if DelimPos > 0 then Result := Copy(Result, 1, DelimPos - 1); if Result = '' then Result := '*'; end; function ExpandPath(const Path: string): string; var Dir, Drive, name: string; i, Count: Integer; Dirs: array [0..127] of string; Buffer: array [0..MAX_PATH - 1] of Char; FName: PChar; FD: WIN32_FIND_DATA; HDir: THandle; NxtFile: Boolean; begin Result := ''; SetString(Dir, Buffer, GetFullPathName(PChar(Path), SizeOf(Buffer), Buffer, FName)); Drive := ExtractFileDrive(Dir); Count := 0; for i := Low(Dirs) to High(Dirs) do begin if (Length(Dir) = 3) or (Length(Dir) = Length(Drive)) then Break; name := ExtractFileName(Dir); Dir := ExtractFileDir(Dir); if name <> '' then begin Dirs[Count] := name; Inc(Count); end; end; if Count > 0 then Dir := Drive; name := UpperCase(Dir); for i := Count - 1 downto 0 do begin Dir := Concat(Dir, '', Dirs[i]); HDir := FindFirstFile(PChar(Dir), FD); if HDir = INVALID_HANDLE_VALUE then Exit; try NxtFile := FindNextFile(HDir, FD); finally Windows.FindClose(HDir); end; if NxtFile then Exit; if FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then Exit; name := Concat(name, '', FD.cFileName); end; Result := name; end; function FT2DT(FileTime: TFileTime): TDateTime; var LocalFileTime: TFileTime; Tmp: Int64; begin FileTimeToLocalFileTime(FileTime, LocalFileTime); with Int64Rec(Tmp), LocalFileTime do begin Hi := dwHighDateTime; Lo := dwLowDateTime; end; Result := (Tmp - 94353120000000000) / 8.64e11; end; function LowBound(Arr: array of Integer; index: Integer): Integer; begin if index = 0 then Result := 0 else Result := Arr[index - 1]; end; constructor TFileFinderThread.Create(ATM: TThreadManager); begin inherited Create(True); FreeOnTerminate := True; ThrManager := ATM; SetLength(Bounds, Length(ThrManager.FWildcards)); SetLength(FilesInfo, 8); ServerProc := ''; with NetRes do begin dwScope := RESOURCE_GLOBALNET; dwType := RESOURCETYPE_DISK; dwDisplayType := RESOURCEDISPLAYTYPE_SERVER; dwUsage := RESOURCEUSAGE_CONTAINER; lpLocalName := ''; lpComment := ''; lpProvider := ''; end; end; procedure TFileFinderThread.SafeCallFind; begin ThrManager.FFF.FindFileCB(CurFileInfo^); end; procedure TFileFinderThread.SafeCallNotify; begin ThrManager.FFF.ScanDirCB(CurrentDir); end; function TFileFinderThread.SubSearch(Low, High: Integer): Boolean; var Tmp: Integer; begin Tmp := High - Low; if Tmp <= 0 then Result := False else if Tmp = 1 then Result := FilesInfo[Low].FileName = ProcFileName else begin Tmp := Low + Tmp div 2; if FilesInfo[Tmp].FileName <= ProcFileName then Result := SubSearch(Tmp, High) else Result := SubSearch(Low, Tmp); end; end; function TFileFinderThread.SearchFile: Boolean; var i: Integer; begin Result := True; for i := 0 to High(Bounds) do if SubSearch(LowBound(Bounds, i), Bounds[i]) then Exit; Result := False; end; function TFileFinderThread.TestFile(var FT: TFileAttributes): Boolean; begin Result := False; FT := []; if ProcFileAttr and FILE_ATTRIBUTE_DIRECTORY <> 0 then Exit; if ProcFileAttr and FILE_ATTRIBUTE_ARCHIVE <> 0 then Include(FT, faArchive); if ProcFileAttr and FILE_ATTRIBUTE_READONLY <> 0 then Include(FT, faReadOnly); if ProcFileAttr and FILE_ATTRIBUTE_HIDDEN <> 0 then Include(FT, faHidden); if ProcFileAttr and FILE_ATTRIBUTE_SYSTEM <> 0 then Include(FT, faSystem); if ProcFileAttr and FILE_ATTRIBUTE_COMPRESSED <> 0 then Include(FT, faCompressed); if ProcFileAttr and FILE_ATTRIBUTE_TEMPORARY <> 0 then Include(FT, faTemporary); if ProcFileAttr and FILE_ATTRIBUTE_OFFLINE <> 0 then Include(FT, faOffline); Result := ((FT * ThrManager.FFF.FAttributes <> []) or (FT = [])) and not SearchFile; end; procedure TFileFinderThread.IncFilesCount; begin Inc(FilesCount); if FilesCount >= Length(FilesInfo) then SetLength(FilesInfo, Length(FilesInfo) * 3 div 2); end; procedure TFileFinderThread.WildcardProc(const Wildcard: string); var FD: WIN32_FIND_DATA; Files: THandle; Attr: TFileAttributes; begin if Terminated then Exit; Files := FindFirstFile(PChar(Wildcard), FD); if Files <> INVALID_HANDLE_VALUE then try repeat ProcFileName := FD.cFileName; ProcFileAttr := FD.dwFileAttributes; if TestFile(Attr) then with FilesInfo[FilesCount], FD do begin FileName := ProcFileName; FileSize := nFileSizeLow; Attributes := Attr; CreationTime := FT2DT(ftCreationTime); ModifyTime := FT2DT(ftLastWriteTime); LastAccessTime := FT2DT(ftLastAccessTime); IncFilesCount; end until Terminated or not FindNextFile(Files, FD) finally Windows.FindClose(Files); end end; procedure TFileFinderThread.EnumNetRes(Ptr: PNetResource); type PNetResArray = ^TNetResArray; TNetResArray = array[0..MaxInt div sizeof(TNetResource) - 1] of TNetResource; var I, BufSize, NetResult: Integer; Count, Size: LongWord; NetHandle: THandle; NetResources: PNetResArray; begin if Terminated then Exit; if WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, Ptr, NetHandle) <> NO_ERROR then Exit; NetResources := nil; try BufSize := 10 * SizeOf(TNetResource); GetMem(NetResources, BufSize); repeat Count := $FFFFFFFF; Size := BufSize; NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size); if NetResult <> ERROR_MORE_DATA then Break; BufSize := Size; ReallocMem(NetResources, BufSize); until False; if NetResult = NO_ERROR then for I := 0 to Count - 1 do with NetResources^[I] do if dwDisplayType in [RESOURCEDISPLAYTYPE_SHARE, RESOURCEDISPLAYTYPE_SERVER] then ThrManager.AddDir(lpRemoteName) else if (dwUsage and RESOURCEUSAGE_CONTAINER) = RESOURCEUSAGE_CONTAINER then EnumNetRes(@NetResources^[I]); finally if NetResources <> nil then FreeMem(NetResources); WNetCloseEnum(NetHandle); end; end; function TFileFinderThread.PartNetworkPath(const Dir: string): Boolean; begin Result := False; if (Length(Dir) < 2) or (Dir[1] <> '') or (Dir[2] <> '') then Exit; if (Length(Dir) > 2) and (LastDelimiter('', Dir) > 2) then Exit; if Length(Dir) = 2 then EnumNetRes(nil) else begin NetRes.lpRemoteName := PChar(Dir); EnumNetRes(@NetRes); end; Result := True; end; procedure TFileFinderThread.DirProc(const Dir: string); var FD: WIN32_FIND_DATA; Dirs: THandle; i: Integer; begin if Terminated then Exit; CurrentDir := Dir; Synchronize(SafeCallNotify); if PartNetworkPath(Dir) then Exit; FilesCount := 0; for i := 0 to High(Bounds) do Bounds[i] := -1; for i := 0 to High(ThrManager.FWildcards) do begin WildcardProc(MakePath(Dir, ThrManager.FWildcards[i])); Bounds[i] := FilesCount; end; for i := 0 to FilesCount - 1 do begin if Terminated then Exit; CurFileInfo := @FilesInfo[i]; with CurFileInfo^ do begin FileName := MakePath(Dir, FileName); Synchronize(SafeCallFind); FileName := ''; end; end; if ThrManager.FFF.FRecurse and not Terminated then begin Dirs := FindFirstFile(PChar(MakePath(Dir, '*.*')), FD); if Dirs <> INVALID_HANDLE_VALUE then try repeat with FD do if ((dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0) and (cFileName <> string('.')) and (cFileName <> string('..')) then DirProc(MakePath(Dir, cFileName)); until Terminated or not FindNextFile(Dirs, FD); finally Windows.FindClose(Dirs); end end end; procedure TFileFinderThread.Execute; var Dir: string; begin repeat Dir := ThrManager.GetDir(Self); if Dir = '' then Break; DirProc(Dir); until Terminated; end; procedure TFileFinderThread.DoTerminate; begin ThrManager.FFTTerminated(Self); end; constructor TThreadManager.Create(AFF: TCustomFileFinder); var i, j, Count: Integer; ch: Char; Dirs: array of string; begin inherited Create; FFF := AFF; FTerminated := False; FQueue := TThreadList.Create; ThreadList := TThreadList.Create; TermEvent := CreateEvent(nil, False, False, nil); SetLength(FWildcards, FFF.Wildcards.Count); Count := 0; for i := 0 to High(FWildcards) do if Trim(FFF.Wildcards.Strings[i]) <> '' then begin FWildcards[Count] := FFF.Wildcards.Strings[i]; Inc(Count); end; SetLength(FWildcards, Count); SetLength(Dirs, FFF.FDirs.Count); for i := 0 to High(Dirs) do Dirs[Count] := FFF.FDirs.Strings[i]; case FFF.FScanDirs of sdOther: begin for i := 0 to High(Dirs) do Dirs[i] := ExpandPath(Dirs[i]); for i := 0 to High(Dirs) do for j := 0 to High(Dirs) do if (i <> j) and (Dirs[i] <> '') and (Dirs[j] <> '') then if FFF.FRecurse then begin if Pos(Dirs[j], Dirs[i]) > 0 then Dirs[i] := ''; end else begin if Dirs[i] = Dirs[j] then Dirs[i] := ''; end; for i := 0 to High(Dirs) do if Dirs[i] <> '' then AddDir(Dirs[i]); end; sdCurrentDir: AddDir(GetCurrentDir); sdCurrentDrive: AddDir(DrivePath(GetCurrentDir[1])); sdAllNetwork: AddDir(''); else for ch := 'A' to 'Z' do case GetDriveType(PChar(DrivePath(ch))) of DRIVE_REMOVABLE, DRIVE_REMOTE, DRIVE_CDROM: if FFF.FScanDirs = sdAllDrives then AddDir(DrivePath(ch)); DRIVE_FIXED: if FFF.FScanDirs in [sdAllDrives, sdFixedDrives] then AddDir(DrivePath(ch)); end; end; end; destructor TThreadManager.Destroy; begin Terminate; WaitForAll; CloseHandle(TermEvent); ThreadList.Free; FQueue.Free; inherited Destroy; end; procedure TThreadManager.Terminate; var List: TList; i: Integer; begin FTerminated := True; List := ThreadList.LockList; for i := 0 to List.Count - 1 do with TFileFinderThread(List.Items[i]) do begin Suspended := False; Terminate; end; ThreadList.UnlockList; end; procedure TThreadManager.Suspend; var List: TList; i: Integer; begin List := ThreadList.LockList; for i := 0 to List.Count - 1 do TFileFinderThread(List.Items[i]).Suspended := True; ThreadList.UnlockList; end; procedure TThreadManager.Resume; var List: TList; i: Integer; begin List := ThreadList.LockList; for i := 0 to List.Count - 1 do TFileFinderThread(List.Items[i]).Suspended := False; ThreadList.UnlockList; end; procedure TThreadManager.WaitForAll; var Msg: TMsg; H: THandle; begin H := TermEvent; if GetCurrentThreadID = MainThreadID then while MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) else WaitForSingleObject(H, INFINITE); end; procedure TThreadManager.FFTTerminated(Sender: TObject); var List: TList; Termination: Boolean; begin ThreadList.Remove(Sender); ExamineAndStart; List := ThreadList.LockList; Termination := List.Count = 0; ThreadList.UnlockList; if Termination then begin SetEvent(TermEvent); FFF.TMTerminated; end; end; function TThreadManager.GetSuspended: Boolean; var List: TList; i: Integer; begin Result := False; List := ThreadList.LockList; for i := 0 to List.Count - 1 do Result := Result or TFileFinderThread(List.Items[i]).Suspended; ThreadList.UnlockList; end; function TThreadManager.GetDir(Sender: TObject): string; var List: TList; i: Integer; ServerProc: string; begin Result := ''; List := FQueue.LockList; for i := 0 to List.Count - 1 do with PQueueRecord(List.Items[i])^ do if Thread = Sender then begin Result := Dir; Dispose(List.Items[i]); List.Delete(i); Break; end; if Result = '' then begin ServerProc := ''; for i := 0 to List.Count - 1 do with PQueueRecord(List.Items[i])^ do if Thread = nil then begin ServerProc := ExtractServerName(Dir); Result := Dir; Dispose(List.Items[i]); List.Delete(i); Break; end; if ServerProc <> '' then begin if Sender is TFileFinderThread then TFileFinderThread(Sender).ServerProc := ServerProc; for i := 0 to List.Count - 1 do with PQueueRecord(List.Items[i])^ do if ExtractServerName(Dir) = ServerProc then Thread := Sender; end; end; FQueue.UnlockList; end; procedure TThreadManager.AddDir(const Dir: string); var i: Integer; List: TList; QRec: PQueueRecord; Caller: TFileFinderThread; ServerProc: string; begin ServerProc := ExtractServerName(Dir); Caller := nil; List := ThreadList.LockList; for i := 0 to List.Count - 1 do if TFileFinderThread(List.Items[i]).ServerProc = ServerProc then begin Caller := TFileFinderThread(List.Items[i]); Break; end; ThreadList.UnlockList; New(QRec); QRec.Dir := Dir; QRec.Thread := Caller; FQueue.Add(QRec); ExamineAndStart; end; procedure TThreadManager.ExamineAndStart; var Threads, Queue: TList; i: Integer; NewThread: TFileFinderThread; ServerProc: string; begin if FTerminated then Exit; Threads := ThreadList.LockList; Queue := FQueue.LockList; repeat ServerProc := ''; if (FFF.FMaxThreads = 0) or (Cardinal(Threads.Count) < FFF.FMaxThreads) then begin for i := 0 to Queue.Count - 1 do with PQueueRecord(Queue.Items[i])^ do if Thread = nil then begin ServerProc := ExtractServerName(Dir); Break; end; if ServerProc <> '' then begin NewThread := TFileFinderThread.Create(Self); Threads.Add(NewThread); NewThread.ServerProc := ServerProc; for i := 0 to Queue.Count - 1 do with PQueueRecord(Queue.Items[i])^ do if ExtractServerName(Dir) = ServerProc then Thread := NewThread; NewThread.Resume; end; end; until ServerProc = ''; FQueue.UnlockList; ThreadList.UnlockList; end; constructor TCustomFileFinder.Create(Owner: TComponent); begin inherited Create(Owner); FDirs := TStringList.Create; FWildcards := TStringList.Create; FAttributes := [faArchive, faReadOnly]; FRecurse := True; FScanDirs := sdFixedDrives; FMaxThreads := 10; FThrManager := nil; FWildcards.Add('*.*'); FStat_BeginTime := 0; FStat_EndTime := 0; FStat_IncTime := 0; FStat_NumFiles := 0; FStat_NumDirs := 0; end; destructor TCustomFileFinder.Destroy; begin if Assigned(FThrManager) then TThreadManager(FThrManager).Free; FDirs.Free; FWildcards.Free; inherited Destroy; end; procedure TCustomFileFinder.FindFileCB(var FileInfo: TFileInfo); begin Inc(FStat_NumFiles); DoFindFile(FileInfo); end; procedure TCustomFileFinder.ScanDirCB(const Dir: string); begin Inc(FStat_NumDirs); DoScanDir(Dir); end; procedure TCustomFileFinder.DoFindFile(var FileInfo: TFileInfo); begin if Assigned(FOnFindFile) then FOnFindFile(self, FileInfo); end; procedure TCustomFileFinder.DoScanDir(const Dir: string); begin if Assigned(FOnScanDir) then FOnScanDir(self, Dir); end; function TCustomFileFinder.Scaning: Boolean; begin Result := Assigned(FThrManager); end; procedure TCustomFileFinder.SetDirs(Value: TStrings); begin if Assigned(FThrManager) then raise EFileFinderError.Create(ScaningProcessError); FDirs.Assign(Value); FScanDirs := sdOther; end; procedure TCustomFileFinder.SetWildcards(Value: TStrings); begin if Assigned(FThrManager) then raise EFileFinderError.Create(ScaningProcessError); FWildcards.Assign(Value); end; procedure TCustomFileFinder.SetScanDirs(Value: TScanDirs); begin if Assigned(FThrManager) then raise EFileFinderError.Create(ScaningProcessError); FScanDirs := Value; end; procedure TCustomFileFinder.SetRecurse(Value: Boolean); begin if Assigned(FThrManager) then raise EFileFinderError.Create(ScaningProcessError); FRecurse := Value; end; procedure TCustomFileFinder.SetAttributes(Value: TFileAttributes); begin if Assigned(FThrManager) then raise EFileFinderError.Create(ScaningProcessError); FAttributes := Value; end; procedure TCustomFileFinder.SetMaxThreads(Value: Cardinal); begin FMaxThreads := Value; end; procedure TCustomFileFinder.Terminate; begin if not Assigned(FThrManager) then raise EFileFinderError.Create(ProcThreadError); TThreadManager(FThrManager).Terminate; end; function TCustomFileFinder.GetPause: Boolean; begin if not Assigned(FThrManager) then raise EFileFinderError.Create(ProcThreadError); Result := TThreadManager(FThrManager).GetSuspended; end; procedure TCustomFileFinder.SetPause(Value: Boolean); var Suspended: Boolean; begin if not Assigned(FThrManager) then raise EFileFinderError.Create(ProcThreadError); Suspended := TThreadManager(FThrManager).GetSuspended; if not Suspended and Value then begin TThreadManager(FThrManager).Suspend; FStat_IncTime := FStat_IncTime + (Now - FStat_BegScan); end; if Suspended and not Value then begin FStat_BegScan := Now; TThreadManager(FThrManager).Resume; end; end; procedure TCustomFileFinder.Start(Wait: Boolean); begin if Assigned(FThrManager) then raise EFileFinderError.Create(BeginScaningError); FStat_BeginTime := Now; FStat_BegScan := FStat_BeginTime; FStat_IncTime := 0; FStat_NumFiles := 0; FStat_NumDirs := 0; FThrManager := TThreadManager.Create(Self); if Wait then TThreadManager(FThrManager).WaitForAll; end; procedure TCustomFileFinder.TMTerminated; var Tmp: Boolean; begin Tmp := TThreadManager(FThrManager).FTerminated; FreeAndNil(FThrManager); FStat_EndTime := Now; FStat_IncTime := FStat_IncTime + (FStat_EndTime - FStat_BegScan); if Assigned(FOnEndScan) then FOnEndScan(self, Tmp); end; function TCustomFileFinder.GetStat_DateTimeBegin: TDateTime; begin if FStat_BeginTime = 0 then raise EFileFinderError.Create(StatNotCollected); Result := FStat_BeginTime; end; function TCustomFileFinder.GetStat_DateTimeEnd: TDateTime; begin if (FStat_EndTime = 0) or Assigned(FThrManager) then raise EFileFinderError.Create(StatNotCollected); Result := FStat_EndTime; end; function TCustomFileFinder.GetStat_ScaningTime: TDateTime; begin Result := FStat_IncTime; if Assigned(FThrManager) and not TThreadManager(FThrManager).GetSuspended then Result := Result + (Now - FStat_BegScan); end; procedure register; begin RegisterComponents(NamePalette, [TFileFinder]); end; end.