在C#中,SetWindowsHookEx似乎不适合我(WH_KEYBOARD_LL,全局)

时间:2010-10-21 16:59:07

标签: c# winapi setwindowshookex

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

使用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);
}

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

3 个答案:

答案 0 :(得分:5)

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

肯定有办法解决这个问题,我确实使用过很久以前我建立的应用程序之一。但我现在不记得了。你可以找出你是否搜索足够长的时间。

答案 1 :(得分:3)

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

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

答案 2 :(得分: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();

无需释放它。