我为计算器做了一个钩子,想得到计算器收到的消息。为此,我设置了自己的窗口过程,但是在取消挂钩期间,如果我使用SetWindowLong(..)
来恢复旧窗口过程程序则会崩溃。
DLL代码:
#define EXPORT_API extern "C" __declspec(dllexport)
EXPORT_API void InstallHook();
EXPORT_API void UninstallHook();
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
WNDPROC g_OldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")
HWND GetTargetWindowHwnd()
{
return ::FindWindowA(0, "Calculator");
}
// my new wnd procedure to catch messages
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
switch(uMsg)
{
case WM_CLOSE:
{
MessageBoxA(0, "Here we are!", "", 0);
}
break;
default:
lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
break;
}
lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
return lResult;
}
// hook procedure
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *pMsg = (MSG *)lParam;
HWND hWnd = GetTargetWindowHwnd();
bool flagIn = false;
if( hWnd == pMsg->hwnd )
{// if messege was sent to my target window
if(g_OldWndProc == NULL)
{
// save the adress of old wnd procedure to recover it later
g_OldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
// set my wnd procedure
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)NewWndProc);
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
EXPORT_API void InstallHook()
{
try
{
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hInstance, 0);
}
catch(...)
{
MessageBoxA(0, "Hook error", "Error", 0);
}
}
EXPORT_API void UninstallHook()
{
if(g_OldWndProc)
{
// recovering old wnd proc
HWND hWnd = GetTargetWindowHwnd();
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)g_OldWndProc);
g_OldWndProc = NULL;
}
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
EXE CODE:
void CHookTestDlg::OnBnClickedBtnInstall()
{
InstallHook();
}
void CHookTestDlg::OnBnClickedBtnUninstall()
{
UninstallHook();
}
如果我不使用我的wnd程序,它可以正常工作。如果我使用SetWindowLong(..)恢复旧窗口程序程序在取消挂钩时粉碎。有什么问题?
答案 0 :(得分:2)
在取消分类时,请始终检查窗口是否未被其他人子类化。即在恢复WindowProc之前,您应该再次阅读它并与预期值(NewWndProc
)进行比较。如果它不同,你一定不能卸载 DLL,因为另一个子类有一个指向存储的DLL代码的指针,一旦你的DLL被卸载,这个指针就会变成悬空。
答案 1 :(得分:2)
问题是您正在目标进程(calc)中的目标窗口上设置窗口proc,在这种情况下,它正在成功。但是当你调用UninstallHook时,该代码在你自己的exe进程中运行;在这种情况下,SetWindowLong将失败。
(将钩子值放在共享内存中无济于事; SetWindowLong仍然会拒绝更改进程边界的窗口过程 - 有关详细信息,请参阅MSDN。)
要使其工作,您需要与DLL的钩子实例进行通信,并要求它从该目标进程中重置wndproc,一旦完成,然后取消钩子。
(atzz关于解钩的建议也是有效的。通常最好避免挂钩你不拥有的窗户。)