WinAPI经典应用中基于HDC的图形定时刷新实现方案问询
问题描述
需求
绘制一个正方形,并使其颜色每0.1秒左右变化一次。
尝试情况
- 用
InvalidateRect搭配UpdateWindow,仅在WndProc的default分支能勉强运行,但颜色只变化几次就停止,还会导致对话框变形、菜单部分消失; - 若不放在default分支,
InvalidateRect仅在点击操作时触发,无法实现0.1秒自动刷新; - 曾在WinMain主消息循环中手动发送WM_PAINT消息,无效果;
- 考虑将主窗口句柄
hWnd设为全局变量,但不确定是否合理。
当前代码
主消息循环代码
while (windowState) { BOOL msgState; iter_Count++; if (iter_Count >= 10) { SendMessage(nullptr, WM_COMMAND, (WPARAM)NULL, (LPARAM)NULL); DispatchMessage(&msg); iter_Count = 0; } else { msgState = PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE); if (msgState) { windowState = GetMessage(&msg, nullptr, 0, 0); msgState = false; } else { //msg.message = WM_PAINT; Tried this but it also doesn't work // DispatchMessage(&msg); } if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } }
窗口过程代码
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; case ID_SETTINGS_SETPALETTE: res = true; DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, set_Palette); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); updater.updateImage(hdc); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: InvalidateRect(hWnd, NULL, TRUE);//this screws up the dialog boxes and works maybe twice UpdateWindow(hWnd); return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
咨询
实现该需求的最优方式是什么?
解决方案
最优方案是使用Windows原生定时器(SetTimer),这是系统专门为周期性任务设计的机制,比手动在消息循环里计数稳定得多,不会干扰正常消息处理流程。
具体步骤
- 创建定时器
在窗口初始化时(比如WM_CREATE消息分支)设置定时器,间隔设为100毫秒(对应0.1秒):
case WM_CREATE: // 定时器ID设为1,间隔100ms SetTimer(hWnd, 1, 100, NULL); break;
- 处理定时消息
在WndProc中添加WM_TIMER分支,每次定时触发时刷新窗口:
case WM_TIMER: if (wParam == 1) { // 匹配定时器ID InvalidateRect(hWnd, NULL, TRUE); // 可选:如果需要立即刷新可加UpdateWindow(hWnd),否则系统会自动处理WM_PAINT } break;
- 销毁定时器
窗口销毁前(WM_DESTROY分支)销毁定时器,避免资源泄漏:
case WM_DESTROY: KillTimer(hWnd, 1); // 销毁ID为1的定时器 PostQuitMessage(0); break;
原有方案问题解析
- 在
WndProc的default分支调用InvalidateRect会让几乎所有系统消息都触发刷新,严重干扰对话框、菜单等控件的正常消息处理,导致界面异常; - 手动在消息循环计数的方式不可靠,消息循环的执行速度不固定,还会被阻塞消息打断,定时精度无法保证;
- 手动发送
WM_PAINT无效,该消息由系统维护,必须通过InvalidateRect触发系统生成,而非手动发送。
关于全局hWnd的问题
使用定时器方案不需要将hWnd设为全局变量,WM_CREATE和WM_TIMER消息中都能直接拿到当前窗口的hWnd参数。如果确实需要全局访问窗口句柄,只要确保窗口创建后正确赋值,也是合理的,但定时器方案完全不需要这么做。
内容的提问来源于stack exchange,提问作者Vroom ZOOM
相关产品推荐
相关产品推荐

