Use object method as WinApi WndProc callback
Solution 1
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).
Solution 2
The other question you linked to only applies partially.
The WindowProc method does need to be static. Then right after the call to CreateWindowEx call SetWindowLongPtr with GWLP_USERDATA as the second parameter and this as the third one. That associates the HWND with the class instance. Then in the static WindowProc method call GetWindowLongPtr (again with GWLP_USERDATA) to get the WindowConsole instance that received the UI event.
That's all explained in detail here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx
Solution 3
I use a simple solution. The winproc is a template function. The message receiver is inside setwindowptr.
If the receiver has a function with the message name , for instance, onpaint , then the wm_paint is included at message switch.
http://www.thradams.com/codeblog/wndproc.htm
Tomáš Zato
It might be easier to hire new management than to get a new community of volunteers. - James jenkins If you play League of Legends, check my repository: http://darker.github.io/auto-client/ I no longer play and I am actively looking for someone to maintain the project. It helped thousands of people, literally.
Updated on June 20, 2022Comments
-
Tomáš Zato about 2 years
I'm trying to make a little class that displays console window in parent window. (you can imagine chat or debug info being displayed there)
Now, since diferent instanes do have different private variables (such as message array or parent window), I need to use non-static method as callback for the Windows events.
I have thought of ways, where I'd pass the actual class instance to static callback function and then called the proper method on it, but in winAPI, everything is done usingTranslateMessage
andDispatchMessage
giving me no chance to use arguments of my own.
I found some code here: Class method as winAPI callback, but I don't understand it, and I think it is not exactly what I need. If it is, then please give me further explanation of code provided.
Error I get:error: argument of type 'LRESULT (
WindowConsole::
)(HWND__, UINT, WPARAM, LPARAM)' does not match 'LRESULT (*
)(HWND__, UINT, WPARAM, LPARAM)'I don't know what that star in brackets means, but this is what does not match.
And the code:class WindowConsole { char messages[255][255]; HWND mainWindow; public: int width; int height; inline HWND create(HWND parent); inline bool update(); inline LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); }; HWND WindowConsole::create(HWND parent) { HINSTANCE inst = GetModuleHandle (0); WNDCLASSEX wincl; /* The Window structure */ wincl.hInstance = inst; wincl.lpszClassName = "ConsoleClass"; wincl.lpfnWndProc = this->WndProc; /* This function is called by windows */ /* more WNDCLASSEX crap...*/ mainWindow = CreateWindow ( /*PARAMS*/ ); ShowWindow(mainWindow,1); return mainWindow; } bool WindowConsole::update() { return true; } LRESULT CALLBACK WindowConsole::WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) /* handle the messages */ { /*EVENT crap*/ } return 0; }
-
Tomáš Zato over 11 yearsThank you, with your example, I produced something, that, at least, compiles without errors. Now there is obvions question: where do I put the loop to capture window events and dispatch them? Do I have to make my program asynchronous (add another thread?)?
-
Jerry Coffin over 11 years@TomášZato: The message loop is (essentially) always in WinMain, which will typically just create an instance of the window, then go into a message loop. No, you don't normally want to run it in a separate thread.
-
Tomáš Zato over 11 yearsWell, but my class can create many windows as I can make many instances. So should I keep window handlers (HWND) in some global variable, to dispatch events for each of them in WinMain()?
-
Jeroen almost 10 yearsThis method doesn't work if you want to overwrite non-client-area messages.
-
Terrance about 9 years