noch einmal BM_SETIMAGE / ERLEDIGT



  • Hallo,
    ich wollte auch einmal mit einer Bitmap auf einem Button experimentieren, bekomme aber einen Parameterfehler gemeldet.
    Mein auf das nötigste gekürzter Code:

    #include <windows.h>
    #include <sstream>
    
    #define  ID_BUTTON3    3
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("ButtonTest") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         = 0; //CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {    // UNICODE-Compilierung ist die einzige realistische Fehlermöglichkeit 
              MessageBox (NULL, TEXT ("Programm arbeitet mit Unicode und setzt Windows NT voraus!"), 
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
    
         hwnd = CreateWindow (szAppName, TEXT ("Test des BitmapButtons"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              500, 300,
                              NULL, NULL, hInstance, NULL) ;
    
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
         while (GetMessage (&msg, NULL, 0, 0))
              {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
              }
         return msg.wParam ;
         }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	  static HBITMAP bitmap;
    
         switch (message)
         {
    	     case WM_CREATE:
    		       {
    		       	//Button erstellen
    		          HWND hbtn = CreateWindow("BUTTON", "BT1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 235, 5, 100, 50, hwnd,
    		                       (HMENU) ID_BUTTON3, (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL);
    
    				 	//Bitmap und Device Context erstellen und Bitmap in DC selecten
    					 HDC hdc = CreateCompatibleDC(NULL);
    					 if(!hdc)
    					 	 MessageBox(hwnd, "... creating DC", "Error ...", MB_OK);
    
    					 if(!(bitmap = CreateCompatibleBitmap(hdc, 100, 50)))
    					 	 MessageBox(hwnd, "... creating Bitmap", "Error ...", MB_OK);
    
    					 SelectObject(hdc, bitmap);
    
    					 //Bitmap mit weißem Brush füllen
    					 RECT rect = {0, 0, 100, 50};
    					 FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
    
    					 //DC wird nicht mehr benötigt
    					 DeleteDC(hdc);
    
    					 //Bitmap an den Button binden
    					 LRESULT res = SendMessage(hbtn, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmap);
    					 if(!res)
    					 {
    					 	int err = GetLastError();  //ergibt 87: The parameter is incorrect.
    					 	std::stringstream ss;
    					 	ss << "... assigning Bitmap:\n" << err;
    					 	MessageBox(hwnd, ss.str().c_str(), "Error ...", MB_OK);
    					 } 
    				 }
    
    	          return 0 ;
    
    		  case WM_PAINT:
    		  		 HDC hdc, chdc;
    		  		 PAINTSTRUCT ps;
    
    		  		 hdc = BeginPaint(hwnd, &ps);
    
    		  		 //meine Bitmap
    		  		 chdc = CreateCompatibleDC(NULL);
    		  		 SelectObject(chdc, bitmap);
    
    				 //ins Fenster blitten, klappt, an der Bitmap scheint es also nicht zu liegen
    				 BitBlt(hdc, /*Pos xy*/30, 120, /*größe xy*/100, 50, chdc, 0, 0, SRCCOPY);
    
    				 EndPaint(hwnd, &ps);
    
    				 DeleteObject(chdc);
    
    				 return 0;
    
    	     case WM_DESTROY :
    	     		 DeleteObject(bitmap);
    	          PostQuitMessage (0) ;
    	          return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    

    Wenn ich in WM_PAINT die Bitmap ins Fenster blitte, klappt das einwandfrei, aber die SendMessage - Instruktion auf Zeile 82 schlägt fehl und ich habe keinen Schimmer, warum. Erkennt jemand meinen Fehler?





  • Ja ... nun bin ich ein wenig verwirrt:
    Auf http://msdn.microsoft.com/en-us/library/bb761822 habe ich den Eindruck gewonnen, das würde auch ohne BS_BITMAP funktionieren, da steht sinngemäß:
    Wenn BS_ICON und BS_BITMAP NICHT gesetzt sind, aber BM_SETIMAGE gesendet wird, wird das Icon (die Bitmap?) UND Text angezeigt.

    Edit: das Gesagte steht übrigens auch in dem von Dir genannten Link ganz unten auf der Seite.

    Wenn ich in meinem obigen Beispiel dem Button nun aber trotzdem mal BS_BITMAP hinzufüge, bekomme ich von GetLastError 0 zurück, was aber bedeutet, daß SendMessage immer noch 0 zurückgibt - es sollte aber im Erfolgsfall doch != 0 liefern, oder?

    Ausserdem ist mein Button nun grau, d.h. weder die (weisse) Bitmap, noch der Text (BT1) wird angezeigt ...



  • Belli schrieb:

    Wenn ich in meinem obigen Beispiel dem Button nun aber trotzdem mal BS_BITMAP hinzufüge, bekomme ich von GetLastError 0 zurück, was aber bedeutet, daß SendMessage immer noch 0 zurückgibt - es sollte aber im Erfolgsfall doch != 0 liefern, oder?

    Ausserdem ist mein Button nun grau, d.h. weder die (weisse) Bitmap, noch der Text (BT1) wird angezeigt ...

    Hat niemand mehr eine Idee für mich, ich komme nicht mehr weiter, ich habe noch den Buttontext ("BT1") durch "" ersetzt, aber das hat erwartungsgemäß nichts geändert ...



  • Im XP-Style gehts bei mir!

    #pragma comment(linker, "/MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
    


  • XP, äh, ich hab ja Windows 7... Common Controls 6 halt.


  • Mod

    Evtl. nimmt die klassiche Button Klasse nur DIBs.
    Während die Commnon Control 6.0 Klasse alles frisst auch device dependant bitmaps.

    BTW: Dein Umgang mit SelectObject erzeugt ein GDI-Leak.



  • Ich habs jetzt.

    HDC hdc = CreateCompatibleDC(NULL);
    bitmap = CreateCompatibleBitmap(hdc, 100, 50);
    

    erzeugt nicht die korrekte Bitmap. Es muss so aussehen:

    HDC hdc = CreateCompatibleDC(NULL);
    HDC myHdc = GetDC(hwnd);
    bitmap = CreateCompatibleBitmap(myHdc, 100, 50);
    

    Martin Richter schrieb:

    BTW: Dein Umgang mit SelectObject erzeugt ein GDI-Leak.

    Du meinst, weil die Bitmap in dem Speichergerätekontext nicht wiederhergestellt wird? Dazu schreibt Petzold, das sei bei Speichergerätekontexten (bzgl. der Bitmaps nicht nötig). Interessanterweise macht er das aber auch bei zB Brushes iVm Speichergerätekontexten nicht in seinen Beispielen, was mich etwas verwirrt. Wie gesagt, für Bitmaps erwähnt er es ausdrücklich auf Seite 636 ganz oben, 5. Auflage deutsch.


  • Mod

    Du selektierst die Bitmap in einen DC nd merkst Dir die alte nicht.
    d.h.
    1. Die alte Bitmap di eim DC war wird ciht freigegeben.
    2. Die einselektierte Bitmap ist im DC in Benutzung und wird nicht freigegeben.



  • Ähh, jetzt verwirrst Du mich auch ...
    Die von mir erstellte Bitmap gebe ich bei WM_DESTROY frei - der DC existiert zu dem Zeitpunkt ja längst nicht mehr. Die, die vorher standardmäßig drin ist, gebe ich in der Tat nicht frei - weil sie mir nicht gehört und ich sie ja auch nicht selbst erstellt habe <- kann ich die überhaupt erfolgreich freigeben?

    Wie ist das denn nun mit Speichergerätekontexten? Liegt der Petzold falsch, wenn er schreibt, dass die Bitmap nicht wiederhergestellt werden muss, bevor der DC freigegeben wird?

    Er schreibt an der oben zitierten Stelle: ... weil die GDI beim Anlegen von Speichergerätekontexten immer wieder dasselbe Standard-Bimap mit 1 x 1 Pixeln verwendet ...

    das hört sich so an, als wenn ich das nicht freigeben dürfte/sollte?!



  • Belli schrieb:

    Ich habs jetzt.

    HDC hdc = CreateCompatibleDC(NULL);
    bitmap = CreateCompatibleBitmap(hdc, 100, 50);
    

    erzeugt nicht die korrekte Bitmap. Es muss so aussehen:

    HDC hdc = CreateCompatibleDC(NULL);
    HDC myHdc = GetDC(hwnd);
    bitmap = CreateCompatibleBitmap(myHdc, 100, 50);
    

    Das glaube ich nicht, und hat bei mir auch nichts geändert.



  • Hier nochmal der komplette Code, wie er bei mir nun das tut, was ich erwarte - bis auf eine Ausnahme: Die Message-Box, die signalisiert, dass SendMessage(..., BM_SETIMAGE, ...); 0 zurückgibt, kommt immer noch ... aber ich habe mein Image auf dem Button:

    #include <windows.h>
    #include <sstream>
    
    #define  ID_BUTTON3    3
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("ButtonTest") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         = 0; //CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {    // UNICODE-Compilierung ist die einzige realistische Fehlermöglichkeit 
              MessageBox (NULL, TEXT ("Programm arbeitet mit Unicode und setzt Windows NT voraus!"), 
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
    
         hwnd = CreateWindow (szAppName, TEXT ("Test des BitmapButtons"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              500, 300,
                              NULL, NULL, hInstance, NULL) ;
    
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
         while (GetMessage (&msg, NULL, 0, 0))
              {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
              }
         return msg.wParam ;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	  static HBITMAP bitmap;
    
         switch (message)
         {
    	     case WM_CREATE:
    		       {
    		       	//Button erstellen
    		          HWND hbtn = CreateWindow("BUTTON", "", WS_CHILD | BS_BITMAP | WS_VISIBLE | BS_PUSHBUTTON, 235, 5, 100, 50, hwnd,
    		                       (HMENU) ID_BUTTON3, (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL);
    
    				 	//Bitmap und Device Context erstellen und Bitmap in DC selecten
    					 //HDC hdc = CreateCompatibleDC(NULL);
    					 HDC thdc = GetDC(hwnd);
    					 HDC hdc = CreateCompatibleDC(thdc);
    
    					 bitmap = CreateCompatibleBitmap(thdc, 100, 50);
    
    					 ReleaseDC(hwnd, thdc);
    					 HBITMAP altBitMap = (HBITMAP)SelectObject(hdc, bitmap);
    
    					 //Bitmap mit Brush füllen
    					 RECT rect = {0, 0, 100, 50};
    					 HBRUSH brush = CreateSolidBrush(RGB(0, 250, 0));
    					 FillRect(hdc, &rect, brush);
    					 DeleteObject(brush);
    					 int altBkMode = SetBkMode(hdc, TRANSPARENT);
    					 DrawText(hdc, "Okay", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    
    					 //DC wird nicht mehr benötigt
    					 SelectObject(hdc, altBitMap);
    					 SetBkMode(hdc, altBkMode);
    					 DeleteDC(hdc);
    
    					 //Bitmap an den Button binden
    					 LRESULT res = SendMessage(hbtn, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmap);
    					 if(!res)
    					 {
    					 	int err = GetLastError();  //ergibt 87: The parameter is incorrect.
    					 	std::stringstream ss;
    					 	ss << "... assigning Bitmap:\n" << err;
    					 	MessageBox(hwnd, ss.str().c_str(), "Error ...", MB_OK);
    					 } 
    				 }
    
    	          return 0 ;
    
    		  case WM_PAINT:
    		  		 HDC hdc, chdc;
    		  		 PAINTSTRUCT ps;
    		  		 HBITMAP altBitMap;
    
    		  		 hdc = BeginPaint(hwnd, &ps);
    
    		  		 //meine Bitmap
    		  		 chdc = CreateCompatibleDC(hdc);
    		  		 altBitMap = (HBITMAP)SelectObject(chdc, bitmap);
    
    				 //ins Fenster blitten, klappt, an der Bitmap scheint es also nicht zu liegen
    				 BitBlt(hdc, /*Pos xy*/30, 120, /*größe xy*/100, 50, chdc, 0, 0, SRCCOPY);
    
    				 EndPaint(hwnd, &ps);
    
    				 SelectObject(chdc, altBitMap);
    				 DeleteObject(chdc);
    
    				 return 0;
    
    	     case WM_DESTROY :
    	     		 DeleteObject(bitmap);
    	          PostQuitMessage (0) ;
    	          return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    


  • Alter Frickler 😃

    Der Rückgabewert von BM_SETIMAGE sagt nichts über einen Fehler aus... http://msdn.microsoft.com/en-us/library/bb761822



  • Ah, Schei....
    ich hab das 'previously' völlig falsch interpretiert ... haha, ich hatte erwartet, dass ich ein HANDLE auf das soeben von mir 'angehängte' Image bekomme, aber es ist natürlich das, was vor meinem SendMessage mit dem Button assoziiert war, mithin 0, nu fällt es mir wie Schuppen aus den Haaren ...

    Vielen Dank an alle, die sich mit meinem Problem beschäftigt haben.


Anmelden zum Antworten