我们通过SetWindowsHookEx
和WH_KEYBOARD_LL
安装了一些全局键盘挂钩,这些挂钩似乎随机被Windows取消。
我们验证了他们的钩子已不再附加,因为在句柄上调用UnhookWindowsHookEx
会返回false
。 (还验证了它在正常工作时返回true
)
似乎没有一致的repro,我听说他们可以因为超时或异常被抛出而脱钩,但我已经尝试了两个只是让它坐在处理方法的断点上结束一分钟,以及抛出一个随机异常(C#),它似乎仍然有用。
在我们的回调中,我们很快发布到另一个线程,因此可能不是问题。我已经阅读了Windows 7中的解决方案,用于在注册表中设置更高的超时,因为Windows 7显然更加积极地执行超时(我们都在这里运行Win7,所以不确定这是否发生在其他操作系统上),但这并不是看起来似乎是一个理想的解决方案。
我认为只是偶尔运行一个后台线程来刷新钩子,这很黑,但我不知道这样做会带来什么真正的负面影响,而且似乎比改变全局更好Windows注册表设置。
还有其他建议或解决方案吗? 设置挂钩的类和它们所附加的代理都是静态的,因此它们不应该是GC。
编辑:通过调用GC.Collect();
验证他们仍然有效,因此他们不会被收集。
答案 0 :(得分:16)
我认为这必须是超时问题。
其他开发人员报告了Windows7特定问题,如果超级挂钩超过(未记录的)超时值,则会解除挂钩。
有关讨论同一问题的其他开发人员,请参阅this thread。可能是您需要执行繁忙循环(或慢速垃圾收集)而不是睡眠以导致取消吊钩行为。 LowLevelKeyboardProc函数中的断点也可能会产生超时问题。 (还有一种观察认为,另一项任务导致的CPU负载过重可能会引发这种行为 - 大概是因为另一项任务从LowLevelKeyboardProc函数中窃取CPU周期并导致它花费的时间太长。)
该线程中建议的解决方案是尝试将 HKEY_CURRENT_USER \ Control Panel \ Desktop 的注册表中的 LowLevelHooksTimeout DWORD值设置为更大的值。
请记住,C#的一个荣耀是,如果发生垃圾收集,即使是简单的语句也会占用过多的时间。这(或其他线程的CPU加载)可能会解释问题的间歇性。
答案 1 :(得分:3)
我想到的有两件事可以帮助你找出问题所在。
为了帮助隔离问题的位置,请在当前挂钩的同时运行另一个WH_KEYBOARD_LL
挂钩,除了在挂钩链上传递数据之外什么都不做。当你发现原来的钩子脱钩时,检查一下这个“假”钩子是否也脱钩了。如果“虚拟”钩子也被解开,你可以相当肯定问题是在你的钩子之外(即在Windows或整个过程中与你的过程相关的东西?)如果“虚拟”钩子没有解开,那么问题可能在你的钩子里。
通过回调记录挂钩的信息并运行它直到钩子脱钩。多次重复此操作并检查记录的数据,看看是否可以识别导致解除钩子的模式。
我会一次尝试这些,以防万一会影响其他人的结果。如果在那之后你没有找到问题所在的线索,你可以尝试一起运行它们。
答案 2 :(得分:2)
这是一个很长的镜头,但你有没有机会运行防病毒软件?这很可能会注意到键盘钩并将其踢出去。
它更可能会警告你,并立即删除它,但这是值得检查的奇怪事情之一。
答案 3 :(得分:1)
也许其他人有一个没有调用CallNextHookEx()的钩子?
答案 4 :(得分:1)
我知道这是一个丑陋的解决方案但你可以设置一个定时器5分钟,然后你可以重新挂钩你的键盘事件?
我遇到了与Win7机器相同的问题,经过一段时间的键盘挂钩丢失了它的参考,我不得不每隔5分钟设置一个Timer来再次挂钩事件,现在它保持新鲜。
答案 5 :(得分:1)
我正在使用以下项目:http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C来执行按下某个键时的任务。
我注意到在用热键执行任务后它停止侦听新的按键,然后我将KeyboardHookListener更改为静态,它似乎解决了我的问题。我不知道为什么这会解决我的问题,所以请随意评论这个!
我的热门课程:
class Hotkey
{
private static KeyboardHookListener _keyboardHookListener;
public void Start()
{
_keyboardHookListener = new KeyboardHookListener(new GlobalHooker()) { Enabled = true };
_keyboardHookListener.KeyDown += KeyboardListener_OnkeyPress;
}
private void KeyboardListener_OnkeyPress(object sender, KeyEventArgs e)
{
// Let's backup all projects
if (e.KeyCode == Keys.F1)
{
// Initialize files
var files = new Files();
// Backup all projects
files.BackupAllProjects();
}
// Quick backup - one project
else if (e.KeyCode == Keys.F2)
{
var quickBackupForm = new QuickBackup();
quickBackupForm.Show();
}
}
}
答案 6 :(得分:0)
很晚但想到我会分享一些东西。 从here收集有一些提示,比如把SetWindowsHookEx放在一个单独的线程中,这更像是一个调度问题。
我还注意到Application.DoEvents使用了相当多的CPU,发现PeekMessage使用的更少。
public static bool active = true;
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr handle;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool PeekMessage(out NativeMessage message,
IntPtr handle, uint filterMin, uint filterMax, uint flags);
public const int PM_NOREMOVE = 0;
public const int PM_REMOVE = 0x0001;
protected void MouseHooker()
{
NativeMessage msg;
using (Process curProcess = Process.GetCurrentProcess())
{
using (ProcessModule curModule = curProcess.MainModule)
{
// Install the low level mouse hook that will put events into _mouseEvents
_hookproc = MouseHookCallback;
_hookId = User32.SetWindowsHookEx(WH.WH_MOUSE_LL, _hookproc, Kernel32.GetModuleHandle(curModule.ModuleName), 0);
}
}
while (active)
{
while (PeekMessage(out msg, IntPtr.Zero,
(uint)WM.WM_MOUSEFIRST, (uint)WM.WM_MOUSELAST, PM_NOREMOVE))
;
Thread.Sleep(10);
}
User32.UnhookWindowsHookEx(_hookId);
_hookId = IntPtr.Zero;
}
public void HookMouse()
{
active = true;
if (_hookId == IntPtr.Zero)
{
_mouseHookThread = new Thread(MouseHooker);
_mouseHookThread.IsBackground = true;
_mouseHookThread.Priority = ThreadPriority.Highest;
_mouseHookThread.Start();
}
}
所以只需在Deactivate事件中将活动bool更改为false,然后在Activated事件中调用HookMouse。
HTH
编辑:我注意到游戏因此放慢了速度,因此决定在使用“激活”和“停用”事件时应用程序处于非活动状态时取消挂钩。