Verstaendnissfrage(n)



  • Tja, wie der Titel schon sagt ich brauche mal Hilfe mit allgemeinen Win-Api-Fragen!

    Ich habe versucht meinen Fensterhintergrund im Nachhinein zu aendern, also nachdem ich in der WNDCLASS-structure einen Brush gesetzt habe:

    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
    

    So, jetzt habe ich zum aendern des Brushes erst mal folgende Funktionen gefunden, zum einen

    SelectObject(hdc,GetStockObject(BLACK_BRUSH));
    

    und zum andern

    SetDCBrushColor(hdc,RGB(0,0,0));
    

    Frage #1: Wer "hat" denn nun einen Brush, mein Fenster oder der DC ?
    direkt anschliessend Frage #2: Wird bei jedem Zeichenvorgang auf dem Fenster ein neuer Device Context erstellt, oder liefert mir BeginPaint bzw GetDC immer "den selben" ? Sollte immer ein neuer DC erstellt werden, muss es ja eine Stelle geben, an der Windows den Brush des DCs auf den Fensterbrush setzt...

    Wie auch immer, keine der beiden Methoden funktioniert - egal ob ich jetzt in WM_PAINT zwischen BeginPain und EndPaint den Brush setze, oder ob ich in WM_ERASEBKGND den DC aus wParam hole und dort versuche den Brush zu aendern.

    Im Beispiel auf MSDN wird das ganze sogar so angewendet:

    SelectObject(hDC,GetStockObject(DC_BRUSH));
    SetDCBrushColor(hDC,RGB(0x00,0x00,0x00));
    

    Frage #3: Was passiert in diesen 2 Zeilen? Ich vermute mal, dass DC_BRUSH ein konstanter Brush des Systems ist - eben der, den ich mit SetDCBrushColor veraendern kann..
    long story short: Ich wuerde gerne wissen *wie* und *wo* das Zeichnen des Hintergrundes stattfindet! Danke schonmal 🙂



  • Ok, durch rumprobieren bin ich zu folgenden Schluessen gekommen, es waere nicht schlecht wenn mir jemand sagen kann ob diese Annahmen stimmen:
    EDIT: nochmal etwas geaendert

    1. Bei jedem Aufruf von BeginPaint oder GetDC wird ein neuer Device Context erstellt, der eigene Eigenschaften hat.

    2. Ein DC hat unter anderem einen Handle auf einen Brush. Dieser muss jedesmal aufs neue gesetzt werden (SelectObject).

    3. Wird WM_ERASEBKGND von DefWindowProc bearbeitet, wird der Brush des DC's auf den in der WNDCLASS-structure angegeben Hintergrundbrush gesetzt.

    4. Wird ein Device Context freigegeben und spaeter wieder angefordet, geht der gesetze Brush wieder verloren (er ist wieder default).

    5. DC_BRUSH ist ein einfarbiger Brush des DC, der mit SetDCBrushColor auf einen Farbwert gesetzt werden kann. Dadurch spart man sich einen Aufruf von CreateSolidBrush und muss auch das angeforderte Objekt nicht loeschen, da es vom System verwaltet wird.

    6. Man kann problemlos mehrere DCs fuer das gleiche "Device" erstellen.
    Meine wichtigste Erkenntnis war

    HDC hdc = GetDC(hwnd);
    SelectObject(hdc,GetStockObject(BLACK_BRUSH));
    
    HDC hdc2 = (HDC)wParam;
    SelectObject(hdc2,GetStockObject(WHITE_BRUSH));
    
    Rectangle(hdc,0,0,rc.right,rc.bottom); // zeichnet schwarz
    Rectangle(hdc2,0,0,rc.right,rc.bottom);// zeichnet weiss
    


  • Hört sich für mich alles ok an, 100%ig sicher bin ich mir aber nicht - musst also noch warten, bis einer der Profis vorbeischaut 🙂



  • N'Abend zusammen 😉 ,

    Also ist eigentlich (fast) alles richtig ... würd ich sagen.

    Chase schrieb:

    1. Bei jedem Aufruf von BeginPaint oder GetDC wird ein neuer Device Context erstellt, der eigene Eigenschaften hat.

    Also ein HDC ist ein Handle to a Device Context. Ein Handle ist ein Zeiger. In diesem Fall handelt es sich um einen Zeiger auf eine von Windows intern verwaltete Struktur (struct), die Informationen zum Zeichnen beinhaltet. Beim Anfordern eines HDC's holst Du dir also einen Zeiger auf diese interne Struktur. Diese 'Eigenschaften' sind also schlichtweg Elemente der Struktur, die direkt nach dem Anfordern immer auf Standardwerte besitzen.

    Chase schrieb:

    2. Ein DC hat unter anderem einen Handle auf einen Brush. Dieser muss jedesmal aufs neue gesetzt werden (SelectObject).

    Das stimmt, siehe Erklärung oben; das ist also eine 'Eigenschaft' bzw. ein Element der Stuktur.

    Chase schrieb:

    4. Wird ein Device Context freigegeben und spaeter wieder angefordet, geht der gesetze Brush wieder verloren (er ist wieder default).

    Jo, ABER: Du musst unbedingt dafür sorgen, dass Du einen nicht vom System verwalteten Brush vorm löschen(Speicherkontext)/freigeben(Gerätekontext) heraus selektierst und anschließend den vorherigen 'Standard-Brush' wieder einsetzt.
    Dies geht zum Beispiel so (hört sich nämlich komplizierter an, als es ist 😉 ):

    // Anforderung eines Gerätekontextes via BeginPaint/GetDC...: 'hDC'
    // ...
    HBRUSH hbPrevBrush = reinterpret_cast<HBRUSH>(SelectObject(hDC, CreateSolidBrush(RGB(255,255,0))));
    // ...
    // Alle Zeichenaktionen durchführen...
    // ...
    // Alten Brush wieder einsetzen und erstellten löschen:
    DeleteObject(SelectObject(hDC, hbPrevBrush));
    /*
    Hinweis:
    SelectObject gibt hier das vorherige selektierte
    Objekt über den Return-Wert zurück.
    */
    // Freigabe des angeforderten Gerätekontextes...
    

    Chase schrieb:

    6. Man kann problemlos mehrere DCs fuer das gleiche "Device" erstellen.
    Meine wichtigste Erkenntnis war

    HDC hdc = GetDC(hwnd);
    SelectObject(hdc,GetStockObject(BLACK_BRUSH));
    
    HDC hdc2 = (HDC)wParam;
    SelectObject(hdc2,GetStockObject(WHITE_BRUSH));
    
    Rectangle(hdc,0,0,rc.right,rc.bottom); // zeichnet schwarz
    Rectangle(hdc2,0,0,rc.right,rc.bottom);// zeichnet weiss
    

    Nein! Man sollte immer nur einen Gerätekontext anfordern, diesen erst freigeben und danach wieder anfordern, sonst besteht die Gefahr von GDI-Leaks. Bei Speicherkontexten ist das natürlich etwas anderes 😉 .

    Huji, das war ne Tipperei... 😋 . Naja ich hoffe das ist verständlich/richtig 👍 .
    Falls da was nicht verständlich ist, einfach nochmal posten, bin aber jetzt inner Haia 😃 ... ➡ Gute Nacht 🙂 .

    EDIT: Den Punkten, auf die ich nicht eingegangen bin, stimme ich zu 😉 .



  • Ok, das hab ich soweit verstanden - Danke!

    Eine Frage noch: Wenn ich einen Brush z.b. statisch in der WndProc speichere
    (Bei WM_CREATE initialisier ich den dann einfach mit CreateSolidBrush, bei WM_DESTROY loesche ich ihn mit DeleteObject) kann ich auch sowas machen wie:

    case WM_PAINT:
    
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd,&ps);
      SelectObject(hdc,static_brush);
      EndPaint(hwnd,&ps);
    
    break;
    

    Oder muss ich da auch wieder "zurueckselecten" ?



  • Ich glaube, der alte muss wieder zurück. Wenn Windows den alten Brush angelegt hat, will es ihn auch wieder kaputtmachen. Und wenn dann deiner dadrin steht merkt das Windows nicht und macht deinen kaputt (evtl). Und der alte bleibt bestehen.
    Ist nicht 100% sicher, aber bevor man im Systemspeicher Leaks erzeugt... Im eigenen Speicher ist es ja nicht soooo schlimm, wenn man Speicher nicht wieder freigibt, bei Beendung des Programms ist der weg.
    Aber wenn Windows Speicher durch die Lappen geht, liegt der bis zum nächsten Reboot irgendwo faul rum. Und das will ja keiner 😉



  • Japs musst Du auch den alten wieder einsetzen, wie Badestrand geschrieben hat 😉 .

    Siehe hier:

    MSDN zu SelectObject(...) schrieb:

    Remarks
    This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object.


Anmelden zum Antworten