SetWindowsHookEx挂钩停止工作

时间:2018-02-08 22:20:08

标签: c# winapi keyboard unmanaged

键盘挂钩不触发事件并在dispose

上抛出win32异常

我的c#应用程序创建用于处理键盘事件的键盘钩子(许多读卡器,扫描仪和其他POS设备模拟键盘)。 有时我的应用程序会创建没有错误的键盘钩子,但它不是发射事件和处置抛出异常:

  

System.ComponentModel.Win32Exception(0x80004005):无法删除' app'的键盘挂钩。错误1404:钩子句柄无效

其他日志条目是相同的错误,但它告诉

  

ERROR_NOT_ALL_ASSIGNED

Source code of library and demo app.

我无法在我的电脑上重现此问题,也不知道我应该调查什么或谷歌。 我知道:

  • 使用x86操作系统的所有客户端都有这种奇怪的行为。
  • Windows权限或权利可能存在一些问题。
  • 有时会破坏(并不总是)。
  • 图书馆以.NET 4为目标
  • 应用程序目标.NET 4.5.1
  • 编译平台:任何CPU

另外,我不熟悉非托管代码并赢得api。我从一些线程获得了这个代码并根据我的需要修改了它,但是在高级抽象上。

Hook ctor:

public GlobalKeyboardHook()
{
    _windowsHookHandle = IntPtr.Zero;
    _user32LibraryHandle = IntPtr.Zero;
    _hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.

    _user32LibraryHandle = LoadLibrary("User32");
    if (_user32LibraryHandle == IntPtr.Zero)
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new Win32Exception(errorCode,
            $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
    }


    _windowsHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
    if (_windowsHookHandle == IntPtr.Zero)
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new Win32Exception(errorCode,
            $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
    }
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("USER32", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

Hook dispose:

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        // because we can unhook only in the same thread, not in garbage collector thread
        if (_windowsHookHandle != IntPtr.Zero)
        {
            if (!UnhookWindowsHookEx(_windowsHookHandle))
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode,
                    $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }
            _windowsHookHandle = IntPtr.Zero;

            // ReSharper disable once DelegateSubtraction
            _hookProc -= LowLevelKeyboardProc;
        }
    }

    if (_user32LibraryHandle != IntPtr.Zero)
    {
        if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new Win32Exception(errorCode,
                $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
        }
        _user32LibraryHandle = IntPtr.Zero;
    }
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);

[DllImport("USER32", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hHook);

1 个答案:

答案 0 :(得分:2)

如果你在钩子程序中做错了什么,Windows会在不告诉你的情况下解开你。

  • LowLevelKeyboardProc中,您没有检查nCode!如果nCode是< CallNextHookEx,则必须先执行此操作。 0 必须返回LowLevelHooksTimeout而不进行任何其他处理。
  • 你不能在钩子proc中花太多时间。没有记录确切的限制AFAIK 可以通过注册表值更改。你应该瞄准< 200ms是安全的。

您可以尝试在有问题的系统上设置/更改service.center.ts注册表值,看看是否有帮助。