为何使用WM_INPUT时程序运行缓慢甚至无响应?
问题:WM_INPUT消息导致程序卡顿崩溃
在Windows平台开发简易虚拟窗口管理器时,使用WM_KEY*/WM_SYSKEY*系列消息时程序运行流畅、响应迅速,但改用WM_INPUT消息后,大量键盘操作会导致程序严重卡顿,最终出现“未响应”提示并终止。代码采用C风格编写,使用g++编译,可视为C语言代码。
代码实现
#ifdef __MINGW32__ # define _WIN32_WINNT 0x0501 #endif // __MINGW32__ #include <windows.h> #include <stdint.h> #include <stdio.h> typedef struct{ bool keys[256]; } KEYBOARD; typedef struct{ bool keys[5]; int16_t deltaWheel; int deltaX; int deltaY; } MOUSE; KEYBOARD Keyboard; MOUSE Mouse; const int ScreenHeight = 1080; const int ScreenWidth = 1920; uint32_t* ScreenBitmap; BITMAPINFO BitmapInfo = {}; bool Running; void SetPixel(int x, int y, uint32_t color){ if(x >= 0 & y >= 0 & x < ScreenWidth & y < ScreenHeight){ ScreenBitmap[y * ScreenWidth + x] = color; } } uint32_t GetPixel(int x, int y){ if(x >= 0 & y >= 0 & x < ScreenWidth & y < ScreenHeight){ return ScreenBitmap[y * ScreenWidth + x]; } else return 0; } void DrawRectangle(int X1, int Y1, int X2, int Y2, uint32_t color){ int maxX = X2 >= ScreenWidth ? ScreenWidth - 1 : X2; int maxY = Y2 >= ScreenHeight ? ScreenHeight - 1: Y2; int minX = X1 < 0 ? 0 : X1; int minY = X2 < 0 ? 0 : Y1; for(int x = minX; x <= maxX; x++){ for(int y = minY; y <= maxY; y++){ SetPixel(x, y, color); } } } void UpdateScreenBitmap(){ DrawRectangle(0, 0, ScreenWidth, ScreenHeight, 0x00000000); } LRESULT CALLBACK WindowProcedure(HWND WindowHandle, uint32_t Message, WPARAM WParam, LPARAM LParam){ switch(Message){ case WM_CREATE:{ ScreenBitmap = (uint32_t*) VirtualAlloc(0, ScreenHeight * ScreenWidth * 4, MEM_COMMIT, PAGE_READWRITE); BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO); BitmapInfo.bmiHeader.biWidth = ScreenWidth; BitmapInfo.bmiHeader.biHeight = -ScreenHeight; BitmapInfo.bmiHeader.biPlanes = 1; BitmapInfo.bmiHeader.biBitCount = 32; BitmapInfo.bmiHeader.biCompression = BI_RGB; } break; case WM_NCDESTROY:{ Running = false; } break; case WM_PAINT:{ InvalidateRect(WindowHandle, 0, 0); SetDIBitsToDevice(GetDC(WindowHandle), 0, 0, ScreenWidth, ScreenHeight, 0, 0, 0, ScreenHeight, ScreenBitmap, &BitmapInfo, DIB_RGB_COLORS); ValidateRect(WindowHandle, 0); } break; case WM_INPUT:{ uint32_t RawInputSize = sizeof(RAWINPUT); RAWINPUT RawInput = {}; GetRawInputData((HRAWINPUT)LParam, RID_INPUT, &RawInput, &RawInputSize, sizeof(RAWINPUTHEADER)); if(RawInput.header.dwType == RIM_TYPEKEYBOARD){ RAWKEYBOARD kb = RawInput.data.keyboard; Keyboard.keys[kb.MakeCode] = kb.Flags == 0 ? true : false; } else if(RawInput.header.dwType == RIM_TYPEMOUSE){ for(int i = 0; i < 5; i++){ if(RawInput.data.mouse.usButtonFlags == (0b01 << 2*i)) Mouse.keys[i] = true; if(RawInput.data.mouse.usButtonFlags == (0b10 << 2*i)) Mouse.keys[i] = false; } Mouse.deltaWheel = RawInput.data.mouse.usButtonData; Mouse.deltaX = RawInput.data.mouse.lLastX; Mouse.deltaY = RawInput.data.mouse.lLastY; } } break; } return DefWindowProc(WindowHandle, Message, WParam, LParam); } //int WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR ComandLine, int ShowComand){ int main(){ if(true){ AllocConsole(); freopen("conin$", "r", stdin); freopen("conout$", "w", stdout); freopen("conout$", "w", stderr); } WNDCLASS WindowClass = {}; WindowClass.lpfnWndProc = WindowProcedure; WindowClass.lpszClassName = "Window Class Name"; WindowClass.hInstance = (HINSTANCE) GetModuleHandle(0); RegisterClass(&WindowClass); HWND WindowHandle = CreateWindow(WindowClass.lpszClassName, 0, WS_POPUP, 0, 0, ScreenWidth, ScreenHeight, 0, 0, WindowClass.hInstance, 0); RAWINPUTDEVICE RawInputDevice[2]; RawInputDevice[0].usUsagePage = 0x01; RawInputDevice[0].usUsage = 0x02; RawInputDevice[0].dwFlags = RIDEV_NOLEGACY; RawInputDevice[0].hwndTarget = WindowHandle; RawInputDevice[1].usUsagePage = 0x01; RawInputDevice[1].usUsage = 0x06; RawInputDevice[1].dwFlags = RIDEV_NOLEGACY; RawInputDevice[1].hwndTarget = WindowHandle; RegisterRawInputDevices(RawInputDevice, 2, sizeof(RAWINPUTDEVICE)); ShowWindow(WindowHandle, 1); SetWindowPos(WindowHandle, HWND_TOP, 0, 0, ScreenWidth, ScreenHeight, SWP_SHOWWINDOW); ShowCursor(false); Running = true; while(Running){ MSG Message = {}; while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)){ TranslateMessage(&Message); DispatchMessage(&Message); } UpdateScreenBitmap(); SetDIBitsToDevice(GetDC(WindowHandle), 0, 0, ScreenWidth, ScreenHeight, 0, 0, 0, ScreenHeight, ScreenBitmap, &BitmapInfo, DIB_RGB_COLORS); } return 0; }
自我排查
推测问题可能是消息队列因未处理消息堆积导致,但WM_INPUT分支中的处理逻辑极少。查阅文档及相关资料均未找到解释,尝试在SDL源码中寻找线索也未成功。
内容的提问来源于stack exchange,提问作者Prateek Bana
相关产品推荐
相关产品推荐

