调用外部 SetWindowsHookEx 和 GetModuleHandle 的 PInvoke 错误
Posted
技术标签:
【中文标题】调用外部 SetWindowsHookEx 和 GetModuleHandle 的 PInvoke 错误【英文标题】:PInvoke errors calling external SetWindowsHookEx and GetModuleHandle 【发布时间】:2011-10-14 23:10:06 【问题描述】:我正在尝试将程序中的 Windows 挂钩设置为外部 EXE。这将用于监视窗口的大小调整/最小化,因此我可以类似地调整程序大小,停靠到窗口。
如何绕过以下错误代码 1428 和 126?
当使用 null hMod 调用 SetWindowsHookEx 时,我收到此错误 1428。如果传递当前模块(而不是 IntPtr.Zero),我会得到同样的错误,它似乎是正确的,如下所示:
IntPtr module = PInvoke.GetModuleHandle(null);
[...]
SetWindowsHookEx(...,...,module,...);
int error = PInvoke.GetLastError();
1428 = 不能在没有模块句柄的情况下设置非本地挂钩
我还尝试使用 GetModuleHandle 来获取我作为模块连接的外部程序:
IntPtr module = PInvoke.GetModuleHandle("communicator.exe");
int error = PInvoke.GetLastError();
但是 error 然后设置为:
126 = 找不到指定的模块。
我正在使用以下 PInvoke 语句:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);
这是有问题的程序:
public void Install(IntPtr hWnd)
uint threadId;
uint processId;
if (hWnd == IntPtr.Zero)
threadId = (uint)AppDomain.GetCurrentThreadId();
throw new Exception("Lync thread not found!");
else
threadId = PInvoke.GetWindowThreadProcessId(hWnd, out processId);
//IntPtr module = PInvoke.GetModuleHandle(null);
//IntPtr module = PInvoke.GetModuleHandle(GetType().Module.FullyQualifiedName);
IntPtr module = PInvoke.GetModuleHandle("communicator.exe");
int error = PInvoke.GetLastError();
m_hhook = PInvoke.SetWindowsHookEx(
m_hookType,
m_filterFunc,
//Process.GetCurrentProcess().Handle,
//threadId);
//IntPtr.Zero,
//module,
//Marshal.GetHINSTANCE(
// System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]
// ).ToInt32()
module,
threadId);
//IntPtr hinst = Marshal.GetHINSTANCE(Process.GetCurrentProcess().Handle);
// http://msdn.microsoft.com/en-us/library/ms681385
// ERROR_HOOK_NEEDS_HMOD - 1428 = Cannot set nonlocal hook without a module handle
error = PInvoke.GetLastError();
【问题讨论】:
您遗漏了重要的部分,但错误代码足够清晰,您无法使用 C# 代码设置全局挂钩。 有什么替代方案?我是否必须编写一个外部 C++ DLL 来处理它? 另外我认为向 SetWindowsHookEx 提供一个线程 ID 使它成为一个线程钩子——而不是一个全局钩子——所以我认为它是被允许的。 模块句柄必须是加载到您的进程中的 DLL 的句柄。该 DLL 必须包含过滤函数(非托管函数)。然后 Windows 会将该 DLL 加载到系统中的每个进程中,并根据需要在每个进程中调用过滤器函数。所以是的,它需要在 C/C++ DLL 中才能工作。 "Windows 会将该 DLL 加载到系统中的每个进程中,并根据需要在每个进程中调用过滤器函数。"哦,好吧,有道理,很好的解释 【参考方案1】:您不能将GetModuleHandle 用于外部进程。它必须是已经加载到当前进程中的模块。
【讨论】:
这就是我的猜测。标记为已接受以及来自@shf301 的解释。我猜没有办法将额外的EXE加载到当前进程中?听起来我必须像他提到的那样做,并将钩子过滤器放在一个单独的 DLL 中,然后传递给单独的 communicator.exe 进程。 我相信您可以使用 LoadLibrary 在当前进程中加载 exe,但我认为这不会让您按照自己的方式在另一个进程中设置挂钩。【参考方案2】:我遇到了同样的问题:126 = 找不到指定的模块。 我在我的应用中添加了丢失的消息循环,它又开始工作了。
我正在使用这样的 Hook 函数:
hKeyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProcedure,
Marshal.GetHINSTANCE(typeof(your_class_type).Module),
0);
我在主函数的末尾添加了Application.Run()
【讨论】:
您的意思是将此答案添加到此问题吗? 是的。我用更详细的描述改进了我的答案:) 您也遇到错误代码 126 的事实并不意味着您的解决方案适用于此。以上是关于调用外部 SetWindowsHookEx 和 GetModuleHandle 的 PInvoke 错误的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 SetWindowsHookEx 和 LowLevelKeyboardProc 分配多个低级热键
在我的线程上下文中调用“SetWindowsHookEx”函数