调用外部 SetWindowsHookEx 和 GetModuleHandle 的 PInvoke 错误

Posted

技术标签:

【中文标题】调用外部 SetWindowsHookEx 和 GetModuleHandle 的 PInvoke 错误【英文标题】:PInvoke errors calling external SetWindowsHookEx and GetModuleHandle 【发布时间】:2011-10-14 23:10:06 【问题描述】:

我正在尝试将程序中的 Windows 挂钩设置为外部 EXE。这将用于监视窗口的大小调整/最小化,因此我可以类似地调整程序大小,停靠到窗口。

如何绕过以下错误代码 1428126

当使用 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”函数

在 VS2008 调试器中调用 SetWindowsHookEx 总是返回 NULL

如何从 SetWindowsHookEx 回调调用函数指针

SetWindowsHookEx c#

SetWindowsHookEx 注入失败