非GUI应用中GetMessageTime函数返回异常的问题求助
GetMessageTime返回0的问题(使用HWND_MESSAGE窗口接收消息时)
问题描述
我开发了一款截图助手,上层应用通过发送窗口消息触发它模拟Win+Prtsc组合键完成截图。原本功能正常,但添加两次消息间隔检测时发现GetMessageTime()行为异常:
- 保留
MessageBox调试代码时,GetMessageTime()能返回消息发送的正确计时周期 - 移除
MessageBox后,该函数持续返回0
我需要在保留HWND_MESSAGE窗口架构的前提下,找到问题原因和解决办法。
相关代码
MessageReceiver
#include <Windows.h> #include <stdio.h> HWND hwndMain; UINT hotkeyTriggeredMessageId; INPUT winPlusPrtscKeyCombo[4]; LONG lastHotkeyTime = -1000; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == hotkeyTriggeredMessageId) { printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, GetMessageTime(), lastHotkeyTime - GetMessageTime()); MessageBox(NULL, L"triggered!", L"", NULL); printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, GetMessageTime(), lastHotkeyTime - GetMessageTime()); SendInput(4, winPlusPrtscKeyCombo, sizeof(INPUT)); } return DefWindowProc(hWnd, message, wParam, lParam); } int main() { MSG msg; BOOL bRet; WNDCLASS wc = { 0 }; memset(winPlusPrtscKeyCombo, 0, sizeof(INPUT) * 4); winPlusPrtscKeyCombo[0].type = INPUT_KEYBOARD; winPlusPrtscKeyCombo[0].ki.wVk = VK_LWIN; winPlusPrtscKeyCombo[1].type = INPUT_KEYBOARD; winPlusPrtscKeyCombo[1].ki.wVk = VK_SNAPSHOT; winPlusPrtscKeyCombo[2].type = INPUT_KEYBOARD; winPlusPrtscKeyCombo[2].ki.wVk = VK_SNAPSHOT; winPlusPrtscKeyCombo[2].ki.dwFlags = KEYEVENTF_KEYUP; winPlusPrtscKeyCombo[3].type = INPUT_KEYBOARD; winPlusPrtscKeyCombo[3].ki.wVk = VK_LWIN; winPlusPrtscKeyCombo[3].ki.dwFlags = KEYEVENTF_KEYUP; HINSTANCE hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = (WNDPROC)WndProc; wc.hInstance = hInstance; wc.lpszClassName = L"MessageReceiverWindow"; if (!RegisterClass(&wc)) { return FALSE; } hwndMain = CreateWindow(L"MessageReceiverWindow", L"MessageReceiverWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0); hotkeyTriggeredMessageId = RegisterWindowMessage(L"MESSAGE_HOTKEY_TRIGGERED"); ChangeWindowMessageFilterEx(hwndMain, hotkeyTriggeredMessageId, MSGFLT_ALLOW, NULL); UpdateWindow(hwndMain); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return 0; }
MessageSender
#include <stdio.h> #include <Windows.h> UINT hotkeyTriggeredMessageId; HWND targetWindow; int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { hotkeyTriggeredMessageId = RegisterWindowMessage(L"MESSAGE_HOTKEY_TRIGGERED"); targetWindow = FindWindowEx(HWND_MESSAGE, NULL, L"MessageReceiverWindow", NULL); SendMessage(targetWindow, hotkeyTriggeredMessageId, NULL, NULL); return 0; }
问题原因
GetMessageTime()的返回值依赖当前线程消息队列中正在处理的MSG结构体的time成员。而你使用SendMessage()是同步调用:发送方会阻塞直到接收方处理完消息。
- 保留
MessageBox时,它会触发额外的消息循环处理,此时当前线程的MSG结构体被正确填充,GetMessageTime()能拿到有效时间 - 移除
MessageBox后,消息处理速度极快,同步调用的SendMessage()在接收方消息循环还没来得及将MSG的time成员关联到当前处理的消息时就已完成,导致GetMessageTime()无法获取有效计时,返回0
HWND_MESSAGE是无界面消息窗口,它的消息处理路径和普通窗口略有不同,这种同步消息处理的时序问题会被进一步放大。
解决方案
不要依赖GetMessageTime()获取消息发送时间,直接在发送消息时将时间戳作为参数传递,这是最可靠的方案:
1. 修改MessageSender,传递时间戳
#include <stdio.h> #include <Windows.h> UINT hotkeyTriggeredMessageId; HWND targetWindow; int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { hotkeyTriggeredMessageId = RegisterWindowMessage(L"MESSAGE_HOTKEY_TRIGGERED"); targetWindow = FindWindowEx(HWND_MESSAGE, NULL, L"MessageReceiverWindow", NULL); // 获取和GetMessageTime同单位的系统计时(系统启动后的毫秒数) DWORD currentTime = GetTickCount(); SendMessage(targetWindow, hotkeyTriggeredMessageId, (WPARAM)currentTime, NULL); return 0; }
2. 修改MessageReceiver,直接使用传递的时间戳
#include <Windows.h> #include <stdio.h> HWND hwndMain; UINT hotkeyTriggeredMessageId; INPUT winPlusPrtscKeyCombo[4]; LONG lastHotkeyTime = -1000; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == hotkeyTriggeredMessageId) { DWORD currentTime = (DWORD)wParam; printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, currentTime, currentTime - lastHotkeyTime); // MessageBox(NULL, L"triggered!", L"", NULL); printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, currentTime, currentTime - lastHotkeyTime); SendInput(4, winPlusPrtscKeyCombo, sizeof(INPUT)); lastHotkeyTime = currentTime; // 更新上次触发时间 } return DefWindowProc(hWnd, message, wParam, lParam); } // main函数部分保持不变
另一种可选方案是在接收方消息循环中提前保存MSG的time成员,再传递给WndProc处理,但这种方式仍受同步消息处理的时序影响,可靠性不如发送方直接传递时间戳。
内容的提问来源于stack exchange,提问作者June
相关产品推荐
相关产品推荐

