WndProc 和 DispatchMessage 的奇怪错误。成员函数不存在?
因此,当我的 Window 类中收到 WM_KEYDOWN 消息时,会发生一些奇怪的错误。
我有一个全局 WndProc 函数,它确定它是哪个窗口实例并将消息发送到它自己的本地 WndProc 函数。
//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);
}
我的本地 wndproc 看起来像这样:
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);
}
}
所以此时非常简单。如果按下任意键,窗口就应该调用它的成员函数ToggleFullScreen。
现在,由于某种原因,当我运行此命令并按键盘上的任意键时,我收到异常错误:
Athena_Debug.exe 中 0x77c015ee 处未处理的异常:0xC0000005:访问冲突读取位置 0x0000012d。
使用调用堆栈:
ntdll.dll!77c015ee()
ntdll.dll!77bf015e()
user32.dll!7588788a()
Athena_Debug.exe!Athena::Window::HandleWindowsMessage() 第 195 行 + 0xc 字节 C++ Athena_Debug.exe!Athena::AthenaCore::StartEngine() 第 96 行 + 0x12 字节 C++ Athena_Debug.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) 第 44 行 C++ Athena_Debug.exe!__tmainCRTStartup() 第 547 行 + 0x2c 字节 C Athena_Debug.exe!WinMainCRTStartup() 第 371 行 C kernel32.dll!7702339a()
它实际中断的行是位于此处的 DispatchMessage(&msg) 行:
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;
}
所以有趣的是,如果我注释掉 ToggleFullScreen 并在那里放置一个 OutputDebugTrace ,它就可以正常工作并输出调试痕迹。似乎无法解析实例的函数?如果我在 ToggleFullScreen 中注释掉所有内容,它仍然会崩溃。如果我创建一个不执行任何操作的全新函数并调用它来响应 WM_KEYDOWN,它仍然会出错。
任何人都知道为什么会发生这种情况,或者知道我如何解决它?
谢谢!
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我猜测您从 GetWindowLongPtr 获得了错误的指针,因为它不是由 SetWindowLongPtr 设置的。正如我在评论中所写,您不应该测试它是否为空(我不知道默认情况下有什么,但我不会假设任何内容。),而是在收到 WM_CRERATE 时设置它(这保证是第一条消息)发送到窗口)。您是否测试过 SetWindowLongPtr 是否确实被调用?
其他消息不依赖于 Window 类的任何内容,因此它们可以在错误的
this
指针下正常工作 - 为错误的指针调用方法是错误的,但如果方法实际上并未使用它,则它会发生错误。仍然会运作良好。编辑:
另一个可能的原因:为什么你不使用 PeekMessage 的结果值?我看到您正在测试 WM_NULL,此时它应该为 0(您正在将内存归零),但 PeekMessage 不能保证它不会触及 MSG 结构,即使它没有检索任何内容。应始终使用 PeekMessage 结果。另外,此时内存归零效率相当低。
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.
第一条消息不一定是 WM_NCCREATE(或 WM_CREATE),您必须准备好在任何创建消息之前获取 WM_SIZE 或 WM_GETMINMAXINFO(可能还有其他消息)!
在您的代码中,您可以将
if (createStruct) {
更改为if (WM_NCCREATE==msg && createStruct) {
...您可以在 Raymond Chen 的 < a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx" rel="nofollow">划痕程序。
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) {
toif (WM_NCCREATE==msg && createStruct) {
...You can see this in Raymond Chen's scratch program.
您的 WindowProc 回调应该看起来更像这样:
Your WindowProc callback should look more like this: