AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) FindFirstFileEx liefert Error Falscher Parameter?
Thema durchsuchen
Ansicht
Themen-Optionen

FindFirstFileEx liefert Error Falscher Parameter?

Ein Thema von DieDolly · begonnen am 27. Aug 2022 · letzter Beitrag vom 9. Sep 2022
Antwort Antwort
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.202 Beiträge
 
Delphi 12 Athens
 
#1

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 27. Aug 2022, 23:51
Delphi-Quellcode:
lIndexInfoLevels := FindExInfoBasic;
lIndexSearchOps := FindExSearchNameMatch;
lhFoundFile := FindFirstFileEx(PChar(ParentFolder + Mask), lIndexInfoLevels, @lfdStruct, lIndexSearchOps, nil, 0);
Dein Code aus Post #1:
Verboten sind Variablen nicht, aber man sollte ihnen auch etwas zuweisen.

Und genau das sollte der Compiler dir auch gesagt haben,
Zitat:
Code:
[dcc32 Warnung] Unit11.pas(35): W1036 Variable 'lIndexInfoLevels' ist möglicherweise nicht initialisiert worden
[dcc32 Warnung] Unit11.pas(35): W1036 Variable 'lIndexSearchOps' ist möglicherweise nicht initialisiert worden
denn es war auch der Grund, warum sich FindFirstFileEx bei dir ab und an beschwert hat, wenn da im Speicher nicht zufällig eine mal 0 oder 1 drin war.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (27. Aug 2022 um 23:56 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.690 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 7. Sep 2022, 18:46
Vielleicht ist das hilfreich für andere die nach FindFirstFileEx suchen:
(mit FPC/Lazarus geschrieben, aber das gröbste sollte identisch sein)

Delphi-Quellcode:
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Windows,
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;

type
  TStrArr = array of string;

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    ListBox1: TListBox;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
    function MySearch(const BasePath, BaseFolder: string; const FileMask: string = '*.*'): TStrArr;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure AddStrArr(var AArr: TStrArr; const AString: string);
var
  i: Integer;
begin
  i := Length(AArr);
  SetLength(AArr, Succ(i));
  AArr[i] := AString;
end;

function GetAllFolders(const BasePath: string; const IncludeSubFolders: Boolean = False): TStrArr;
var
  FindExHandle : THandle;
  Win32FindData : TWin32FindData;
  FindExInfoLevels: TFINDEX_INFO_LEVELS;
  FindExSearchOps : TFINDEX_SEARCH_OPS;
  AdditionalFlags : DWORD;
  i, ii : Integer;
  tmp : TStrArr;
begin
  SetLength(Result, 0);
  FindExInfoLevels := FindExInfoBasic;
  FindExSearchOps := FindExSearchLimitToDirectories;
  AdditionalFlags := 0;
  FindExHandle := Windows.FindFirstFileEx(PChar(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, IncludeTrailingBackslash(BasePath) + Win32FindData.cFileName);
    until not Windows.FindNextFile(FindExHandle, Win32FindData);
  Windows.FindClose(FindExHandle);
  if IncludeSubFolders then
    for i := Low(Result) to High(Result) do
      begin
        tmp := GetAllFolders(Result[i], IncludeSubFolders);
        for ii := Low(tmp) to High(tmp) do
          AddStrArr(Result, tmp[ii]);
      end;
  SetLength(tmp, 0);
end;

function GetAllFiles(const BaseFolder: string; const FileMask: string = '*.*'): TStrArr;
var
  FindExHandle : THandle;
  Win32FindData : TWin32FindData;
  FindExInfoLevels: TFINDEX_INFO_LEVELS;
  FindExSearchOps : TFINDEX_SEARCH_OPS;
  AdditionalFlags : DWORD;
begin
  SetLength(Result, 0);
  FindExInfoLevels := FindExInfoBasic;
  FindExSearchOps := FindExSearchLimitToDirectories;
  AdditionalFlags := 0;
  FindExHandle := Windows.FindFirstFileEx(PChar(IncludeTrailingBackslash(BaseFolder) + FileMask)
                   ,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, IncludeTrailingBackslash(BaseFolder) + Win32FindData.cFileName);
    until not Windows.FindNextFile(FindExHandle, Win32FindData);
  Windows.FindClose(FindExHandle);
end;

{ TForm1 }

function TForm1.MySearch(const BasePath, BaseFolder: string; const FileMask: string = '*.*'): TStrArr;
var
 tmp, Folders, Files: TStrArr;
 i,ii: Integer;
begin
  SetLength(tmp, 0);
  SetLength(Folders, 0);
  SetLength(Files, 0);
  tmp := GetAllFolders(BasePath, True);
  // sieve out folders that match criteria
  for i := Low(tmp) to High(tmp) do
    if (Pos(UpperCase(BaseFolder), UpperCase(tmp[i])) <> 0) then
      AddStrArr(Folders, tmp[i]);
  SetLength(tmp, 0);
  // get files that matching criteria
  for i := Low(Folders) to High(Folders) do
    begin
      tmp := GetAllFiles(Folders[i], FileMask);
      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;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  List: TStrArr;
begin
  List := MySearch(Edit1.Text, Edit2.Text, Edit3.Text);
  ListBox1.Items.BeginUpdate;
  ListBox1.Clear;
  for i := Low(List) to High(List) do
    ListBox1.Items.Add(List[i]);
  ListBox1.Items.EndUpdate;
end;

end.
Ziel war es, eine Suche durchzuführen in der man 3 Parameter eingibt,
1er Parameter = Ein Basisverzeichnis (zBsp: C:\Users)
2er Parameter = Ein Verzeichnis worin gesucht werden soll (zBps: Documents)
3er Parameter = Eine Datei-Maske (zBsp: *.pas)
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.202 Beiträge
 
Delphi 12 Athens
 
#3

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 7. Sep 2022, 19:44
Falls ich mich jetzt nicht verschrieben hab
Delphi-Quellcode:
uses
  StrUtils, IOUtils;

procedure TForm11.Button1Click(Sender: TObject);
var
  List: TArray<string>;
begin
  // GetAllFiles -> TDirectory.GetDirectories
  // GetAllFolders -> TDirectory.GetFiles
  for var Path in TDirectory.GetDirectories(BasePath.Text, TSearchOption.soAllDirectories {oder .soTopDirectoryOnly},
    function(const Path: string; const SearchRec: TSearchRec): Boolean
    begin
      Result := ContainsText(TPath.Combine(Path, SearchRec.Name), BaseFolder.Text);
    end) do
  begin
    // AddStrArr(List, S); -> List := List + [S]; (schade, dass List += S; "noch" nicht geht)
    List := List + TDirectory.GetFiles(Path, Filter.Text, TSearchOption.soTopDirectoryOnly);
  end;
  ListBox1.Items.Clear;
  ListBox1.Items.AddStrings(List);
end;
wobei
Delphi-Quellcode:
begin
  List := TDirectory.GetFiles(BasePath.Text, Filter.Text, TSearchOption.soAllDirectories,
    function(const Path: string; const SearchRec: TSearchRec): Boolean
    begin
      Result := ContainsText(Path, BaseFolder.Text);
    end);
  ListBox1.Items.Clear;
  ListBox1.Items.AddStrings(List);
end;
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu ( 7. Sep 2022 um 23:10 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.690 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 8. Sep 2022, 21:21
Hmmm ich kann meine Post nicht bearbeiten, wollte noch mein update dazu nachliefern.
Delphi-Quellcode:
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Windows,
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    ListBox1: TListBox;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

type
  // My variant of an "StringList"
  TStrArr = array of WideString;

// Small helper to add strings in my "StringList"
procedure AddStrArr(var AArr: TStrArr; const AString: WideString);
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;
  i, ii : Integer;
  tmp : TStrArr;
begin
  SetLength(Result{%H-}, 0);
  FindExInfoLevels := FindExInfoBasic;
  FindExSearchOps := FindExSearchLimitToDirectories;
  AdditionalFlags := 0;
  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 := 0;
  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;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  List: TStrArr;
begin
  List := FindEx(WideString(Edit1.Text), WideString(Edit2.Text), WideString(Edit3.Text), CheckBox1.Checked);
  ListBox1.Items.BeginUpdate;
  ListBox1.Clear;
  for i := Low(List) to High(List) do
    ListBox1.Items.Add(AnsiString(List[i]));
  ListBox1.Items.EndUpdate;
end;

end.
Wohlgemerkt, geschrieben mit Lazarus und da ist ein String noch ein AnsiString, deswegen die casts.
( die {%H-} direktive schaltet in Lazarus Warnungen ab )

@himitsu
Jo, mit Delphi oder Lazarus Bordmitteln geht es natürlich auch, ich war mehr hinter FindFirstFileEx (Threadname) her und dessen Bedienung.
Gruß vom KodeZwerg

Geändert von KodeZwerg ( 8. Sep 2022 um 21:47 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.202 Beiträge
 
Delphi 12 Athens
 
#5

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 8. Sep 2022, 23:56
Nja, da könnte man auch direkt Assembler schreiben, wenn man die vorhandenen einfacheren Lösungen nicht nimmt.


Die lokalen TStrArr sollten doch auch im FPC zu Beginn immer Length 0 sein und am Ende auch automatisch freigegeben werden, oder nicht?
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.690 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 9. Sep 2022, 00:29
Nja, da könnte man auch direkt Assembler schreiben, wenn man die vorhandenen einfacheren Lösungen nicht nimmt.
Ich gebe Dir vollkommen Recht, aber auch mich hatte es mal interessiert wie die WindowsAPI so funktioniert.
Die lokalen TStrArr sollten doch auch im FPC zu Beginn immer Length 0 sein und am Ende auch automatisch freigegeben werden, oder nicht?
Bei Rekursion sollte es schon auf 0 gesetzt sein sonst gibt es Fehler. Den rest meiner SetLength(0) dinge kann man bestimmt drauf verzichten.
Ursprünglich wollte ich eine stringlist nehmen aber fpc hat da eine macke mit der ich nicht zurecht gekommen bin, deshalb der umweg über was eigenes
die macke ist das hier:
Delphi-Quellcode:
if ((StringList = nil) or (not Assigned(StringList))) then
  StringList := TStringList.Create.
in fpc kann man nicht prüfen, aber das ist ein völlig anderes thema.


//edit
und hey, du kennst das doch bestimmt auch, wenn etwas neues genau so funktioniert wie du es erhofft hast, dann ist man stolz und mag sein werk zeigen
Gruß vom KodeZwerg

Geändert von KodeZwerg ( 9. Sep 2022 um 00:46 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.202 Beiträge
 
Delphi 12 Athens
 
#7

AW: FindFirstFileEx liefert Error Falscher Parameter?

  Alt 9. Sep 2022, 01:23
Es kommt zwar seltener vor, dann man wirklich mal viel schnell sucht,
aber wenn, dann sind FIND_FIRST_EX_LARGE_FETCH, FindExSearchLimitToDirectories und vielleicht noch FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY ganz nett.
Wobei direkt MFT unschlagbar ist, auch wenn es selten genutzt werden kann.


Schade, überall werden immernoch die Transacted-Varianten present verlinkt, aber wenn man dann rein sieht, dann wird man trauchig, weil es praktisch schon tot ist.



Ja, das Result muß man leider immer initialisieren, blöd nur, wenn der Compiler bei gemangten Typen dort nichts sagt


Keine Sorge, ich nehme auch lieber Arrays (wegen der Kompatibilität als TArray<>).
eine IStringList ist bissl unpraktisch, aber eine automatische Speicherverwaltung ist unschlagbar ... nie vergessen die "Liste" freizugeben oder mal ausversehn doppelt versuchen freizugeben.

Mit Managed Records und den TArray-Helpern (die in den Generics und paar Eigene, weil Delphi viel vergessen hat) und seit 'ner kurzen Weile endlich auch Insert, Delete und + für Arrays gleichen das aus.


Zitat:
Delphi-Quellcode:
procedure AddStrArr(var AArr: TStrArr; const AString: WideString);
var
  i: Integer;
begin
  i := Length(AArr);
  SetLength(AArr, Succ(i));
  AArr[i] := AString;
end;
AArr := AArr + [AString]; (vielleicht nicht der optimalste Code, aber egal ... noch nicht nachgesehn, ob Delphi wirklich das macht, was man schleibt, oder ob es doch optimaler arbeitet und "nur" anhängt/inserted)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:44 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz