Open-/Save-Dialog: Liste zuletzt geöffneter Dateien leeren



  • Ich habs jetzt grad mal nur für den Standartdialog Fontselection parat, aber mit den Filedialogen sollte es ebenso funktionieren:

    UINT CALLBACK DialogFontSelection_EventHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
    
    	switch(message){
    		case WM_INITDIALOG:{
    //			do something here
    			break;
    		}
    	}
    	return 0;
    }
    
    boolean DialogFontSelection_Create(char *headline){
    
    	CHOOSEFONT		cf;
    
    	memset(&cf, 0, sizeof(cf));
    	cf.lStructSize = sizeof(CHOOSEFONT);
    	cf...... = .....;
    	cf...... = .....;
    	cf...... = .....;
    	cf.lpfnHook =  (LPCCHOOKPROC)DialogFontSelection_EventHandler;
    	cf.lpTemplateName = headline;
    	cf...... = .....;
    	cf...... = .....;
    	if(ChooseFont(&cf) != 0){
    		return TRUE;
    	}else{
    		return FALSE;
    	}
    }
    


  • Jepp, Danke für das Beispiel nochmal - nach dem Schema sind auch alle anderen Beispiele im Netz aufgebaut, welche ohne dll auskommen - aber der Compiler des BDS2006 mag es irgendwie nicht oder ich mache irgendeinen dummen Fehler. Hier der aktuelle Aufbau in meinem Programm (die Problemstelle ist markiert, die Fehlermeldungen poste ich unter dem Quelltext):

    Main.h:

    class TFormMain : public TForm
    {
    __published:
        //...
    private:
        //...
        UINT_PTR CALLBACK DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd);
    public:
        //...
    };
    

    Main.cpp:

    void __fastcall TFormMain::MenuLoadFileClick(TObject *Sender)
    {
        //...
        char FullFileName[1024];
        OPENFILENAME OpnDlg;
        OpnDlg.lStructSize= sizeof(OpnDlg);
        OpnDlg.hwndOwner= FrmMain->Handle;
        OpnDlg.hInstance= NULL;                                // Handle zu Dialogbox-Template -> ungenutzt, wenn entsprechende Flags nicht gesetzt sind
        OpnDlg.lpstrFilter= "Textdateien (*.txt)\0*.txt\0\0";
        OpnDlg.lpstrCustomFilter= NULL;                        // 2 Strings -> voreingestellter & zuletzt gewählter Filter
        OpnDlg.nMaxCustFilter= NULL;                           // ignored if lpstrCustomFilter is NULL
        OpnDlg.nFilterIndex= 0;                                // if zero and lpstrCustomFilter is NULL, first filter in lpstrFilter is used
        OpnDlg.lpstrFile= &FullFileName[0];
        OpnDlg.nMaxFile= 1024;
        OpnDlg.lpstrFileTitle= NULL;                           // Puffer für Dateinamen ohne Pfad
        OpnDlg.nMaxFileTitle= NULL;                            // ignored if lpstrFileTitle is NULL
        OpnDlg.lpstrInitialDir= &(ExtractFilePath(Application->ExeName) + "Daten")[0];
        OpnDlg.lpstrTitle= "Textdatei öffnen";
        OpnDlg.Flags= OFN_DONTADDTORECENT | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON;
        OpnDlg.nFileOffset= NULL;
        OpnDlg.nFileExtension= NULL;
        OpnDlg.lpstrDefExt= "txt";
        OpnDlg.lCustData= NULL;
        OpnDlg.lpfnHook= (LPOFNHOOKPROC)DlgHook;               // => Fehler 1
        OpnDlg.lpfnHook= (LPOFNHOOKPROC)&DlgHook;              // => Fehler 2
        OpnDlg.lpTemplateName= NULL;
        OpnDlg.pvReserved= NULL
        OpnDlg.dwReserved= 0;
        OpnDlg.FlagsEx= NULL;
        //...
    }
    
    //...
    
    UINT_PTR CALLBACK TFormMain::DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd)
    {
    	return(0);
    }
    //------------------------------------------------------------------------------
    

    Fehler 1 schrieb:

    [C++ Fehler] Main.cpp(465): E2235 Elementfunktion muß aufgerufen oder ihre Adresse übernommen werden

    Fehler 2 schrieb:

    [C++ Fehler] Main.cpp(465): E2031 Typumwandlung von 'unsigned int (__stdcall * (_closure )(void *,unsigned int,unsigned int,long))(void *,unsigned int,unsigned int,long)' nach 'unsigned int (__stdcall *)(void *,unsigned int,unsigned int,long)' nicht zulässig

    Das ganze habe ich auch mit einem Funktionszeiger als Zwischenschritt versucht:

    Main.h:

    class TFormMain : public TForm
    {
    __published:
        //...
    private:
        //...
        UINT_PTR CALLBACK DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd);
        UINT_PTR CALLBACK (*pDlgHook)(HWND, UINT, WPARAM, LPARAM);                // Funktionszeiger
    public:
        //...
    };
    

    Main.cpp:

    void __fastcall TFormMain::FormShow(TObject *Sender)
    {
        //...
        pDlgHook= DlgHook;             // => Fehler 1
        pDlgHook= &DlgHook;            // => Fehler 2
        //...
    }
    
    //...
    
    void __fastcall TFormMain::MenuLoadFileClick(TObject *Sender)
    {
        //...
        char FullFileName[1024];
        OPENFILENAME OpnDlg;
        OpnDlg.lStructSize= sizeof(OpnDlg);
        OpnDlg.hwndOwner= FrmMain->Handle;
        OpnDlg.hInstance= NULL;                                // Handle zu Dialogbox-Template -> ungenutzt, wenn entsprechende Flags nicht gesetzt sind
        OpnDlg.lpstrFilter= "Textdateien (*.txt)\0*.txt\0\0";
        OpnDlg.lpstrCustomFilter= NULL;                        // 2 Strings -> voreingestellter & zuletzt gewählter Filter
        OpnDlg.nMaxCustFilter= NULL;                           // ignored if lpstrCustomFilter is NULL
        OpnDlg.nFilterIndex= 0;                                // if zero and lpstrCustomFilter is NULL, first filter in lpstrFilter is used
        OpnDlg.lpstrFile= &FullFileName[0];
        OpnDlg.nMaxFile= 1024;
        OpnDlg.lpstrFileTitle= NULL;                           // Puffer für Dateinamen ohne Pfad
        OpnDlg.nMaxFileTitle= NULL;                            // ignored if lpstrFileTitle is NULL
        OpnDlg.lpstrInitialDir= &(ExtractFilePath(Application->ExeName) + "Daten")[0];
        OpnDlg.lpstrTitle= "Textdatei öffnen";
        OpnDlg.Flags= OFN_DONTADDTORECENT | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON;
        OpnDlg.nFileOffset= NULL;
        OpnDlg.nFileExtension= NULL;
        OpnDlg.lpstrDefExt= "txt";
        OpnDlg.lCustData= NULL;
        OpnDlg.lpfnHook= (LPOFNHOOKPROC)pDlgHook;
        OpnDlg.lpTemplateName= NULL;
        OpnDlg.pvReserved= NULL
        OpnDlg.dwReserved= 0;
        OpnDlg.FlagsEx= NULL;
        //...
    }
    
    //...
    
    UINT_PTR CALLBACK TFormMain::DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd)
    {
    	return(0);
    }
    //------------------------------------------------------------------------------
    

    Die Compilerfehler bleiben erhalten... Ich denke ich mache irgendwas Grundlegendes falsch, aber wenn im geposteten Quelltext Nichts zu erkennen ist, dann weiß ich auch nicht!
    Vielleicht fällt Jemandem noch Etwas auf!?

    MfG



  • Hm warum setzt du es nicht einfach so um wie ich es dir gepostet habe?



  • Du mußt natürlich für die Hook-Funktionen eine freie Funktion bzw. statische Klassenmethode benutzen...



  • ...... schrieb:

    Hm warum setzt du es nicht einfach so um wie ich es dir gepostet habe?

    Ist nicht persönlich gemeint - ich habe einfach den Gesamtzusammenhang an deinem Beispiel nicht gesehen und den Hook nach meinem Verständnis in meine Anwendung implementiert. 😉

    Th69 schrieb:

    Du mußt natürlich für die Hook-Funktionen eine freie Funktion bzw. statische Klassenmethode benutzen...

    Super, mit static funktionierts, Danke! 👍

    Allerdings ist mir der Zusammenhang nicht ganz klar... wikibooks schreibt (Link):

    Da statische Elemente unabhängig von Instanzen ihrer Klasse sind, existieren sie während der gesamtem Programmlaufzeit. Sie verhalten sich also wie globale Variablen, sind aber (sofern private) nur innerhalb der Klasse sichtbar.

    Warum muss OFNHookProc statisch sein? 😕

    Und da sind wir schon beim nächsten Problem:

    UINT_PTR CALLBACK TFormMain::DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd)
    {
    	if(Msg != WM_INITDIALOG) return(0);
    	PostMessage(hDlg, CDM_HIDECONTROL, 0x0471, NULL);
    	PostMessage(hDlg, CDM_HIDECONTROL, 0x0443, NULL);
    	HANDLE hToolBar= FindWindowEx(hDlg, NULL, "ToolbarWindow32", NULL);
    	if(hToolBar){
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    	}
    	return(0);
    }
    //------------------------------------------------------------------------------
    

    Die Hook wird zwar angesprungen und der Inhalt auch vor dem Anzeigen des Dialog exakt einmal (wenn WM_INITDIALOG in Msg steht) durchlaufen (wobei hToolBar leider NULL bleibt?!), aber die angegebenen Controls werden nicht ausgeblendet / gelöscht... Muss ich an dieser Stelle die Nachrichten an den Dialog auf andere Art versenden?

    MfG

    Edit: Hm... also wenn ich Alles richtig gelesen habe, muss ich auf CDN_INITDONE warten, welches in Form einer WM_NOTIFY kommt...

    Edit2: Den richtigen Zeitpunkt um die ComboBox-Liste zu löschen habe ich scheinbar schon gefunden:

    UINT_PTR CALLBACK TFormMain::DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd)
    {
        if(Msg != WM_NOTIFY) return(0);
        NMHEADER* pNMHDR= (NMHEADER*)(lAdd);
        if(pNMHDR->hdr.code != CDN_INITDONE) return(0);
        //...
    }
    

    Jetzt muss ich "nur noch" die passenden Nachrichten an die ComboBox senden, damit die zuletzt verwendeten Dateien nicht mehr zu sehen sind. Und ich muss herausfinden, warum die Controls zum Verzeichniswechsel nicht mehr ausgeblendet / gelöscht werden... Das hatte ich vorher in der OnShow()-(Borland-)Eventmethode mit

    void __fastcall TFormMain::DlgShow(TObject *Sender)
    {
    	HANDLE hDlg= GetParent(Sender == OpenDlg ? OpenDlg->Handle : SaveDlg->Handle);
    	PostMessage(hDlg, CDM_HIDECONTROL, 0x0471, NULL);
    	PostMessage(hDlg, CDM_HIDECONTROL, 0x0443, NULL);
    	HANDLE hToolBar= FindWindowEx(hDlg, NULL, "ToolbarWindow32", NULL);
    	if(hToolBar){
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    		SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
    	}
    }
    

    erreicht, aber den VCL-Standarddialog nutze ich ja nun nichtmehr und habe folglich auch die Borland-Eventmethode nichtmehr zur Verfügung... Das Ausblenden / Löschen kann ich doch hoffentlich auch in der Hook-Prozedur erledigen!?!

    **Edit3:**Komisch, das Handle hat scheinbar nicht gestimmt. Ich habe jetzt die Nachrichten nach erhaltenem CDN_INITDONE an ((LPNMHDR)lParam)->hwndFrom geschickt und plötzlich werden die Controls ausgeblendet (wie vorher im OnShow()). Das verwundert mich deshalb, weil in der Hilfe zu OFNHookProc steht:

    hdlg
    [in] Handle to the child dialog box of the Open or Save As dialog box.

    Das hätte doch eigentlich schon das korrekte Handle sein müssen, oder?
    Jetzt noch die Liste zuletzt verwendeter Dateien leeren...



  • Ohhh nein... viel gelernt, aber Alles umsonst 😞 😞 😞

    UINT_PTR CALLBACK TFormMain::DlgHook(HWND hDlg, UINT Msg, WPARAM wAdd, LPARAM lAdd)
    {
        if(Msg != WM_NOTIFY || ((LPNMHDR)lAdd)->code != CDN_INITDONE) return(0);        // Hook-Prozedur verlassen, wenn es sich nicht um CDN_INITDONE handelt
        HANDLE hChildDialog= ((LPNMHDR)lAdd)->hwndFrom;
        SendMessage(hChildDialog, CDM_HIDECONTROL, 0x0471, NULL);
        SendMessage(hChildDialog, CDM_HIDECONTROL, 0x0443, NULL);
        HANDLE hToolBar= FindWindowEx(hChildDialog, NULL, "ToolbarWindow32", NULL);
        if(hToolBar == NULL) return(0);
        SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
        SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
        SendMessage(hToolBar, TB_DELETEBUTTON, 0, NULL);
        HANDLE hFileCmb= GetDlgItem(hChildDialog, 0x047C);
        if(hFileCmb == NULL) return(0);
        SendMessage(hFileCmb, CB_SHOWDROPDOWN, true, NULL);                             // <= 
        while(SendMessage(hFileCmb, CB_DELETESTRING, 0, NULL) > 0) ;
        return(0);
    }
    //------------------------------------------------------------------------------
    

    Die Einträge in der Liste der zuletzt verwendeten Dateien in der Dateiauswahl-Combobox des Dialog bleibt erhalten!!! Um mit CB_DELETESTRING überhaupt weiter zu kommen muss ich die ComboBox-Liste downdroppen, was zur Laufzeit unschöner Weise für den Anwender sichbar ist... Tja, und wenn die CB_DELETESTRING-Schleife durch ist, sind trotzdem noch die zuletzt verwendeten Dateien in der Liste...! F**K! *kopfschüttel*
    So, nun vermute ich, dass die besagte Liste in einer Art "OnBeforeDropDown"-Ereignis gefüllt wird... kann das sein? wie könnte ich da herankommen???

    Danke an Alle, die meine Romane bis hierhin lesen! 😉

    Edit1: Ich versuche mir mal mittels CB_GETCOMBOBOXINFO ein Handle der Liste zu holen... vielleicht habe ich damit mehr Erfolg...

    Edit2: Ok, von der Liste bekomme ich kein Handle...

    COMBOBOXINFO CmbInfStruct;
    //F SendMessage(hFileCmb, CB_GETCOMBOBOXINFO, NULL, (LPARAM)&CmbInfStruct);		// [C++ Fehler] Main.cpp(2033): E2451 Undefiniertes Symbol 'CB_GETCOMBOBOXINFO'
        GetComboBoxInfo(hFileCmb, &CmbInfStruct);
        HANDLE hDamnList= CmbInfStruct.hwndList;
        if(hDamnList == NULL) return(0);                                               // <= hier wird die Hook-Prozedur verlassen
        SendMessage(hDamnList, LB_RESETCONTENT, NULL, NULL);
    

    Nun habe ich gesehen, dass in COMBOBOXINFO die Koordinaten des DropDown-Button stehen... vielleicht kann ich den ja einfach deaktivieren...!?! 🕶



  • Also, Frage in aller Kürze: Ich möchte die Liste zuletzt geöffneter Dateien in der "Dateiname"-ComboBox eines "Datei öffnen"-Standarddialog leeren oder den entsprechenden DropDown-Button deaktivieren / ausblenden. Ich habe habe zur Verfügung:

    * eine lokale Hook-Prozedur für den Standarddialog
    * das Handle der entsprechenden ComboBox
    * die Koordinaten des DropDown-Button

    Ich kann nicht:

    * die DropDown-Liste mittels CB_RESETCONTENT / LBRESETCONTENT / CB_DELETESTRING / LB_DELETESTRING leeren
    * ein Handle der DropDown-Liste bekommen
    * ein Handle des DropDown-Button bekommen

    Was kann ich tun um mein Ziel zu erreichen?

    MfG



  • Es ist einfach zum Mäuse melken 😡

    Ich habe ein Handle auf das Edit und ein Handle auf die ComboBox:

    HANDLE hFileCB= GetDlgItem(hDlg, 0x047C);
    HANDLE hCmb= (HWND)SendMessage(hFileCB, CBEM_GETCOMBOCONTROL, NULL, NULL);
    if(hCmb)
      SendMessage(hCmb, CB_SHOWDROPDOWN, true, NULL);
    HANDLE hEdit= (HWND)SendMessage(hFileCB, CBEM_GETEDITCONTROL, NULL, NULL);
    if(hEdit)
      SetWindowText(hEdit, "Test");
    

    Ergebnis: Die ComboBox wird sichtbar ausgeklappt und im Edit steht "Test"!

    1.) Warum ist es nicht möglich die ComboBox mit CB_RESETCONTENT komplett zu leeren?

    2.) Wird die ComboBox (Liste zuletzt verwendeter Dateien) jedes Mal beim Ausklappen neu gefüllt? Wie kann ich das verhindern?

    3.) Wie kann ich ein Handle auf den DropDown-Button der ComboBox bekommen / ihn ausblenden / ihn deaktivieren?

    Irgendeine Möglichkeit MUSS es doch geben!? Wer kann helfen?



  • Ich weiss ja nicht zu welcher Zeit der Inhalt der Combobox gefüllt wird. Aber fang doch mal den CBN_DROPDOWN Event ab und lösche die Items genau dann.



  • CBN_DROPDOWN kann ich laut MSDN nicht mit dem Hook abfangen 😞 Das ist ja das Dilemma: mit dem Hook der von MS für OPENFILENAME vorgesehen ist kann ich rein garnichts anfangen! Alles was ich dort machen / abfangen kann, kann ich auch anderweitig erledigen. Auch Mausklicks im Dialog oder einer untergeordneten Komponente werden nicht durch den Hook gereicht, sondern umgehen ihn. Je länger ich mich damit beschäftige, desto mehr empfinde ich den Hook als Programmiererver*rsche!


Anmelden zum Antworten