描述一下问题先:
    我需要用全局Hook来监控鼠标,键盘事件,还要监控菜单的选择操作。于是我做了这样几件事情:首先我写了一个C++ 的DLL,主要的函数是安装钩子,卸载钩子,设置用户回调函数。内部使用一个外部回调函数的三个实例,另外再写三个内部回调函数,对应三个Hook(都是全局的),一个是WH_KEYBOARD_LL,一个是WH_MOUSE_LL,一个是WH_CALLWNDPROC(第三个是监控菜单的,分析lParam参数当消息是WM_MENUSELECT时,就是菜单选择事件)。接下来要做的事情是,首先是在外部定义回调函数,将函数指针设置到DLL内部,然后InstallHook,安装Hook中使用的回调函数是DLL内部回调函数,在内部回调函数中调用外部回调函数。先来点关键代码首先是安装钩子:
bool InitializeHook(UINT hookID, int threadID)
{
if (g_appInstance == NULL)
{
return false;
}
if (hookID == WH_KEYBOARD_LL)
{
if (UserKeyboardHookCallback == NULL)
{
return false;
}
hookKeyboard = SetWindowsHookEx(hookID, (HOOKPROC)InternalKeyboardHookCallback, g_appInstance, threadID);
return hookKeyboard != NULL;
}
else if (hookID == WH_MOUSE_LL)
{
if (UserMouseHookCallback == NULL)
{
return false;
}
hookMouse = SetWindowsHookEx(hookID, (HOOKPROC)InternalMouseHookCallback, g_appInstance, threadID);
return hookMouse != NULL;
}
else if (hookID == WH_CALLWNDPROC)
{
if (UserMenuHookCallback == NULL)
{
return false;
}
hookMenu = SetWindowsHookEx(hookID, (HOOKPROC)InternalMenuHookCallback, g_appInstance, threadID);
return hookMenu != NULL;
}
return false;
}对应三个内部回调函数的实现:
static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code < 0)
{
return CallNextHookEx(hookMouse, code, wparam, lparam);
}
if (UserMouseHookCallback != NULL && !mouseFilter.IsFiltered((int)wparam))
{
UserMouseHookCallback(code, wparam, lparam);
}
return CallNextHookEx(hookMouse, code, wparam, lparam);
}static LRESULT CALLBACK InternalKeyboardHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code < 0)
{
return CallNextHookEx(hookKeyboard, code, wparam, lparam);
}
if (UserKeyboardHookCallback != NULL && !keyboardFilter.IsFiltered((int)wparam))
{
UserKeyboardHookCallback(code, wparam, lparam);
}
return CallNextHookEx(hookKeyboard, code, wparam, lparam);
}static LRESULT CALLBACK InternalMenuHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code < 0)
{
return CallNextHookEx(hookMenu, code, wparam, lparam);
}
if(((CWPSTRUCT*)lparam)->message == WM_MENUSELECT)
{
if (UserKeyboardHookCallback != NULL && !menuFilter.IsFiltered((int)WM_MENUSELECT))
{
UserMenuHookCallback(code, wparam, lparam);
}
} return CallNextHookEx(hookMenu, code, wparam, lparam);
}最后是设置外部回调函数的地方:
static LRESULT CALLBACK InternalKeyboardHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK InternalMenuHookCallback(int code, WPARAM wparam, LPARAM lparam);int SetUserHookCallback(HookProc userProc, UINT hookID)
{
if (userProc == NULL)
{
return HookCoreErrors::SetCallBack::ARGUMENT_ERROR;
}
if (hookID == WH_KEYBOARD_LL)
{
if (UserKeyboardHookCallback != NULL)
{
return HookCoreErrors::SetCallBack::ALREADY_SET;
}
UserKeyboardHookCallback = userProc;
keyboardFilter.Clear();
return HookCoreErrors::SetCallBack::SUCCESS;
}
else if (hookID == WH_MOUSE_LL)
{
if (UserMouseHookCallback != NULL)
{
return HookCoreErrors::SetCallBack::ALREADY_SET;
}
UserMouseHookCallback = userProc;
mouseFilter.Clear();
return HookCoreErrors::SetCallBack::SUCCESS;
}
else if (hookID == WH_CALLWNDPROC)
{
if (UserMenuHookCallback != NULL)
{
return HookCoreErrors::SetCallBack::ALREADY_SET;
}
UserMenuHookCallback = userProc;
menuFilter.Clear();
return HookCoreErrors::SetCallBack::SUCCESS;
}
         return HookCoreErrors::SetCallBack::NOT_IMPLEMENTED;
}好了,问题出来了,鼠标事件和键盘事件可以正确被Hook(这里是指外部回调函数被执行),但是菜单事件发生的时候,内部回调函数被执行了,外部回调函数却不被执行,我一查,原来InternalMenuHookCallback函数是被调用了,但是if (UserKeyboardHookCallback != NULL && !menuFilter.IsFiltered((int)WM_MENUSELECT))判断的结果是false,原来UserKeyboardHookCallback != NULL通不过,我没有释放UserKeyboardHookCallback对应的外部调用,并且我做了另外一个尝试:将InternalKeyboardHookCallback函数中的“if (UserKeyboardHookCallback != NULL && !keyboardFilter.IsFiltered((int)wparam))”这句话换成
“if (UserMenuHookCallback != NULL && !keyboardFilter.IsFiltered((int)wparam))”键盘事件发生的时候外部回调函数也能被调用,然后我又试验发现,在InternalKeyboardHookCallback函数中判断UserKeyboardHookCallback != NULL和UserMenuHookCallback != NULL,结果都是true,(我猜测在InternalMouseHookCallback函数中也是这样),但是在InternalMenuHookCallback函数中判断UserKeyboardHookCallback != NULL和UserMenuHookCallback != NULL,结果都是false,如果不判断就执行UserMenuHookCallback(code, wparam, lparam);这句,就会报指针操作异常。好了,问题描述好了,谁能知道为什么?是WH_CALLWNDPROC和WH_MOUSE_LL,WH_KEYBOARD_LL的差别造成的吗?

解决方案 »

  1.   

    我说明一下,上面的代码如果没有监控菜单那块,是运行良好的,如果要监控全局Hook鼠标和键盘,可以直接用上面的代码去作为C++DLL部分。
      

  2.   

    再Up:
        后来将WH_CallWndProc Hook换成了 WH_MSGFILTER Hook,但是还是一样的问题,回调被执行,但是用户自定义回调还是为NULL
      

  3.   

    造成原因,可能是由于你的全局Hook鼠标和键盘,那么当点击菜单的时候,不管是鼠标还是键盘所触发的,肯定要被全局Hook鼠标和键盘先捕获到。由于它的hook可能无法触发到menu的hook事件。一般使用全局Hook鼠标和键盘,像菜单之类的hook事件,是在前面的事件中判断类型后再进行转发。
      

  4.   

    http://www.codeproject.com/csharp/GlobalSystemHook.asp
      

  5.   

    To Knight94(愚翁):
        Hook可以加载多个不冲突,捕获到了那个事件了,只是在调用外部回调函数的时候发现那个没有了
    我使用的都是全局Hook,不知道这个会不会有问题
      

  6.   

    我也是全局Hook鼠标和键盘事件,没有问题,在这两个回调函数中可以访问另外的回调函数,但是在WH_CallWndProc Hook和 WH_MSGFILTER Hook中就不行,事件Hook到了,DLL内部回调函数触发了,但是没办法调用外部的回调函数,郁闷
      

  7.   

    刚才的新尝试,将WH_KEYBOARD_LL换成WH_KEYBOARD进行Hook,发现也出现了外部回调函数判断为NULL,谁能告诉我这是为什么?
      

  8.   

    刚刚发现问题所在了,唉,MSDN上关于WH_KEYBOARD_LL的解释是这样的,“However, the WH_KEYBOARD_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event. ”,所以可以调用外面的函数,如果是注入的话,就要另外想办法了,使用共享内存区域,发送消息等等。