CDocument aufräumen und Messagebox ausgeben können



  • Hallo,

    ich muss beim Beenden eines Programms ein paar Aufräumarbeiten durchführen. Sowas packt man üblicherweise ja auch in den Ctor. Mein Problem ist jetzt, dass diese Strukturen im Fehlerfall noch Meldungsfenster generieren. Wenn die View schon weg ist, dann gibts einen Crash. Wo packt man das am besten hin? Hab auch schon versucht im View WM_CLOSE abzufangen, jedoch wird das nicht angesprungen. Bin über jeden Hinweis dankbar.



  • Du meinst D(estruc)tor?!

    Es gibt doch onclosedocument.



  • @Th69
    Sorry, ich hab mich verschrieben. Sollte Dtor also Destructor heißen. In OnCloseDocument kann ich keine Messagebox mehr aufrufen, weil es das Hauptfenster schon nicht mehr gibt. Das wirft eine Exception.



  • Eine MessageBox sollte jederzeit aufrufbar sein (evtl. sonst einfach NULL als HWND übergeben, d.h. keinen Owner).



  • Ich hab das jetzt mal in CMainFrame::OnDestroy() platziert. Da klappt das. Weiß aber nicht ob das der richtige Platz dafür ist



  • Um was für Arten von Fehlern handelt es sich denn? Also in welchen Fällen willst du so eine Message-Box anzeigen?



  • Wir haben das Dokumentensystem umgestellt. Vorher wurden Dateien von einer Netzwerkfreigabe geöffnet. Die wurde derart umgestellt, dass die Dateien jetzt in einer Art Cloud liegen und man nur mit einem Link (https) drauf zugreifen kann. Das Programm läd jetzt in ein temporäres Verzeichnis diese Dateien. Beim Beenden des Programms muss ich dieses Verzeichnis wieder löschen bzw. leeren. Diese Routine signalisiert über Messageboxen, wenn z.B. eine Datei nicht gelöscht werden kann weil sie warum auch immer z.B. noch geöffnet ist. Weder AfxMessageBox noch MessageBox funktionieren, egal ob ich NULL übergebe oder nicht. Es wird direkt nach dem Aufruf der Messagebox eine Exception geworfen. Nehme ich den Aufruf in einen Bereich rein wo das Fenster noch nicht zerstört wird dann geht das komischerweise. Sonst dachte ich auch immer das man Messageboxen immer anzeigen kann.



  • Was für eine Exception wird denn da geworfen?
    Und zeige mal den Code.



  • So, ich konnte jetzt feststellen, dass es eine CFileException in einer DLL war, die ich im Programm benutzen muss. Das ist fremder Code, den ich nicht kenne. Vermutlich bewirkt der Aufruf das Löschen einer Datei, die schon gelöscht ist. Jetzt hab ich mal testweise in den Konstruktor eine Messagebox gesetzt:

    CMyAppDoc::~CMyAppDoc()
    {
    	CMyAppDoc::Speichere_Daten(Datendatei);
    	AfxMessageBox(_T("Test"));
    }
    

    Diese wird nicht aufgerufen.



  • Naja... die einfachste und beste Lösung wäre vermutlich das Löschen im WM_CLOSE Handler des top-level Fensters zu machen. Dort kannst du noch ganz normal die MessageBox anzeigen - und wenn du willst sogar das Schliessen des Fensters verhindern (indem du die WM_CLOSE Message ignorierst).

    Alternativ könntest du einen Hilfs-Thread starten der die Message-Box anzeigt, und dann auf die Beendigung des Hilfs-Threads warten. Das geht wirklich überall 🙂



  • @hustbaer sagte in CDocument aufräumen und Messagebox ausgeben können:

    Naja... die einfachste und beste Lösung wäre vermutlich das Löschen im WM_CLOSE Handler des top-level Fensters zu machen. Dort kannst du noch ganz normal die MessageBox anzeigen - und wenn du willst sogar das Schliessen des Fensters verhindern (indem du die WM_CLOSE Message ignorierst).

    Alternativ könntest du einen Hilfs-Thread starten der die Message-Box anzeigt, und dann auf die Beendigung des Hilfs-Threads warten. Das geht wirklich überall 🙂

    Der erste Ansatz war das, was mir auch gleich eingefallen ist. Hat nicht funktioniert. Gleiches Problem. Ich denke aber ich hab das Problem einkreisen können. Die Routinen, die ich für das Aufräumen brauche, kommen aus einer MFC-Erweiterungs-DLL. Das Problem scheint zu sein, dass zum Zeitpunkt des Aufrufs die DLL schon entladen ist und es deshalb knallt. Warum das aber jetzt in dem Beispiel von vorhin nicht klappt weiß ich auch nicht. Das klappt aber grundsätzlich nicht.



  • Wie, gleiches Problem? Du meinst du kannst in WM_CLOSE keine MessageBox anzeigen? Doch, das geht. Sicher. Ganz sicher.

    EDIT: Nur sicherheitshalber, du musst natürlich zwei Dinge beachten:

    1. Du musst ON_WM_CLOSE() in der Message-Map haben
    BEGIN_MESSAGE_MAP(CMyWindow, CWnd)
        //{{AFX_MSG_MAP(CMyWindow)
        ON_WM_CLOSE()
        //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    
    1. Du solltest in deiner CMyWindow::OnClose Funktion die MessageBox anzeigen bevor du die OnClose Funktion der Basisklasse aufrufst.

    /EDIT

    Die Routinen, die ich für das Aufräumen brauche, kommen aus einer MFC-Erweiterungs-DLL. Das Problem scheint zu sein, dass zum Zeitpunkt des Aufrufs die DLL schon entladen ist und es deshalb knallt.

    Wieso sollte in WM_CLOSE des top-level Fensters schon etwas entladen sein? Verstehe ich nicht.



  • @hustbaer sagte in CDocument aufräumen und Messagebox ausgeben können:

    1. Du solltest in deiner CMyWindow::OnClose Funktion die MessageBox anzeigen bevor du die OnClose Funktion der Basisklasse aufrufst.
      Die Routinen, die ich für das Aufräumen brauche, kommen aus einer MFC-Erweiterungs-DLL. Das Problem scheint zu sein, dass zum Zeitpunkt des Aufrufs die DLL schon entladen ist und es deshalb knallt.

    Wieso sollte in WM_CLOSE des top-level Fensters schon etwas entladen sein? Verstehe ich nicht.

    Das erste ist klar und das zweite verstehe ich auch nicht. Folgendes hab ich jetzt und es wird nicht angesprungen:

    BEGIN_MESSAGE_MAP(CMFCApplication12View, CFormView)
    	ON_WM_CONTEXTMENU()
    	ON_WM_RBUTTONUP()
    	ON_WM_CLOSE()
    END_MESSAGE_MAP()
    
    void CMFCApplication12View::OnClose()
    {
    	// TODO: Fügen Sie hier Ihren Meldungsbehandlungscode ein, und/oder benutzen Sie den Standard.
    	AfxMessageBox(_T("Test"));
    	CFormView::OnClose();
    }
    
    

    Wenn ich einen Breakpoint in die OnClose setzte wird das nicht angesprungen. Hingegen:

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
    	ON_WM_CREATE()
    	ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnApplicationLook)
    	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnUpdateApplicationLook)
    	ON_COMMAND(ID_VIEW_CAPTION_BAR, &CMainFrame::OnViewCaptionBar)
    	ON_UPDATE_COMMAND_UI(ID_VIEW_CAPTION_BAR, &CMainFrame::OnUpdateViewCaptionBar)
    	ON_COMMAND(ID_TOOLS_OPTIONS, &CMainFrame::OnOptions)
    	ON_WM_CLOSE()
    END_MESSAGE_MAP()
    
    void CMainFrame::OnClose()
    {
    	// TODO: Fügen Sie hier Ihren Meldungsbehandlungscode ein, und/oder benutzen Sie den Standard.
    	AfxMessageBox(_T("Test"));
    	CFrameWndEx::OnClose();
    }
    

    funktioniert.



  • Dass es in der View nicht geht ist klar, da die View kein WM_CLOSE bekommt. Die WM_CLOSE Message teilt dem top-level Fenster mit dass jemand es bitte gerne schliessen möchte. z.B. wenn man auf den "X" Button klickt oder Alt-F4 drückt. Das Fenster muss dieser Bitte nicht nachkommen. Wenn es der Bitte nachkommen möchte, dann zerstört es sich im WM_CLOSE Handler selbst indem es DestroyWindow aufruft. Im MFC Framework wird das gemacht indem man die OnClose Funktion der Basisklasse aufruft, also z.B. CFrameWndEx::OnClose(). Duch den DestroyWindow Aufruf werden dann auch alle Child-Fenster mit zerstört.

    Daher würde es auch keinen Sinn machen WM_CLOSE an ein Child-Fenster zu schicken. Die Entscheidung trifft immer das top-level Fenster, das Child-Fenster hat da nix mitzureden.



  • @hustbaer
    Vielen Dank, so direkt war mir das nicht klar. Dachte immer das der Weg über die View zum Mainframe geht. Aber noch mal ne Frage zur Messagebox. Was wird mindestens benötigt um die anzuzeigen? Wenn das View-Objekt nicht existiert müsste es ja noch gehen. Wie sieht es da mit Mainframe und Anwendungsobjekt aus?



  • @AndyDD
    Es wird eigentlich überhaupt nichts benötigt, zumindest nicht bei MessageBox aus der WINAPI. Was der AfxMessageBox Aufruf genau braucht kann ich nur vermuten, vielleicht wird da das Parent-Fensterhandle über das Hauptfenster der Anwendung geholt. Wenn das dann schon zerstört wurde (also das komplette Objekt, nicht nur die Windows-Resourcen), dann geht ein CWnd->GetSafeHwnd() Aufruf natürlich in die Hose.
    Im Grunde kann ein AfxMessageBox Aufruf nicht wesentlich viel mehr machen als ein MessageBox Aufruf. Wenn das so ein Timing Problem und man nicht genau feststellen kann, welches Objekt wann noch lebt, dann ist MessageBox mit 0 als Parent vielleicht eine Lösung.



  • Ich bin bei meiner Aussage auch von der Standard-WinAPI Funktion MessageBox ausgegangen und nicht einer speziellen Funktion.



  • @Th69
    Jau, hab ich auch grad gesehen, dass du das schon vor 2 Tagen vorgeschlagen hast. Ist offensichtlich auch am TE vorbeigegangen.



  • @DocShoe

    Ich habe sowohl MessageBox mit NULL und AfxMessageBox probiert im Destructor der Dokumentenklasse aufzurufen. Bei letzterem kommt zumindest noch ein Warnton, bei ersterm passiert gar nichts. Ich hab das schon hinbekommen Messageboxen beim Programmstart anzuzeigen, ohne das ein Fenster sichtbar war. Jedoch heißt das ja nicht, dass davon nicht schon eine Instanz existieren kann.



  • Ich weiss nicht ob es in allen Situationen möglich ist eine Message-Box anzuzeigen. Mag sein dass es da ein paar Sonderfälle gibt wo es nicht geht. Kann auch sein dass es vom OS aus ginge aber die MFC es nicht in allen Situationen verkraftet.

    Das Anzeigen einer Message-Box führt ja dazu dass alle möglichen Messages "gesendet" werden (beim Erzeugen/Anzeigen des Message-Box Fensters) und danach wird eine Message-Schleife für das Message-Box Fenster ausgeführt. Dabei wird vermutlich auch das gerade aktive Fenster eine Message bekommen, nämlich z.B. dass es jetzt nicht mehr aktiv ist. Was passiert wenn z.B. gerade eine WM_DESTROY Handler für dieses Fenster "aktiv" ist (also wenn man MessageBox aus dem WM_DESTROY heraus aufruft), weiss ich nicht. Jetzt nur als ein Beispiel wo ich mir vorstellen könnte dass es evtl. Probleme gibt. Hab's aber nicht ausprobiert, kann auch gut sein dass es in dem beschriebenen Fall eh funktioniert.


Anmelden zum Antworten