系统宽的Windows CBT挂钩不能正常工作

我试图挂钩Windows操作系统上的CBT钩子。 我目前正在使用Windows 7 x64。

我读过很多关于这个问题的话题,但是没有一个能解决我的问题。 应用程序运行良好; 挂钩安装,我可以看到一些通知即将到来。

实际上出现的问题是应用程序没有通知在同一台机器上运行的其他进程的CBT钩子。

该应用程序是用C#编写的(使用Microsoft .NET)。 这是一个正在运行的示例:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Text; using System.Threading; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WindowsHook { class Program { [STAThread] static void Main(string[] args) { uint thid = (uint)AppDomain.GetCurrentThreadId(); bool global = true; mHookDelegate = Marshal.GetFunctionPointerForDelegate(new HookProc(ManagedCallback)); if (global == true) { mNativeWrapperInstance = LoadLibrary("Native_x64.dll"); thid = 0; } else { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { mNativeWrapperInstance = GetModuleHandle(curModule.ModuleName); } } mNativeWrappedDelegate = AllocHookWrapper(mHookDelegate); mHookHandle = SetWindowsHookEx(/*WH_CBT*/5, mNativeWrappedDelegate, mNativeWrapperInstance, thid); if (mHookHandle == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); Application.Run(new Form()); if (FreeHookWrapper(mNativeWrappedDelegate) == false) throw new Win32Exception("FreeHookWrapper has failed"); if (FreeLibrary(mNativeWrapperInstance) == false) throw new Win32Exception("FreeLibrary has failed"); if (UnhookWindowsHookEx(mHookHandle) == false) throw new Win32Exception(Marshal.GetLastWin32Error()); } static int ManagedCallback(int code, IntPtr wParam, IntPtr lParam) { Trace.TraceInformation("Code: {0}", code); if (code >= 0) { return (0); } else { return (CallNextHookEx(mHookHandle, code, wParam, lParam)); } } delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); static IntPtr mHookHandle; static IntPtr mHookDelegate; static IntPtr mNativeWrapperInstance = IntPtr.Zero; static IntPtr mNativeWrappedDelegate = IntPtr.Zero; [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int hook, IntPtr callback, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", SetLastError = true)] internal static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] internal static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr hModule); [DllImport("Native_x64.dll")] private static extern IntPtr AllocHookWrapper(IntPtr callback); [DllImport("Native_x64.dll")] private static extern bool FreeHookWrapper(IntPtr wrapper); [DllImport("Native_x64.dll")] private static extern int FreeHooksCount(); } 

}

AllocHookWrapper和FreeHookWrapper是来自x64平台的DLL(Native_x64.dll)编译器的导入例程,位于应用程序的同一目录中。 AllocHookWrapper存储函数指针(被pipe理的例程)并返callback用函数指针的DLL例程。

这是DLL的代码:

 #include "stdafx.h" #include "iGecko.Native.h" #ifdef _MANAGED #pragma managed(push, off) #endif #define WIN32_LEAN_AND_MEAN #include <windows.h> #define WRAPPER_NAME(idx) Wrapper ## idx #define WRAPPER_IMPLEMENTATION(idx) \ LRESULT WINAPI WRAPPER_NAME(idx)(int code, WPARAM wparam, LPARAM lparam) \ { \ if (sHooksWrapped[idx] != NULL) \ return (sHooksWrapped[idx])(code, wparam, lparam); \ else \ return (0); \ } #define WRAPPER_COUNT 16 HOOKPROC sHooksWrapped[WRAPPER_COUNT] = { NULL }; WRAPPER_IMPLEMENTATION(0x00); WRAPPER_IMPLEMENTATION(0x01); WRAPPER_IMPLEMENTATION(0x02); WRAPPER_IMPLEMENTATION(0x03); WRAPPER_IMPLEMENTATION(0x04); WRAPPER_IMPLEMENTATION(0x05); WRAPPER_IMPLEMENTATION(0x06); WRAPPER_IMPLEMENTATION(0x07); WRAPPER_IMPLEMENTATION(0x08); WRAPPER_IMPLEMENTATION(0x09); WRAPPER_IMPLEMENTATION(0x0A); WRAPPER_IMPLEMENTATION(0x0B); WRAPPER_IMPLEMENTATION(0x0C); WRAPPER_IMPLEMENTATION(0x0D); WRAPPER_IMPLEMENTATION(0x0E); WRAPPER_IMPLEMENTATION(0x0F); const HOOKPROC sHookWrappers[] = { WRAPPER_NAME(0x00), WRAPPER_NAME(0x01), WRAPPER_NAME(0x02), WRAPPER_NAME(0x03), WRAPPER_NAME(0x04), WRAPPER_NAME(0x05), WRAPPER_NAME(0x06), WRAPPER_NAME(0x07), WRAPPER_NAME(0x08), WRAPPER_NAME(0x09), WRAPPER_NAME(0x0A), WRAPPER_NAME(0x0B), WRAPPER_NAME(0x0C), WRAPPER_NAME(0x0D), WRAPPER_NAME(0x0E), WRAPPER_NAME(0x0F) }; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return (TRUE); } #ifdef _MANAGED #pragma managed(pop) #endif extern "C" IGECKONATIVE_API HOOKPROC WINAPI AllocHookWrapper(HOOKPROC wrapped) { for(int i = 0; i < WRAPPER_COUNT; i++) { if (sHooksWrapped[i] == NULL) { sHooksWrapped[i] = wrapped; return sHookWrappers[i]; } } return (NULL); } extern "C" IGECKONATIVE_API BOOL WINAPI FreeHookWrapper(HOOKPROC wrapper) { for(int i = 0; i < WRAPPER_COUNT; i++) { if (sHookWrappers[i] == wrapper) { sHooksWrapped[i] = NULL; return TRUE; } } return (FALSE); } extern "C" IGECKONATIVE_API INT WINAPI FreeHooksCount() { int c = 0; for(int i = 0; i < WRAPPER_COUNT; i++) { if (sHooksWrapped[i] == NULL) c++; } return (c); } 

其实我对某个系统的窗口相关事件(创build,销毁)感兴趣,但实际上我无法通知操作系统。

这是怎么回事? 我错过了什么?

请注意,我正在使用Administratos组。


我在这个页面中find了这个有趣的部分

全局挂钩在.NET Framework中不受支持您无法在Microsoft .NET Framework中实现全局挂钩。 要安装一个全局钩子,一个钩子必须有一个本地DLL导出,以将自己插入另一个需要调用有效的,一致的函数的进程中。 此行为需要DLL导出。 .NET Framework不支持DLL导出。 托pipe代码没有一个函数指针的一致值的概念,因为这些函数指针是dynamic构build的代理。

我想通过实现一个包含钩子callback的本地DLL,来调用托pipecallback。 但是,只有在调用SetWindowsHookEx例程并且不被其他进程调用的进程中调用托pipecallback。

什么是可能的解决方法?

也许分配堆内存存储进程ID(托pipe的),并发送用户消息描述钩function?


我试图实现的是一个全系统的监视器,它检测新的进程执行,检测创build的​​窗口的位置和大小,以及closures窗口,移动窗口,最小化/最大化的窗口。 监视器应该继续检测鼠标和键盘事件(总是系统范围),并且必须“模拟”鼠标和键盘事件。

在同一个桌面上的每个进程都必须被监视,独立于归档(32位或64位)和底层框架(本地或托pipe)。

监视器应强制处理窗口的位置,大小和移动,并能够作为本地用户,以允许远程用户充当本地用户(如VNC)。

对不起,但我不明白“包装”的非托管DLL和ManagedCallback作为托管EXE内部钩子的感觉。

您应该明白,您用作系统范围CBT钩子( SetWindowsHookEx参数)的回调的方法必须加载到all process的地址空间all process (将执行钩子函数的模块的DLL注入) 。 在Windows SDK(MSDN)中,您可以阅读以下内容(请参阅http://msdn.microsoft.com/zh-cn/library/ms644990(VS.85).aspx )上的注释:

SetWindowsHookEx可以用来将DLL注入到另一个进程中。 一个32位的DLL不能被注入一个64位的进程,并且一个64位的DLL不能被注入一个32位的进程。 如果应用程序需要在其他进程中使用钩子,则需要32位应用程序调用SetWindowsHookEx将32位DLL注入到32位进程中,而64位应用程序调用SetWindowsHookEx来注入64位DLL转换成64位进程。 32位和64位DLL必须具有不同的名称。

此外,你写在你的问题关于系统范围钩子和使用不作为SetWindowsHookEx的最后一个参数0。 还有一个问题:作为SetWindowsHookEx (HINSTANCE hMod)的第三个参数,您使用的不是dll的实例, 而是钩子代码(您当前在EXE中的钩子代码)。

所以我的建议是:你必须编写一个新的本地代码来实现全系统的CBT钩子,并把它放在一个DLL中。 我建议你也选择一个基地址(链接器开关)的DLL,这是不是一个标准的值,以减少DLL重新定义。 这不是强制性的,但这将节省内存资源。

对不起,坏消息,但在我看来,你现在的代码应该完全重写。

根据问题中的更新进行更新:我再一次重复,如果你调用一个SetWindowsHookEx来设置一个CBT钩子,你应该给一个DLL的模块实例(起始地址)和一个地址函数在实现钩子的DLL中。 从哪个进程调用SetWindowsHookEx函数并不重要。 用作参数的DLL将被加载(注入)在使用User32.dll的同一个Windows工作站的所有进程中。 所以你有一些本地的限制。 如果你想支持32位和64位平台,你必须实现两个 DLL:一个32位和64位的DLL。 而且,在同一个进程中使用不同的.NET版本也存在一个问题。 从理论上讲,只有.NET 4.0才能做到这一点。 一般来说这是非常复杂的问题。 你应该明白,我写的DLL我的意思不仅是DLL,而是所有的依赖 。 所以,如果你实现一个调用托管DLL(.NET DLL)的本地DLL,这将是不可能的。

所以,如果你想使用全局CBT钩子,你必须实现,如果作为一个两个本地DLL (一个32位和64位),并设置在两个进程(一个32位和64位)的钩子。 所以请确切地说明SetWindowsHookEx文档http://msdn.microsoft.com/en-us/library/ms644990 ( SetWindowsHookEx .aspx (请参见上面的引用)的注释中描述的内容。 我看不到更简单的方法。