How to use WndProc as a class function [duplicate] - c++

This question already has answers here:
Win32 WndProc as class member
(3 answers)
Closed 9 years ago.
I'm trying to create a class that includes the WndProc, but I'm getting an error :
Error 2 error C2440: '=' : cannot convert from 'LRESULT (__stdcall Client::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'
I searched the web for it, and seen that you need to make the WndProc static, but then, it compiles and everything is great, though if I want to change something, it doesnt let me :
Error 3 error C2352: 'Client::CreateMen' : illegal call of non-static member function
(CreateMen is a function in the class that creates the menu, using HMENU and such).
this is my function title:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
What can I do? I'm really confused...
Thanks!

A non-static class method has a hidden this parameter. That is what prevents the method from being used as a WndProc (or any other API callback). You must declare the class method as static to remove that this parameter. But as you already noticed, you cannot access non-static members from a static method. You need a pointer to the object in order to access them.
In the specific case of a WndProc callback, you can store the object pointer in the HWND itself (using either SetWindowLongPtr(GWLP_USERDATA) or SetProp()), then your static method can retrieve that object pointer from the hWnd parameter (using GetWindowLongPtr(GWLP_USERDATA) or GetProp()) and access non-static members using that object pointer as needed.
For example:
private:
HWND m_Wnd;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK Client::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Client *pThis;
if (msg == WM_NCCREATE)
{
pThis = static_cast<Client*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetLastError(0);
if (!SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis)))
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
pThis = reinterpret_cast<Client*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (pThis)
{
// use pThis->member as needed...
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
m_Wnd = CreateWindowEx(..., this);

Unfortunately you cannot use a class function as a wndproc because as the compiler tries to tell you the calling convention differs, even though the two functions have the same signature, a class function expects the this pointer to be passed to it. On 64 bit builds it will expect it to be in the RCX/ECX registry while on 32 bit builds it will expect the this pointer to be the last argument pushed on the stack. The window code won't do that when calling your WndProc essentially turning this into a function call on a garbage pointer.
What you can do is make a static method that does something like the following:
LRESULT Client::CreateMen(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
// The OS makes sure GWLP_USERDATA is always 0 before being initialized by the application
Client* client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(msg == WM_INIT)
{
client = new Client();
SetWindowLongPtr(hwnd, GWLP_USERDATA, client);
}
if(msg == WM_DESTROY)
{
client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA, client);
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
delete client;
client = NULL;
}
if(client)
{
// Do stuff with the client instance
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
I haven't tested this, so it might have some bugs, but let me know if you have any problems with it and I'll refine it if need be.

Related

Get rid of non-static reference error when using drag and drop functionality [duplicate]

This question already has answers here:
Win32 WndProc as class member
(3 answers)
Closed 9 years ago.
I'm trying to create a class that includes the WndProc, but I'm getting an error :
Error 2 error C2440: '=' : cannot convert from 'LRESULT (__stdcall Client::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'
I searched the web for it, and seen that you need to make the WndProc static, but then, it compiles and everything is great, though if I want to change something, it doesnt let me :
Error 3 error C2352: 'Client::CreateMen' : illegal call of non-static member function
(CreateMen is a function in the class that creates the menu, using HMENU and such).
this is my function title:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
What can I do? I'm really confused...
Thanks!
A non-static class method has a hidden this parameter. That is what prevents the method from being used as a WndProc (or any other API callback). You must declare the class method as static to remove that this parameter. But as you already noticed, you cannot access non-static members from a static method. You need a pointer to the object in order to access them.
In the specific case of a WndProc callback, you can store the object pointer in the HWND itself (using either SetWindowLongPtr(GWLP_USERDATA) or SetProp()), then your static method can retrieve that object pointer from the hWnd parameter (using GetWindowLongPtr(GWLP_USERDATA) or GetProp()) and access non-static members using that object pointer as needed.
For example:
private:
HWND m_Wnd;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK Client::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Client *pThis;
if (msg == WM_NCCREATE)
{
pThis = static_cast<Client*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetLastError(0);
if (!SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis)))
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
pThis = reinterpret_cast<Client*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (pThis)
{
// use pThis->member as needed...
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
m_Wnd = CreateWindowEx(..., this);
Unfortunately you cannot use a class function as a wndproc because as the compiler tries to tell you the calling convention differs, even though the two functions have the same signature, a class function expects the this pointer to be passed to it. On 64 bit builds it will expect it to be in the RCX/ECX registry while on 32 bit builds it will expect the this pointer to be the last argument pushed on the stack. The window code won't do that when calling your WndProc essentially turning this into a function call on a garbage pointer.
What you can do is make a static method that does something like the following:
LRESULT Client::CreateMen(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
// The OS makes sure GWLP_USERDATA is always 0 before being initialized by the application
Client* client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(msg == WM_INIT)
{
client = new Client();
SetWindowLongPtr(hwnd, GWLP_USERDATA, client);
}
if(msg == WM_DESTROY)
{
client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA, client);
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
delete client;
client = NULL;
}
if(client)
{
// Do stuff with the client instance
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
I haven't tested this, so it might have some bugs, but let me know if you have any problems with it and I'll refine it if need be.

Calling a win32 API and giving a callback to a class function

I'm trying to clean up some existing win32 UI code by putting it into a class. Previously I had an AppDlgProc function like this:
BOOL CALLBACK AppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { ... }
Which I used like so:
DialogBoxParam(hInstance, (LPCTSTR)IDD_SETTINGS, 0, AppDlgProc, 0);
Now I'm putting all this in a SettingsWindow object, and I call settingsWindow->show() which kicks off this:
void SettingsWindow::show(HINSTANCE hInstance) {
DialogBoxParam(hInstance, (LPCTSTR)IDD_SETTINGS, 0, &SettingsWindow::AppDlgProc, 0);
}
I'm pretty sure I'm giving the callback method incorrectly here. Visual Studio tells me "Intellisense: Argument of type ... is incompatible with parameter of type DLGPROC". Googling seems to tell me seems to tell me I need another argument - is there no other way?
For reference, my AppDlgProc function now looks like this:
BOOL CALLBACK SettingsWindow::AppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { ... }
Window and dialog procedures (and other Win32 callback functions) need to be static or global functions - they can't be non-static class functions. Win32 is fundamentally a C-based API and it has no concept of the hidden this pointer that class functions require.
The normal way to do this is to declare the function as static and store a pointer to the class instance in a window property. For example,
struct SettingsWindow
{
// static wrapper that manages the "this" pointer
static INT_PTR CALLBACK AppDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
SetProp(hWnd, L"my_class_data", (HANDLE)lParam);
else
if (uMsg == WM_NCDESTROY)
RemoveProp(hWnd, L"my_class_data");
SettingsWindow* pThis = (SettingsWindow*)GetProp(hWnd, L"my_class_data");
return pThis ? pThis->AppDlgFunc(hWnd, uMsg, wParam, lParam) : FALSE;
}
INT_PTR AppDlgFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// the real dialog procedure goes in here
}
};
// to show the dialog - pass "this" as the dialog parameter
DialogBoxParam(hInstance, (LPCTSTR)IDD_SETTINGS, 0, SettingsWindow::AppDlgProc,
(LPARAM)this);

Function does not work inside a class - function call missing argument list

I've built a working keylogger and now I want to move it to class so I can re-use it whenever I want and even on different languages like c# however I encountered error because same code does not work inside a class.
main.cpp (working)
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
void main()
{
HINSTANCE h_instance = GetModuleHandle(NULL);
SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, h_instance, 0); // Works here
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// Populate typedChars
return NULL;
}
KeyboardHook.cpp (not working)
class KeyboardHook
{
stringstream typedChars;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// Populate typedChars
return NULL;
}
KeyboardHook()
{
HINSTANCE h_instance = GetModuleHandle(NULL);
SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, h_instance, 0); // Does not work
}
public:
std::string Get()
{
return typedChars.str();
}
void Clear()
{
typedChars.str(std::string());
typedChars.clear();
}
};
Error
C3867: 'KeyboardHook::KeyboardProc': function call missing argument list; use '&KeyboardHook::KeyboardProc' to create a pointer to member
So I modify it to SetWindowsHookEx(WH_KEYBOARD_LL, &KeyboardProc, h_instance, 0); and now different error occurs
C2276: '&' : illegal operation on bound member function expression
I've also tried but no success:
SetWindowsHookEx(WH_KEYBOARD_LL, (LRESULT)&KeyboardProc, h_instance, 0);
SetWindowsHookEx(WH_KEYBOARD_LL, (LRESULT)KeyboardProc, h_instance, 0);
SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHook::KeyboardProc, h_instance, 0);
SetWindowsHookEx(WH_KEYBOARD_LL, &KeyboardHook::KeyboardProc, h_instance, 0);
Assuming SetWindowsHookEx is supposed to take a function pointer (I can never understand the horrible Windows API documentation), you need to bind your pointer to member function to an object on which it should be called. If you want to bind it to the object pointed to by this (i.e. the KeyboardHook object you're creating at the time), try this:
using std::placeholders;
SetWindowsHookEx(WH_KEYBOARD_LL,
std::bind(&KeyboardHook::KeyboardProc, this, _1, _2, _3),
h_instance, 0);
Alternatively, KeyboardProc can be declared as a static member function, but that means it won't be able to use the typedChars member.
You must define KeyboardProc as a static member
static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
...
}
if it must and can be called without an object.

attaching WNDPROC function from child class

I have a problem with attaching a windows procedure to a window.
I have a baseclass called BaseWindow, that uses GWPL_USERDATA to call a virtual function called HandleMessage() of the child classes.
However, if i try to change the window procedure without creating a custom Window Class, it gives a type error from the child procedure to long.
Here's the code:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BaseWindow *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (BaseWindow*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->m_hwnd = hwnd;
}
else
{
pThis = (BaseWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{return 0;};
PlayList Class : BaseWindow
SetWindowLong(m_hwnd, GWL_WNDPROC,(long) HandleMessage); //Error
LRESULT PlayList::HandleMessage(UINT message,WPARAM wParam,LPARAM lParam) //Need to attach this window procedure
{}
It works if the child procedure is static, however I use non static members in that procedure.
I want to subclass a common control, while using this base class (because a lot of code is redundant), is it possible?
Here's the whole code for the base class: http://pastebin.com/ME8ks7XK
The compiler doesn't like your declaration for HandleMessage, it isn't static. It's missing CALLBACK too, not good.
Not sure why you are trying to do this, the whole point of your WindowProc() function is to get the message forwarded to a virtual HandleMessage() method. At best you'd use WindowProc in your SetWindowLong() call instead of HandleMesssage. Or just specify it directly in the CreateWindowEx() call.
From MSDN:
An application subclasses an instance of a window by using the SetWindowLong function. The application passes the GWL_WNDPROC flag, the handle to the window to subclass, and the address of the subclass procedure to SetWindowLong. The subclass procedure can reside in either the application's executable or a DLL.
So, you should write this:
SetWindowLong(m_hwnd, GWL_WNDPROC,(long) & HandleMessage);
But this doesn't compile again! the reason: all non-static member functions have hidden this parameter (pointer to owner class). So, you HandleMessage doesn't fit the WindowProc declaration.

Get Unhandled exception at 0x00f85069

my program breaks and says this
Unhandled exception at 0x00f85069 in Monopoly.exe: 0xC0000005: Access violation writing location 0x0000000c.
i made a win32 wrapper. i have two WndProc one is static and the other one not. the static WndProc calls the nonstatic WndProc. when i try to get messages it works fine but when i try to set a value for something it throw a exception.
here is my code for two WndProc(the first one its that static)
LRESULT CALLBACK Window::StaticWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
if ( msg == WM_CREATE )
{
SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG)((CREATESTRUCT *)lParam)->lpCreateParams );
}
Window *targetApp = (Window*)GetWindowLongPtr( hWnd, GWLP_USERDATA );
return targetApp->WndProc( hWnd, msg, wParam, lParam );
}
LRESULT CALLBACK Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
z=-14; //IT THROW EXCEPTION
break;
case WM_RBUTTONDOWN:
z-=1;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Thanks in Advance
EDIT:
VARABILES
int z;
How are you creating your window? Did you pass a valid pointer to an instance of Window to your CreateWindow() or CreateWindowEx() function via the lpParam parameter? For example, if your window wrapper has a Create() function or something like that:
void Window::Create()
{
/* ... */
// Pass a pointer to ourselves to CreateWindow() via the lpParam parameter.
// CreateWindow() then passes that pointer to your window procedure
// (StaticWndProc) via WM_CREATE and WM_NCCREATE in the lpCreateParams member
// of CREATESTRUCT. This way the window procedure will know which instance to
// call WndProc() on.
CreateWindow(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
hWndParent, hMenu, hInstance, (LPVOID)this);
/* ... */
}
The object on which you're calling WndProc seems to be non-existent (null?) and you have an access violation upon trying to write the memory in z -= 14 which is this->z -= 14 (this being an invalid pointer). That's my guess.
Also, Access Violation is not not an exception in C++ terms. :)
Well, an access violation (0xC0000005) means you accessed memory that you shouldn't have. In this case it says you were trying to write to 0x0000000c. Since you (your debugger?) say that the assignment to z causes it, may we see the definition of that symbol, please? Also, is it really z = -14 or z -= 14?
Edit: I think you need to replace ((CREATESTRUCT *)lParam)->lpCreateParams with a valid pointer to an instance of class Window.
Edit #2: What happens is this: On WM_CREATE, you set GWLP_USERDATA to a value that happens to be equal to NULL. Subsequently, you read that value and treat it as a valid pointer to a Window by invoking a non-static member function on that pointer. Class member functions are implemented by the compiler a lot like so
LRESULT CALLBACK <mangled_name ("Window::WndProc")> (Window * const this, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// ...
}
That is the reason that you can actually invoke a member function on a NULL pointer. However, once you access a member variable, like z, this breaks. The compiler inserted code similar to this *((int*) (this + 0xc)) = -14, (which BTW means that z lies 0xc bytes into your Window instance), which, since this == NULL, broke.