Sign in to follow this  
SL33pY_Kr3W

Making WinProc a member function

Recommended Posts

Hello, I'm writing a simple wrapper class for creating and managing windows in the Win32 API. I got to the point where I needed to give the window message handler access to data members of the wrapper class, and decided to include it as a member function. However, when I tried to assign its pointer to the lpfnWndProc field in the WNDCLASSEX structure I got an error saying that it wasn't possible. The question is: How can you give WinProc access to private data members without making the wrapper class global (and making WinProc a friend fucntion), or some cheap quick fix like that?

Share this post


Link to post
Share on other sites
My implemenation does just that - the global WndProc is a friend of the class, and calls a private WndProc of the application class. I don't know if there's a "better" way to do it, but this method is the simplest and most straightforward to use.

Share this post


Link to post
Share on other sites
Use SetWindowLongPtr to store a pointer to your object in the window instance, and register a trampoline (non-member or static member) WndProc that retrieves that pointer with GetWindowsLongPtr, casts it to the appropriate type then uses it to call the appropriate member function.

The amount extra storage for that pointer must be specified when the window class is registered.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
Use SetWindowLongPtr to store a pointer to your object in the window instance, and register a trampoline (non-member or static member) WndProc that retrieves that pointer with GetWindowsLongPtr, casts it to the appropriate type then uses it to call the appropriate member function.
Trampoline WndProc? Juh? Never heard a non-member/static member function be called that before.

Anyway, I have a program thta does what Fruny is suggesting, but I use hooks in my "trampoline" WndProc to do the job.

Share this post


Link to post
Share on other sites
Actually, I just had an idea. You see, my main class is what coordinates the actual Window classes. All they do is store important information, like the window handle/rendering context etc. The main class does things like creating the individual windows and dispatching messages to a single WndProc. So, could I just eliminate the need for a WndProc ( and just create a dumby WndProc for winclass.lpfnWndProc ) by doing all of the message handling in a regular member function of the main class?

An example:



//inside of a member function of the central class

while( PeekMessage( &msg, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );

//then cycle through container containing the Window classes until a matching
//handle to window is found.


//DO NOT dispatch to WnDProc

//do specific message handling for the particular window
switch( msg.message )
{
case WM_CREATE:
//stuff...
break;

case WM_CLOSE:
//you get the idea...
break;

default:break;
}

//and then something along the lines of....
return( DefWindowProc( msg.hwnd, msg.message, msg.wParam, msg.lParam ) );
}


Share this post


Link to post
Share on other sites
Quote:
Original post by SL33pY_Kr3W
So, could I just eliminate the need for a WndProc ( and just create a dumby WndProc for winclass.lpfnWndProc ) by doing all of the message handling in a regular member function of the main class?
I suppose you could, but I would recommend against it because with this method you'd have to determine which window generated the message and then handle that howerver, whereas if each window had its own instance of a WndProc then that step is handled for you. In other words, you could do it the way you've suggested, but it seems like you'd just be making things harder for yourself in the long run.

Share this post


Link to post
Share on other sites
Can't you just make the WndProc function a static member function?

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)?

EDIT: Ooh. If you need multiple windows this might be a problem. I'm going to leave this here in case it's any help though. [disturbed]

Share this post


Link to post
Share on other sites
I think you should stick to what Fruny has said about attaching the pointer of your object owning the window handle to the actual window itself

So your modifications to your WNDCLASSEX struct might be:

wcex.cbWndExtra = sizeof(Window*);

I put the sizeof(Window*) in there just in case sometime down the timeline we get a >4 byte pointer.



class Window {
public:
bool CreateWindow(); // Remeber to #undef CreateWindow otherwise compiler will spew, or you could name it different

protected:
virtual LRESULT HandleMessage(UINT umsg, WPARAM wparam, LPARAM lparam);

private:
HWND hwnd;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam);

};

bool Window::CreateWindow()
{
hwnd = CreateWindowEx(...);
if (!hwnd)
return false;

SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));

// Additional window setup code

return true;
}

LRESULT HandleMessage(UINT umsg, WPARAM wparam, LPARAM lparam)
{
return DefWindowProc(this->hwnd, umsg, wparam, lparam);
}

LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
// Get the window object attached to the window handle
Window* wnd = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));

if (wnd) return wnd->HandleMessage(umsg, wparam, lparam);
else return DefWindowProc(hwnd, umsg, wparam, lparam);
}




You can derive off Window and the correct window object will be called from your WndProc function.

Share this post


Link to post
Share on other sites
Quote:
Original post by clb
My implemenation does just that - the global WndProc is a friend of the class, and calls a private WndProc of the application class. I don't know if there's a "better" way to do it, but this method is the simplest and most straightforward to use.
I too do it this way.

I like trampolines[smile]

Share this post


Link to post
Share on other sites
My site is down unfortunately, so I can't link you to my article, but what I do is something like this:

// Header file:
class CWindow
{
public:
// ...

private:
static LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam);
LRESULT WndProc(UINT umsg, WPARAM wparam, LPARAM lparam);

protected:
HWND m_hWnd;
// ...
};

// Source file:
LRESULT CALLBACK CWindow::StaticWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
CWindow* pParent;

if(umsg == WM_NCCREATE)
{
pParent = ((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)pParent);
}
else
{
pParent = (CWindow*)GetWindowLongPtr(hWnd, GWL_USERDATA);
}

if(pParent) return pParent->WndProc(umsg, wparam, lparam);
else return DefWindowProc(hWnd, usmg, wparam, lparam);
}



And then you use StaticWndProc when you set up your WNDCLASS structure. You also need to use CreateWindowEx and pass this as the last parameter. Note that code is just off the top of my head and may not be accurate...

Share this post


Link to post
Share on other sites

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