C++ & Win32

Started by
9 comments, last by NuffSaid 23 years, 3 months ago
Hi. I''m trying to implement my own Window class and I''ve run into a problem. LRESULT CALLBACK CMainWnd::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { //usual message handling stuff here } Somewhere in the file, I have the statement void CMainWnd::Create() { //stuff deleted wc.lpfnWndProc = MsgProc;//this generates the error //more stuff deleted } This line generates the following error. cannot convert from ''long (__stdcall CMainWnd::*)(struct HWND__ *,unsigned int,unsigned int,long)'' to ''long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)''. Any idea how I should proceed? I''ve tried casting MsgProc to a WNDPROC and it doesn''t work. Any help would be appreciated. Thanks ========================================== In a team, you either lead, follow or GET OUT OF THE WAY.
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
Advertisement
Your problem is that lpfnWndProc is expecting to point to a normal style C function which can be called by the Win32 API. You''re passing a C++ class method which requires that the object pointer be the first parameter passed - something which is done implicitly done with the C++ language. So, the compiler correctly flags an error for you.

To get around this you have to make your MsgProc method a static member of the class. In your class definition, make sure it''s:

static LRESULT CALLBACK MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

That''s all.

Unfortunately this leaves you with the fact that you have a message handler that when called, doesn''t know which object (class instance) it belongs to; it doesn''t have a ''this'' pointer. To rectify this you can attach the object to the window after you''ve created it using the window''s user data:

// After CreateWindowEx call...
SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);

And within your MsgProc method you retrieve the ''this'' pointer to access your object members (if you need to, that is):

CMainWnd *this_ptr = (CMainWnd *)GetWindowLong(hWnd, GWL_USERDATA);

Hope that helps...
I did think about using a static member function. But what happens if there are multiple instances of the same class. What would happen to the Message handler? Or do I make it a virtual static function (is that possible ?) and override it when necessary?

Thanks.

Edited by - NuffSaid on December 29, 2000 11:29:00 AM
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
That''s why I suggested attaching the instance pointer to the window itself. So when the message handler gets called you can retrieve the appropriate instance, regardless of how many different instances their are:

LRESULT CALLBACK CMainWnd::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CMainWnd *this_ptr = (CMainWnd *)GetWindowLong(hWnd, GWL_USERDATA);

// Example use...

if (uMsg == WM_CLOSE) this_ptr->IsOpen = FALSE;
}
virtual statics are not possible
quote:Original post by Anonymous Poster

virtual statics are not possible


That was silly You can tell I''m new to C++.

==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
//make a global pointer to an object of your class.
CMainWnd *g_pMyWnd;


// prototype a window proc
static LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM );

//make a constructor:
CMainWnd::CMainWnd ( )
{
g_pMyWnd = this;
}


// in the body of the window proc....
LRESULT CALLBACK
WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
if( g_pMyWnd )
return g_pMyWnd->MsgProc( hWnd, msg, wParam, lParam );

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



// when you fill in the window class, set the WndProc to be the
// default window procedure.






I''m not even going to go into why using global variables are a Bad Thing.

However, even if they were considered a good programming technique I would still avoid it like the plague in this instance.

1. What happens when WindowA creates a WindowB? Now the global variable is changed to WindowB, and after windowB closes the variable will STILL point to WindowB even tho it should be WindowA. Yes, you COULD save the previous value and reset it after the window closes, but that is sloppy.

2. You will never be able to use it with modeless windows (ie, you''ll never be able to have a window with a toolbar) because both windows will be using the same MsgProc function at the same time.

Use AP''s suggestion of storing it using SetWindowLong with GWL_USERDATA.


- Houdini
- Houdini
Alright. Now I’ve managed to get the code to compile properly, but I get illegal operations. Here’s what I’m doing.

int CMainWnd::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//usual message handling stuff here
}

static LRESULT CALLBACK CmainWnd::CallBackFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CmainWnd *Wnd = (CMainWnd *)GetWindowLong (hWnd, GWL_USERDATA);

//the following line gives problems with certain messages
Return Wnd->MsgProc(hWnd, uMsg, wParam, lParam);//call the window’s own Msg Handling function
}

void CMainWnd::Create()
{
//stuff deleted
wc.lpfnWndProc = CallBackFunc;//this works now
//more stuff deleted

m_hWnd = CreateWindow(blah);

SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);

//more stuff
}

The problem I’m facing is that CreateWindow makes calls to the window’s callback function too. That means that the callback function I wrote won’t work as expected because the this pointer hasn’t been stored yet. I’m still in dire need of ideas. Or more precisely, I need to know what Messages are sent to the callback function by CreateWindow or if there is a way to store the this pointer in m_hWnd without using SetWindowLong.
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
I got it to work

static LRESULT CALLBACK CallBackFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

if((uMsg == WM_NCCREATE))
{
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
CMainWnd * Wnd = (CMainWnd *)lpcs->lpCreateParams;

assert(Wnd != NULL);
SetWindowLong(hWnd, GWL_USERDATA,(LONG)Wnd);

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

CMainWnd *Wnd = (CMainWnd *)GetWindowLong(hWnd, GWL_USERDATA);//get the class pointer


if(Wnd)
{
//call the window''s own msg Proc
return Wnd->MsgProc(hWnd, uMsg, wParam, lParam);
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

This is how I made my static callback function. What it does is check if the message is WM_NCCREATE. If it is, then it stores the pointer to the class in the UserData of the window. If it is any other message, it calls GetWindowLong to retrieve the this pointer then checks if the return was NULL. If it isn’t, it calls the class’s callback function.
I think it’s a poor hack, but I just couldn’t find any other way to do it. I''m open to suggestions.
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.

This topic is closed to new replies.

Advertisement