Speicherleck beim dynamischen Erzeugen eines CDocument

jokey2

Erfahrenes Mitglied
Hallo Gemeinde!

Folgendes Problem:
Wenn ich meine MFC-Applikation starte und als Programmargument einen Dateinamen angebe, dann versucht das MFC-Framework die Datei in ProcessShellCommand zu öffnen. Wenn das allerdings fehlschlägt, z.B. weil es die angegebene Datei nicht gibt, dann beendet sich die Anwendung wieder kommentarlos und das mit IMPLEMENT_DYNCREATE erzeugte CDocument wird nicht wieder zerstört. Die Folge: ein Speicherleck!
Das kann ja eigentlich nicht sein, oder? Ich erzeuge das Dokument ja nicht selber, sondern es wird vom Framewürg erzeugt. Dann sollte das Framewürg es doch auch wieder zerstören, oder? :confused:
Ich habe mir jetzt erstmal so beholfen, daß ich sowohl das CDocument als auch den MainFrame vor dem Aufruf von ProcessShellCommand selber erzeuge. Dadurch habe ich einen Zeiger auf das Dokument, den ich deleten kann, wenn das ProcessShellCommand FALSE zurückliefert, bzw. setze ich dann den 'm_nShellCommand' Parameter von 'CCommandLineInfo' auf 'FileNew' und lasse ProcessShellCommand so nochmal laufen. Dadurch wird die Applikation eben nicht einfach kommentarlos geschlossen sondern sie wird mit einem leeren Dokument geöffnet. So kann ich auch noch eine Meldung ausgeben, daß die Datei nicht gefunden wurde.
So sieht das ganze dann aus:
Code:
    // Dokument und MainWnd erzeugen, um eventuelles Speicherleck zu vermeiden,
    // falls die Datei im Aufrufparameter nicht existiert
    CVDMIDEDoc * pDoc = (CVDMIDEDoc *)pDocTemplate->CreateNewDocument();
    m_pMainWnd = pDocTemplate->CreateNewFrame(pDoc, NULL);
    // Verteilung der in der Befehlszeile angegebenen Befehle
    if (!ProcessShellCommand(cmdInfo))
    {
        cmdInfo.m_nShellCommand = CCommandLineInfo::FileNew;
        cmdInfo.m_strFileName.Empty();
        if (!ProcessShellCommand(cmdInfo))
        {
            delete pDoc;
            return FALSE;
        }
    }
Aber, wie gesagt, das kann's ja eigentlich nicht sein, daß man die MFC derartig austricksen muß, um kein Speicherleck zu bekommen. Hab' ich da irgendwas falsch gemacht? Hat jemand hier ähnliche Erfahrungen gemacht?

Über Kommentare dazu würde ich mich sehr freuen.
 
Blöde Frage, wie siehst du, dass du ein Speicherleck hast?

Bekommst du einen Eintrag im Ausgabe-Fenster dazu?

Ich habe das mal bei mir ausprobiert (VS 2003) und ich bekomme zwar eine Fehlermeldung, dass Datei xxx nicht geöffnet werden kann. Ein Leck bekomme ich am Ende aber nicht angezeigt?
 
wie siehst du, dass du ein Speicherleck hast?
Nach dem Beenden im DevStudio kommt die übliche Anzeige von nicht freigegebenem Speicher:
Document::OnOpenDocument returned FALSE.
Warning: destroying CSingleDocTemplate with live document.
Detected memory leaks!
Dumping objects ->
{680} normal block at 0x00D163F0, 48 bytes long.
Data: < K b K > 10 4B D1 00 A8 62 D1 00 10 4B D1 00 CC CD CD CD
{679} normal block at 0x00D16388, 33 bytes long.
Data: < encoding > 00 65 6E 63 6F 64 69 6E 67 00 CD CD CD CD CD CD
{678} normal block at 0x00D16320, 33 bytes long.
Data: < ISO-8859-1 > 00 49 53 4F 2D 38 38 35 39 2D 31 00 CD CD CD CD
{677} normal block at 0x00D162A8, 48 bytes long.
Data: < c @X K > F0 63 D1 00 40 58 D1 00 10 4B D1 00 CC CD CD CD
{676} normal block at 0x00D1BDD8, 33 bytes long.
Data: < version > 00 76 65 72 73 69 6F 6E 00 CD CD CD CD CD CD CD
{675} normal block at 0x00D1BD70, 33 bytes long.
Data: < 1.0 > 00 31 2E 30 00 CD CD CD CD CD CD CD CD CD CD CD
{674} normal block at 0x00D16240, 33 bytes long.
Data: < xml > FF 78 6D 6C 00 CD CD CD CD CD CD CD CD CD CD CD
{671} normal block at 0x00D15840, 48 bytes long.
Data: < c b b > F0 63 D1 00 A8 62 D1 00 A8 62 D1 00 CD CD CD CD
{670} normal block at 0x00D14B10, 48 bytes long.
Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 CD CD CD CD
{669} normal block at 0x00D12D48, 12 bytes long.
Data: <H- H- > 48 2D D1 00 48 2D D1 00 CD CD CD CD
{668} normal block at 0x00D14C00, 44 bytes long.
Data: < L L > 00 4C D1 00 00 4C D1 00 CD CD CD CD CD CD CD CD
c:\projekte\vc++\vdmide\vdmidedoc.cpp(36) : {667} client block at 0x00D15FA8, subtype 0, 592 bytes long.
a CVDMIDEDoc object at $00D15FA8, 592 bytes long
Das ist meine InitInstance:
C++:
// OLE-Bibliotheken initialisieren
    if (!AfxOleInit())
    {
        AfxMessageBox(IDP_OLE_INIT_FAILED);
        return FALSE;
    }
 
    AfxEnableControlContainer();
 
    // Standardinitialisierung
    // Wenn Sie diese Funktionen nicht nutzen und die Größe Ihrer fertigen 
    //  ausführbaren Datei reduzieren wollen, sollten Sie die nachfolgenden
    //  spezifischen Initialisierungsroutinen, die Sie nicht benötigen, entfernen.
 
#ifdef _AFXDLL
    Enable3dControls();            // Diese Funktion bei Verwendung von MFC in gemeinsam genutzten DLLs aufrufen
#else
    Enable3dControlsStatic();    // Diese Funktion bei statischen MFC-Anbindungen aufrufen
#endif
 
    AfxInitRichEdit( );
 
    // Ändern des Registrierungsschlüssels, unter dem unsere Einstellungen gespeichert sind.
    // ZU ERLEDIGEN: Sie sollten dieser Zeichenfolge einen geeigneten Inhalt geben
    // wie z.B. den Namen Ihrer Firma oder Organisation.
    SetRegistryKey(_T("Firmenname"));
 
    LoadStdProfileSettings();  // Standard INI-Dateioptionen laden (einschließlich MRU)
 
    // Dokumentvorlagen der Anwendung registrieren. Dokumentvorlagen
    //  dienen als Verbindung zwischen Dokumenten, Rahmenfenstern und Ansichten.
 
    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CMyDoc),
        RUNTIME_CLASS(CMainFrame),       // Haupt-SDI-Rahmenfenster
        RUNTIME_CLASS(CLeftView));
    AddDocTemplate(pDocTemplate);
 
    // Verbinden des COleTemplateServer mit der Dokumentvorlage.
    //  Der COleTemplateServer legt auf Basis der Informationen in der
    //  Dokumentvorlage bei der Anforderung von OLE-Containern
    //  neue Dokumente an.
    m_server.ConnectTemplate(clsid, pDocTemplate, TRUE);
        // Hinweis: SDI-Anwendungen registrieren Server-Objekte nur dann, wenn /Embedding
        //   oder /Automation in der Befehlszeile enthalten ist.
 
    //Konfiguration laden
    m_Config.Load();
 
    // Befehlszeile parsen, um zu prüfen auf Standard-Umgebungsbefehle DDE, Datei offen
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
 
 
    // Testen, ob Ausführung als OLE-Server
    if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
    {
        // Alle OLE-Server (-fabriken) als aktiv registrieren. Dies aktiviert die
        //  OLE-Bibliotheken, um Objekte von anderen Anwendungen zu erstellen.
        COleTemplateServer::RegisterAll();
 
        // Anwendung mit /Embedding oder /Automation gestartet. In diesem Fall
        //  kein Hauptfenster anzeigen.
        return TRUE;
    }
 
    // Wird eine Server-Anwendung im Standalone-Modus betrieben, ist es ratsam,
    //  die Systemregistrierung zu aktualisieren, falls diese beschädigt wurde.
    m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
    COleObjectFactory::UpdateRegistryAll();
 
    //Standard-Dateierweiterung registrieren
    RegisterFileExtension();
 
    // Verteilung der in der Befehlszeile angegebenen Befehle
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
 
    // Öffnen per DragDrop aktivieren
    m_pMainWnd->DragAcceptFiles();
    // Das einzige Fenster ist initialisiert und kann jetzt angezeigt und aktualisiert werden.
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
 
    return TRUE;
 
Zuletzt bearbeitet:
Das einzige, das mir da direkt auffällt, ist der COleTemplateServer. Ich könnte mir vorstellen, dass der COleTemplateServer als Object Factory da einen CDocTemplate-Klon von deinem Dokument erstellt und das wird nicht freigegeben.

Rufst du denn da Unregister mit auf?
 
Rufst du denn da Unregister mit auf?
Nö, sollte ich das? ;-)

Das wurde vom VS hinzugefügt, ehrlich gesagt weiß ich gar nicht, ob ich das überhaupt brauche. Eigentlich habe ich keine besondere OLE-Unterstützung implementiert.
 
Also MFC räumt normalerweise seine Templates auf.

Jetzt muss ich allerdings gestehen, dass ich diese OLE-Server-Geschichte da noch nie gesehen habe (habe ich wohl beim Erstellen immer abgeschaltet? Oder hast du da VS 2005, ich habe nur 2003).
 
Ne, ich habe hier VS6.
Ich hab's gerade mal ausprobiert. Ich habe eine neue Applikation mit dem Wizard erstellt und eine nicht existierende Datei als Parameter angegeben. Da kommt dann, wie es sein soll, die MessageBox, daß die datei nicht geöffnet werden konnte und die Anwendung schließt sich ohne Speicherleck.
Warum das bei meiner App nicht so ist, weiß ich leider nicht! :(
 
In dem Fall hilft nur Hardcore:

Du bekommst ja bei dem Speicherleck eine Nummer angezeigt:

c:\projekte\vc++\vdmide\vdmidedoc.cpp(36) : {667} client block

Du kannst mit _CrtSetBreakAlloc jetzt die Nummer angeben. Das muss im Sourcecode passieren, am besten so früh wie möglich (im Constructor von der App zum Bleistift):

_CtrSetBreakAlloc( 667 );

Die Funktion benötigt das Include <crtdbg.h>.

Dann müsste beim Erzeugen ein Breakpoint aufgerufen werden und du kannst am CallStack prüfen, ob das jetzt dein CDocTemplate ist (das in InitInstance angelegt wird) oder ein anderes. Ich setze hier auf die Annahme auf, dass ein DocTemplate, das mit AddDocTemplate angemeldet wird, auf jeden Fall aufgeräumt wird; und das evtl. irgendwo ein zweites Doc-Template erzeugt wird.
 
Zurück