Welcome

首页 / 软件开发 / C++ / 安装钩子,托管C++中的字符串及其它

安装钩子,托管C++中的字符串及其它2010-12-22Paul DiLascia我想调用 SetWindowsHookEx 来设置 WH_CBT 钩子,但我了解到 MFC 也安装 了这个钩子,也就是在一个线程中安装了两次 WH_CBT,这样做能行吗?

Ken Dang

答案是肯定的。只要遵循正确的步骤,你可以安装几个相 同类型的钩子。Windows 的钩子是被设计用于一系列类似子类化这样的操作。为 了安装钩子,得调用 SetWindowsHookEx 函数,参数为钩子类型和指向钩子过程 的指针。SetWindowsHookEx 返回一个指向旧钩子的句柄:HHOOK hOldHook; // 全局
...
hOldHook = SetWindowsHookEx(WH_CBT, MyCbtProc, ...);

现在只要发生有钩子事件,Windows 便调用你的钩 子过程。当你的过程处理完该事件,则应该用 CallNextHookEx 调用下一个钩子 :

LRESULT CALLBACK
MyCbtProc(int code, ...)
{
if (code==/* whatever */) {
// do something
}
return CallNextHookEx(hOldHook, code, ...);
}

当然,没有人强迫你调用 CallNextHookEx,但是如果不调用,那么你的程序可 能会垮掉。MFC 使用 CBT 钩子来监视窗口的创建。只要一创建窗口,Windows 都会用 HCBT_CREATEWND 调用此 CBT 钩子。MFC 通过子类化窗口来处理 HCBT_CREATEWND,并将它附属到其 CWnd 对象。具体细节比较复杂,这里仅给出 一个简版的代码:

// 来自 wincore.cpp 的简化代码
LRESULT _AfxCbtFilterHook(int code, WPARAM wp, ...)
{
if (code==HCBT_CREATEWND) {
CWnd* pWndInit = pThreadState- >m_pWndInit;
HWND hWnd = (HWND)wp;
pWndInit->Attach(hWnd);
SetWindowLongPtr(hWnd, GWLP_WNDPROC, &AfxWndProcafxWndProc);
}
return CallNextHookEx(...);
}

这里是去粗取精后的代码,MFC 将窗 口对象附属到其 HWND 并通过安装 AfxWndProc 对之进行子类化处理。正是通过 这种方式,MFC 将 C++ 窗口对象与它们的 HWNDs 联系起来。AfxWndProc 过程 的作用是(通过非常曲折的途径)将 WM_XXX 消息路由到你的消息映射处理函数 。

当 Windows 调用 CBT 钩子时,它用 WPARAM 传递 HWND。但 MFC 是 如何知道要附属哪个 CWnd 派生对象呢?通过一个全局变量。为了创建窗口,你 必须调用 CWnd::Create 或 CWnd::CreateEx。前者调用后者,所以不管怎样都 要经过 CWnd::CreateEx 调用。在创建窗口之前, CWnd::CreateEx 安装 CBT 钩子并设置全局变量。代码是这样的:

// 来自 wincore.cpp 的简 化代码
BOOL CWnd::CreateEx(...)
{
AfxHookWindowCreate(this);
::CreateWindowEx(...);
AfxUnhookWindowCreate();
return TRUE;
}

AfxHookWindowCreate 安装 CBT 钩子 _AfxCbtFilterHook。它还在 线程状态中保存窗口对象指针,pThreadState->m_pWndInit。

void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(
WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
pThreadState->m_pWndInit = pWnd;
}