Autor Téma: Komponenta s FindFirst položí Delphi  (Přečteno 516 krát)

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Komponenta s FindFirst položí Delphi
« kdy: 31-08-2020, 08:07:54 »
Dobrý den,

narazil jsem na zvláštní problém. Vytvářím si vlastní komponentu, která mi zobrazí adresářovou strukturu i se soubory, tj. po zadání výchozího adresáře se vytvoří pole podadresářů a souborů. Když nastavím výchozí adresář v runtime, tak vše dopadne bez problémů. Když však nastavím výchozí adresář v design okně, padne Delphi s tím, že přestalo pracovat a bude ukončeno. Napadá někoho proč?

Tento výchozí adresář mám definovaný takto:

Kód: Delphi [Vybrat]
  1. private
  2.   FBaseDirectory: string;
  3.   procedure SetBaseDirectory(const Value: string);
  4. published
  5.   property BaseDirectory: string read FBaseDirectory write SetBaseDirectory;
  6. end;
  7.  

V SetBaseDirectory je obyčejné FindFirst volané rekurzivně. Vše ostatní, co by dále pracovalo s výstupním polem adresářů a souborů jsem vykomentoval, abych zmenšil oblast možného vzniku chyby.

Kód, který proběhne když se nastaví výchozí adresář:

Kód: Delphi [Vybrat]
  1. Type
  2.   TItemType = (itDIR, itFILE);
  3.   TTreeItem = record
  4.     ItemType: TItemType;
  5.     ItemPacked: boolean;
  6.     ItemPath: string;
  7.     ItemDate: double;
  8.   end;
  9.   TTreeItemArray = array of TTreeItem;
  10.  
  11. procedure TFileTreeView.SetBaseDirectory(const Value: string);
  12. begin
  13.   FBaseDirectory := Value;
  14.   RebuildTree;
  15. end;
  16.  
  17. procedure TFileTreeView.RebuildTree;
  18.  
  19.   procedure GetDirectoriesAndFiles(const DirPath: string);
  20.   var
  21.     SR: TSearchRec;
  22.     DateTimeStamp: integer;
  23.   begin
  24.     if not DirectoryExists(DirPath) then exit;
  25.  
  26.     if FindFirst(DirPath + '*.*', faAnyFile, SR) = 0 then
  27.     try
  28.       repeat
  29.         if (SR.Name = '') or (SR.Name = '.') or (SR.Name = '..') then continue;
  30.  
  31.         if SR.Attr = faDirectory then
  32.         begin
  33.           SetLength(FItems, length(FItems) + 1);
  34.  
  35.           FItems[length(FItems) - 1].ItemType   := itDIR;
  36.           FItems[length(FItems) - 1].ItemDate   := 0;
  37.           FItems[length(FItems) - 1].ItemPacked := true;
  38.           FItems[length(FItems) - 1].ItemPath   := IncludeTrailingPathDelimiter(DirPath) + SR.Name;
  39.  
  40.           GetDirectoriesAndFiles(IncludeTrailingPathDelimiter(FItems[length(FItems) - 1].ItemPath));
  41.         end
  42.         else
  43.         begin
  44.           SetLength(FItems, length(FItems) + 1);
  45.  
  46.           FItems[length(FItems) - 1].ItemType   := itFILE;
  47.           DateTimeStamp                         := FileAge(IncludeTrailingPathDelimiter(DirPath) + SR.Name);
  48.           FItems[length(FItems) - 1].ItemDate   := FileDateToDateTime(DateTimeStamp);
  49.           FItems[length(FItems) - 1].ItemPacked := true;
  50.           FItems[length(FItems) - 1].ItemPath   := IncludeTrailingPathDelimiter(DirPath) + SR.Name;
  51.         end;
  52.  
  53.       until FindNext(SR) <> 0;
  54.  
  55.     finally
  56.       FindClose(SR);
  57.     end;
  58.   end;
  59.  
  60. begin
  61.   SetLength(FItems, 0);
  62.   if (FBaseDirectory <> '') and DirectoryExists(FBaseDirectory) then
  63.     GetDirectoriesAndFiles(IncludeTrailingPathDelimiter(BaseDirectory));
  64. end;
  65.  

Děkuji.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1296
  • Karma: 51
    • Verze Delphi: 10.3
Re:Komponenta s FindFirst položí Delphi
« Odpověď #1 kdy: 31-08-2020, 08:54:48 »
Přijde mi, že když narazí v C:\ na složku třeba Test, tak pak volá GetDirectoriesAndFiles(C:\Test), ta zkusí najít soubory C:\Test*.* (chybí lomítko za Test) - asi najde složku C:\Test, zavolá se zase rekurzivně... Takže by pomohlo na řádce 26 přidávat koncové lomítko k DirPath.

Jinak zvětšovat kapacitu toho pole po jedné položce bude šíleně neefektivní, jakmile těch položek bude trochu větší množství.
« Poslední změna: 31-08-2020, 09:10:44 od vandrovnik »

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6141
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Komponenta s FindFirst položí Delphi
« Odpověď #2 kdy: 31-08-2020, 09:07:07 »
Citace
Jinak zvětšovat kapacitu toho pole po jedné položce bude šíleně neefektivní, jakmile těch položek bude trochu větší množství.
Je to síce mimo tému, ale... Ja v takých prípadoch používam TList<T>. Čo som čítal nejaké diskusie, tak sa odporúča veľkosť poľa (aj iných podobných vecí) nastaviť na 1,5 až 2 násobok predpokladanej veľkosti. A takto to ďalej zväčšovať pri vyčerpaní kapacity.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:Komponenta s FindFirst položí Delphi
« Odpověď #3 kdy: 31-08-2020, 09:39:57 »
Přijde mi, že když narazí v C:\ na složku třeba Test, tak pak volá GetDirectoriesAndFiles(C:\Test), ta zkusí najít soubory C:\Test*.* (chybí lomítko za Test) - asi najde složku C:\Test, zavolá se zase rekurzivně... Takže by pomohlo na řádce 26 přidávat koncové lomítko k DirPath.

Jinak zvětšovat kapacitu toho pole po jedné položce bude šíleně neefektivní, jakmile těch položek bude trochu větší množství.

To nemůže nastat, protože funkce IncludeTrailingPathDelimiter zajistí, aby tam to lomítko bylo.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1296
  • Karma: 51
    • Verze Delphi: 10.3
Re:Komponenta s FindFirst položí Delphi
« Odpověď #4 kdy: 31-08-2020, 09:42:09 »
To nemůže nastat, protože funkce IncludeTrailingPathDelimiter zajistí, aby tam to lomítko bylo.

A jo, řádek 40, to jsem přehlídnul, máš pravdu.

Offline Jan Fiala

  • Hrdina
  • ****
  • Příspěvků: 256
  • Karma: 3
    • Verze Delphi: 10.4.1
    • PSPad editor
Re:Komponenta s FindFirst položí Delphi
« Odpověď #5 kdy: 31-08-2020, 12:15:10 »
Citace
Jinak zvětšovat kapacitu toho pole po jedné položce bude šíleně neefektivní, jakmile těch položek bude trochu větší množství.
Je to síce mimo tému, ale... Ja v takých prípadoch používam TList<T>. Čo som čítal nejaké diskusie, tak sa odporúča veľkosť poľa (aj iných podobných vecí) nastaviť na 1,5 až 2 násobok predpokladanej veľkosti. A takto to ďalej zväčšovať pri vyčerpaní kapacity.

Standardně se to dělá tak, že pole zvětším při dosažení velikosti např. o polovinu kapacity. Tak se dynamicky zvětšuje přírůstek a nových alokování je tam minimum. Na konci pak nastavím skutečnou velikost.

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:Komponenta s FindFirst položí Delphi
« Odpověď #6 kdy: 01-09-2020, 07:08:11 »
Tak problém byl v konstantách ve "file attribute" a zjišťování co FindFirst / FindNext nalezl. V podmínce místo if SR.Attr = faDirectory then by mělo být if SR.Attr and faDirectory <> 0 then. Padalo mi pak do else větve i možnosti u kterých nešlo zjistit datum modifikace pomocí FileAge. Je ale zajímavé, že spuštění aplikace nezahlásilo žádnou chybu, ale v design režimu to padlo. V každém případě teď už to běhá jak má.