WinAPI中WM_LBUTTONDOWN消息延迟问题求解
问题解决方法
问题根源
快速点击按钮时,Windows系统会将第二次的WM_LBUTTONDOWN消息替换为WM_LBUTTONDBLCLK,你的代码未处理该消息,因此计数器不会递增。此外,全局变量t的共享状态可能导致逻辑混乱,且重复启动定时器会引发不必要的消息队列堆积。
修复步骤
- 在按钮子类窗口过程中添加
WM_LBUTTONDBLCLK的处理,复用WM_LBUTTONDOWN的递增与定时器逻辑; - 启动定时器前先调用
KillTimer,确保同一ID的定时器不会重复创建; - 将全局变量
t改为按钮窗口的附加数据(通过SetWindowLongPtr/GetWindowLongPtr存储),避免全局状态冲突。
修改后的完整代码
#include <Windows.h> HINSTANCE g_hInst; HWND hEdit; HWND hButton; WNDPROC btnProc; int counter = 0; // 用于存储按钮的延迟计时数据的窗口附加数据索引 #define BTN_TIMER_DELAY_INDEX 0 static LRESULT CALLBACK StaticButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: { // 递增计数器并更新编辑框 counter++; wchar_t buf[40]; wsprintf(buf, L"%d", counter); SetWindowText(hEdit, buf); // 先停止已有定时器,再启动新的 HWND hParent = GetParent(hWnd); KillTimer(hParent, 1); SetTimer(hParent, 1, 100, 0); // 重置按钮的延迟计时 SetWindowLongPtr(hWnd, GWLP_USERDATA + BTN_TIMER_DELAY_INDEX, 0); return btnProc(hWnd, uMsg, wParam, lParam); } case WM_LBUTTONUP: { // 停止定时器 HWND hParent = GetParent(hWnd); KillTimer(hParent, 1); // 重置延迟计时 SetWindowLongPtr(hWnd, GWLP_USERDATA + BTN_TIMER_DELAY_INDEX, 0); return btnProc(hWnd, uMsg, wParam, lParam); } default: return btnProc(hWnd, uMsg, wParam, lParam); } return 0; } LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: hEdit = CreateWindow(L"EDIT", L"0", WS_CHILD | WS_VISIBLE | ES_CENTER | ES_READONLY, 100, 100, 100, 20, hWnd, (HMENU)1, g_hInst, NULL); hButton = CreateWindow(L"BUTTON", L"▴", WS_CHILD | WS_VISIBLE | BS_LEFT, 204, 100, 20, 20, hWnd, (HMENU)2, g_hInst, NULL); // 初始化按钮的延迟计时附加数据 SetWindowLongPtr(hButton, GWLP_USERDATA + BTN_TIMER_DELAY_INDEX, 0); // 子类化按钮控件 btnProc = (WNDPROC)SetWindowLongPtr(hButton, GWLP_WNDPROC, (LONG_PTR)StaticButtonProc); break; case WM_TIMER: { // 获取按钮的延迟计时数据 int t = (int)GetWindowLongPtr(hButton, GWLP_USERDATA + BTN_TIMER_DELAY_INDEX); if (t < 300) { t += 100; SetWindowLongPtr(hButton, GWLP_USERDATA + BTN_TIMER_DELAY_INDEX, t); } else { // 持续递增计数器并更新编辑框 counter += 1; wchar_t buf[32]; wsprintf(buf, L"%d", counter); SetWindowText(hEdit, buf); } break; } case WM_DESTROY: PostQuitMessage(EXIT_SUCCESS); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int iCmdShow) { g_hInst = hInst; WNDCLASS wndClass = { .style = CS_HREDRAW | CS_VREDRAW, .lpfnWndProc = WindowProc, .hInstance = hInst, .hIcon = LoadIcon(NULL, IDI_APPLICATION), .hCursor = LoadCursor(NULL, IDC_ARROW), .hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH), .lpszMenuName = NULL, .lpszClassName = L"app", }; if (!RegisterClass(&wndClass)) { OutputDebugString(L"Failed to register window class\n"); return EXIT_FAILURE; } if (!CreateWindow( L"app", L"Counter", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 1280, 720, NULL, NULL, hInst, NULL)) { OutputDebugString(L"Failed to create window\n"); return EXIT_FAILURE; } MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
额外说明
- 处理
WM_LBUTTONDBLCLK确保快速点击时计数器能正常递增; - 启动定时器前先停止,避免同一ID的定时器重复创建;
- 使用窗口附加数据存储
t,替代全局变量,避免多控件场景下的状态冲突。
内容的提问来源于stack exchange,提问作者machine_1
相关产品推荐
相关产品推荐

