I want to create my own class to handle creating windows and the window procedure but I have noticed that the window procedure has to be static! I'm now wondering whether its possible to make the window procedure object oriented? I have read some tutorials on object oriented windows, but they always make the procedure static -.- whats the use in that? :/
Any links or info on how to get around this problem would be appreciated,
thanks
You can get around that by making the static WndProc delegate everything to the members:
// Forward declarations
class MyWindowClass;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
std::map<HWND, MyWindowClass *> windowMap;
// Your class
class MyWindowClass {
private:
HWND m_handle;
// The member WndProc
LRESULT MyWndProc(UINT message, WPARAM wParam, LPARAM lParam) { /* ... */ }
public:
MyWindowClass()
{
/* TODO: Create the window here and assign its handle to m_handle */
/* Pass &WndProc as the pointer to the Window procedure */
// Register the window
windowMap[m_handle] = this;
}
};
// The delegating WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<HWND, MyWindowClass *>::iterator it = windowMap.find(hWnd);
if (it != windowMap.end())
return it->second->MyWndProc(message, wParam, lParam);
return 0;
}
The general technique of allowing a window instance to be represented by as class instance is to make use of the SetWindowLongPtr and GetWindowLongPtr to associate your class instance pointer with the window handle. Below is some sample code to get you started. It may not compile without a few tweaks. It's only meant to be a reference.
Personally, I've stopped rolling my own window classes back a few years ago when I discovered ATL's CWindow and CWindowImpl template class. They take care of doing all this mundane coding for you so can focus on just writing methods that handle window messages. See the example code I wrote up here.
Hope this helps.
class CYourWindowClass
{
private:
HWND m_hwnd;
public:
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE: return OnCreate(wParam, lParam);
case wM_PAINT: return OnPaint(wParam, lParam);
case WM_DESTROY:
{
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL);
m_hwnd = NULL;
return 0;
}
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
CYourWindowClass()
{
m_hwnd = NULL;
}
~CYourWindowClass()
{
ASSERT(m_hwnd == NULL && "You forgot to destroy your window!");
if (m_hwnd)
{
SetWindowLong(m_hwnd, GWLP_USERDATA, 0);
}
}
bool Create(...) // add whatever parameters you want
{
HWND hwnd = CreateWindow("Your Window Class Name", "Your Window title", dwStyle, x, y, width, height, NULL, hMenu, g_hInstance, (LPARAM)this);
if (hwnd == NULL)
return false;
ASSERT(m_hwnd == hwnd);
return true;
}
static LRESULT __stdcall StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CYourWindowClass* pWindow = (CYourWindowClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (uMsg == WM_CREATE)
{
pWindow = ((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (void*)pWindow);
m_hWnd = hwnd;
}
if (pWindow != NULL)
{
return pWindow->WndProc(uMsg, wParam, lParam);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};
};
If you are looking for object oriented Win32 API then you should look to MFC and/or WTL.
Just to add to Brian's answer but for a win32 framework that's more beginner friendly take a look at Win32++. The library itself isn't as comprehensive in features compared to MFC or QT but that is a tradeoff the designer made at the beginning to keep the library easy to understand and simple to use.
If you're still interested in this topic, I highly encourage you to take a look at it since it uses yet another technique for saving the 'this' pointer by utilizing thread local storage.
You can use the window handle passed to the WindowProc to grab an object you've created for that particular window and delegate the event handling to that object.
e.g.
IMyWindowInterface* pWnd = getMyWindowObject(hWnd);
pWnd->ProcessMessage(uMsg, wParam, lParam);
Related
I apologize if I'm overlooking something, but I'm trying to just create a placeholder window within an ATL dialog, which will be used to host a preview handler. I thought placing a custom control might be the thing to do, since it's blank and would occupy a designated place, but that's causing the dialog to crash, and I get the feeling doing something with a custom control is more complicated than I'm looking for. So is there a way to just put a dummy window inside a dialog for use as a host site? Thanks for any input.
Update: I seem to have achieved the desired result using a simple blank picture control. But I'm still wondering if there's a more official way of doing this.
for placeholder we need use exactly custom control. the point - need specify window class name. and this class must be registered.
let name of class will be MyClass
so in .rc file must be
CONTROL "Custom1",IDC_CUSTOM1,"MyClass",...
and we need register "MyClass", minimal code
class MyWndCls
{
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
delete this;
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK _WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return reinterpret_cast<MyWndCls*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA))->WindowProc(hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK StartWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_NCCREATE)
{
if (MyWndCls* p = new MyWndCls)
{
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)p);
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)_WindowProc);
return p->WindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
inline static const WCHAR clsname[] = L"MyClass";
public:
static ULONG Register()
{
WNDCLASS wndcls = {
0, StartWindowProc, 0, 0, (HINSTANCE)&__ImageBase, 0,
LoadCursorW(0, IDC_HAND), (HBRUSH)(COLOR_INFOBK + 1), 0, clsname
};
return RegisterClassW(&wndcls) ? NOERROR : GetLastError();
}
static ULONG Unregister()
{
return UnregisterClassW(clsname, (HINSTANCE)&__ImageBase) ? NOERROR : GetLastError();
}
};
of course we need call MyWndCls::Register(); before create any dialog with this custom control
Consider the following code snippet:
// MyWindow.h
struct MyWindow
{
LRESULT CALLBACK myWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK myWindowProcWrapper(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
extern MyWindow *windowPtr; // windowPtr is initialized on startup using raw new
// MyWindow.cpp
MyWindow *windowPtr = 0;
LRESULT CALLBACK MyWindow::myWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_NCDESTROY:
delete windowPtr;
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK MyWindow::myWindowProcWrapper(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return windowPtr->myWindowProc(hwnd, msg, wParam, lParam);
}
The question is whether the given code snippet is safe as written.
Basically, MyWindow is the class for a window created with the WinAPI. I need to do some final cleanup when the window is destroyed.
Notice that the instance of MyWindow, windowPtr, is created using a raw new. I have to delete the instance somewhere in a member function, so I delete the reference to the object itself from within a member function.
The code relies on the assumption that WM_NCDESTROY is the last message ever received by that window.
So the questions are as follows:
Is it safe to assume that WM_NCDESTROY is always the last message a window receives and to perform final cleanup there?
Is the listed code safe? If not, under what conditions could it break?
Remark: I'm only interested in whether the code is technically safe, not if it is good practice to use a raw new and/or global variables. I have some good reasons for this implementation.
It is not explicitly documented, that WM_NCDESTROY is the final message a window receives. If you read in between the lines, you can deduce this information, though.
The documentation for WM_NCDESTROY contains the following remark:
This message frees any memory internally allocated for the window.
Window Features: Window Destruction outlines the consequences of this:
When a window is destroyed, the system [...] removes any internal data associated with the window. This invalidates the window handle, which can no longer be used by the application.
Putting those together, destroying a window invalidates its window handle. Once the WM_NCDESTROY message handler has run to completion, the window handle is no longer valid. An invalid window handle no longer receives any messages.
Your implementation is thus safe.
It is doubtful that any of these rules will change in the future (with so many applications relying on WM_NCDESTROY being the final message), but if you want to be prepared, you might want to consider placing a windowPtr = nullptr; statement following delete windowPtr;. Doing so ensures, that your application fails in a predictable way, in case it receives a message after the MyWindow instance has been disposed.
yes, WM_NCDESTROY is the last message sended to WindowProc (how minimum now).
but direct, unconditionally call delete this in myWindowProc can be unsafe in complex case and produce very bad error (because hard to found)
you not take to account that myWindowProc can be called recursively.
let us first consider a simple version:
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
DestroyWindow(hwnd);
}
so we destroy window on ESCAPE pressed. in this case myWindowProc will be called recursively with WM_NCDESTROY where we delete this and then we return to up level myWindowProc (for WM_KEYDOWN ). but at this point this already destroyed and invalid. so we must not more access any members fields or virtual functions. good if we call direct DefWindowProc in the end of own WindowProc. but what be if say our class implement and call virtual LRESULT DefWinProc(..) for allow overwrite DefWindowProc behaviour ? (say to DefMDIChildProc ) ?
now more complex case - assume you implement some child control. based on WM_SOMETHING_1 you set WM_NOTIFY_SOMETHING_1 to parent (via SendMessage ). and parent decide call DestrowWindow when handle this notify (for self, and as result for all childs). so internally your call delete this and when you return from SendMessage(..WM_NOTIFY_SOMETHING_1..) your this already will be deleted, but you will not even know about this.
access this after it will be deleted real in complex windows case, and if we modify some member data - can be not direct crash(which is good for detection) but heap corruption, which will manifest itself later and this is very hard to detect.
however for this exist 100% correct solution, which will be work even if WM_NCDESTROY will be not final message for window.
we must not use global MyWindow *windowPtr but assign it to window data via GWLP_USERDATA and remove on WM_NCDESTROY . and call myWindowProc only when GetWindowLongPtr(hwnd, GWLP_USERDATA) return not 0 . in this case even if will some WM_* message after WM_NCDESTROY - static myWindowProcWrapper not call myWindowProc and we must use reference counting for MyWindow class :
call AddRef(); before SetWindowLongPtrW(hwnd, GWLP_USERDATA,(LONG_PTR)windowPtr);
call Release(); after SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
wrap windowPtr->myWindowProc(hwnd, msg, wParam, lParam) in
AddRef(); Release();
call delete this only from Release and make ~MyWindow()
private:
with this rules we can absolute safe access this pointer in myWindowProc at any time and any situation. and not dependening about WM_NCDESTROY is the last message
struct MyWindow
{
private:
PSTR _somedata;
LONG _dwRef;
public:
MyWindow() : _dwRef(1), _somedata(0)
{
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
static LRESULT CALLBACK myWindowProcWrapper(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
MyWindow *windowPtr;
if (msg == WM_NCCREATE)
{
windowPtr = (MyWindow *)((LPCREATESTRUCT)lParam)->lpCreateParams;
windowPtr->AddRef();
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)windowPtr);
}
else
{
windowPtr = (MyWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
LRESULT lr;
if (windowPtr)
{
windowPtr->AddRef();
lr = windowPtr->myWindowProc(hwnd, msg, wParam, lParam);
windowPtr->Release();
}
else
{
lr = DefWindowProc(hwnd, msg, wParam, lParam);
}
if (msg == WM_NCDESTROY && windowPtr)
{
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
windowPtr->Release();
}
return lr;
}
protected:
LRESULT CALLBACK myWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_NCDESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
DestroyWindow(hwnd);
DbgPrint("%s\n", _somedata);// bug can be here if not use ref semantic
}
break;
case WM_CREATE:
if (_somedata = new CHAR[64])
{
strcpy(_somedata, "1234567890");
}
break;
}
// bug can be here if not use ref semantic, because myDefWinProc virtual
return myDefWinProc(hwnd, msg, wParam, lParam);
//return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// for demo only here, not need in simply case
virtual LRESULT myDefWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
private:
~MyWindow()
{
if (_somedata) delete _somedata;
}
};
if (MyWindow* p = new MyWindow)
{
CreateWindowEx(0, L"lpszClassName", L"lpWindowName", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, 0, 0, p);
p->Release();
}
Yes. Self deallocate is safe in WM_NCDESTROY.
But just to be sure your program will not fail under Wine, for example, I'd recommend to set windowPtr to null after delete, and check it before call windowPtr->myWindowProc.
And, ofcause, it must be only window of that wndclass.
I am attempting to add a low level mouse hook to a class. I am able to do so by placing this function in my CPP file:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
Then, I set up the hook in the class constructor like so:
HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
This works fine for intercepting mouse events, however since the callback function is not defined in my class, it does not have access to any of my class variables.
Therefore, I tried defining the callback function in my header file like so:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
and in my CPP file like this (TMainForm being the class):
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
However, when I attempt to compile like this, I get the following errors:
[bcc32 Error] MainFormU.cpp(67): E2034 Cannot convert 'long (__stdcall * (_closure )(int,unsigned int,long))(int,unsigned int,long)' to 'long (__stdcall *)(int,unsigned int,long)'
[bcc32 Error] MainFormU.cpp(67): E2342 Type mismatch in parameter 'lpfn' (wanted 'long (__stdcall *)(int,unsigned int,long)', got 'void')
What exactly am I doing wrong here? How is the method now different since I have made it a part of my TMainForm class?
You cannot use a non-static class methods as the callback. Non-static methods have a hidden this parameter, thus the signature of the callback does not match the signature that SetWindowsHookEx() is expecting. Even if the compiler allowed it (which can only be done with casting), the API would not be able to account for the this parameter anyway.
If you want to make the callback be a member of the class (so it can access private fields and such), it has to be declared as static to remove the this parameter, but then you will have to use the form's global pointer to reach it when needed, eg:
class TMainForm : public TForm
{
private:
HHOOK hMouseHook;
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
void MouseHook(int nCode, WPARAM wParam, LPARAM lParam);
public:
__fastcall TMainForm(TComponent *Owner);
__fastcall ~TMainForm();
};
extern TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent *Owner)
: TForm(Owner)
{
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, &MouseHookProc, NULL, 0);
}
__fastcall TMainForm::~TMainForm()
{
if (hMouseHook)
UnhookWindowsHookEx(hMouseHook);
}
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MainForm->MouseHook(nCode, wParam, lParam);
return CallNextHookEx(0, nCode, wParam, lParam);
}
void TMainForm::MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
// my hook code here
}
With that said, you should consider using the Raw Input API instead of SetWindowsHookEx(). The LowLevelMouseProc documentation even says so:
Note Debug hooks cannot track this type of low level mouse hooks. If the application must use low level hooks, it should run the hooks on a dedicated thread that passes the work off to a worker thread and then immediately returns. In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can. For more information on raw input, see Raw Input.
Using Raw Input, the mouse will send WM_INPUT messages directly to your window.
If you are using VCL, you can override the virtual WndProc() method to handle the WM_INPUT message, no static method needed:
class TMainForm : public TForm
{
protected:
virtual void __fastcall CreateWnd();
virtual void __fastcall WndProc(TMessage &Message);
};
void __fastcall TMainForm::CreateWnd()
{
TForm::CreateWnd();
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = this->Handle;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
void __fastcall TMainForm::WndProc(TMessage &Message)
{
if (Message.Msg == WM_INPUT)
{
HRAWINPUT hRawInput = (HRAWINPUT) Message.LParam;
UINT size = 0;
if (GetRawInputData(hRawInput, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) == 0)
{
LPBYTE buf = new BYTE[size];
if (GetRawInputData(hRawInput, RID_INPUT, buf, &size, sizeof(RAWINPUTHEADER)) != 0)
{
RAWINPUT *input = (RAWINPUT*) buf;
// use input->data.mouse or input->data.hid as needed...
}
delete[] buf;
}
}
TForm::WndProc(Message);
}
If you are using FireMonkey, there is no WndProc() method for handling window messages (FireMonkey does not dispatch window messages to user code at all). However, you can subclass the window that FireMonkey creates internally so you can still receive the WM_INPUT message. A static method is needed, but you do not have to rely on a global pointer, the Form object can be passed as a parameter of the subclassing:
class TMainForm : public TForm
{
private:
static LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
protected:
virtual void __fastcall CreateHandle();
};
void __fastcall TMainForm::CreateHandle()
{
TForm::CreateHandle();
HWND hWnd = Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd;
SetWindowSubclass(hWnd, &SubclassProc, 1, (DWORD_PTR)this);
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = hWnd;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
LRESULT CALLBACK TMainForm::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
TMainForm *pThis = (TMainForm*) dwRefData;
switch (uMsg)
{
case WM_INPUT:
{
// ...
break;
}
case WM_NCDESTROY:
{
RemoveWindowSubclass(hWnd, &SubclassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
I came across the same issue and I found that the best method, for my particular case, was to create a static array of pointers to my class. Then inside the static hook method, I just iterate through my class pointers and call their hook functions.
kb_hook.h
typedef KBDLLHOOKSTRUCT khookstruct;
typedef LRESULT lresult;
typedef void (*h_func)(uint64_t key_message, khookstruct* kbdhook);
typedef std::vector<kb_hook*> h_array;
class kb_hook
{
public:
kb_hook();
virtual ~kb_hook();
h_func hook_func;
private:
static h_array hook_array;
static lresult static_hook(int code, uint64_t key_message, khookstruct* kbdhook);
};
kb_hook.cpp
kb_hook::kb_hook() : hook_func(NULL)
{
this->hook_array.push_back(this);
}
lresult kb_hook::static_hook(int code, uint64_t key_message, khookstruct* kbdhook)
{
if(code == HC_ACTION)
{
for(auto it=kb_hook::hook_array.begin();it!=kb_hook::hook_array.end();it++)
{
if((*it)->hook_func) std::thread((*it)->hook_func, key_message, kbdhook).detach();
}
}
return CallNextHookEx(NULL, code, key_message, reinterpret_cast<LPARAM>(kbdhook));
}
I know it's an old question but I just wanted to throw in my two cents. I hope this is helpful to someone.
I'm working in an IDE which creates a hwnd and its respective WndProc LRESULT CALLBACK. I need to change the WndProc to a custom one.
I've read that SetWindowLong would do the job, but I can't find any working example. For example:
HWND hwnd; //My window
SetWindowLong(hwnd, GWL_WNDPROC, myNewWndProc);
The third parameter for SetWindowLong is a Long as the name of the function names it. How can I make a reference from my WndProc function to a Long?
My WndProc:
LRESULT CALLBACK WndProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
msg_dev(toString(uMsg));
switch(uMsg){
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
default:
DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
};
You need to use something like this:
WNDPROC prevWndProc;
...
prevWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
...
LRESULT CALLBACK myNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
msg_dev(toString(uMsg));
switch(uMsg)
{
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
}
return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}
See this article:
When you subclass a window, it's the original window procedure of the window you subclass you have to call when you want to call the original window procedure
That being said, you should use SetWindowSubclass() instead of SetWindowLongPtr(). Let it handle this for you. See this article for more details:
Safer subclassing
For example:
#define MY_SUBCLASS_ID 1
SetWindowSubclass(hwnd, &mySubClassProc, MY_SUBCLASS_ID, 0);
...
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
msg_dev(toString(uMsg));
switch(uMsg)
{
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &mySubClassProc, uIdSubclass);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
A simple cast does the job.
SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
Otherwise It would be incompatible types: LRESULT and LONG.
The MSDN documentation for SetWindowLong() states that GWL_WNDPROC
Sets a new address for the window procedure.
This means that your third parameter should be a pointer to a function. Your SetWindowLong() call should therefore look like this:
SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
Note also the Remarks section that states:
An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc.
You can use setWindowLong to address your problem.
setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);
However you would be setting the window procedure twice. Once with the IDE default and then to yours. What you need to dobis set the window procedure when the window is being REGISTERED.
#include <windows.h>
void registerWindow();
void createWindow();
void messageLoop();
int main()
{
registerWindow();
createWindow();
messageLoop();
}
LRESULT CALLBACK myWindowProcedure(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
return DefWindowProc(hwnd,msg,wparam,lparam);
}
void registerWindow()
{
/** This is the important part.
* Find this part in your code.
* Set WNDCLASS::lpfnWndProc to what ever
* window procedure you want.
**/
WNDCLASS wc = {};
wc.lpfnWndProc = myWindowProcedure;
wc.hInstance = hInstance;
wc.lpszClassName = "CLASS NAME";
RegisterClass(&wc);
// WARNING: Your app will crash at runtime if the
// windows procedure is "NOT PROPER"
}
void createWindow()
{
auto hwnd = CreateWindowEx(
0, // Optional window styles.
"CLASS NAME", // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
HINSTANCE(), // Instance handle
NULL // Additional application data
);
ShowWindow(hwnd, nCmdShow
}
void messageLoop()
{
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
You have to use SetWindowLongPtr (which on 32-bit is a macro but a separate function on 64-bit) to ensure compatibility with both 32- and 64-bit systems.
Syntax would be as follows:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&myNewWndProc);
Note SetWindowLongPtr is used instead of SetWindowLong, and GWLP_WNDPROC is used as the nIndex constant.
I'll go ahead and give a summary to this, how can I use a dialog procedure that is a member of a class? I am creating a window wrapper class, but CreateDialogParam needs a global dialog procedure, so I tried this workaround:
I have done a bit of searching on this topic. I am making a Dialog class which I am subclassing to make a CMainWnd and then instantiating that. In the Dialog class I have a member function defined as INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM). Now, I know that windows must have a global function as a callback procedure.
So I made a std::map<HWND,Dialog*> DlgProcs map to associate the dialogs window handle with its Dialog class pointer.
And a INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM) so I could pass that to CreateDialogParam(). In the body of DlgMainProc(...) I search the map for using the hWnd parameter to find the Dialog* and return its cb_proc(..) member.
My problem is that none of the messages get processed, this is because the member procedure in my Dialog class never gets called. Even though when I put a MessageBox() in DlgMainProc inside a if (DlgProcs.find(hWnd) != DlgProcs.end()) { statement, the messagebox is displayed, over and over again until I have to abort the program from Visual Studio 2008. Which tells me that it is finding the hWnd in my map. The weird thing is it also does this if I put it in the else statement after that, which contradictingly tells me it is NOT finding the hWnd in the map.
If I put a messagebox in the cb_proc member function it does not get displayed at all. But during this I never get any compiler, linker, or runtime errors. When I remove the messagebox from it (as to not have to abort the program, it was just for debugging purposes) the program runs but no messages get processed, the X button does not close the program, button clicks do nothing.
Here is the PasteBin code: http://pastebin.com/GsGUBpZU
Btw, I have no problem subclassing this, my window is created fine, just no messages are processed, cb_proc just never gets called.
EDIT: Here is the relevant parts of the code
map<HWND,Dialog*> g_DlgProcs;
INT_PTR CALLBACK g_MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (g_DlgProcs.find(hWnd) != g_DlgProcs.end()) {
Alert("blah"); // Gets executed repeatedly
return g_DlgProcs[hWnd]->cb_proc(hWnd, msg, wParam, lParam);
} else {
Alert("blah"); // Removing the above alert, this gets
// executed repeatedly, erm, as well.. O.o strange
return FALSE;
}
}
Dialog::Dialog(int id, HWND parent /* = HWND_DESKTOP */) {
_id = id;
_parent = parent;
// Tried this before CreateDialogParam
g_DlgProcs.insert(make_pair(_handle, this));
_handle = CreateDialogParam(
(HINSTANCE)GetModuleHandle(NULL),
MAKEINTRESOURCE(id), _parent,
(DLGPROC)g_MainDlgProc, NULL
);
// Then tried it after CreateDialogParam
g_DlgProcs.insert(make_pair(_handle, this));
}
INT_PTR CALLBACK Dialog::cb_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
Alert("blah"); // Never gets executed
bool handled = true;
switch (msg)
{
case WM_INITDIALOG:
OnInitialize();
break;
case WM_COMMAND:
if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
OnMenuCommand((HIWORD(wParam) == 1), (int)LOWORD(wParam));
} else {
OnCtrlCommand((int)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam);
}
break;
case WM_NOTIFY:
{
LPNMHDR head = (LPNMHDR)lParam;
OnNotification(head->code, head->idFrom, head->hwndFrom);
}
break;
case WM_CLOSE:
OnClose(); // DestroyWindow(_handle)
break;
case WM_DESTROY:
OnDestroy(); // PostQuitMessage(0)
default:
handled = ProcessMsg(msg, wParam, lParam);
}
// Convert bool to Windows BOOL enum
return ((handled == true) ? TRUE : FALSE);
}
Does anybody know why it never gets called? Or maybe just guide me to another way to use a member function as a DLGPROC?
The standard solution is to pass your this pointer as the last parameter to Create,DialogParam, stash it in DWLP_USER in your WM_INITDIALOG handler, and retrieve it from DWLP_USER thereafter. Basically you use DWLP_USER as your map.
I tried your code and it worked: cb_proc gets called. You will miss any messages (e.g. WM_INITDIALOG) that get sent before CreateDialogParam returns.
You can fix the latter problem by adding the window handle and the object to the map in g_MainDlgProc. If you get a message for an unknown window, you know it belongs to the window you're creating; put the object in a global and you can add the handle/object to the map.
I'm just adding this here in case someone finds it useful; using the magic of C++11 lambdas and templates you can have a simple wrapper template that means you don't have to continually rewrite the boiler-plate code for saving and loading userdata in window and dialog box procedures.
Here's an example for the DialogBoxParam function, but the same technique can be applied to CreateDialogParam and CreateWindowEx as well.
template <typename T, INT_PTR (T::*P)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)>
INT_PTR DialogBoxThis(T* pThis, HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent)
{
return ::DialogBoxParam(hInstance, lpTemplateName, hWndParent, [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> INT_PTR {
if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
T* pThis = reinterpret_cast<T*>(GetWindowLongPtr(hWnd, DWLP_USER));
return pThis ? (pThis->*P)(hWnd, uMsg, wParam, lParam) : FALSE;
}, reinterpret_cast<LPARAM>(pThis));
}
You would use it like this:
class MyClass
{
INT_PTR MyDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
// from inside MyClass, we can show a dialog that uses member function MyDlgProc as the dialog procedure
// note it is NOT a static function
DialogBoxThis<MyClass, &MyClass::MyDlgProc>(this, hInstance,
MAKEINTRESOURCE(IDD_MYDIALOG), hWndParent);