"OpenDialog1->Files->Count" auf 830 Dateien beschränkt?



  • ich arbeite mit dem BCB5



  • Den hab ich nicht. Mit dem C++Builder 6 habe ich aber gerade mal alle Dateien in %WINDIR%\system32 eingelesen, das waren über 2000. Keine Probleme.

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        if (OpenDialog1->Execute ())
        {
            for (int i = 0, c = OpenDialog1->Files->Count; i < c; ++i)
                ListBox1->Items->Add (OpenDialog1->Files->Strings[i]);
            Label1->Caption = String (OpenDialog1->Files->Count);
        }
    }
    


  • Ich kann es auch mit dem BCB5 nicht reproduzieren (auf Windows XP).

    Wenn ich den Inhalt von Windows\System32 öffne, steht in OpenDialog1->Files->Count irgendwas über 2300.



  • Ok hab das mit der System32 auch mal probiert. Und siehe da. Auch bei mir werden 2300 Dateien geladen. Dann bin ich draufgekommen. Die Dateinamen, die ich lade, haben zu viele Zeichen. Hier eine Beispieldatei: 120710_1051_45_RR2_555_ABTZ_2344.txt

    Wenn man nun die Eigenschaft unter Options PathMustExist auf true setzt erscheint zumindest eine Fehlermeldung wenn man den OK Button im Dialog betätigt.

    Die Dateinamen kann ich leider nicht kürzen. Gibt es eine Lösung für das Problem?



  • rudpower schrieb:

    Gibt es eine Lösung für das Problem?

    Einen eigenen Dialog programmieren ?

    Ist denn die Auswahl so vieler Dateien in dieser Art sinnvoll ?

    Die haben doch sicher Gemeinsamkeiten im Namen ? Da macht wahrscheinlich eine andere Art von Benutzerinterface Sinn.



  • Auf die Dateinamenbenennung habe ich keinen Einfluss. Mein Programm ist eine Art Einleseroutine für ein anderes Programm.

    Einen eigenen Dialog hatte ich schon. Ich habe alle Dateien eines ausgewählten Verzeichnisses in eine ListBox geladen. Problem dabei ist, dass die Sortierung dort nicht stimmt. In meinem Dateinamen sind einstellige Ziffern die falsch sortiert werden. Beispiel:

    120711_BI1
    120711_BI2
    ...
    120711_BI10
    120711_BI11
    usw.

    Die ListBox sortiert so:
    120711_BI1
    120711_BI10
    usw.

    Bei dem OpenDialog ist dies nicht so.



  • Dann vielleicht die Namen selber sortieren und dann in eine Listbox einfügen ?



  • rudpower schrieb:

    Ok hab das mit der System32 auch mal probiert. Und siehe da. Auch bei mir werden 2300 Dateien geladen. Dann bin ich draufgekommen. Die Dateinamen, die ich lade, haben zu viele Zeichen. Hier eine Beispieldatei: 120710_1051_45_RR2_555_ABTZ_2344.txt

    Wenn man nun die Eigenschaft unter Options PathMustExist auf true setzt erscheint zumindest eine Fehlermeldung wenn man den OK Button im Dialog betätigt.

    Die Dateinamen kann ich leider nicht kürzen. Gibt es eine Lösung für das Problem?

    Aha, das erklärt das Problem. TOpenDialog ruft die Windows-Funktion GetOpenFileName() auf, und deren Schnittstelle ist, sagen wir mal, suboptimal: man muß eine OPENFILENAME-Struktur übergeben und darin einen Zeiger auf einen Speicherblock mitgeben, in den der Dateiname geschrieben wird. Das setzt natürlich voraus, daß man raten kann, wie viel Speicherplatz benötigt wird. In dialogs.pas steht folgendes:

    function TOpenDialog.DoExecute(Func: Pointer): Bool;
    const
      MultiSelectBufferSize = High(Word) - 16;
      ...
    begin
      ...
      with OpenFilename do
      begin
        ...
        if ofAllowMultiSelect in FOptions then
          nMaxFile := MultiSelectBufferSize else
          nMaxFile := MAX_PATH;
    

    Das heißt, wenn die Menge aller Dateinamen 64 KB überschreitet, wird hinten abgeschnitten oder der Dialog schlägt fehl, je nachdem.

    Du kannst das Problem auf einem der folgenden Wege beheben:
    - einen eigenen Dialog von TOpenDialog ableiten, die Execute()-Methode überschreiben und mehr Speicherplatz übergeben (vielleicht ein paar MB). Das verlagert das Problem zwar nur, aber es funktioniert wahrscheinlich für die meisten Fälle.
    - C++Builder >= 2007 und Windows >= Vista benutzen. Da gibt es neue Dateidialoge mit gescheitem Interface, das diese Probleme nicht hat, und wenn man es nicht explizit abstellt, benutzen VCL-Anwendungen automatisch die neuen Dialoge



  • vielen Dank für die Antwort.

    Du kannst das Problem auf einem der folgenden Wege beheben:
    - einen eigenen Dialog von TOpenDialog ableiten, die Execute()-Methode überschreiben und mehr Speicherplatz übergeben (vielleicht ein paar MB). Das verlagert das Problem zwar nur, aber es funktioniert wahrscheinlich für die meisten Fälle.
    - C++Builder >= 2007 und Windows >= Vista benutzen. Da gibt es neue Dateidialoge mit gescheitem Interface, das diese Probleme nicht hat, und wenn man es nicht explizit abstellt, benutzen VCL-Anwendungen automatisch die neuen Dialoge

    Möglichkeit 2 scheidet bei mir aus da ich mit Windows XP und dem BCB 5 arbeite.

    Leider hab ich mit Vererbung bisher nicht gearbeitet bzw. hab es nicht gebraucht.

    Das müsst ja so ungefähr funktionieren:

    class MyTOpenDialog : public TOpenDialog {
    
    };
    

    Kann ich die Klasse in private der Form deklarieren? Wie überschreibe ich die Execute Methode bzw ändere die maximale Größe der Dateinamenmenge?



  • rudpower schrieb:

    Du kannst das Problem auf einem der folgenden Wege beheben:
    - einen eigenen Dialog von TOpenDialog ableiten, die Execute()-Methode überschreiben und mehr Speicherplatz übergeben (vielleicht ein paar MB). Das verlagert das Problem zwar nur, aber es funktioniert wahrscheinlich für die meisten Fälle.
    - C++Builder >= 2007 und Windows >= Vista benutzen. Da gibt es neue Dateidialoge mit gescheitem Interface, das diese Probleme nicht hat, und wenn man es nicht explizit abstellt, benutzen VCL-Anwendungen automatisch die neuen Dialoge

    Möglichkeit 2 scheidet bei mir aus da ich mit Windows XP und dem BCB 5 arbeite.

    War auch eher der obligatorische Wink mit dem Zaunpfahl. Mit alter Software machst du dir nur das Leben schwer.

    rudpower schrieb:

    Leider hab ich mit Vererbung bisher nicht gearbeitet bzw. hab es nicht gebraucht.

    Aha, du machst Learning by Doing?

    Ich rate dir, nimm dir mal die Zeit für ein paar richtige Bücher zum Thema. Es lohnt sich.

    rudpower schrieb:

    Das müsst ja so ungefähr funktionieren:

    class MyTOpenDialog : public TOpenDialog {
        
    };
    

    Kann ich die Klasse in private der Form deklarieren? Wie überschreibe ich die Execute Methode bzw ändere die maximale Größe der Dateinamenmenge?

    Zunächst wirst du feststellen, daß TOpenDialog dir die Änderung nicht einfach machen wird (alle Codebeispiele sind aus C++Builder 6, weil ich C++Builder 5 nicht besitze):

    // dialogs.pas, l. 643ff
    function TOpenDialog.DoExecute(Func: Pointer): Bool;
    const
      MultiSelectBufferSize = High(Word) - 16; // <-- hier wird die Puffergröße definiert
      ...
    var
      TempFilter, TempFilename, TempExt: string;
    begin
      ...
        if ofAllowMultiSelect in FOptions then // <-- hier wird die Puffergröße gesetzt
          nMaxFile := MultiSelectBufferSize else
          nMaxFile := MAX_PATH;
        SetLength(TempFilename, nMaxFile + 2); // <-- hier wird der Puffer angelegt
        ...
    end;
    
      // l. 868ff
    function TOpenDialog.Execute: Boolean;
    begin
      Result := DoExecute(@GetOpenFileName);
    end;
    

    All das verbirgt sich nicht etwa in einer kleinen, dem spezifischen Zweck der Pufferallokation zugedachten virtuellen Funktion, sondern in der monolithischen, nichtvirtuellen Funktion DoExecute(). TOpenDialog.Execute(), dankenswerterweise überschreibbar, ruft die Funktion in recht einfacher Weise auf. Du wirst also von TOpenDialog ableiten, die Execute()-Funktion überschreiben und die komplette Implementation von DoExecute() wiederholen müssen.

    Damit du nicht den schönen Tag damit verbringen mußt, Delphi nach C++ zu übersetzen, entscheidest du sodann, dein Derivat, nennen wir es zweckgemäß TOpenDialogForManyFiles, in Delphi zu implementieren. Du erstellst also in C++Builder ein neues Package, sagen wir "MyFixesForAncientBCB5VCL.bpl", und speicherst es irgendwo. In demselben Ordner erstellst du eine .pas-Datei folgenden Inhalts:

    unit OpenDialogForManyFiles;
    
    interface
    
    uses
      Types, Dialogs;
    
    type
      TOpenDialogForManyFiles = class (TOpenDialog)
      private
        function DoExecute(Func: Pointer): Bool;
      public
        function Execute: Boolean; override;
      end;
    
    procedure Register;
    
    implementation
    
    uses
      SysUtils, Classes, Controls, Forms, CommDlg, Windows;
    
    function TOpenDialogForManyFiles.DoExecute(Func: Pointer): Bool;
    const
      MultiSelectBufferSize = 1024 * 1024 * 4; // <-- neue Puffergröße: 4 MB
      OFN_ENABLESIZING = $00800000; // auch ganz praktisch
      // den Rest übernimmst du unverändert aus TOpenDialog.DoExecute() in dialogs.pas!
    end;
    
    function TOpenDialogForManyFiles.Execute: Boolean;
    begin
      Result := DoExecute(@GetSaveFileName);
    end;
    
    procedure Register;
    begin
      RegisterComponents ('Dialoge', [TOpenDialogForManyFiles]);
    end;
    
    end.
    

    Die fügst du dann dem Package-Projekt hinzu und kompilierst es. Du wirst einen Haufen Fehlermeldungen bekommen, weil DoExecute() auf diverse private Felddeklarationen von TOpenDialog zugreift ("undefinierter Bezeichner FFiles" oder so). Die Fehler kannst du allesamt umgehen, indem du den "F"-Präfix wegnimmst, so daß deine Funktion auf die korrespondierenden (öffentlichen) Eigenschaften zugreift - mit folgenden Ausnahmen:

    • diese Zeile:
    FCurrentFilterIndex := FFilterIndex;
    

    für FCurrentFilterIndex gibt es keine korrespondierende Eigenschaft; kommentiere die Zeile einfach aus.

    • das hier:
    if (FFlags and OFN_EXTENSIONDIFFERENT) <> 0 then
            Include(FOptions, ofExtensionDifferent) else
            Exclude(FOptions, ofExtensionDifferent);
          if (FFlags and OFN_READONLY) <> 0 then
            Include(FOptions, ofReadOnly) else
            Exclude(FOptions, ofReadOnly);
    

    Dieser Zugriff ist nur für L-Values gestattet, und das sind Properties nur in Zuweisungen; ändere es ab in

    if (Flags and OFN_EXTENSIONDIFFERENT) <> 0 then
            Options := Options + [ofExtensionDifferent] else
            Options := Options - [ofExtensionDifferent];
          if (Flags and OFN_READONLY) <> 0 then
            Options := Options + [ofReadOnly] else
            Options := Options - [ofReadOnly];
    
    • Außerdem wirst du noch über das hier stolpern, was auf private Methoden aus Dialogs.pas zugreift:
    if (ofOldStyleDialog in FOptions) or not NewStyleControls then
          lpfnHook := DialogHook
        else
          lpfnHook := ExplorerHook;
    

    Du kannst die vier Zeilen auskommentieren.

    Sodann installierst du das Package (im Projektmanager: Rechtsklick|Installieren) und hast in der Tool-Palette deine neue Komponente zur Verfügung.

    (Bonusaufgabe für Sharkbyte, falls du hier mitliest:
    Dir ist sicher aufgefallen, daß ich oben eine Definition für OFN_ENABLESIZING angegeben habe, die im Original nicht drinsteht. Du solltest in der Lage sein, damit dein Problem hier zu lösen und deinen Dialog resizable zu bekommen.)


Anmelden zum Antworten