Related
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 have an MFC Application that show a simple dialog containing a custom control the custom create dynamic control windows. after the first child of the custom control is created the application crash after a stack overflow
this is the call stack after the crash
Amn80Lib1.dll!TPXBitButton::GetThisMessageMap() Line 1627 C++
Amn80Lib1.dll!TPXBitButton::GetMessageMap() Line 1627 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2181 C++
mfc100d.dll!CWnd::ReflectChildNotify(unsigned int uMsg, unsigned int wParam, long lParam, long * pResult) Line 3408 C++
> mfc100d.dll!CWnd::OnChildNotify(unsigned int uMsg, unsigned int wParam, long lParam, long * pResult) Line 3387 C++
mfc100d.dll!CButton::OnChildNotify(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 91 C++
mfc100d.dll!CWnd::SendChildNotifyLastMsg(long * pResult) Line 3327 C++
mfc100d.dll!CWnd::ReflectLastMsg(HWND__ * hWndChild, long * pResult) Line 3365 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3463 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
user32.dll!75d1c4e7() Unknown
user32.dll!75d1c5e7() Unknown
user32.dll!75d1c590() Unknown
user32.dll!75d11b31() Unknown
user32.dll!75d32bee() Unknown
mfc100d.dll!CWnd::DefWindowProcA(unsigned int nMsg, unsigned int wParam, long lParam) Line 1089 C++
mfc100d.dll!CWnd::Default() Line 291 C++
mfc100d.dll!CWnd::OnParentNotify(unsigned int message, long lParam) Line 3468 C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult) Line 2435 C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 2087 C++
Amn80Lib1.dll!TControl::WindowProc(unsigned int message, unsigned int wParam, long lParam) Line 29 C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 257 C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam) Line 420 C++
The maximum number of stack frames supported by Visual Studio has been exceeded.
The message that is sent always is 528 this is 0x210 = WM_PARENTNOTIFY
The wParam = 77594625, lParam = 3409408
I don't know what is the reason for that, and how to stop it, I searched the childs if any of them send any message to the parent, but there is no messages, the dialog is not shown , but it's window handle was created and the custom control also has got a handle and it's first child has got a handle then we are stuck with the crash and the other childs didn't got the chance to be created. I traced the execution and got that the crash happen after calling the create method of the child of the custom control inside the dialog.
The custom control is a database navigation control that contains buttons for the next, previous, new, edit to enter record number, this custom control is inherited from TControl which is inherited from CWnd. the buttons inside it are inheriting from TButton which inherits from CButton.
UPDATE
The problem was as the following:
when I registered the custom control window class I set the windowproc to the MFC windowProc , when the MFC proc didn't find any method to eat the message it called the default proc of the window which is the MFC windowPoc that caused an open recursion, I fixed that by setting the window proc to the default one like the following
WNDCLASS wndClass;
//fill the window class data
wndClass.lpfnWndProc = ::DefWindowProc;//AfxWndProc;//this fixed it
that fixed my problem
I updated the answer to contain the solution of that problem, but in response to #Michael Walz I added the solution to this answer
when I registered the custom control window class I set the windowproc to the MFC windowProc , when the MFC proc didn't find any method to eat the message it called the default proc of the window which is the MFC windowPoc that caused an open recursion, I fixed that by setting the window proc to the default one like the following
WNDCLASS wndClass;
//fill the window class data
wndClass.lpfnWndProc = ::DefWindowProc;//AfxWndProc;//this fixed it
that fixed my problem
How can I access to the handle of a hook from his procedure ?
Example :
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)hookProc, GetModuleHandle(NULL), 0);
LRESULT CALLBACK hookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//I want my HHOOK here :O
}
You need to store the HHOOK variable in global memory. Don't declare it as a local variable of whatever function is calling SetWindowsHookEx().
Edit: Here is a class-based example for 32-bit CPUs:
class THookKeyboardLL
{
private:
HHOOK hHook;
void *pProxy;
static LRESULT CALLBACK ProxyStub(THookKeyboardLL *This, int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
public:
THookKeyboardLL();
~THookKeyboardLL();
};
.
#include <pshpack1.h>
struct sProxy
{
unsigned char PopEax;
unsigned char Push;
void *ThisPtr;
unsigned char PushEax;
unsigned char Jmp;
int JmpOffset;
};
#include <poppack.h>
long CalcJmpOffset(void *Src, void *Dest)
{
return reinterpret_cast<long>(Dest) - (reinterpret_cast<long>(Src) + 5);
}
LRESULT CALLBACK THookKeyboardLL::ProxyStub(THookKeyboardLL *This, int nCode, WPARAM wParam, LPARAM lParam)
{
return This->HookProc(nCode, wParam, lParam);
}
THookKeyboardLL::THookKeyboardLL()
: hHook(NULL), pProxy(NULL)
{
sProxy *Proxy = (sProxy*) VirtualAlloc(NULL, sizeof(sProxy), MEM_COMMIT, PAGE_READWRITE);
Proxy->PopEax = 0x58;
Proxy->Push = 0x68;
Proxy->ThisPtr = this;
Proxy->PushEax = 0x50;
Proxy->Jmp = 0xE9;
Proxy->JmpOffset = CalcJmpOffset(&(Proxy->Jmp), &ProxyStub);
// Note: it is possible, but not in a portable manner, to
// get the memory address of THookKeyboardLL::HookProc()
// directly in some compilers. If you can get that address,
// then you can pass it to CalcJmpOffset() above and eliminate
// THookKeyboardLL::ProxyStub() completely. The important
// piece is that the Proxy code above injects this class
// instance's "this" pointer into the call stack before
// calling THookKeyboardLL::HookProc()...
DWORD dwOldProtect;
VirtualProtect(Proxy, sizeof(sProxy), PAGE_EXECUTE, &dwOldProtect);
FlushInstructionCache(GetCurrentProcess(), Proxy, sizeof(sProxy));
pProxy = Proxy;
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)pProxy, GetModuleHandle(NULL), 0);
}
THookKeyboardLL::~THookKeyboardLL()
{
if (hHook != NULL)
UnhookWindowsHookEx(hHook);
if (pProxy)
VirtualFree(pProxy, 0, MEM_RELEASE);
}
LRESULT CALLBACK THookKeyboardLL::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// ...
return CallNextHookEx(hHook, nCode, wParam, lParam);
// when this method exits, it will automatically jump
// back to the code that originally called the Proxy.
// The Proxy massaged the call stack to ensure that...
}
If you look at the documentation for CallNextHookEx you see that the HHOOK parameter is optional on Windows NT, if you need to support Windows 9x then you need to store the HHOOK in a global variable.
Your example code shows that you are creating a global hook, global hooks are expensive so if you want to register more than one callback function you should abstract this so that your application only sets one hook and the callback function you register there calls your real functions (in a linked list etc).
So a bit of a weird bug going on when getting a WM_KEYDOWN msg in my Window class.
I have a Global WndProc function that determines which window instance it is and sends the message to it's own local WndProc function.
//Every Windows Message will hit this function.
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
//Get the Window Instance from the userdata
Window* targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
//If no window exists, then it must be the first time so we should set it
if (!targetWindow) {
//First let's try and extract the Window instance pointer from the lparam
CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;
if (createStruct) {
targetWindow = (Window*)createStruct->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
}
else {
//It was some other message which we just can't deal with right now
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
//Now we can pipe it to the Window's local wnd proc function
return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
}
And my local wndproc looks like this:
LRESULT CALLBACK Window::LocalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_KEYDOWN:
ToggleFullScreen(!m_fullScreen);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
So it's pretty simple at this point. If any key is pressed, the window should call its member function ToggleFullScreen.
Now for some reason when i run this and I hit any key on the keyboard, I get an exception fault:
Unhandled exception at 0x77c015ee in Athena_Debug.exe: 0xC0000005: Access violation reading location 0x0000012d.
With CallStack:
ntdll.dll!77c015ee()
ntdll.dll!77bf015e()
user32.dll!7588788a()
Athena_Debug.exe!Athena::Window::HandleWindowsMessage() Line 195 + 0xc bytes C++
Athena_Debug.exe!Athena::AthenaCore::StartEngine() Line 96 + 0x12 bytes C++
Athena_Debug.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 44 C++
Athena_Debug.exe!__tmainCRTStartup() Line 547 + 0x2c bytes C
Athena_Debug.exe!WinMainCRTStartup() Line 371 C
kernel32.dll!7702339a()
The line it actually breaks on is the DispatchMessage(&msg) line located here:
MSG Window::HandleWindowsMessage() {
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
if (m_realTime) {
PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE);
}
else {
GetMessage(&msg, m_hwnd, 0, 0);
}
if (msg.message != WM_NULL) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg;
}
So the interesting thing is that if I comment out ToggleFullScreen and instead put an OutputDebugTrace there it works just fine and outputs the debug trace. It appears like it can't resolve the function for an instance? If I comment everything out in ToggleFullScreen, it still crashes. If i create a brand new function that does nothing and call it in response to WM_KEYDOWN it still errors.
Anyone have any idea why this is happening or some ideas on how I can go about fixing it?
Thanks!
I would be guessing that you have wrong pointer obntained from GetWindowLongPtr, as it was not set by SetWindowLongPtr. As I wrote in my comment, you should not test if it's empty (I have no idea what is there by default but i wouldn't assume anything.), but set it when you recieve WM_CRERATE (which is guaranteed to be the first message sent to a window). Have you tested if SetWindowLongPtr is acctually called?
Other messages are not dependent on any contents of Window class, so they are working well with wrong this pointer - it is an error to call method for wrong pointer, but if method is not actually using it, it will still work well.
EDIT:
Another possbile cause: Why are you not using result value from PeekMessage? i see that you are testing for WM_NULL, which should be 0 at this point (you are zeroing memory), but PeekMessage does not guarantee that it's not touching MSG structure even if it's not retrieving anything. Using PeekMessage result should be used at all times. Plus zeroing memory is rather inefficient at this point.
The first message does not have to be WM_NCCREATE (or WM_CREATE), you have to be prepared to get WM_SIZE or WM_GETMINMAXINFO (and probably other messages) before any create message!
In your code you would change if (createStruct) { to if (WM_NCCREATE==msg && createStruct) {...
You can see this in Raymond Chen's scratch program.
Your WindowProc callback should look more like this:
//Every Windows Message will hit this function.
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
Window* targetWindow;
if (msg == WM_CREATE)
{
//extract the Window instance pointer from the lparam
CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;
targetWindow = (Window*)createStruct->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
}
else
{
//Get the Window Instance from the userdata
targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (targetWindow)
{
//Now we can pipe it to the Window's local wnd proc function
return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
}
//It was some other message which we just can't deal with right now
return DefWindowProc(hwnd, msg, wparam, lparam);
}