| Autor |
Nachricht |
cyberleon
Mitglied
Benutzerprofil
Anmeldungsdatum: 26.04.2012
Beiträge: 35
|
cyberleon Mitglied
23:17:59 27.05.2012 Titel: |
Bitmaps: Speicherallokation, Freigabe und Löschung |
Zitieren |
| Zitat: |
In Deiner WM_PAINT ist auch noch ein grober Schnitzer drin:
Du erzeugst dort jedesmal einen Speicher-Gerätekontext, löschst diesen aber nie mit DeleteDC, sondern gibst ihn lediglich frei mit ReleaseDC.
Wenn Du Dein Programm so eine Weile laufen lässt, hast Du bald alle GDI-Objekte des Systems verbraucht. Nicht gut, denn GDI-Objekte sind nicht unbegrenzt vorhanden.
Für die Performance wäre es auch besser, wenn Du LoadImage und CreateCompatibleDC in die WM_CREATE auslagerst und die Handles global abspeicherst. In der WM_DESTROY löschst Du die beiden dann wieder.
Gruß
Greenhorn |
Zuerst einmal vielen lieben Dank für diese tollen Hinweise! Ich habe mich leider erst heute an die Arbeit machen können - folgendes kam dabei heraus (Der Code funktioniert! Ich bin nur unsicher, ob ich alles richtig gemacht habe!):
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | // Backgroundpainting
HBITMAP h1Bitmap;
HDC h1BitmapDC;
HDC h1DC;
int Bitmaphoehe;
int Bitmapbreite;
int PaintBackground(HWND hwnd)
{
static PAINTSTRUCT ps;
static HDC h1DC;
RECT rect;
h1DC = BeginPaint( hwnd, &ps ); // draw the following stuff:
GetClientRect(hwnd, &rect); // Fenstergrösse herausfinden, um in LoadImage einzufügen
int hoehe = rect.bottom - rect.top;
int breite = rect.right - rect.left;
BITMAP bmp;
GetObject(h1Bitmap,sizeof(bmp),&bmp);
SelectObject(h1BitmapDC,h1Bitmap);
StretchBlt(h1DC,0,0, breite, hoehe,h1BitmapDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY); // (hDC, Anfangsposix, AnfangsposiY
ReleaseDC(hwnd,h1BitmapDC);
ReleaseDC(hwnd,h1DC);
Bitmaphoehe = bmp.bmHeight;
Bitmapbreite = bmp.bmWidth;
if(h1Bitmap == NULL)
{
MessageBox(hwnd, "Could not load Bitmap!", "Error", MB_OK | MB_ICONEXCLAMATION);
}
Ellipse( h1DC, 300, 100, 330, 130 ); // Ellipse: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Ellipse( h1DC, 160, 100, 190, 130 ); // Ellipse: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Rectangle( h1DC, 20, 90, 330, 100 ); // Rectangle: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Rectangle( h1DC, 220, 70, 300, 90 ); // Rectangle: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
EndPaint( hwnd, &ps );
}
// Buttonpainting
HBITMAP h2Bitmap; // Bitmap, die später das Image für Buttons aufnehmen wird
static HWND hButton; // hButton an dieser Stelle deklariert, da er in int Paintbuttons benötigt wird
HDC h2BitmapDC;
HDC h2DC;
int Checkvariable; // zum Überprüfen interner Werte in dieser Funktion (momentan abrufbar über Klick auf button1)
#define FastDT(buffer,rect) DrawText(hdc,buffer,-1,&rect,DT_CENTER|DT_SINGLELINE|DT_VCENTER);
int PaintButtons(HWND hButton)
{
static PAINTSTRUCT ps;
static HDC hdc;
RECT rect;
int hoehe;
int breite;
char buffer9[256] = "0";
GetWindowText(hButton, buffer9, 256);
hdc = BeginPaint(hButton, &ps);
GetClientRect(hButton, &rect); // Fenstergrösse herausfinden, um in LoadImage einzufügen
hoehe = rect.bottom - rect.top;
breite = rect.right - rect.left;
BITMAP bmp;
GetObject(h2Bitmap,sizeof(bmp),&bmp);
Checkvariable = sizeof(bmp);
SelectObject(h2BitmapDC,h2Bitmap);
StretchBlt(hdc,0,0, breite, hoehe,h2BitmapDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY); // (hDC, Anfangsposix, AnfangsposiY
// hier war LoadImage h2Bitmap
SetBkMode (hdc, TRANSPARENT); // damit der Text nicht in einem weissen Rechteck erscheint
FastDT(buffer9,rect); //siehe Makro oben: DrawText(hdc, buffer9,-1,&rect,DT_CENTER | DT_SINGLELINE | DT_VCENTER);
/*
n Deiner WM_PAINT ist auch noch ein grober Schnitzer drin:
Du erzeugst dort jedesmal einen Speicher-Gerätekontext, löschst diesen aber nie mit DeleteDC, sondern gibst ihn lediglich frei mit ReleaseDC.
Wenn Du Dein Programm so eine Weile laufen lässt, hast Du bald alle GDI-Objekte des Systems verbraucht. Nicht gut, denn GDI-Objekte sind nicht unbegrenzt vorhanden.
Für die Performance wäre es auch besser, wenn Du LoadImage und CreateCompatibleDC in die WM_CREATE auslagerst und die Handles global abspeicherst. In der WM_DESTROY löschst Du die beiden dann wieder.
*/
ReleaseDC(hButton,h2BitmapDC);
ReleaseDC(hButton,h2DC);
EndPaint(hButton, &ps );
return Checkvariable;
} | |
Anschließend die Aufrufe in der WM_CREATE, in die ich wie in der Kritik gefordert meine LoadImage und meine CreateCompatibleDC ausgelagert habe:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | [ ..... ]
WM_CREATE
{
[ ..... ]
// Background
h1Bitmap = (HBITMAP)LoadImage(NULL, "test.bmp", IMAGE_BITMAP, 400, 400, LR_LOADFROMFILE); // Achtung: die beiden Zahlenwerte müssen gleich gross sein, ansonsten kann man sie benutzen um Teile der Bitmap von unten oder von links abzuschneiden.
h1DC = GetDC(hwnd);
h1BitmapDC = CreateCompatibleDC(h1DC);
// Button Images
h2Bitmap = (HBITMAP)LoadImage(NULL, "test2.bmp", IMAGE_BITMAP, 40, 40, LR_LOADFROMFILE); // Achtung: die beiden Zahlenwerte müssen gleich gross sein, ansonsten kann man sie benutzen um Teile der Bitmap von unten oder von links abzuschneiden.
// Buttons
h1Button = CreateWindow( "button",
"Experience",
WS_CHILD | WS_VISIBLE,
0, 0, 100, 30,
hwnd,
NULL,
((LPCREATESTRUCT) lParam) -> hInstance,
NULL);
h2DC = GetDC(h1Button); // Vorbereitung der Buttonwerte. Momentan nur für identische Buttons (Größe, Bitmap)
h2BitmapDC = CreateCompatibleDC(h2DC);
[ ..... ]
} | |
Dann die Aufrufe in der WM_PAINT:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | case WM_PAINT:
{
// Background zeichnen
PaintBackground(hwnd);
// Buttons einfärben durch Funktionsaufruf der zu Programmbeginn deklarierten Funktion PaintButtons():
PaintButtons(h1Button);
PaintButtons(h2Button);
PaintButtons(h3Button);
PaintButtons(h4Button);
PaintButtons(h5Button);
PaintButtons(h6Button);
PaintButtons(h7Button);
PaintButtons(h8Button);
} | |
Und zuletzt die Speicherbereinigung beim Programmende
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | case WM_DESTROY:
{
del_img();
PostQuitMessage(0);
// Clean Background Bitmap
DeleteObject(h1Bitmap);
DeleteDC(h1DC);
DeleteDC(h1BitmapDC);
// Clean Button Bitmap(s)
DeleteObject(h2Bitmap);
DeleteDC(h2DC);
DeleteDC(h2BitmapDC);
return 0;
} | |
Habe ich das soweit richtig gemacht? Also in Bezug auf den Quote vom Anfang? Oder habe ich vielleicht etwas übersehen? (Ich wette darauf 8)( )
Alles Gute und frohe Pfingsten! |
_________________ C++ Learning Start at: Di, 26. 04. 2012
C++ Status: Dorftrottel (me is nuub)
Zuletzt bearbeitet von cyberleon am 01:06:02 28.05.2012, insgesamt 2-mal bearbeitet |
|
 |
Martin Richter
Moderator
Benutzerprofil
Anmeldungsdatum: 18.04.2006
Beiträge: 14186
|
Martin Richter Moderator
08:53:53 28.05.2012 Titel: |
|
Zitieren |
Also dieser Code ist mit x-Fehlern gesprickt.
static HDC h1DC; ??? verdeckt globalö h1DC. Gleiches für h2DC.
Hier kann doch gar nichts funktionieren.
Typisches GDI leak.
Wenn Du ein Objektin einen GDI Kontext selektierst (SelectObject), dann gekomst Du das alte Objekt zurück. Dein Objekt bleibt im Kontext und kann nicht freigegeben werden, wenn der Kontext gelöscht wird bzw. wenn Du das Objekt zerstötren willst.
Immeer den Rückgabewert sichern und am Ende der GDI-Operation wieder das alte Objekt zurück selektieren.
Waruim bitte sind die PAINTSTRUCTs statisch? |
_________________ Martin Richter (MVP für C++) WWJD http://blog.m-ri.de
"A well-written program is its own heaven; a poorly written program is its own hell!" The Tao of Programming
|
|
 |
Greenhorn__
Mitglied
Benutzerprofil
Anmeldungsdatum: 27.06.2010
Beiträge: 38
|
Greenhorn__ Mitglied
14:23:41 28.05.2012 Titel: |
|
Zitieren |
Habe es nur kurz überflogen, aber ich würde es eher so machen ...
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | // Globals
HDC hdcMem ;
HBITMAP hbmBkGnd ;
HBITMAP hbmButton [8] ;
[ ..... ]
case WM_CREATE:
{
[ ..... ]
POINT pt [8] = {50, 40, ..., ..., usw.} ; // Positionen der Schaltflächen
// Background
hbmBkGnd = (HBITMAP)LoadImage(NULL, "test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC hDC = GetDC(hwnd);
hdcMem = CreateCompatibleDC(hDC);
ReleaseDC (hwnd, hDC) ;
// Button Images
for (int i = 0 ; i < 8 ; i++ )
{
hbmButton[i] = (HBITMAP)LoadImage(NULL, "test2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
// Buttons
hButton = CreateWindow( "button",
"Experience",
WS_CHILD | WS_VISIBLE | BS_BITMAP,
pt[i].x, pt[i].y, 100, 30,
hwnd,
NULL,
((LPCREATESTRUCT) lParam) -> hInstance,
NULL);
SendMessage (hButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmButton[i]) ;
}
[ ..... ]
}
case WM_DESTROY:
{
// Clean Background Bitmap
DeleteObject(hbmBkGnd);
DeleteDC(hdcMem);
// Clean Button Bitmap(s)
for (int i = 0 ; i < 8 ; i++ )
{
DeleteObject(hbmButton[i]);
}
PostQuitMessage(0);
return 0;
}
[ ..... ]
void PaintBackground(HWND hwnd)
{
PAINTSTRUCT ps;
HGDIOBJ hObjOld ;
RECT rect;
HDC hDC = BeginPaint( hwnd, &ps );
GetClientRect(hwnd, &rect);
BITMAP bmp;
GetObject(hbmBkGnd, sizeof(bmp), &bmp);
hObjOld = SelectObject(hdcMem, hbmBkGnd);
StretchBlt(hDC, 0, 0, rect.right, rect.bottom,hdcMem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
SelectObject(hdcMem, hObjOld);
Ellipse( hDC, 300, 100, 330, 130 ); // Ellipse: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Ellipse( hDC, 160, 100, 190, 130 ); // Ellipse: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Rectangle( hDC, 20, 90, 330, 100 ); // Rectangle: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
Rectangle( hDC, 220, 70, 300, 90 ); // Rectangle: ( hdc, Anfangspunktx, Anfangspunkty, Endpunktx, Endpunkty)
EndPaint( hwnd, &ps );
} | |
Ungetestet, können noch Fehler drin sein ...
Gruß
Greenhorn |
Zuletzt bearbeitet von Greenhorn__ am 18:16:30 28.05.2012, insgesamt 5-mal bearbeitet |
|
 |
cyberleon
Mitglied
Benutzerprofil
Anmeldungsdatum: 26.04.2012
Beiträge: 35
|
cyberleon Mitglied
23:24:09 30.05.2012 Titel: |
|
Zitieren |
Einen Keksbaum @ Martin!
Vielen lieben Dank für die Hinweise!
| Zitat: |
Also dieser Code ist mit x-Fehlern gesprickt.
static HDC h1DC; ??? verdeckt globalö h1DC. Gleiches für h2DC.
Hier kann doch gar nichts funktionieren.
Typisches GDI leak.
|
Da mein Code (seltsamerweise) funktionierte, hatte ich die Doppeldeklaration von h1DC übersehen. Außerdem war HDC hdc unnötig, ich habe es entfernt und alle hdc durch h2DC ersetzt.
| Zitat: |
Waruim bitte sind die PAINTSTRUCTs statisch?
|
Das "static" der Paintstruct war ein Relikt aus der Zeit, als ich mal versucht hatte, die Paintstruct "dauerhaft" das ganze Programm über zu benutzen - und erst beim WM_DESTROY wieder zu entfernen - was natürlich voll in die Hose ...
reicht völlig.
| Zitat: |
Wenn Du ein Objektin einen GDI Kontext selektierst (SelectObject), dann gekomst Du das alte Objekt zurück. Dein Objekt bleibt im Kontext und kann nicht freigegeben werden, wenn der Kontext gelöscht wird bzw. wenn Du das Objekt zerstötren willst.
Immeer den Rückgabewert sichern und am Ende der GDI-Operation wieder das alte Objekt zurück selektieren.
|
auf
http://msdn.microsoft.com/en-us/library/dd162957(v=vs.85).aspx
habe ich
| Zitat: |
The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type.
|
gefunden, daher ging ich davon aus, dass das neue Objekt das alte ersetzt und dabei das alte gelöscht wird. Nubfragen: Wie sichere ich den Rückgabewert? Mit "das alte Objekt zurückselektieren" meinst du sicher eben diesen gesicherten Rückgabewert, den ich selektieren muss?
Kekskiste @ Greenhorn!
Super Ideen!
Die mit dem point pt array hebe ich mir auf jeden Fall für später auf, solange ich mir noch nicht sicher über die Positionen der Buttons bin!
Besonders gefällt mir die Idee, den Buttons schon bei Createwindow dauerhaft die Images zuzuweisen!
Den Background in eine void am Programmende zu stecken werde ich auf jeden Fall auch ausprobieren!
Vielen lieben Dank euch zweien, ich melde mich wenn ich fertig bin (oder(wahrscheinlicher) versage 8)( )! |
_________________ C++ Learning Start at: Di, 26. 04. 2012
C++ Status: Dorftrottel (me is nuub)
|
|
 |
|
Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
|
|
|
|
|