SetWindowsHookEx 挂钩停止工作
SetWindowsHookEx hook stops working
键盘挂钩未触发事件并在处理时抛出 win32 异常
我的 c# 应用程序创建键盘挂钩来处理键盘事件(许多读卡器、扫描仪和其他 POS 设备模拟键盘)。
有时我的应用程序创建键盘钩子没有错误,但它没有触发事件并且在处理时抛出异常:
System.ComponentModel.Win32Exception (0x80004005): Failed to remove keyboard hooks for 'app'. Error 1404: Invalid hook handle
其他日志条目是同样的错误,但它告诉
ERROR_NOT_ALL_ASSIGNED
Source code of library and demo app.
我无法在我的电脑上重现这个问题,不知道我应该调查什么或 google。
我知道:
- 所有使用 x86 操作系统的具有这种奇怪行为的客户端。
- windows 特权或权利可能存在一些问题。
- 它有时会坏掉(不总是)。
- 库以 .NET 4 为目标
- 应用程序目标 .NET 4.5.1
- 编译平台:任意CPU
此外,我对非托管代码并不流利,所以我赢了 api。我从某个线程获得了这段代码,并根据我的需要对其进行了修改,但抽象程度较高。
钩子函数:
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);
挂钩处理:
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);
如果你在 hook 过程中做错了什么 Windows 会在不告诉你的情况下解开你。
- 在您的
LowLevelKeyboardProc
中您没有检查 nCode
!你必须先这样做,如果 nCode
< 0 你 必须 return 和 CallNextHookEx
没有任何其他处理。
- 你不能在钩子过程中花太多时间。确切的限制没有记录 AFAIK 和 可以通过注册表值更改。为了安全起见,您的目标应该是 < 200 毫秒。
您可以在有问题的系统上尝试 setting/changing LowLevelHooksTimeout
注册表值,看看是否有帮助。
键盘挂钩未触发事件并在处理时抛出 win32 异常
我的 c# 应用程序创建键盘挂钩来处理键盘事件(许多读卡器、扫描仪和其他 POS 设备模拟键盘)。 有时我的应用程序创建键盘钩子没有错误,但它没有触发事件并且在处理时抛出异常:
System.ComponentModel.Win32Exception (0x80004005): Failed to remove keyboard hooks for 'app'. Error 1404: Invalid hook handle
其他日志条目是同样的错误,但它告诉
ERROR_NOT_ALL_ASSIGNED
Source code of library and demo app.
我无法在我的电脑上重现这个问题,不知道我应该调查什么或 google。 我知道:
- 所有使用 x86 操作系统的具有这种奇怪行为的客户端。
- windows 特权或权利可能存在一些问题。
- 它有时会坏掉(不总是)。
- 库以 .NET 4 为目标
- 应用程序目标 .NET 4.5.1
- 编译平台:任意CPU
此外,我对非托管代码并不流利,所以我赢了 api。我从某个线程获得了这段代码,并根据我的需要对其进行了修改,但抽象程度较高。
钩子函数:
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);
挂钩处理:
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);
如果你在 hook 过程中做错了什么 Windows 会在不告诉你的情况下解开你。
- 在您的
LowLevelKeyboardProc
中您没有检查nCode
!你必须先这样做,如果nCode
< 0 你 必须 return 和CallNextHookEx
没有任何其他处理。 - 你不能在钩子过程中花太多时间。确切的限制没有记录 AFAIK 和 可以通过注册表值更改。为了安全起见,您的目标应该是 < 200 毫秒。
您可以在有问题的系统上尝试 setting/changing LowLevelHooksTimeout
注册表值,看看是否有帮助。