Что может привести к тому, что Windows отцепит низкоуровневую (глобальную) клавиатуру?
У нас есть несколько глобальных клавиатурных крючков, установленных через SetWindowsHookEx
с WH_KEYBOARD_LL
, которые кажутся случайным образом отцеплены Windows.
Мы проверили, что они больше не привязаны, потому что вызов UnhookWindowsHookEx
в дескрипторе возвращает false
. (Также проверено, что он возвращает true
, когда он работает правильно)
Кажется, что нет последовательного воспроизведения, я слышал, что они могут отцепляться из-за тайм-аутов или исключений, которые могут быть сброшены, но я попробовал как просто позволить ему сидеть на контрольной точке в методе обработки для более минута, а также просто бросать случайное исключение (С#), и оно все еще работает.
В нашем обратном вызове мы быстро отправляем сообщение в другой поток, так что, вероятно, это не проблема. Я прочитал о решениях в Windows 7 для установки тайм-аута выше в реестре, потому что Windows 7 более агрессивно относится к таймаутам (мы все запускаем Win7 здесь, поэтому не уверен, что это происходит на других ОС), но это не так 'похоже на идеальное решение.
Я считал, что у меня есть фоновый поток, работающий для обновления крючка каждый раз, что является хакерским, но я не знаю никаких реальных негативных последствий для этого, и это кажется лучше, чем изменение глобального Настройка реестра Windows.
Любые другие предложения или решения? Оба класса, который устанавливает крючки и связанные с ними делегаты, являются статическими, поэтому они не должны получать GC'd.
EDIT: проверено с помощью вызовов GC.Collect();
, что они все еще работают, поэтому они не собираются собирать мусор.
Ответы
Ответ 1
Я думаю, что это должна быть проблема с тайм-аутом.
Другие разработчики сообщили о специфической проблеме Windows7 с отцеплением низкого уровня, если они превышают значение (недокументированное) тайм-аута.
Смотрите этот поток для других разработчиков, обсуждающих ту же проблему. Возможно, вам нужно выполнить цикл занятости (или медленную сборку мусора), а не спящий режим, чтобы вызвать отцепление. Точка останова в функции LowLevelKeyboardProc также может создавать проблемы с тайм-аутом. (Также наблюдалось, что большая загрузка процессора другой задачей может спровоцировать поведение - по-видимому, потому, что другая задача крадет циклы процессора из функции LowLevelKeyboardProc и заставляет ее занять слишком много времени.)
Решение, предложенное в этом потоке, заключается в попытке установить значение LowLevelHooksTimeout DWORD в реестре на HKEY_CURRENT_USER\Control Panel\Desktop на большее значение.
Помните, что одна из славы С# заключается в том, что даже простые операторы могут занимать чрезмерное количество времени, если происходит сборка мусора. Это (или загрузка процессора другими потоками) может объяснить прерывистый характер проблемы.
Ответ 2
Есть две вещи, о которых я подумал, которые могут помочь вам выяснить, в чем проблема.
-
Чтобы помочь изолировать местоположение проблемы, запустите еще один крюк WH_KEYBOARD_LL
одновременно с вашим текущим крюком и не делайте ничего, кроме передачи данных по цепочке крюков. Когда вы узнаете, что ваш оригинальный крючок отцеплен, проверьте и проверьте, не был ли этот крюк "dummy" снят. Если крюк "dummy" также был отцеплен, вы можете быть достаточно уверены, что проблема находится за пределами вашего крючка (то есть в Windows или что-то, связанное с вашим процессом в целом?) Если крючки "dummy" не были отцеплены, тогда проблема вероятно, где-то внутри вашего крюка.
-
Зарегистрируйте информацию, которая попадает на ваш крючок через обратный вызов, и запускайте его до тех пор, пока крюк не будет отцеплен. Повторите это несколько раз и просмотрите зарегистрированные данные, чтобы увидеть, можете ли вы различить шаблон, ведущий к отцеплению.
Я бы попробовал их по одному, на всякий случай повлиял бы на результат других. Если после этого у вас нет указаний на то, что проблема может быть, вы можете попробовать запустить их вместе.
Ответ 3
Это длинный выстрел, но случайно у вас есть антивирусное программное обеспечение? Это очень хорошо заметило крючок клавиатуры и выгнал его.
Скорее всего, это предупредит вас и немедленно удалит его, но это одна из тех странных вещей, которые стоит проверить.
Ответ 4
Возможно, у кого-то есть крючок, который не вызывает CallNextHookEx()?
Ответ 5
Я знаю, что это уродливое решение, но вы можете установить таймер навсегда 5 минут, после чего вы сможете перехватить события на клавиатуре?
У меня была такая же проблема с машиной Win7, после того, как клавиатурные крючки теряли ее ссылку, и мне приходилось настраивать таймер каждые 5 минут, чтобы снова перехватывать события, и теперь он сохраняет его свежими.
Ответ 6
Я использую следующий проект: http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C для выполнения задач при нажатии определенного ключа.
Я заметил, что после того, как я выполнил задачу с помощью горячей клавиши, она перестала слушать новые нажатия клавиш, а затем изменила KeyboardHookListener на статическую и, похоже, решила мою проблему. Я не знаю, почему это решило мою проблему, поэтому не стесняйтесь комментировать это!
Мой класс hotkey:
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 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();
}
}
}