【发布时间】:2016-06-17 13:50:36
【问题描述】:
我正在尝试使用全局 Windows 挂钩观察打开和关闭窗口。因此,我试图将回调函数传递给包含我的钩子过程的 DLL。但是,挂钩过程本身永远不会被调用。
/** Handle to this DLL */
HINSTANCE dllModule = 0;
/** Hook procedure */
HHOOK windowHook = 0;
/** Callbacks are called when a new window is opened, or closed. */
HWND_CALLBACK windowOpenCallback = 0;
HWND_CALLBACK windowCloseCallback = 0;
WINDOWHOOK_API int WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID reserved)
{
if(reason == DLL_PROCESS_ATTACH)
{
dllModule = hInstance;
std::cout << "Process attached." << std::endl;
}
else if (reason == DLL_PROCESS_DETACH)
{
std::cout << "Process detached." << std::endl;
}
else if (reason == DLL_THREAD_ATTACH)
{
}
else if (reason == DLL_THREAD_DETACH)
{
}
return 1;
}
WINDOWHOOK_API bool setWindowHook(HWND_CALLBACK openCallback, HWND_CALLBACK closeCallback)
{
if(windowHook != 0 || windowOpenCallback != 0 || windowCloseCallback != 0)
{
return false;
}
windowOpenCallback = openCallback;
windowCloseCallback = closeCallback;
windowHook = SetWindowsHookEx(WH_SHELL, (HOOKPROC)hookCallback, dllModule, 0);
return windowHook != 0;
}
WINDOWHOOK_API bool releaseWindowHook()
{
bool result = windowHook != 0 && UnhookWindowsHookEx(windowHook) != 0;
windowHook = 0;
windowOpenCallback = 0;
windowCloseCallback = 0;
return result;
}
LRESULT CALLBACK hookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
std::cout << "Hook callback" << std::endl;
return CallNextHookEx(windowHook, nCode, wParam, lParam);
}
从我的主程序中,我加载库并调用 setWindowHook 过程。然后我简单地启动一个无限循环并等待调用钩子,但是虽然 setWindowHook 没有失败,但钩子过程永远不会被调用。我不明白为什么。
我尝试设置一个 WH_KEYBOARD 挂钩。这样,钩子程序就会在按键上被调用,但前提是它们位于我程序的控制台窗口内。
我在 64 位机器上将其编译为 32 位。但是,它至少不应该适用于我所有的 32 位应用程序吗?
提前致谢。
【问题讨论】:
-
你的钩子可能会被调用,但是它将在属于它被注入的进程的线程上调用。它将尝试在另一个进程的上下文中输出到
std::cout,对于大多数 Windows 进程,这将失败,因为它们没有附加的控制台。使用不涉及写入控制台的另一种方法来监控您的钩子。 -
谢谢,这确实是问题所在。然而,现在我面临着另一个。每当窗口打开时传递要调用的回调的想法实际上并不奏效。虽然我可以使函数 ptr 在加载了 DLL 的所有进程之间共享,但这些进程无法调用它,因为它们位于不同的地址空间中。我怎样才能让他们与我的程序进行通信?我可以发送窗口消息,但不能保证会处理这些消息,是吗?
-
将消息发布回您的主窗口可以正常工作(阅读有关 WM_COPYDATA 的信息)。共享内存将起作用(使用适当的锁定)。套接字可以工作,但您必须小心处理。
-
WM_COPYDATA专门设计用于跨越进程边界。话虽如此,请注意窗口消息受 User Interface Privilege Isolation (UIPI) 的约束,因此您应该调用ChangeWindowMessageFilterEx()以允许您接收 HWND 接收WM_COPYDATA(或任何您选择的窗口消息)来自任何完整性级别的任何进程。 -
全局挂钩是错误的工具。要监视窗口的创建和销毁,请使用WinEvents。额外的好处:这种方法不需要任何 IPC,您不再需要等待
PostThreadMessage失败(请参阅 Why do messages posted by PostThreadMessage disappear?)。