Как сделать калькулятор в Delphi?

Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.

Как Delphi реализует многоплатформенную разработку?

Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...

TFileFinder - механизм поиска файлов

Статьи » Файлы » TFileFinder - механизм поиска файлов

Было дело, надо было создать компонент, котрый производит поиск файлов. Он был создан и в периодически дополнялся новыми возможностями. Вот и получился компонент с огромными возможностями. Единственное "но" - он был опробован только на 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;

Указывает, где будет производиться поиск.

  • sdOther - каталоги указаны в перменной Dirs
  • sdCurrentDir - В текущей директории
  • sdCurrentDrive - На текущем диске (диск откуда запускалась программа,

но не где находится исполняемый файл)

  • sdFixedDrives - Только на жестких дисках (исключаются дискеты, CDROM, сетевые диски и т.п.)
  • sdAllDrives - На всех дисках которые присутсвеют в системе
  • sdAllNetwork - По всем ресурсам сети (исключаются локальные ресурсы)
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.

Другое по теме:

Категории

Статьи

Советы

Copyright © 2024 - All Rights Reserved - www.delphirus.com