在我的Window类中获取WM_KEYDOWN消息时,会发生一些奇怪的错误。
我有一个Global 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。
使用CallStack:
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时调用它仍然会出错。
任何人都知道为什么会发生这种情况或有关我如何修复它的一些想法?
谢谢!
答案 0 :(得分:2)
我猜你从GetWindowLongPtr得到了错误的指针,因为它没有被SetWindowLongPtr设置。正如我在评论中所写的那样,你不应该测试它是否为空(我不知道默认情况下是什么,但我不会假设任何东西。),但是当你收到WM_CRERATE(它保证是第一条消息)时设置它发送到窗口)。你是否测试过SetWindowLongPtr是否被实际调用?
其他消息不依赖于Window类的任何内容,因此它们适用于错误的this
指针 - 调用错误指针的方法是错误的,但如果方法实际上没有使用它,那么它仍然会运作良好。
编辑:
另一个可能的原因:你为什么不使用PeekMessage的结果值?我看到你正在测试WM_NULL,此时它应该为0(你正在将内存归零),但PeekMessage并不能保证它不会触及MSG结构,即使它没有检索任何东西。应始终使用PeekMessage结果。此时归零内存的效率相当低。
答案 1 :(得分:2)
第一条消息不必是WM_NCCREATE(或WM_CREATE),您必须准备好在任何创建消息之前获取WM_SIZE或WM_GETMINMAXINFO(可能还有其他消息)!
在您的代码中,您可以将if (createStruct) {
更改为if (WM_NCCREATE==msg && createStruct) {
...
你可以在Raymond Chen的scratch program中看到这个。
答案 2 :(得分:2)
你的WindowProc回调应该更像这样:
//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);
}