Best Method for Storing This Pointer for Use in Wndproc

Class method for WndProc

I personally would not use either of these methods. The global variable approach works, but feels dirty. Especially with the lock. And the CBT hook is, well over the top. Although it points in the right direction.

The standard way to pass state information to your window procedure during creation is through lpParam parameter of CreateWindow or CreateWindowEx. So the technique is as follows:

  1. Pass your instance pointer in the lpParam parameter of CreateWindow or CreateWindowEx.
  2. Read this value in your WM_NCCREATE handler. That message supplies the information as part of the CREATESTRUCT struct.
  3. Still in WM_NCCREATE call SetWindowLongPtr to set the user data of the window to the instance pointer.
  4. All future calls to the window procedure can now obtain the instance pointer by calling GetWindowLongPtr.

Raymond Chen illustrates the details here: How can I make a WNDPROC or DLGPROC a member of my C++ class?

Use object method as WinApi WndProc callback

The usual is something on this order:

#include <windows.h>

class BaseWindow {

static LRESULT CALLBACK internal_WndProc(HWND hWnd, int msg, WORD wParam, LONG lParam) {
BaseWindow *c = (BaseWindow *)GetWindowLong(hWnd,GWLP_USERDATA);

if (c == NULL)
return DefWindowProc(hWnd, msg, wParam, lParam);

return c->WindowProc(hWnd, msg, wParam, lParam);
}

public:
virtual int WindowProc(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam) = 0;

BaseWindow(HINSTANCE instance) {
WNDCLASS window_class = {0};
HWND window;
HMENU my_menu;

window_class.lpfnWndProc = (WNDPROC)internal_WndProc;
/* fill in window_class here */
RegisterClass(&window_class);

window = CreateWindow(
"My Application", "Stupidity",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, my_menu, instance, NULL);

// save the address of the class as the Window's USERDATA.
SetWindowLong(window, GWLP_USERDATA, (long)this);
}
};

With this, you derive a class from BaseWindow. In your derived class, you provide a "WindowProc" that overrides the (pure virtual) one in BaseWindow. The trick here is fairly simple: since you can't pass a parameter directly, you store the address of the class in the window's GWLP_USERDATA, then in the window proc (try to) retrieve that and use it to call the derived class' virtual window proc.

As an aside, note that this is a sketch, not a finished work (so to speak). Though it should compile as-is, the result won't actually work unless you fill in a fair number of pieces that aren't here (e.g., the other fields of the WNDCLASS structure being only one of the most obvious).

Win32: More object oriented window message handling system

Suggestion: make WndProc virtual in your window base class. Request Windows to allocate extra memory for each window instance big enough to store a pointer (use cbWndExtra). When creating a window, put a pointer to your windows class object into this extra memory associated with each window instance using SetWindowLongPtr. In your static sWndProc, retrieve this pointer using GetWindowLongPtr and call your window base class function virtual WndProc. In my opinion, it's more neat way than having a whole extra map object dedicated to dispatching WndProc calls.

EDIT: Plus, you do realize, in your code you are trying to register Windows window class every time you create an object of your window class? If you create just one window this is technically fine, I guess, but even then it's error-prone design. Windows window class should be registered just once, not every time you create a window with this Windows window class.

EDIT: Also, in your code, if you are not processing Windows message in your WndProc, you code will call DefWindowProc twice for this message: first time in member function within switch clause, second time in static sWndProc. DefWindowProc shouldn't be called twice on the same message.

EDIT: Sorry, I missed your actual question before somehow, I thought your post was about design, not about WM_CREATE. In order to process WM_NCCREATE, WM_NCCALCSIZE or WM_CREATE in a uniform way, you can set lpParam in call to CreateWindowEx to, again, pointer to the object of your window class. This parameter will be passed to your static sWndProc as a member of CREATESTRUCT with WM_NCCREATE and WM_CREATE. You can process, say, WM_NCCREATE (first message to be sent) inside static sWndProc, get this pointer to your object, use SetWindowLongPtr to put it into window instance extra memory, and then use it to call member function WndProc (just be careful about calling not fully created object of your windows class, if you call CreateWindowEx from its constructor). This way, you don't need to worry about "low-level" Windows message dispatching anywhere else in your program. You, of course, will still need your member function WndProc to dispatch messages to actual function calls.

Storing a function pointer as a member within a class

You have to enclose the function pointer name in brackets like this: void (*SocketFunction)(...)
So instead of your method declaration:

void registerSocketFunction( void (*SocketFunction(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)));

You have to write:

void registerSocketFunction( void (*SocketFunction)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam));

Also rewrite your method body to this:

void NetworkConnection::registerSocketFunction( void (*SocketFunction)(HWND, UINT, WPARAM, LPARAM))
{
WMSocketFunction = SocketFunction;
};

Next thing, i think your HandleEvents is unfinished yet, you have a syntax error there, i think you wanted to call the function behind the pointer.

void NetworkConnection::HandleEvents(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
WMSocketFunction(hwnd,msg,wParam,lParam);
}

What's an alternative to GWL_USERDATA for storing an object pointer?

SetWindowLongPtr was created to replace SetWindowLong in these instances. It's LONG_PTR parameter allows you to store a pointer for 32-bit or 64-bit compilations.

LONG_PTR SetWindowLongPtr(      
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);

Remember that the constants have changed too, so usage now looks like:

SetWindowLongPtr(hWnd, GWLP_USERDATA, this);

Also don't forget that now to retrieve the pointer, you must use GetWindowLongPtr:

LONG_PTR GetWindowLongPtr(      
HWND hWnd,
int nIndex
);

And usage would look like (again, with changed constants):

LONG_PTR lpUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
MyObject* pMyObject = (MyObject*)lpUserData;

Finding WndProc Address

Perhaps you are being stymied because you are asking for the wrong version of the windowproc.

Window Procs, like applications, occur in two flavors: ansi and unicode. Windows cannot return a raw pointer to a ansi window to a unicode application, or visa versa, as they will attempt to call it with the wrong string type.

So, there is no GetWindowLongPtr function. Its a macro that resolves to two 'real' functions the windows api provides: GetWindowLongPtrA and GetWindowLongPtrW. If the window is a unicode window, and GetWindowLongPtrA is called windows will return a handle instead of the raw pointer, so that it can intercept calls (made via CallWindowProc) and marshal the string's from ansi to unicode. The opposite conversion holds the other way.

Even if you call the correct function, you still might get a handle back - its completely possible that ansi code has subclassed a unicode window. so the windowproc has been completely replaced by one of the callWindowProc handles.

In that case - tough luck I guess.

Pointer to function of derived class withing parent class

You cannot use a non-static class method as a window procedure callback. The parameter list is not compatible due to the hidden this pointer. What you have to do instead is:

  1. In BaseWindow, define a static method as the actual message callback registered with RegisterClassEx(), and then define a separate virtual method for processing messages. Have the BaseWindow implementation of the virtual method call DefWindowProc(), and descendants that override the virtual method need to call the base method for unhandled messages.

  2. Pass the object's this pointer as the lpCreateParam of CreateWindow/Ex().

  3. In the WM_NCCREATE message handler, retrieve the lpCreateParam value from the message and assign it to the HWND using either SetWindowLongPtr(GWL_USERDATA) or SetProp(), then type-cast the value to a BaseWindow* pointer and use it to call the virtual method.

  4. For subsequent messages, use GetWindowLongPtr(GWL_USERDATA) or GetProp() to retrieve the BaseWindow* pointer from the HWND and use it to call the virtual method.



Related Topics



Leave a reply



Submit