Datei öffnen Dialog



  • Moin,

    ich habe eigentlich keine Ahnung in Sachen
    WinAPI-Programmierung, wollte aber ganz gerne
    eine oder mehrere WinAPI-Funktionen nutzen.

    Ich habe eine Konsolenanwendung geschrieben, in
    der man den Pfad zu einer Datei angeben muss.
    Das ist ein bisschen unpraktisch, da manche
    Leute keine Ahnung haben, wie man einen Pfad angibt.

    Von daher wollte ich fragen, ob es machbar wäre in
    das Programm den "normalen" 'Datei öffnen'-Dialog von Windows
    einzubauen, der mir dann den Pfad/einen Zeiger
    auf die ausgewählte Datei zurückgibt.

    Das ganze sollte in C sein.
    Wäre nett, wenn ihr einen Beispielcode
    (oder einen fertigen Code 🙂 ) dazuschreibt.
    Sollte das genze zu schwierig für einen 'Anfänger'
    sein, dann teilt mir doch das bitte mit.

    Danke für Antworten!



  • in
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-172024-and-highlight-is-openfilename.html
    steht:

    OPENFILENAME ofn;
    
    int show_dialog(char *title, BYTE *lastpath)
    {
        LPTSTR lpstrFile = (TCHAR*)malloc((unsigned int)PATHBUFSIZ);
        *lpstrFile = 0;
    
        ofn.lStructSize = sizeof(ofn);
        ofn.lpstrFilter = NULL; //"All files (*.*)|*.*";
        ofn.hInstance = NULL;
        ofn.hwndOwner = NULL;
        ofn.lpstrCustomFilter = NULL;
        ofn.nMaxCustFilter = 0;
        ofn.lpstrFile = lpstrFile;
        ofn.nMaxFile = PATHBUFSIZ;
        ofn.lpstrFileTitle = NULL;
        ofn.nMaxFileTitle = 0;
        ofn.lpstrInitialDir = (char*)lastpath;
        ofn.lpstrTitle = title;
        ofn.nFileExtension = 0;
        ofn.Flags = OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_FILEMUSTEXIST |
            OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_EXPLORER;
        ofn.lpstrDefExt = NULL;
        return GetOpenFileName(&ofn);
    }
    


  • Der Code ist aber nicht sehr schön: ANSI/ASCII <-> UNICODE ... *angst* 😃 .



  • Moin,

    ich habe jetzt erst mal folgendes daraus gemacht:

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <windows.h> 
    #include <commdlg.h> 
    #include <malloc.h> 
    #include <conio.h>
    
    #define PATHBUFSIZ (1024*1024*8) 
    #define LASTBUFSIZ (1024) 
    #define KEYNAME "Software\\WasAuchImmer" 
    #define VALNAME "LastPath" 
    #define TITLE (argv[0]) 
    
    // man schimpfe nicht: 
    OPENFILENAME ofn; 
    int helpreq = 0; 
    char *title = NULL; 
    
    int show_dialog(char *title, BYTE *lastpath) 
    { 
        LPTSTR lpstrFile = (TCHAR*)malloc((unsigned int)PATHBUFSIZ); 
        *lpstrFile = 0; 
    
        ofn.lStructSize = sizeof(ofn); 
        ofn.lpstrFilter = NULL; //"All files (*.*)|*.*"; 
        ofn.hInstance = NULL; 
        ofn.hwndOwner = NULL; 
        ofn.lpstrCustomFilter = NULL; 
        ofn.nMaxCustFilter = 0; 
        ofn.lpstrFile = lpstrFile; 
        ofn.nMaxFile = PATHBUFSIZ; 
        ofn.lpstrFileTitle = NULL; 
        ofn.nMaxFileTitle = 0; 
        ofn.lpstrInitialDir = (char*)lastpath; 
        ofn.lpstrTitle = title; 
        ofn.nFileExtension = 0; 
        ofn.Flags = OFN_ALLOWMULTISELECT /*| OFN_ENABLESIZING*/ | OFN_FILEMUSTEXIST | 
            OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_EXPLORER; 
        ofn.lpstrDefExt = NULL; 
        return GetOpenFileName(&ofn); 
    }
    
    int main(int argc, char *argv[])
    {
     int k;
     char path[1024];
     char *pt;
    
     strcpy(path,argv[0]);
     for(pt = &path[0]; *pt != '\0'; pt++);
     for(;*pt != '\\'; pt--)
       *pt = '\0';
    
     k = show_dialog("Öffnen",path);
    
     printf("%i", k);
    
     getch();
     return 0;
    }
    

    Dazu sei gesagt, dass ich nicht weiß, welche header wirklich
    includiert werden müssen 🙂 und, dass es

    OFN_ENABLESIZING
    

    bei mir nicht gibt.
    Auch klappt das mit dem Rückgabewert irgedwie nicht so ganz.
    (Immer NULL/0)
    Könnt ihr mir da noch mal irgenwie helfen?

    Danke!



  • Der Code ist aber nicht sehr schön

    Ich hab' ihn auch schnell zusammengeschustert. Er gibt sicher kein Beispiel für guten Stil ab, geschweige denn für eine vernünftige Speicherverwaltung.
    Ich dachte, dass die Sache mit den Zeichensätzen klar ist, man kann ja, wenn
    man sich fürchtet, einstweilen auf GetOpenFileNameA zurückgreifen.

    dass es OFN_ENABLESIZING bei mir nicht gibt.

    Bei VC 6 (gerade nichts neueres zur Hand) steht in COMMDLG.H in Zeile 162:

    #define OFN_ENABLESIZING             0x00800000
    

    In show_dialog habe ich lastpath deklariert als BYTE*, das kam vom Registry-Lesen und wurde (ugly, ugly) in show_dialog gecastet. Gleich char* zu nehmen, wäre hier besser. lastpath war gedacht, um dem Dialog den zuletzt geöffneten Ordner zu sagen, den ich in der Registry gespeichert hatte. Du kannst lastpath getrost entfernen, bzw auf NULL setzen. VALNAME und KEYNAME brauchst du dann auch nicht.

    Nachdem ich lastpath zu einem char* gemacht habe, kann ich deinen Code mit VC 6 und mit Dev-C++ 4.9.9.2 übersetzen. Es läuft dann auch gut.

    Auch klappt das mit dem Rückgabewert irgedwie nicht so ganz.
    (Immer NULL/0)

    0, nicht NULL! GetOpenFileName liefert BOOL zurück, das ist ganz sicher kein Zeiger.
    Der Rückgabewert 0 soll heißen, dass du auf "Abbrechen" gedrückt hast. Das hast du doch, oder? Wenn du "Öffnen" drückst, kommt 1 zurück; und nur in diesem Fall hast du eine vernünftige Eingabe zu erwarten.

    Fragen? MSDN

    PS: (wichtig!)
    Deiner Funktion main fehlt am Ende ein free(ofn.lpstrFile), sonst verlierst du bei jedem Aufruf 8M Speicher 😉 . Wie gesagt, das ganze ist ein wenig ugly.



  • cheopz schrieb:

    Deiner Funktion main fehlt am Ende ein free(ofn.lpstrFile), sonst verlierst du bei jedem Aufruf 8M Speicher 😉 . Wie gesagt, das ganze ist ein wenig ugly.

    Natürlich ist die Allozierung eines Speicherbereichs, dessen Grösse von Anfang an feststeht, das dümmste, was man sich nur vorstellen kann. Ich hatte malloc benutzt, weil ich vorhatte, das Ganze weiter auszubauen. Zu den Problemen mit der Puffergröße bei OFN_ALLOWMULTISELECT siehe die Forumsuche. So, wie es jetzt ist, sollte die Zeile 21 besser lauten:

    TCHAR lpstrFile[PATHBUFSIZ];
    

    Damit werden die Speicherlecks auch ohne Aufruf von free() ausbleiben.



  • Also, wenn ich

    #define OFN_ENABLESIZING             0x00800000
    

    mit im Quelltext einfüge, dann funktioniert das alles sogar 😉 .
    Wenn ich Zeile 21 austausche, dann geht gar nix mehr.... 😞

    Wofür genau ist denn der 8MB Buffer? (Wie gesagt, ich hab
    keine Ahnung von WinAPI)
    Wie bekomme ich denn nun den Zeiger auf die
    ausgewählte Datei, damit ich diese mit fopen() öffnen kann?
    Und wie kann ich die Dateitypen auf "Textdateien *.txt", sowie
    "Alle Dateien ." setzen?

    Danke aber noch mal für die Antworten.



  • jzd schrieb:

    Wofür genau ist denn der 8MB Buffer? (Wie gesagt, ich hab
    keine Ahnung von WinAPI)

    Der ist für die Pfade der ausgewählten Dateien. Er ist so gross, weil der Benutzer beliebig viele Dateien auswählen kann, wenn OFN_ALLOWMUTLISELECT gesetzt ist. Wenn du nur einzelne Dateien auswählen willst, hast du ein großes Problem weniger, dann kannst du PATHBUFSIZ auf 512 (besser MAXPATH, oder wie das auch immer heißt) setzen.

    jzd schrieb:

    Wenn ich Zeile 21 austausche, dann geht gar nix mehr....

    Hmm 😕 Wie äußert sich das? Im Zweifel lass es halt bleiben, aber dann musst du höllisch darauf achten, dass auch free() aufgerufen wird.
    (Ich kann's von hier aus leider nicht testen)

    jzd schrieb:

    Wie bekomme ich denn nun den Zeiger auf die
    ausgewählte Datei, damit ich diese mit fopen() öffnen kann?

    Der Pfad steht im Puffer (ofn.ptstrFile). Wenn FN_ALLOWMUTLISELECT gesetzt ist, ist das ein bißchen komplizierter.

    jzd schrieb:

    Und wie kann ich die Dateitypen auf "Textdateien *.txt", sowie
    "Alle Dateien ." setzen?

    Das geht mit ofn.lpstrFilter. Genaueres -> MSDN Stichwort OPENFILENAME
    Ich gebe dir also den Link, aber du verpflichtest dich, das nächste mal selbst zu suchen, oder? 😉
    http://msdn2.microsoft.com/en-us/library/ms646839.aspx



  • Also ich würd mal anfangen TCHAR, TEXT, etc Kohorten zu verwenden. Des weiteren empfiehlt sich hier die Verwendung von std::string/std::wstring, wenn verfügbar (C++).



  • Des weiteren empfiehlt sich hier die Verwendung von std::string/std::wstring, wenn verfügbar (C++).

    Warten wir einmal ab, ob er überhaupt Multiselect braucht. Wenn nein, ist's nämlich ganz egal.

    Also ich würd mal anfangen TCHAR, TEXT, etc Kohorten zu verwenden.

    Das ist natürlich eine gute Idee.

    Ich fühle mich da ein wenig verantwortlich, weil ich den mittelmäßigen Code, auf den der Unreg verweist, gepostet habe.
    Du müsstest aber auch nicht so wortkarg sein...



  • cheopz schrieb:

    Anstatt zu meckern, könntest du auch helfen!

    ROFL, ist doch nur ersetzen...also gut, voila:

    #include <windows.h>
    // ggfs. noch:
    // #include <commdlg.h>
    #include <string>
    #ifdef UNICODE
      #define cString std::wstring
    #else
      #define cString std::string
    #endif // UNICODE
    
    bool CreateOpenDlg(HWND hWndParent, cString& strFileName, const cString& strCaption = TEXT(""))
    {
        OPENFILENAME ofnOpen;
        const unsigned int BUFFER_SIZE = 1024 * 1024 * 8;// Zeichenanzahl, nicht Bytes!
        // Dieser Wert sollte nicht statisch allokiert werden, da es sonst zum Stack-Overflow kommen kann.
        TCHAR* pszOpenFile = new TCHAR[BUFFER_SIZE];
        *pszOpenFile = 0;
    
        ofnOpen.lStructSize        = sizeof(ofnOpen);
        ofnOpen.lpstrFilter        = TEXT("Alle Dateien\0*.*\0\0"); // ggfs. anpassen
        ofnOpen.hInstance          = NULL;
        ofnOpen.hwndOwner          = hWndParent;
        ofnOpen.lpstrCustomFilter  = NULL;
        ofnOpen.nMaxCustFilter     = 0;
        ofnOpen.lpstrFile          = pszOpenFile;
        ofnOpen.nMaxFile           = BUFFER_SIZE];
        ofnOpen.lpstrFileTitle     = NULL;
        ofnOpen.nMaxFileTitle      = 0;
        if(strFileName.empty)
           ofnOpen.lpstrInitialDir = NULL;
        else
           ofnOpen.lpstrInitialDir = strFileName.c_str();
        ofnOpen.lpstrTitle         = strCaption.c_str();
        ofnOpen.nFileExtension     = 0;
        ofnOpen.Flags              = OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_FILEMUSTEXIST |
                                     OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_EXPLORER; // ggfs. anpassen
        ofnOpen.lpstrDefExt        = NULL; // ggfs. anpassen
        BOOL bRet = GetOpenFileName(&ofnOpen);
        if(!bRet)
        {
           delete [] pszOpenFile;
           return (false);
        }
        // Hier wäre ein parsen in ein std::vector mit cString sinnvoll, damit die einzelnen Dateien
        // später leichter verarbeitet werden können... .
        strFileName = pszOpenFile;
        delete [] pszOpenFile;
        return (true);
    }
    

    Ein Beispielaufruf:

    cString strName(TEXT("C:\\")); // Startverzeichnis C:
    if(CreateOpenDlg(hWnd, strName))
       MessageBox(hWnd, strName.c_str(), TEXT("Ihre ausgewählte(!) Datei:"), MB_OK);
    


  • Klappt jetzt alles prima.
    Danke noch mal für die viele Hilfe!
    Nur mit C++ kann ich nicht wirklich viel anfangen 🙂
    (fragt euch jetzt aber bitte nicht, womit der denn
    überhaupt etwas anfangen kann; ich lerne C erst
    seit ein paar wenigen Monaten, nebenbei noch Schule, AG,
    Computerspiele ... 😃 )

    Multiselect brauch ich übrigens nicht.
    Und ich glaub, dass ist auch ganz gut so...



  • CodeFinder schrieb:

    Anstatt zu meckern, könntest du auch helfen!

    Das hatte ich gesagt, aber kurz danach wieder editiert, weil's gar unfreundlich ist. Ich habe dann nämlich ein paar deiner letzten Beiträge überfolgen und erkannt, dass du viel mehr hilfst als ich. Ich habe gehofft, dass es keiner gelesen hat. Sorry!

    jzd schrieb:

    Klappt jetzt alles prima.
    Danke noch mal für die viele Hilfe!

    Freut mich. Jederzeit gerne.


Anmelden zum Antworten