Speicherproblem mit Bitmap?!



  • Hi an alle! Mein Programm soll Bitmaps einblenden, wenn man auf einen Button klickt. Insgesamt gibt es vier Buttons. Welches Bitmap angezeigt wird ist davon abhängig, welcher Button gedrückt worden ist, und wie oft andere Buttons schon gedrückt worden sind! Das klingt kompliziert, aber meine eigentliche Frage ist für die meisten wahrscheinlich einfach zu beantworten. Für mich stellt sich die Frage, wie das mit der Reservierung der Speicherbereiche aussieht. Denn jedesmal wenn eine onButton-Funktion aufgerufen wird, wird folgender Code durchlaufen:

    void CBitmapladenDlg::OnButton2() 
    {
    
    CDC *pDC = GetDC();
    
    CBitmap bitmap;
    
    bitmap.LoadBitmap(IDB_BITMAP2);
    
    BITMAP bm;
    
    bitmap.GetObject(sizeof(bm), &bm);
    
    CDC speicherDC; 
    
    speicherDC.CreateCompatibleDC(pDC);
    
    speicherDC.SelectObject(&bitmap);
    
    RECT rect; 
    
    GetClientRect(&rect);
    
    pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, 
    
    &speicherDC, 0, 0, SRCCOPY);
    

    Ein neues Bitmap wird einfach über das alte geblendet! Ist es sinnvoll zuerst das alte Bitmap zu löschen und den Speicherbereich freizugeben bevor das neue eingeblendet wird?? Wenn ja, wie mach ich das? Zusätzlich wäre es sicher sinnvoll, wenn ich so viel wie möglich einmal global ausführen lassen würde, oder??

    Für jegliche Hilfestellungen bin ich dankbar!!

    Cha-OS



  • wenn du mit GetDC() nen Kontext holst, musst du mit ReleaseDC diesen wieder freigeben, ansonsten gibt DAS MemLeak à la carde^^

    einfach is hier das zu benutzen

    CClientDC dc(this);

    Beim Zerstören des Objektes gibt dieser DC sich selber wieder frei



  • @Pellaeon;
    Hab den Code von dir jetzt einfach an den Anfang einer onButton-Funktion gesetzt. Sonst habe ich alles belassen wie es ist! Damit müsste es doch passen, oder?? Das ich das bestehende Bitmap "nur" überblende ist also kein Problem?

    Danke für deine hilfreiche Antwort

    cha-OS



  • cha-OS schrieb:

    Hab den Code von dir jetzt einfach an den Anfang einer onButton-Funktion gesetzt.

    Hast du auch deinen pDC überall weggemacht?

    cha-OS schrieb:

    Das ich das bestehende Bitmap "nur" überblende ist also kein Problem?

    Das weis ich nicht. ICh selbst arbeite momentan auch mit CBitmap und benutze da aber die Create-Methode, wenn ich ein zweites mal Create aufrufe muss ich vorher das alte Bild löschen sonst gibts nen Fehler. Wie das bei LoadBitmap ist, weis ich nicht.
    Löschen kann man mit "CGdiObject::DeleteObject()", Hinweise dazu siehe MSDN.



  • Wenn ich die pDC wegmache dann gehts nicht mehr! Was muss ich da ändern?
    Ich würde es genial von dir finden, wenn du mir konkret sagen könntest, was ich im obigen Code weglassen muss. Komme (noch nicht) alleine drauf

    Danke für deine Geduld

    chiao cha-OS



  • void CBitmapladenDlg::OnButton2()
    {
        CClientDC dc(this);
    
        CBitmap bitmap;
        bitmap.LoadBitmap(IDB_BITMAP2);
    
        BITMAP bm;
        bitmap.GetObject(sizeof(bm), &bm);
    
        CDC speicherDC;
        speicherDC.CreateCompatibleDC(&dc);
        CBitmap* pOldBitmap = speicherDC.SelectObject(&bitmap);
    
        RECT rect;
        GetClientRect(&rect);
    
        dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight,
                  &speicherDC, 0, 0, SRCCOPY);
    
        speicherDC.SelectObject(pOldBitmap);
    


  • THX Pellaeon du beeindruckst mich mit deiner Hilfeleistung 😮
    Von solchen Leuten leben Foren!
    Aber genug mit den Schmeicheleien. Jetzt hab ich also (fast) nichts mehr vor irgendwelchen Speicherfehlern zu befürchten, oder?!
    Wenn du noch kurz Zeit hast würde es mich freuen, wenn du mir erlären würdest was du da jetzt geändert hast. Ich will ja schließlich dazulernen 😉

    Nochmals Danke, cha-OS



  • nein keine mehr
    deine Bitmapklasse ist ein lokales Objekt der Methode. Beim Verlassen wird das Objekt zerstört und gibt seinen Speicher wieder frei.

    Was ich nur geändert habe: du hast dir einen Zeiger auf einen DeviceContext mit GetCD() geholt. Den müsstest du mit ReleaseDC() freigeben, sonst hast du MemLeaks.

    Ich habe deinen Zeiger durch CClientDC ersetzt. Diese Klasse ist von CDC abgeleitet, aber im Konstruktor kannst du u.A. jetzt einen Zeiger auf das Fenster übergeben, auf das du zeichnen willst. Mehr muss man nicht machen, den Rest richtet die Klasse ein. Und das praktische: beim Zerstören des Objektes, was Beim verlassen der Methode geschieht, gibt das Objekt de DeviceContext von allein frei, so kann man es nicht vergessen.



  • wenn du mit GetDC() nen Kontext holst, musst du mit ReleaseDC diesen wieder freigeben, ansonsten gibt DAS MemLeak à la carde^^

    Dazu hätte ich mal eine Frage: Wieso kann es hier Memory Leaks geben? Ich meine, mit CDC *pDC=GetDC () hole ich mir doch den DC von CWnd (also quasi nichts anderes als CDC *pDC=this->GetDC ()). Es wird doch hier kein neuer Speicherbereich erstellt, sondern der Zeiger zeigt nur auf einen ohnehin schon vorhandenen DC oder irre ich mich da? Würde ich mit ReleaseDC () nicht den DC des Fensters generell zerschießen?



  • Das reine GetDC ist eine Win-API-Funktion, diese öffnet einen Device Context anhand eines Handles,welches man als Parameter übergeben muss.

    Wenn man das GetDC-der CWnd-Klasse aufruft, ruft diese nur die WinAPI-Funktion auf und setzt auotmatisch das eigene Handle ein(kannst mit dem Debugger nachgucken 🙂 ).
    Wie du siehst, wird auch hier ein Context erstellt und nicht nur ein Zeiger auf einen vorhandenen geholt.
    Und diesen muss man nun mal wieder selbst freigeben.

    MSDN schrieb:

    Unless the device context belongs to a window class, the CWnd::ReleaseDC method must be called to release the context after painting. Since only five common device contexts are available at any given time, failure to release a device context can prevent other applications from accessing a device context.

    Das steht zu CWnd::GetDC()



  • An Pellaeon:
    Vielen Dank für die Erklärung.

    Ich hätte noch eine Frage: In einem Tutorial, wo Blitten mit transparenter Hintergrundfarbe beschrieben wurde (www.henkessoft.de/C++/MFC/mfc_einsteigerbuch_kapitel9.htm), wurden, um das zu ermöglichen, eine ganze Reihe von Device Contexten angelegt. In diese wurden dann neu erstellte bzw. aus Ressourcen kommende Bitmaps geladen. Nun ist es ja so, daß die Funktion CDC::SelectObject (CBitmap *pBitmap) einen Zeiger auf das Bitmapobjekt zurückliefert, das vorher im DC war. Der Ersteller hat diesen Wert jedesmal gespeichert und ihn kurz vor Beendigung der Funktion wieder dem DC zugewiesen.
    Also, am Beispiel eines der Device Contexte:

    void CDlg::OnPaint ()
    {
        //...
    
        CDC TempDC; 
        TempDC.CreateCompatibleDC (pDC);
    
        CBitmap TempBitmap; 
        TempBitmap.CreateCompatibleBitmap (&SpeicherDC, bm.bmWidth, bm.bmHeight); 
        CBitmap *pOldTempBitmap=TempDC.SelectObject (&TempBitmap);
    
        //...
    
        TempDC.SelectObject (pOldTempBitmap);
    
        //...
    
        CDialog::OnPaint ();
    }
    

    Gibt es dafür irgendeinen praktischen Grund? Die Funktion ist doch sowieso danach vorbei und das Objekt gelöscht. Wieso bringt man den DC nochmal in den Ursprungszustand? Ist das notwendig oder nur eine Vorliebe des Autors? Wer kennt sich damit aus?


Anmelden zum Antworten