【问题标题】:SetWindowsHookEx seems not working for me in C# (WH_KEYBOARD_LL, global)SetWindowsHookEx 在 C# 中似乎对我不起作用(WH_KEYBOARD_LL,全局)
【发布时间】:2011-04-28 17:55:26
【问题描述】:

每当用户按下窗口中的某些键时,我的应用程序应该执行一些操作。

使用WH_KEYBOARD_LL 选项调用SetWindowsHookEx 似乎是实现此目的的标准方法。但是,在我的情况下,某些事情显然是错误的,并且没有触发回调。

我的调试控制台应用程序的主要方法:

static void Main(string[] args)
{
    IntPtr moduleHandle = GetCurrentModuleHandle();
    IntPtr hookHandle = IntPtr.Zero;

    try
    {
        User32.HookProc hook = (nCode, wParam, lParam) =>
        {
            // code is never called :-(
            if (nCode >= 0)
            {
                Console.WriteLine("{0}, {1}", wParam.ToInt32(), lParam.ToInt32());
            }
            return User32.CallNextHookEx(hookHandle, nCode, wParam, lParam);
        };

        hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);

        Console.ReadLine(); // 
    }
    finally
    {
        if (hoodHandle != IntPtr.Zero)
        {
            var unhooked = User32.UnhookWindowsHookEx(hookHandle);
            Console.WriteLine(unhooked); // true
            hookHandle = IntPtr.Zero;                   
        }
    }
}

GetCurrentModuleHandle 方法:

private static IntPtr GetCurrentModuleHandle()
{
    using (var currentProcess = Process.GetCurrentProcess())
    using (var mainModule = currentProcess.MainModule)
    {
        var moduleName = mainModule.ModuleName;
        return Kernel32.GetModuleHandle(moduleName);
    }           
}

user32.dllkernel32.dll 导入:

public static class User32
{
    public const int WH_KEYBOARD_LL = 13;

    public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);        
}

public static class Kernel32
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);
}

你知道我的问题是什么吗?

【问题讨论】:

  • 你有没有试过让你的 HookProc 成为一个真正的函数,也许是匿名函数声明的一些东西弄乱了它。只是在黑暗中开枪......
  • @Coding Gorilla:我试过了。没有帮助:-(
  • Jakub,我要做的第一件事就是在这里摆脱匿名方法。然后,检查包含该方法的类的持久性 - 当您的托管委托被删除并且仍然从非托管代码调用时会发生奇怪的事情。
  • @Jakub:您的 GetModuleHandle() 是否返回有效的模块句柄? GetModuleHandle 的 MSDN 文档表明,如果您将 NULL 传递给 lpModuleName,它将默认使用调用进程。也许你可以试一试,看看你是否能得到更好的结果。 (更多在黑暗中拍摄=))
  • 这是一个控制台应用程序吗?如果是这样,我认为这可以解释 Cyril Gupta 的回答。

标签: c# winapi setwindowshookex


【解决方案1】:

无法从 C# 控制台应用程序调用 SetwindowHook API。您需要在 Windows DLL 中调用它(它在 .Net DLL 中不起作用)。

当然有一种方法可以解决这个问题,而且我确实在很久以前构建的应用程序之一中使用过它。但我现在不记得了。如果您搜索的时间足够长,您可能会发现。

【讨论】:

【解决方案2】:

当您像这样使用 Win32 API 时,您自己检查错误非常很重要。您不再拥有可以为您执行此操作并引发异常的友好 .NET 包装器。这需要在多个地方完成,但这里有一个:

    hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);
    if (hookHandle == IntPtr.Zero) throw new Win32Exception();

真正的问题是您对 mainModule.ModuleName 的使用。如果只给出文件名,而不是完整路径。 GetModuleHandle() 将失败并返回 IntPtr.Zero。如上所述,如果您对此进行了测试,那么您很快就会发现问题。

当您在 .NET 4.0 中运行此代码时,还有一个额外的故障模式。 CLR 不再为托管代码伪造本机模块。 SetWindowsHookEx() 需要一个有效的 DLL 句柄,但它实际上并不使用它,因为这是一个低级挂钩。获得一个的最好方法是请求 user32.dll,它总是加载在托管程序中:

    IntPtr moduleHandle = LoadLibrary("user32.dll");
    if (moduleHandle == IntPtr.Zero) throw new Win32Exception();

不需要释放它。

【讨论】:

  • 感谢您的回答。你是对的,我应该总是检查本机调用的返回值。然而这不是问题。 UnhookWindowsHookEx 返回 true 很明显回调被钩住了。
【解决方案3】:

这是 Main(string[] args) 和 Console.ReadLine() 建议的控制台应用程序吗?

如果是这样,那么这可能是您问题的根源

【讨论】:

  • 为什么会有这样的问题?
  • @FrancoisBotha 这就是 6 年前的情况。我相信现在控制台应用程序可以接收上下文窗口通知,但老实说,我对 WinAPI 编程并不了解。如果你面临同样的问题,你应该问另一个问题。如果您有更好的信息,您可能想发布答案。
  • 好的,明白了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
相关资源
最近更新 更多