24

我们通过SetWindowsHookExwith安装了一些全局键盘挂钩,WH_KEYBOARD_LL这些挂钩似乎被 Windows 随机取消。

我们验证了它们的钩子不再附加,因为调用UnhookWindowsHookEx句柄返回false。(还验证了它true在正常工作时返回)

似乎没有一致的重现,我听说它们可能会由于超时或抛出异常而脱钩,但我已经尝试过两者都只是让它在处理方法的断点上停留超过一分钟,以及只是抛出一个随机异常(C#),它似乎仍然有效。

在我们的回调中,我们快速发布到另一个线程,所以这可能不是问题。我已经阅读了 Windows 7 中有关在注册表中设置更高超时的解决方案,因为 Windows 7 显然对超时更具侵略性(我们都在这里运行 Win7,所以不确定这是否发生在其他操作系统上),但这并没有这似乎不是一个理想的解决方案。

我考虑过让后台线程运行以每隔一段时间刷新一次钩子,这很骇人听闻,但我不知道这样做有什么真正的负面后果,而且它似乎比更改全局 Windows 注册表设置更好.

还有其他建议或解决方案吗? 设置钩子的类和它们所附加的委托都是静态的,所以它们不应该被 GC 处理。

编辑:通过调用验证GC.Collect();它们仍然有效,因此它们不会被垃圾收集。

4

7 回答 7

18

我认为这必须是一个超时问题。

其他开发人员报告了一个 Windows7 特定问题,即如果低级挂钩超过(未记录的)超时值,则会被解除挂钩。

其他讨论相同问题的开发人员请参阅此线程。可能是您需要执行繁忙的循环(或缓慢的垃圾收集)而不是睡眠来导致脱钩行为。LowLevelKeyboardProc 函数中的断点也可能会产生超时问题。(还有观察表明,另一个任务的 CPU 负载过重可能会引发这种行为 - 大概是因为另一个任务从 LowLevelKeyboardProc 函数中窃取了 CPU 周期并导致它花费了太长时间。)

该线程中建议的解决方案是尝试将注册表中HKEY_CURRENT_USER\Control Panel\Desktop中的LowLevelHooksTimeout DWORD 值设置为更大的值。

请记住,C# 的优点之一是,如果发生垃圾收集,即使是简单的语句也会花费过多的时间。这(或其他线程的 CPU 负载)可能解释了问题的间歇性。

于 2010-04-21T03:46:38.163 回答
3

我想到了两件事可能会帮助您找出问题所在。

  1. 为了帮助隔离问题的位置,WH_KEYBOARD_LL请与当前的钩子同时运行另一个钩子,让它除了将数据传递到钩子链上之外什么都不做。当你发现你原来的钩子没有上钩时,检查一下这个“虚拟”钩子是否也没有上钩。如果“虚拟”钩子也没有被钩住,您可以相当肯定问题不在您的钩子上(即在 Windows 中或与您的整个进程相关的东西?)如果“虚拟”钩子没有被取消钩子,那么问题可能在你的钩子里的某个地方。

  2. 记录通过回调传递给您的钩子的信息并运行它,直到钩子被取消钩子。重复多次并检查记录的数据,看看您是否可以识别导致脱钩的模式。

我会一次尝试这些,以防万一会影响其他人的结果。如果在那之后您没有线索可能是什么问题,您可以尝试将它们一起运行。

于 2010-04-20T20:26:14.117 回答
2

这是一个很长的镜头,但你有没有运行防病毒软件?这很可能是注意到一个键盘挂钩并将其踢出。

它更有可能会警告您并立即将其删除,但这是值得检查的奇怪事情之一。

于 2010-04-26T03:23:45.897 回答
1

也许其他人有一个不调用 CallNextHookEx() 的钩子?

于 2010-04-21T03:47:51.307 回答
1

我知道这是一个丑陋的解决方案,但是您可以将 Timer 设置为每 5 分钟,然后您可以重新挂钩您的键盘事件?

我在 Win7 机器上遇到了同样的问题,一段时间后键盘挂钩失去了它的参考,我不得不每 5 分钟设置一个计时器来再次挂钩事件,现在它保持新鲜。

于 2013-06-27T18:54:09.753 回答
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();
        }
    }
}
于 2014-04-06T16:09:10.267 回答
0

很晚了,但我想我会分享一些东西。从这里收集有一些提示,例如将 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 事件中将 active bool 更改为 false 并在 Activated 事件中调用 HookMouse。

高温高压

编辑:我注意到游戏因此变慢了,所以决定在应用程序不活动时使用 Activated 和 Deactivate 事件取消挂钩。

于 2018-04-22T12:37:57.167 回答