Что может заставить Windows отцепить низкоуровневую (глобальную) зацепку клавиатуры?

У нас есть несколько глобальных клавиатурных хуков, установленных через SetWindowsHookEx с WH_KEYBOARD_LL которые кажутся случайным образом отцепленными Windows.

Мы убедились, что они больше не подключены, потому что звонят UnhookWindowsHookEx на ручке возвращается false, (Также проверено, что он возвращает true когда все заработало нормально)

Похоже, что нет последовательного воспроизведения, я слышал, что они могут отсоединиться из-за тайм-аутов или исключений, но я попробовал и то и другое, просто оставив его на точке останова в методе обработки более минуты, а также просто выбрасывает случайное исключение (C#), и оно все еще работает.

В нашем обратном вызове мы быстро отправляем сообщения в другую ветку, так что, вероятно, проблема не в этом. Я читал о решениях в Windows 7 для увеличения времени ожидания в реестре, потому что Windows 7, по-видимому, более агрессивно относится к тайм-аутам (здесь мы все запускаем Win7, поэтому не уверен, происходит ли это в других ОС), но это не так. не кажется идеальным решением.

Я подумал о том, чтобы просто запустить фоновый поток, чтобы периодически обновлять ловушку, что является хакерским, но я не знаю никаких реальных негативных последствий этого, и это кажется лучше, чем изменение глобального параметра реестра Windows.,

Любые другие предложения или решения? И класс, который устанавливает хуки, и делегаты, к которым они присоединены, являются статическими, поэтому они не должны получать GC'd.

РЕДАКТИРОВАТЬ: проверено с звонками в GC.Collect(); что они по-прежнему работают, поэтому они не получают мусора.

7 ответов

Решение

Я думаю, что это должно быть проблемой тайм-аута.

Другие разработчики сообщали о специфической проблеме Windows7 с отцеплением низкоуровневых хуков, если они превышают (недокументированное) значение тайм-аута.

Посмотрите эту ветку для других разработчиков, обсуждающих ту же проблему. Возможно, вам потребуется выполнить занятый цикл (или медленную сборку мусора), а не режим сна, чтобы вызвать отсоединение. Точка останова в функции LowLevelKeyboardProc также может создавать проблемы тайм-аута. (Существует также наблюдение, что большая загрузка ЦП другой задачей может спровоцировать такое поведение - предположительно потому, что другая задача крадет циклы ЦП из функции LowLevelKeyboardProc и приводит к тому, что она занимает слишком много времени.)

Решение, предложенное в этом потоке, состоит в том, чтобы попытаться установить значение DWORD LowLevelHooksTimeout в реестре в HKEY_CURRENT_USER\Control Panel\Desktop на большее значение.

Помните, что одним из достоинств C# является то, что даже простые операторы могут занимать чрезмерное количество времени, если происходит сборка мусора. Это (или загрузка ЦП другими потоками) может объяснить прерывистый характер проблемы.

Я подумал о двух вещах, которые могут помочь вам понять, в чем проблема.

  1. Чтобы помочь локализовать проблему, запустите другую WH_KEYBOARD_LL перехватывать одновременно с вашим текущим перехватом, и пусть он не делает ничего, кроме передачи данных по цепочке перехватчиков. Когда вы обнаружите, что ваш оригинальный крючок отцеплен, проверьте и посмотрите, не был ли этот крючок "пустышки". Если "фиктивный" хук также был отцеплен, вы можете быть совершенно уверены, что проблема не в вашей хуке (то есть в Windows или в чем-то, что связано с вашим процессом в целом?) Если "фиктивные" хуки не были отцеплены, то проблема вероятно где-то в твоем крючке

  2. Записывайте информацию, которая поступает на ваш хук через обратный вызов, и запускайте ее, пока хук не отцепится. Повторите это несколько раз и изучите зарегистрированные данные, чтобы увидеть, можете ли вы распознать схему, ведущую к отцеплению.

Я бы попробовал их по одному, на случай, если это повлияет на исход других. Если после этого у вас нет сведений о том, в чем может быть проблема, вы можете попробовать запустить их вместе.

Это долгий путь, но случайно ли у вас работает антивирус? Это вполне может быть замечением зацепки клавиатуры и ее выбивания.

Скорее всего, он предупредит вас и немедленно удалит, но это одна из тех странных вещей, которые стоит проверить.

Я знаю, что это уродливое решение, но вы можете установить таймер на 5 минут, а затем переназначить события клавиатуры?

У меня была та же проблема с машиной Win7, через некоторое время перехваты клавиатуры теряли свою привязку, и мне приходилось устанавливать таймер на каждые 5 минут, чтобы перехватывать события снова, и теперь он сохраняет его свежим.

Я использую следующий проект: 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();
        }
    }
}

Возможно, у кого-то еще есть ловушка, которая не вызывает CallNextHookEx()?

Очень поздно, но думал, что поделюсь чем-нибудь. Собираясь отсюда, есть пара подсказок, таких как помещение SetWindowsHookEx в отдельный поток, и это больше проблема планирования.

Я также заметил, что Application.DoEvents использует довольно много ресурсов процессора, и обнаружил, что 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();
    }
}

поэтому просто измените активное значение bool на false в событии Deactivate и вызовите HookMouse в событии Activated.

НТН

РЕДАКТИРОВАТЬ: я заметил, что игры были замедлены с этим, поэтому решил отсоединиться, когда приложение не активно, используя события Activated и Deactivate.

Другие вопросы по тегам