Sign in to follow this  

Using BM_SETIMAGE (button control send message)

This topic is 4706 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to use sendmessage to set a button to a bitmap. The bitmap loads properly (I can display it elsewhere in WM_CREATE) but the LRESULT returned from SendMessage is always null and the bitmap is never displayed on the button. Whats wrong with this code?
HWND hWndButton = CreateWindow( 
	"BUTTON",   // predefined class 
	NULL,       // button text 
	WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON |BS_BITMAP,  // styles 
        //EDIT: ADDED BS_BITMAP
	// Size and position values are given explicitly, because 
	// the CW_USEDEFAULT constant gives zero values for buttons. 
	10,         // starting x position 
	10,         // starting y position 
	31,        // button width 
	31,        // button height 
	(HWND) hWnd,       // parent window 
	NULL,       // No menu 
	(HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
	NULL); // pointer not needed 
HBITMAP oldBMP = (HBITMAP)SendMessage(      
	// returns LRESULT in lResult     
	(HWND) hWndButton,	// handle to destination control     
	(UINT) BM_SETIMAGE, // message ID     
	(WPARAM) IMAGE_BITMAP,    // = (WPARAM) () wParam; 
	(LPARAM) (HBITMAP) bm_right_arrow   	// = (LPARAM) () lParam; 
	);  
	if (!oldBMP)
	{
		MessageBox (hWnd, "lResult = NULL", "NULL", MB_OK);
	}


Any ideas? [Edited by - d1sc0rd on January 18, 2005 7:16:24 PM]

Share this post


Link to post
Share on other sites
Your right, I was definitly missing that. Thank you.

It still doesn't work. I tried sending a different message in the same way. Tried sending BM_GETSTATE. It returns a null value as well. Still no bitmap displayed.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
The LRESULT should be null, because it returns the handle to the prev bitmap [if any], and since you have just created the button, it shouldn't have a bitmap set yet.

One possible reason the bmp isn't getting displayed is if it doesn't have the right lifetime, and is getting destroyed somewhere. For example if you use the MFC
CBitmap class and do this

SetBmpButton() //this is just pseudo-code, don't woory about syntax
{
CBitmap temp;
Temp.Load(IDB_BITMAP);
Button.SetBitmap(Temp);
}//since temp is local to this function, it gets destroyed here
//causeing the button to display nothing

Share this post


Link to post
Share on other sites
Couple of things.

I declared the bitmap locally. I did load the bitmap in WM_CREATE but I declared the bitmap at the top of the file (only file at this point). I also tried BitBlting the bitmap right after the call, and it works just fine.

I tried checking the return from the BM_GETSTATE as well. It should be returning 0 I think, which would be different from null, right?

Also I was wondering, I know that many GDI objects come with default settings that need to be sent back in, such as pens. I was under the impression that the BS_BITMAP setting would cause the Button to have an default object that would need to be returned in order to be destroyed. Would this not register as somehting other than null? I really don't know.

Share this post


Link to post
Share on other sites
I think you need to post more code since obviously the error doesn't lie in what you've shown us already. Also, If you're able to blit it to the window, why not just go ahead and make the button ownerdraw instead?

Share this post


Link to post
Share on other sites
If you loaded the bitmap locally, does that mean the HBITMAP is a local variable to your WndProc()? If so, is it declared static? If not then it wont keep its value between calls to WndProc().

Share this post


Link to post
Share on other sites
I added a second message to load the bitmap twice. The null check no longer shows up, so the send message is working somewhat anyway.
#define	WIN_CLASS_NAME	"UIClass"
#define WIN_APP_NAME "U101"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include ".\resource.h"
static HBITMAP bm_right_arrow;

HINSTANCE hInst;
HWND hWnd;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// The main window message handler
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
bm_right_arrow = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_RIGHT_ARROW));
if (!bm_right_arrow)
{
MessageBox(NULL, "Didn't Load", "ERROR", MB_OK);
}

HWND hWndButton = CreateWindow(
"BUTTON", // predefined class
NULL, // button text
WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_PUSHBUTTON, // styles

// Size and position values are given explicitly, because
// the CW_USEDEFAULT constant gives zero values for buttons.
10, // starting x position
10, // starting y position
30, // button width
30, // button height
(HWND) hWnd, // parent window
NULL, // No menu
(HINSTANCE) hInst,
NULL); // pointer not needed
HBITMAP oldBMP = (HBITMAP)SendMessage(
// returns LRESULT in lResult
(HWND) hWndButton, // handle to destination control
(UINT) BM_SETIMAGE, // message ID
(WPARAM) IMAGE_BITMAP, // = (WPARAM) () wParam;
(LPARAM) bm_right_arrow // = (LPARAM) () lParam;
);
oldBMP = (HBITMAP)SendMessage(
// returns LRESULT in lResult
(HWND) hWndButton, // handle to destination control
(UINT) BM_SETIMAGE, // message ID
(WPARAM) IMAGE_BITMAP, // = (WPARAM) () wParam;
(LPARAM) bm_right_arrow // = (LPARAM) () lParam;
);
if (!oldBMP)
{
MessageBox (hWnd, "lResult = NULL", "NULL", MB_OK);
}

break;
}
case WM_DESTROY:
PostQuitMessage(wParam);
break;


case WM_PAINT:
{
HDC hDC;
PAINTSTRUCT ps;

hDC = BeginPaint(hWnd, &ps);
HDC hDCMem = CreateCompatibleDC(hDC);

HBITMAP oldBM = (HBITMAP) SelectObject(hDCMem, (HBITMAP) bm_right_arrow);
BitBlt(hDC, 80, 80, 80, 80, hDCMem, 0, 0, SRCPAINT);

// What to do, oh, what to do...

EndPaint(hWnd, &ps);
break;
}
case WM_COMMAND:
{
switch (LOWORD (wParam))
{

}
}
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
hInst = hInstance;
WNDCLASSEX wcx;
MSG msg;

int cxScr = GetSystemMetrics(SM_CXSCREEN);
int cyScr = GetSystemMetrics(SM_CYSCREEN);

// Define elements of the window class, register
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcx.lpszMenuName = MAKEINTRESOURCE(IDMAINMENU);
wcx.lpszClassName = WIN_CLASS_NAME;
wcx.hIconSm = NULL;

if(!RegisterClassEx(&wcx))
return 0;

// Create the window: centered, and half the height and width of the screen
if(!(hWnd = CreateWindowEx(NULL,
WIN_CLASS_NAME,
WIN_APP_NAME,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
(int)(cxScr / 4), (int)(cyScr / 4),
(int)(cxScr / 2), (int)(cyScr / 2),
NULL,
NULL,
hInstance,
NULL)))
return 0;

// Main loop here
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

Share this post


Link to post
Share on other sites
It looks to me like your problem is scope. You create hWndButton inside of the WM_CREATE case, so as soon as the windows procedure exits, your handle to the button is destroyed.

I believe that windows (such as buttons) get detroyed automatically, without you having to call a release function.

I'd say try making your button global, or static inside the WndProc.


kingnosis

Share this post


Link to post
Share on other sites
Quote:
Original post by kingnosis
It looks to me like your problem is scope. You create hWndButton inside of the WM_CREATE case, so as soon as the windows procedure exits, your handle to the button is destroyed.

I believe that windows (such as buttons) get detroyed automatically, without you having to call a release function.

I'd say try making your button global, or static inside the WndProc.

kingnosis


Hi kingnosis.

These are only handles. Handles are not objects, therefore they don't have a destructor which is called when you go out of scope. A handle is 'destroyed' when
1) you DestroyWindow() it
2) you DestroyWindow() one of its ancestor.

@d1sc0rd:

a)the first BM_SETIMAGE was already working - the NULL return was correct, since it returns the previous associated bitmap handle. Since you just created the button, tyhere was no associated bitmap, hence the NULL return. You don't have to call it twice - just discard the NULL check. If you want to verify that your bitmap had been set by your SendMessage() call then you can issue another SendMessage() with BM_GETIMAGE. This will return you the handle of the current button bitmap.

Quote:
Original post by d1sc0rd
I tried checking the return from the BM_GETSTATE as well. It should be returning 0 I think, which would be different from null, right?

b) #define NULL 0
So I guess that 0 and NULL should be the same :)

c) take care about your WM_CREATE handling. The MSDN states:
Quote:
WM_CREATE return code
If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.


d) I see nothing wrong with your code. Are you sure you see the button and no bitmap on it? Are you sure IDB_RIGHT_ARROW is correct? Can you try with a system bitmap (hBmp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK)); or hBmp = LoadBitmap(NULL, OBM_CHECK);, I don't remember which one is teh win).

Share this post


Link to post
Share on other sites
Well I'm utterly frustrated. A couple things.

1) I'm fairly sure the bitmap is loaded properly. Reason I'm saying this is because I am able to display it in the wm_paint. However, I tried using MAKEINTRESOURCE(OBM_CHECK). So now I'm not using my own resource. The check is blitted from the WM_PAINT message, but again does not appear on the button.

I'm at a loss here. Windowz > d1sc0rd

Share this post


Link to post
Share on other sites
It'll work if you send BM_SETIMAGE outside your WM_CREATE handler (try putting it in a WM_SHOWWINDOW handler). Why exactly that is, I'm not sure.

BTW, minor comment: don't do #include ".\resource.h". This attempts to escape the r character. Do one of the following:
#include "resource.h"
#include "./resource.h"
#include ".\\resource.h"
Never use a single backslash in a path name.

[Edited by - Kippesoep on January 20, 2005 4:55:10 AM]

Share this post


Link to post
Share on other sites
OK I have found the problem, but I don't understand why it is a problem.
If I do not select the bitmap into the DC in WM_PAINT, the bitmap now appears on the button. Why can the bitmap not be selected into both?

Share this post


Link to post
Share on other sites
Quote:
Original post by Kippesoep
It'll work if you send BM_SETIMAGE outside your WM_CREATE handler (try putting it in a WM_SHOWWINDOW handler). Why exactly that is, I'm not sure.

BTW, minor comment: don't do #include ".\resource.h". This attempts to escape the r character. Do one of the following:
#include "resource.h"
#include "./resource.h"
#include ".\\resource.h"
Never use a single slash in a path name.


Side note: you can include "a\b\c.h" - this is processed by the precompiler, not the compiler itself, and you don't need to double your '\'. But this will probably don't work on non-Windows systems. As a consequence, I agree with Kippesoep and I believe it is better to avoid '\' in included file path.

Quote:
Original post by d1sc0rd
OK I have found the problem, but I don't understand why it is a problem.
If I do not select the bitmap into the DC in WM_PAINT, the bitmap now appears on the button. Why can the bitmap not be selected into both?


Maybe this has something to do with your WM_PAINT handling. The msdn says that it should return 0 if you processed the message or non zero if you didin't processed it. You may try to return 1 at the end of your WM_PAINT handler. I'm not sure this will do what you want to do - and maybe you'll have some other problems.

Regards,

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I had the same problem and found a solution.
The call to CreateWindow does not return until that thread has passed through your winproc WM_CREATE code. The bitmap has not been created yet at that point. (At least that was the case in my code.) That is why it worked when the BM_SETIMAGE message was moved to the WM_SHOWWINDOW code. As soon as I realized this it all fell into place. I was able to use the same bitmap in multiple buttons and to BitBlt onto the client window (for testing) during WM_PAINT processing. I hope this helps.

joe.remes@dbcsoftware.com

Share this post


Link to post
Share on other sites
Addendum to the above (I just registered). The call to CreateWindow that I referred to is not the one making the button but the call to make the main window, usually found in WINMAIN.

Share this post


Link to post
Share on other sites

This topic is 4706 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this