Win32平台Event系统实时响应异常问题求助
问题:Win32自定义事件队列无法实时响应窗口调整事件
基于Win32 API实现的抽象事件系统,使用自定义队列分发系统消息,但在拖拽窗口边框调整尺寸时,Quest_Log不会实时打印窗口尺寸,仅在停止拖拽后批量输出所有期间的尺寸信息。
示例代码
#include "Quest.h" int main(void) { if(Quest_Init(QUEST_SUBSYSTEM_VIDEO) > 0) { return 1; } Quest_Window *window = Quest_CreateWindow("Example", 100, 100, 640, 480, QUEST_WINDOW_DEFAULT); if(window == NULL) { return 2; } bool running = true; while(running) { Quest_Event event; while(Quest_PollEvent(&event)) { switch(event.type) { case QUEST_QUIT: running = false; break; case QUEST_WINDOW_RESIZING: Quest_Log("Window resizing: %d;%d\n", event.window.width, event.window.height); break; default: break; } } } Quest_DestroyWindow(window); Quest_Shutdown(); return 0; }
事件轮询与推送实现
#define QUEST_MAX_EVENTS 0xffffui16 static Quest_Event sQueue[QUEST_MAX_EVENTS] = {0}; static unsigned int Qsize = 0; bool Quest_PollEvent(Quest_Event *event) { MSG msg; bool result = false; if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { if(msg.message == WM_QUIT) { event->type = QUEST_QUIT; result = true; return result; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } if(Qsize > 0) { *event = sQueue[0]; { for(unsigned int i = 0; i < Qsize - 1; i++) { sQueue[i] = sQueue[i + 1]; } --Qsize; } result = true; return result; } return result; } void Quest_PushEvent(const Quest_Event *event) { if(Qsize < sizeof(sQueue) / sizeof(sQueue[0])) { sQueue[Qsize++] = *event; } }
WindowProc核心实现
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) { Quest_Event event; memset(&event, 0x0000, sizeof(Quest_Event)); switch(uMsg) { // other messages case WM_SIZING: { switch(wParam) { case SIZE_MAXIMIZED: event.type = QUEST_WINDOW_MAXIMIZE; break; case SIZE_MINIMIZED: event.type = QUEST_WINDOW_MINIMIZE; break; case SIZE_RESTORED: event.type = QUEST_WINDOW_RESIZE; break; default: break; } event.window.width = LOWORD(lParam); event.window.height = HIWORD(lParam); } break; default: break; } Quest_PushEvent(&e); return DefWindowProc(hWnd, uMsg, wParam, lParam); }
问题原因分析
- 系统模态循环阻塞主循环:拖拽窗口边框时,Win32系统会进入内部模态循环,持续发送
WM_SIZING消息,但此时主循环while(running)会被阻塞,直到拖拽操作结束、模态循环退出后,所有积压的WM_SIZING消息才会被一次性派发给WindowProc,导致事件队列批量积压。 - 事件处理时机滞后:当前
Quest_PollEvent的逻辑是先处理一个Win32消息,再处理自定义队列中的事件,但在模态循环期间,主循环根本无法执行,直到拖拽结束才会一次性处理所有队列中的事件。 - 事件类型不匹配:示例代码中监听的是
QUEST_WINDOW_RESIZING事件,但WindowProc中处理WM_SIZING的default分支未设置该事件类型,同时存在变量名错误(Quest_PushEvent(&e)应为Quest_PushEvent(&event))。
解决方案
方案一:调整事件轮询逻辑,实时处理队列
拆分事件队列处理逻辑,确保每次处理Win32消息后立即处理自定义事件,避免积压。
修改后的事件轮询代码
// 新增仅处理队列事件的函数 bool Quest_PollPendingEvent(Quest_Event *event) { if(Qsize > 0) { *event = sQueue[0]; for(unsigned int i = 0; i < Qsize - 1; i++) { sQueue[i] = sQueue[i + 1]; } --Qsize; return true; } return false; } bool Quest_PollEvent(Quest_Event *event) { MSG msg; // 先处理所有待处理的Win32消息,每处理一个就检查队列 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { if(msg.message == WM_QUIT) { event->type = QUEST_QUIT; return true; } else { TranslateMessage(&msg); DispatchMessage(&msg); // 处理完Win32消息后立即处理一个自定义事件 if(Quest_PollPendingEvent(event)) { return true; } } } // 没有Win32消息时,处理队列剩余事件 return Quest_PollPendingEvent(event); }
修正WindowProc的事件类型与变量错误
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) { Quest_Event event; memset(&event, 0x0000, sizeof(Quest_Event)); switch(uMsg) { // other messages case WM_SIZING: { switch(wParam) { case SIZE_MAXIMIZED: event.type = QUEST_WINDOW_MAXIMIZE; break; case SIZE_MINIMIZED: event.type = QUEST_WINDOW_MINIMIZE; break; case SIZE_RESTORED: event.type = QUEST_WINDOW_RESIZE; break; default: // 匹配示例中的监听事件类型 event.type = QUEST_WINDOW_RESIZING; break; } event.window.width = LOWORD(lParam); event.window.height = HIWORD(lParam); Quest_PushEvent(&event); // 修正变量名错误 } break; default: break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); }
方案二:直接在WindowProc中触发事件回调(推荐)
跳过自定义队列,在WindowProc中直接调用事件回调,完全避免队列积压问题,实现实时响应。
新增事件回调机制
// 定义事件回调类型 typedef void (*Quest_EventCallback)(Quest_Event*); static Quest_EventCallback sEventCallback = NULL; // 设置全局事件回调 void Quest_SetEventCallback(Quest_EventCallback callback) { sEventCallback = callback; }
修改WindowProc直接触发回调
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) { Quest_Event event; memset(&event, 0x0000, sizeof(Quest_Event)); bool hasValidEvent = false; switch(uMsg) { case WM_SIZING: { switch(wParam) { case SIZE_MAXIMIZED: event.type = QUEST_WINDOW_MAXIMIZE; break; case SIZE_MINIMIZED: event.type = QUEST_WINDOW_MINIMIZE; break; case SIZE_RESTORED: event.type = QUEST_WINDOW_RESIZE; break; default: event.type = QUEST_WINDOW_RESIZING; break; } event.window.width = LOWORD(lParam); event.window.height = HIWORD(lParam); hasValidEvent = true; } break; case WM_QUIT: event.type = QUEST_QUIT; hasValidEvent = true; break; // 处理其他消息... default: break; } // 触发回调 if(hasValidEvent && sEventCallback != NULL) { sEventCallback(&event); } return DefWindowProc(hWnd, uMsg, wParam, lParam); }
简化主循环
// 事件处理回调函数 void OnQuestEvent(Quest_Event* event) { switch(event->type) { case QUEST_QUIT: running = false; break; case QUEST_WINDOW_RESIZING: Quest_Log("Window resizing: %d;%d\n", event->window.width, event->window.height); break; default: break; } } int main(void) { if(Quest_Init(QUEST_SUBSYSTEM_VIDEO) > 0) { return 1; } // 设置事件回调 Quest_SetEventCallback(OnQuestEvent); Quest_Window *window = Quest_CreateWindow("Example", 100, 100, 640, 480, QUEST_WINDOW_DEFAULT); if(window == NULL) { return 2; } bool running = true; MSG msg; // 使用GetMessage保证消息被及时处理 while(running && GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } Quest_DestroyWindow(window); Quest_Shutdown(); return 0; }
内容的提问来源于stack exchange,提问作者ガリレオだけ
相关产品推荐
相关产品推荐

