SetWindowsHookEx hook stops working

冷暖自知 提交于 2021-02-07 04:32:26

问题


Keyboard hook not firing events and throws win32 exception on dispose

My c# application creates keyboard hook for processing of keyboard events(many card readers, scanners and other POS equipment emulates keyboard). Sometimes my application creates keyboard hook without errors, but it's not firings events and on dispose throws exception:

System.ComponentModel.Win32Exception (0x80004005): Failed to remove keyboard hooks for 'app'. Error 1404: Invalid hook handle

Other log entry is same error, but it tells about

ERROR_NOT_ALL_ASSIGNED

Source code of library and demo app.

I can't reproduce this problem on my pc, and don't know what I should investigate or google. I know that:

  • All clients with this strange behaviour using x86 operating system.
  • There can be some problems with windows privilegies or rights.
  • It breaks sometimes(not always).
  • Library targets .NET 4
  • Application targets .NET 4.5.1
  • Compile platform: Any CPU

Also, I'm not fluent with unmanaged code and win api. I got this code from some thread and modified it for my needs but on high level of abstraction.

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:


If you do something wrong in the hook proc Windows will unhook you without telling you.

  • In your LowLevelKeyboardProc you are not checking nCode! You must do that first and if nCode is < 0 you must return with CallNextHookEx without any other processing.
  • You cannot spend too much time inside the hook proc. The exact limit is not documented AFAIK and can be changed by a registry value. You should aim for < 200ms to be safe.

You could try setting/changing the LowLevelHooksTimeout registry value on the problematic systems to see if that helps.



来源:https://stackoverflow.com/questions/48695720/setwindowshookex-hook-stops-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!