Что может заставить Windows отцепить низкоуровневую (глобальную) зацепку клавиатуры? - PullRequest
24 голосов
/ 16 апреля 2010

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

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

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

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

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

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

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

Ответы [ 7 ]

16 голосов
/ 21 апреля 2010

Я думаю, это проблема с тайм-аутом.

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

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

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

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

3 голосов
/ 21 апреля 2010

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

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

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

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

2 голосов
/ 26 апреля 2010

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

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

1 голос
/ 06 апреля 2014

Я использую следующий проект из: http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C для выполнения задач при нажатии определенной клавиши.

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

Мой класс горячих клавиш:

 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();
        }
    }
}
1 голос
/ 27 июня 2013

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

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

1 голос
/ 21 апреля 2010

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

0 голосов
/ 22 апреля 2018

Очень поздно, но думал, что поделюсь чем-нибудь. Собирая из здесь , есть пара советов, как помещение 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.

HTH

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

...