Не знаю как, но у меня глобальный хук работает в обычном exe-файле.
И в XP, и в Висте - без проблем работает.
Щас попробую сделать небольшой консольный екзешник, устанавливающий глобальный хук..
Вид для печати
Глобальный хук работает только тогда, когда установившее его приложение проверяет очередь сообщений.
В приложении - программа, которая глобально отключает NumLock на 20 секунд и пишет все нажатые клавиши в GlobalHook.log
...
v1.1 делает то же самое, не отнимая процессорного времени.
...
Действительно, работает.
Буду разбираться, почему.
И что понимается под проверкой очереди сообщений.
---------- Post added at 02:12 ---------- Previous post was at 02:09 ----------
Если под очередью сообщений подразумевается PeekMessage() и т.д., то у меня тоже это все проверяется, но хук не вызывается)
---------- Post added at 03:45 ---------- Previous post was at 02:12 ----------
Перенес затем один в один обработчик в свой эмулятор - не работает)
Не понимаю, как из-за сообщений что-то там вообще может зависеть. Откуда такая информация? Чисто экспериментально?
У тебя они обрабатываются так:
а у меня так:Код:if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
if( msg.message == WM_QUIT ) break;
}
Разницы никакой.Код:if (PeekMessage(&msg, NULL, 0, 0, 0)) {
if (GetMessage(&msg, NULL, 0, 0)) { // Получить сообщение
TranslateMessage(&msg); // Если сообщение не QUIT,
DispatchMessage(&msg); // оттранслировать его окну
}
else SysExit(); // Иначе выйти закрыв все устройства
}
Еще точнее уточнил.
Если активно окно эмулятора, хук не работает. Если не активно работает. Опять активно - не работает.
Причем обработчик окна меняю даже на полностью дефолтный типа:
Это ничего не меняет. Т.е. зависит не от функции обработки окна а... непонятно опять от чего)Цитата:
return (DefWindowProc(hWnd,Message,wParam,lParam));
У меня эмулятор многопоточный. Есть поток интерфейса и потоки эмуляции. Поток интерфейса мало что делает. Главным образом - ждёт сообщений. Ну, разве что активное окно 60 раз в секунду перерисовывает. Может, поэтому я пока и не столкнулся с проблемами при работе хука.
---------- Post added at 03:44 ---------- Previous post was at 03:36 ----------
Это похоже на последствия предыдущих экспериментов, когда при получении фокуса окно эмулятора криво устанавливает какой-то свой кривой обработчик глобального хука, а после потери фокуса - убирает его и опять всё приходит в норму.
Когда программа имеет рабочий (с интересующей точки зрения ) вариант и не рабочий - причину неработоспособности легко определить "методом половинного деления" - добавляя в рабочий вариант куски нерабочего до тех пор, пока не обнаружится тот кусок, при добавлении которого нормальная работа прекращается.
Иногда так и приходится приятать весь программный код ( кроме начального создания главного окна программы ) в комментариях, а потом добавлять по-немногу, чтобы выловить некоторые глюки.
Программа гигантская, чтобы так легко было части прятать)
Но попробую сперва отключить DirectInput, посмотрю, чего получится.
А запусти мой эмуль параллельно своей тестилке хуков, посмотри, работает ли блокировка нумлока при активном моем окне.
---------- Post added at 14:33 ---------- Previous post was at 14:24 ----------
Проверил. Отключил DirectInput, все заработало.
Но проблема в том, что DirectInput мне очень необходим)
Надо будет почитать доки к нему.
Интересно, почему он нарушает глобальный хук.
Причем, совершенно штатно, т.к. никаких кривых методов его использования я не применяю.
Если DirectInput при получении окном фокуса устанавливает свой кривой глобальный хук ( не вызывающий после завершения обработки нажатия следующий глобальный хук в цепочке глобальных хуков ) - то, возможно, если каждый раз устанавливать свой глобальный хук после того, как это сделал DirectInput - то всё заработает.
Проще говоря - при получении фокуса окном нужно проверить следующие варианты:
1. Установить свой хук сразу ( и проверить успешность его установки ).
2. Установить сначала вместо хука таймер на 50 мс и после получения сигнала таймера - установить хук. Пользователь вряд ли успеет нажать на клавишу быстрее чем через 50 мс после активации окна.
Я пробовал устанавливать хук при получении фокуса окном - не помогает. Наличие активного окна с подключенным директинпутом этот хук игнорирует.
С задержкой не пробовал, но это какие-то дикие костыли.
Не должен директинпут быть кривым.
Попробую инициализировать более новую версию. Ведь у меня используется интерфейс DirectInput, а не, скажем, DirectInput8, для совместимости даже с древними версиями виндовсов и дирекиксов. В принципе, функционал особо в них не менялся, но может древняя версия интерфейса как раз глючная.
Такое впечатление, что DirectInput оправдывает своё название, отключая обычную обработку нажатий клавиш в Windows..
Тот факт, что хук нормально работает при выходе из окна с DirectInput - позволяет предположить, что когда DirectInput активен - Windows обрабатывает нажатия клавиш совсем не так, как обычно и ( в частности ) глобальные хуки не вызываются вообще.
Но ведь системные клавиши работают! Капслоки всякие тоже зажигают лампочки.
Кстати, как управлять этими лампочками? Эмулятор Амиги умеет. Вариант - посылать коды нажатий нумлоков и капслоков через хук - не канает, т.к. это позволяет ИНВЕРТИРОВАТЬ лампочку, но не управлять ей напрямую.
Сначала надо прочитать состояние лампочки (которое отслеживает Windows) и не инвертировать лампочку, если она уже в нужном состоянии:
Код:void SetCapsLock( bool bState )
{
BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_CAPITAL] & 1)) ||
(!bState && (keyState[VK_CAPITAL] & 1)) )
{
// Simulate a key press
keybd_event( VK_CAPITAL,
0,
KEYEVENTF_EXTENDEDKEY | 0,
0 );
// Simulate a key release
keybd_event( VK_CAPITAL,
0,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0);
}
}
---------- Post added at 18:02 ---------- Previous post was at 17:58 ----------
DirectX - это подсистема Windows, написанная тем же автором.
Т.е. все-таки метода управления ими напрямую нету?
Зато можно читать? Ну и то хорошо.
Но тогда по-любому через хук.
Странно, что он не дружит с директинпутом, тем более написанным авторами Windows.
---------- Post added at 19:26 ---------- Previous post was at 19:20 ----------
Получается, что массив состояния клавиш хранит не состояние CapsLock - нажата / не нажата, а состояние, активирована / не активирована?
---------- Post added at 19:57 ---------- Previous post was at 19:26 ----------
Хм, а вот это меня насторожило:
Цитата:
While DirectInput forms a part of the DirectX library, it has not been significantly revised since DirectX 8 (2001–2002). Microsoft recommends that new applications make use of the Windows message loop for keyboard and mouse input instead of DirectInput (as indicated in the Meltdown 2005 slideshow[1]), and to use XInput instead of DirectInput for Xbox 360 controllers.
---------- Post added at 19:59 ---------- Previous post was at 19:57 ----------
Т.е. микрософт не рекоммендует использовать директинпут для клавиатуры, а вместо него систему сообщений. Эту тормознутую бяку (как я ее считаю).
В смысле, так, чтобы обмануть Windows? Чтобы Windows думала, что лампочка ещё горит, а мы ( хе-хе! ) её уже потушили. А зачем такое может потребоваться..
Выше я привёл код функции управления состоянием лампочки CapsLock. После вызова SetCapsLock(1) лампочка гарантированно будет гореть, а после SetCapsLock(0) - гарантированно гореть НЕ будет. Какие проблемы.. Что ещё надо?
В бите 15 хранится [нажата / не нажата], а в бите 0 - [активирована / не активирована] ( позже выяснилось, что это ошибочное утверждение - т.к. в матрице хранятся байты ).Цитата:
Получается, что массив состояния клавиш хранит не состояние CapsLock - нажата / не нажата, а состояние, активирована / не активирована?
---------- Post added at 19:01 ---------- Previous post was at 18:59 ----------
Ну, миллион сообщений в секунду Windows без проблем пропускает.
Мне директинпут очень нравится. Он быстрый, дает состояние ВСЕХ клавиш быстренько в одном массиве. А так же различает Enter на основной и дополнительной клавиатурах.
Даёт одним махом состояние всех клавиш и всех лампочек. Причём, Windows дополнительно поддерживает специальные виртуальные клавиши, отражающие "суммарное" состояние пар контролов, шифтов и альтов.Код:BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
Если какой-то информации не хватает - глобальный хук можно не отключать и он всегда будет в курсе всех нажатий, устанавливая для программы любые флаги, какие надо.
GetKeyboardState function (Windows)
Функция возвращает матрицу виртуальных клавиш текщего потока, которая по умолчанию совпадает с матрицей виртуальных кавиш Windows, но может локально изменяться вызовами SetKeyboardState.Цитата:
Связана ли она с фокусом окна?
Список виртуальных клавиш Windows выглядит так Virtual-Key Codes, есть ли там флаг, позволяющий отличить PadEnter - не знаю. Можно написать тест, который будет после каждого нажатия клавиши сравнивать матрицу с предыдущей и показывать отличия. Если у PadEnter нет своего флага - можно научить глобальный хук писать флаг состояния PadEnter в отдельную переменную или в ту же матрицу виртуальных клавиш ( там есть свободные ячейки ).Цитата:
Позволяет ли отличить Enter на основной и доп-клавиатуре?
Проклятый Microsift теперь упорно делает вид, будто первой Windows была Windows 2000, поэтому про более ранние версии ничего узнать нельзя.Цитата:
С какой версии виндовс она появилась?
Пардон, это я спутал с GetKeyState.
Что в байтовой матрице происходит - надо смотреть.
В этом списке что-то не нашел. Да и на форуме уже проскакивала тема, что нельзя отличить эти два энтера.
---------- Post added at 20:42 ---------- Previous post was at 20:41 ----------
А как узнать в глобальном хуке состояние PadEnter? Или у него еще свои коды виртуальных клавиш?
Если эта клавиша при нажатии генерит автоповтор - то когда бы ни был подключен глобальный хук - он состояние этой клавиши тут же "подцепит".
Проверить просто - тестовая программка с хуком есть. Хук вызывается при каждом нажатии, автоповторе и отжатии каждой клавиши, получая её скан-код с битом нажата/отжата.
Формат аргументов глобального хука описан здесь: GlobalKeyboardHook.
В приложении продвинутая версия хук-теста.
Вот, что она пишет в лог при нажатии и отжатии сначала обычного, а потом дополнительного Enter:
Отличие есть - это флаг доп. клавиатуры в наборе флагов.Код:wp[0x100] ; vkCode[0xD] ; scanCode[0x1C] ; flags[0x0]
wp[0x101] ; vkCode[0xD] ; scanCode[0x1C] ; flags[0x80]
wp[0x100] ; vkCode[0xD] ; scanCode[0x1C] ; flags[0x1]
wp[0x101] ; vkCode[0xD] ; scanCode[0x1C] ; flags[0x81]
...
Только что сделал тест для сравнения виртуальных кодов клавиш через GetKeyboardState(). В ней, к сожалению, нажатия разных энтеров ТОЧНО не различаются.
Опять, получается, костыли всякие.
---------- Post added at 21:33 ---------- Previous post was at 21:30 ----------
Все правильно, и в доке так сказано:
Цитата:
0 Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
Ну в ней хоть все единообразно, в одном массиве, где все клавиши различаются.
Ну, допустим, сделать перехват клавиши дополнительного энтера в хуке. Куда потом его сувать? Придумывать новый сканкод? Это будет самодеятельность, т.к. эти же сканкоды получают другие приложения.
Так же всегда есть задержка между включеним и отключением глобального хука по активизации окна.
Можно, конечно, засинхронизироваться, но это все усложненно.
Можно вести полностью свою матрицу всех клавиш - в удобном для себя формате.
Хук можно включать один раз - при запуске программы. При получении фокуса устанавливать флажок для хука, а при потере - убирать. А уже хук, глядя на этот флажок - будет пропускать или не пропускать нажатие NumLock в Windows.Цитата:
Так же всегда есть задержка между включеним и отключением глобального хука по активизации окна.
Чтобы не пустить нажатие в Windows - хук должен сделать return 1;,
а чтобы пропустить - return CallNextHookEx( ghKeyboardHook, nCode, wp, lp );
Кстати - у меня в хук-тесте ошибка, там в CallNextHookEx передаются не оригинальные параметры вызова ( wp и lp ), а переработанные ( wParam и lParam ) - а надо передавать точно те, которые получены хуком.
Да, я тоже заподозрил там ошибку, но решил, пока сам не разберусь, не говорить)
Попробовал DirectInput8 - все тоже самое, но функционала больше. А так тоже блокирует глобльный хук.
Ну что же, похоже, действительно придется делать хук и в нем формировать массив из клавиш плюс дополнительная клавиша энтера нумпада. Сделаю конвертирование сканкодов VK_ в DIK_, т.к. у меня все заточено под них.
Глобальный хук стандартен в модульном API, но на массив клавиш пока стандарта нет - мне этот массив понадобится только в эмуляторе КСМ, делать который я ещё не начал. Поэтому, если есть конструктивные идеи относительно универсального массива клавиш, пригодного для использования в любом эмуляторе - это будет кстати.
Поскольку в модульном API глобальный хук один на все экземпляры программ, использующих этот API - программа не сможет изменять глобальный массив клавиш, а только получать оттуда значения.
И все-таки меня вот ЭТО настараживает:
Цитата:
Теперь надо сделать небольшое лирическое отступление от данной темы, для лучшего понятия описываемого механизма. В 32-битных (а далее в 64-битных) операционных системах Windows каждый процесс в системе имеет своё собственное обособленное адресное пространство. Обратиться к чужому адресному пространству можно только через несколько API функций и имея определённые привилегии. Т.е. по одному и тому же адресу в разных процессах могут быть совершенно разные данные. Для того чтобы фильтрующая функция могла обработать сообщение, она должна находиться в памяти именно того процесса, которому принадлежит целевое окно и оконная функция. Итак, если хук устанавливается на всю систему, то фильтрующая функция должна быть загружена в каждый процесс, у которого есть хотя бы один цикл сообщений c использованием функций GetMessage или PeekMessage. Единственный стандартный способ загрузки нашего кода в чужой процесс, это использование DLL. Т.е. для нормального функционирования хуков установленных на всю систему необходимо использовать DLL.
Чепуха.
Я в этом вопросе собаку съел и в модульном API любой EXE-шник может выступать провайдером глобальной таблицы клавиш, доступной всем процессам Windows, использующим модульный API.
Т.е. код глобального хука есть у всех одновременно запущенных EXE-шников модульного API, но в любой момент времени работает только один из этих глобальных хуков, который и обслуживает глобальную таблицу.
Тут, как я понял, имеется в виду другое.
Библиотечная функция имеет адресное пространство, доступное всем процессам. А твой конкретный экзешник, внутри которого код хука, может и не быть доступен другим процессам. Может это зависит от конкретной системы, многоядерности или еще чего. Я с этой стороны Виндовс знаю слабо.
Вот консольный вариант хук-теста.
Запускаем, переходим в окно блокнота, давим все кнопки подряд и радуемся.
...
Что то не вышло у меня с запуском на ноуте! )Цитата:
Запускаем,
http://savepic.ru/3740038.png
http://savepic.ru/3796377.png