1KB Memory-Leck



  • Hallo.
    Ich schreibe atmo eine Library für OpenGL und hab da ein Memory Leak, das ich nicht wegbekomme.. -.-
    Das komische dabei:
    Dieses Leck tritt bei meiner "normalen" Test-Anwendung nicht auf. Es trat jetzt das erste mal auf, als ich ein Tutorial schreiben wollte für die Verwendung der API.
    Das Leck wird auch nur von der Funktion _setCrtDbgFlag gemeldet. Der Visual Leak Detector meldet kein Leck. Auf den ersten und auf hundertsten Blick konnte ich im Code nix finden -.-.

    Naja, ich poste euch mal den minimalsten Code, den ich brauche um das Leck zu erzeugen.

    // Datei: dtApp.cpp
    // Zweck: Applikations-Klasse für den Eintritt der Anwendung
    
    // Forwards & Inits
    dtApp* dtApp::m_instance = NULL;
    bool g_UsedAppClass = false;
    
    /**************  Functions needed by Windows **************/
    
    #ifdef WIN32
    
    // Entry Point, called in WinMain()
    DIESELTOOLEXPORT
    int dieselEntryPoint( HINSTANCE hInstance, 
                          HINSTANCE hPrevInstance, 
                          LPSTR commandline, 
                          int nShow )
    {
        // No Instance found
        if (dtApp::getApp() == NULL)
        {
            dtDebugOut(wxT("Create an Instance of this class with dtImplementApp()!!!\n"));
            MessageBox(HWND_DESKTOP, wxT("Cannot find an Instance of dtApp().\n Application exits now."), 
                       wxT("DieselError"), MB_OK | MB_ICONERROR);
            return false;
        }       
    
        dtApp::getApp()->m_hInstance = hInstance;
    
        wxString cmd;
        size_t val;
    
        wxChar* ptr = GetCommandLine();
        cmd = ptr;
        val = cmd.find_last_of(wxT("\\"));
        cmd = cmd.substr(1, val);
        dtApp::getApp()->m_dir.setDir(cmd);
    
        int argc = 0; char** argv = NULL;
        wxSetInstance(hInstance);
        wxEntryStart(argc, argv);
        g_UsedAppClass = true;   
    
        // Init the Library
        return dtApp::getApp()->main();
    }
    
    #endif /* WIN32 */
    
    /************** dtApp Implementation **************/
    
    dtApp::dtApp()
    {
        if (m_instance == NULL)
        {        
            m_instance = this;
        }
    }
    
    dtApp::~dtApp()
    {
        dtApp::m_instance = NULL;    
    }
    
    int dtApp::exit(const int code)
    {
        ::PostQuitMessage(code);
        return code;
    }
    
    HINSTANCE dtApp::getHInstance()
    {
        return m_hInstance;
    }
    
    // Datei: dllmain.cpp
    // Zweck: Windows Eintritt
    extern bool g_UsedAppClass;
    
    // Windows DLL Main
    BOOL APIENTRY DllMain( HANDLE, DWORD  dwReason, LPVOID)
    {
        switch (dwReason)
        {
        case DLL_THREAD_ATTACH:
        case DLL_PROCESS_ATTACH:
            {   
                // Create SysLog
                dtSysLog::dtSysLogInstance = new dtSysLog();
                assert(dtSysLog::dtSysLogInstance != NULL);
                break;
            }
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            {
                if (g_UsedAppClass)
                {
                    wxEntryCleanup();            
                }
                dtDelete(dtSysLog::dtSysLogInstance);
                break;
            }    
        }//switch
    
        return TRUE;
    }
    

    Soweit der API-Code. Jetzt zu dem Code, bei dem der Fehler auftritt:

    class CApp : public dtApp, public dtWindowEvents
    {
    public:
        CApp();
        ~CApp();
    
        // dtApp erfodert Implementation dieser Funktion
        virtual int main();
    
        // dtWindowEvents erfordert die Implementation dieser Funktion
        virtual dtUint onQuit();
    
    private:
        dtWindow* m_window;    
    };
    
    dtImplementApp(CApp)
    
    CApp::CApp() : dtApp()
    {
        m_window = NULL;
    }
    
    CApp::~CApp()
    {
    }
    
    int CApp::main()
    {   
        return 0;
    }
    
    dtUint CApp::onQuit()
    {
        return 0;
    }
    

    Ihr seht, die Anwendung macht rein gar nichts, trotzdem kommt eben diese Meldung.. 😕
    dtImplementApp ist so definiert:

    #define dtImplementApp(C)                                       \
    C theApp; C* dtGetApp(){ return (C*)theApp.getApp(); }          \
    extern "C"                                                      \
    BOOL WINAPI WinMain( HINSTANCE hInstance,                       \
                         HINSTANCE hPrevInstance,                   \
                         LPSTR lpCmdLine,                           \
                         int nShowCmd )                             \
    {                                                               \
        /* Enter Library */                                         \
        return dieselEntryPoint(hInstance, hPrevInstance, lpCmdLine, nShowCmd); \
    }
    

    VLD meldet:

    No memory leaks detected.
    Visual Leak Detector is now exiting.

    Visual Studio aber sagt:

    Detected memory leaks!
    Dumping objects ->
    {863} normal block at 0x01C5BB58, 1 bytes long.
    Data: < > CD
    Object dump complete.

    Jemand eine Idee?
    -.-
    rya.



  • Wenn es immer die allokation 863 ist, kannst Du _CrtSetBreakAlloc(863); in Dein WinMain einbauen. Dann stoppt der Debugger an der Allokationsstelle und Du kannst den Callstack anstehen.
    http://msdn2.microsoft.com/en-us/library/4wth1ha5(VS.80).aspx

    Mal eine ganz andere Frage:
    Warum dieser Affenaufstand mit einer DLL?



  • Martin Richter schrieb:

    Wenn es immer die allokation 863 ist, kannst Du _CrtSetBreakAlloc(863); in Dein WinMain einbauen. Dann stoppt der Debugger an der Allokationsstelle und Du kannst den Callstack anstehen.
    http://msdn2.microsoft.com/en-us/library/4wth1ha5(VS.80).aspx

    Mal eine ganz andere Frage:
    Warum dieser Affenaufstand mit einer DLL?

    Danke für den Tipp, aber der Debugger macht irgendwie keinen Stopp... naja, vllt hängt das auch mit wxWidgets zusammen.
    Aber was meinst du mit Affenaufstand? Wegen der App-Klasse? Man kann die API auch ohne benutzen, aber es ist die selbe Methode die wxWidgets für den Eintritt verwendet.
    rya.



  • Setze _CrtSetBreakAlloc(863) in die DllMain. Evtl. ist diese Allokatin schon viel früher, bevor WinMain anläuft.

    Affenaufstand ist für mich, die ganze Implementierung in eine DLL zu verlegen.
    Welchen Sinn macht das? Außer dem extremen Aufwand den man damit hat...



  • Ok, habs gefunden. War ein Singleton, das initialisiert, aber nicht zerstört wurde. Cooler Befehl :-), danke nochmal. Kannte ich bisher nicht.

    Aber was die Sache mit meinem Affenaufstand angeht:

    Ich verwende diese Methode, weil ich einige Abhänigkeiten verwende (andere Libs) die u.a. die HINSTANCE und solche Geschichten benötigen. Noch dazu ist diese Lib auf Portabilität ausgelegt, bzw ich möchte sie auch unter Linux lauffähig haben. Deswegen verwende ich auch OpenGL und nicht DirectX (Grafik-Lib). Daher hab ich mich für die selbe Methode entschieden die wxWidgets auch verwendet, noch dazu verwende ich ja diese Bibliothek. Zwar nicht die GUI-Elemente aber die Basisklassen wie wxString, wxXML etc. Ich finde es schön damit zu arbeiten, weil diese sehr schöne Funktionen mitbringen. Ausserdem ist in dieser Lib vieles enthalten, was ich erst für Linux UND Windows hätte implementieren müssen.
    Ausserdem schaut so jedes Programm gleich aus :-). Und ob ich jetzt in jeder Anwendung die mit der Lib erstellt wird

    #ifdef WIN32
    int WinMain(...)
    #elif _LINUX_
    int main(int argc, char** argv)
    #elif _MAC_ [..]
    

    etc schreibe, oder es mit Makros löse, ist doch egal :-). Ich fand es nicht viel Aufwand.



  • Du hast mich nicht verstanden. Ich habe nichts gegen wxWidgets etc.

    Warum baggerst Du die ganzen Klassen in eine DLL? Das Ganze kannst Du doch auch in Deine EXE legen und es wäre viel einfacher. Dann hast Du ein Modul und gut ists.
    Was hat das mit hInstance zu tun etc. die hast Du auch in einer EXE.
    Ich finde es viel tödlicher, dass Du auch noch soviel Intelligenz in DllMain hast. Der Code dort sollte möglichst klein und kompakt sein, keine CRT verwenden und auch keine USER32.DLL Befehle. (Wenn möglich!)

    Dein gesamter Konstrukt kommt mir komisch vor... :xmas2:



  • Öhm, das ganze sitzt in einer DLL weil ich eine API entwerfe? 😃 Das soll ja so sein. *g*

    Hmm, wieso meinst du soviel Intelligenz?
    Es werden nur 2 Dinge erledigt:
    1.) Speicher für mein SysLog anfordern. Das wird so früh durchgeführt, weil ich von Anfang an sehr viel logge und ausgebe im Debug-Modus um eben Fehler früher zu finden. Kann man mit einem Flag abschalten. Gut, das könnte man auslagern. Das Löschen allerdings nur schwer.
    2.) wxWidgets initialisieren.

    Danach wird die "main" der Client-Anwendung ausgeführt.
    Der _CrtSetDbgFlag() steckt allerdings in der wxWidgets-Lib, allerdings könnte man das mittels #define abstellen. Ist nur aktiv, wenn __WXDEBUG__ definiert ist. Ich finds jedoch gut, so tun sich Speicherlecks sehr früh auf und man kann etwas dagegen unternehmen.

    Was hat das mit hInstance zu tun etc. die hast Du auch in einer EXE.

    Ja, richtig. Ich benötige halt die hInstance der Anwendung, nicht die der DLL. Das hat was mit wxWidgets zu tun. Wenn ich wxWidgets auf die hInstance der DLL intialisiere, muss ichs für die Anwendung nochmal tun. So initialisere ich das einmal auf die .exe und hab keine doppelten Globals oder was auch immer die da anlegen :D.
    D.h. für so ein Dynamisches Konstrukt müsste mir der User (Programmierer der .exe) die hInstance übergeben. Wobei wir wieder beim Stichwort OS-Unabhängigkeit sind. Der Code würde unter Win32 dann wieder anders aussehen :). Und das will ich so gut es geht vermeiden. Soviel Overhead ist das nicht und kommt auch nur beim Start zum tragen. Von daher ist es imho eine gute Technik sowas zu implementieren.
    Ich hoffe du kannst es jetzt nachvollziehen :D.
    rya.



  • Nein verstehe ich nicht. Selbst wenn ich eine API entwicklen würde, würde ich vermutlich andere Wege gehen...

    Ansnsten was hInstabce etc. betrifft lies mal:
    http://blog.m-ri.de/index.php/2007/12/12/die-unsitte-immer-getmodulehandlenull-fuer-hinstance-in-createwindow-und-registerclass-zu-verwenden/


Anmelden zum Antworten