在钩子子程中使用PostMessage发消息,有时GetMessage可以拿到消息,有时则直接到了窗口过程,这是为什么呢?
问题描述
在学 Win 32 SDK,首先创建了一个空白窗口,接着使用 SetWindowsHookEx 安装了一个全局的底层键盘钩子。
在钩子子程下面,使用PostMessage 函数向窗口发消息,然后在窗口过程下面 case 了钩子子程发过来的消息,用 MessageBox 函数显示出来。
当我使用 MinGW-w64 编译器编译为 a.exe,运行起来之后与我的脑海中的想法不同。
我的想法
程序中没有创建线程,也没有做异步的处理,所以 MessageBox 的对话框应该是弹出来一个就会卡住,必须点击 “确定” 按钮才会显示下一个对话框。
实际效果
无论上一个对话框是否关闭,只要触发了钩子子程,对话框都会弹出来。一直弹出来,直到程序崩溃了才停下来。
由此情况我有两个问题,第一个问题,为什么窗口过程与钩子子程同样使用一个线程,却可以跳转自如,想运行哪里就运行到哪里呢?
既然没有多线程, MessageBox 显然堵塞了线程,那么后来的对话框是怎么出来的呢?
问题出现的平台版本及自己尝试过哪些方法
操作系统: Windows 10 专业版 1903
编译器: MinGW-W64 版本 i686-8.1.0-release-win32-sjlj-rt_v6-rev0
尝试过下面的方法来研究问题。
把处理钩子子程发来的消息的代码移动到消息循环里面去,结果发现和我预想的一样了,上一个对话框必须关闭了,下一个对话框才会弹出来。
补充一下,MessageBox阻塞时候的消息无缘无故的丢失了。直接跳到阻塞后的消息了。
不把代码移动过去,只是在消息循环里面跟踪一下消息处理的情况,结果是这样的。
当有MessageBox 堵塞线程的时候,消息循环也就堵塞了,儿窗口过程没有堵塞,好像系统自动就把消息分发给窗口了一样。
相关代码
* 注册、创建一个空白窗口,设置并处理底层键盘消息 * 编译套件: MinGW i686-8.1.0-release-win32-sjlj-rt_v6-rev0
* 时间: 12:21 2019/9/29
* 编译命令行: gcc -municode hooks.c
*/
#include <stdio.h>
#include <windows.h>
#include <strsafe.h>
#define WCHAR_BUFFER_SIZE 1024
#define WM_SHOWKEYINFO (WM_USER +256)
/* 全局函数声明 */
void InitWndClass(LPCWSTR, WNDCLASS *);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
/* 全局变量声明 */
HINSTANCE hInstan;
HHOOK g_hHook;
WCHAR wcharBuffer[WCHAR_BUFFER_SIZE +1];
HWND g_hMainWnd;
DWORD g_idMainThread;
/* 应用程序入口点函数 */
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
g_idMainThread = GetCurrentThreadId();
printf("%d in WinMain\n", GetCurrentThreadId());
WNDCLASS wndClass; // 窗口类结构变量
LPCWSTR szClassName = TEXT("MyWndClass"); // 存放指向窗口类名的指针
hInstan = hInstance;
InitWndClass(szClassName, &wndClass); // 填充窗口类结构
RegisterClass(&wndClass); // 注册窗口类结构
/* 创建一个窗口 */
g_hMainWnd = CreateWindow(szClassName, TEXT("Keyboard Monitor"), WS_OVERLAPPEDWINDOW, // 使用指定的窗口类名创建一个具有标题文字加上指定风格的窗口
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, // 窗口的左上角的 (X, Y) 坐标以及宽度和高度
NULL, NULL, hInstan, NULL); // 父窗口句柄, 菜单句柄, 应用程序实例句柄和额外参数
ShowWindow(g_hMainWnd, nCmdShow); // 显示窗口
UpdateWindow(g_hMainWnd); // 更新窗口
/* 消息循环 */
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
switch(msg.message) {
// 响应钩子子程序发过来的 WM_SHOWKEYINFO 消息
case WM_SHOWKEYINFO:
printf("a new message\n");
break;
}
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 分发消息
switch(msg.message) {
// 响应钩子子程序发过来的 WM_SHOWKEYINFO 消息
case WM_SHOWKEYINFO:
printf("Dispatched message\n");
break;
}
}
return msg.wParam;
}
/* 填充 WNDCLASS 结构 */
void InitWndClass(LPCWSTR lpszClassName, WNDCLASS *wndClass) {
wndClass->lpszClassName = lpszClassName; // 类名
wndClass->hInstance = hInstan; // 应用程序实例句柄
wndClass->style = CS_HREDRAW | CS_VREDRAW; //窗口类样式设置为垂直移动 || 水平移动重画
wndClass->lpfnWndProc = WindowProc; // 窗口过程子程序
wndClass->cbClsExtra = 0; // 窗口类额外空间,初始化为 "0",
wndClass->cbWndExtra = 0; // 窗口实力额外区域初始化为 "0"。由于不需要以上两个参数设置为 "0"。
wndClass->hIcon = LoadIcon(NULL, IDI_APPLICATION); // 窗口需要的图标句柄,这里使用 LoadIcon 装在一个图标
wndClass->hCursor = LoadCursor(NULL, IDC_ARROW); // 窗口的光标,这里使用 LoadCursor 加载一个普通光标
wndClass->hbrBackground = (HBRUSH)(COLOR_WINDOW +1); // 窗口的背景画刷
wndClass->lpszMenuName = NULL; // 菜单句柄
}
/* 窗口过程 */
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
// 窗口被创建
case WM_CREATE:
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0); // 安装一个钩子
if(NULL == g_hHook) // 如果钩子安装失败直接抛出提示并销毁窗口
{
MessageBox(hwnd, TEXT("安装钩子失败!"), TEXT("Failed"), MB_ICONERROR);
DestroyWindow(hwnd);
}
break;
// 响应钩子子程序发过来的 WM_SHOWKEYINFO 消息
case WM_SHOWKEYINFO:
printf("%d before MessageBox\n", GetCurrentThreadId());
MessageBox(g_hMainWnd, (LPWSTR)wParam, TEXT("信息"), MB_ICONINFORMATION);
printf("%d after MessageBox\n", GetCurrentThreadId());
break;
// 窗口被销毁
case WM_DESTROY:
UnhookWindowsHookEx(g_hHook); // 卸载钩子
PostQuitMessage(0); // 向消息队列发一个 WM_QUIT 消息来结束应用程序的运行
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam); // 使用默认窗口过程处理消息
}
/* Windows Hook 回调处理程序 */
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *kbdhook;
if(HC_ACTION == nCode && (WM_KEYUP == wParam || WM_SYSKEYUP == wParam))
{
kbdhook = (KBDLLHOOKSTRUCT *)lParam;
StringCchPrintf(wcharBuffer, WCHAR_BUFFER_SIZE, TEXT("虚拟见码: %#X\n扫描代码: %#X\n是否扩展键: %c\n标志: %#X"), kbdhook->vkCode, kbdhook->scanCode, LLKHF_EXTENDED & kbdhook->flags ? TEXT('是') : TEXT('否'), kbdhook->flags);
printf("%d in Hook Callback\n", GetCurrentThreadId());
PostMessage(g_hMainWnd, WM_SHOWKEYINFO, (WPARAM)wcharBuffer, 0); // 把格式化好的字符串指针发送到消息队列,消息队列会转交给窗口过程
// PostThreadMessage(g_idMainThread, WM_SHOWKEYINFO, (WPARAM)wcharBuffer, 0); // 把格式化好的字符串指针发送到消息队列,消息队列会处理这条消息
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
/*
命令行输出
7804 in WinMain
7804 in Hook Callback
a new message
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
7804 in Hook Callback
7804 before MessageBox
*/
以上是 在钩子子程中使用PostMessage发消息,有时GetMessage可以拿到消息,有时则直接到了窗口过程,这是为什么呢? 的全部内容, 来源链接: utcz.com/p/191419.html