Delphi установка глобального хука на окна

Обновлено: 19.05.2024

Глобальный хук на мышь срабатывает несколько раз в "многооконных" приложениях

Создаю приложение на Delphi - счётчик щелчков мыши. Написал глобальный хук на мышь. Сделал все, как рекомендовано, поместил его в DLL. Отслеживаю, отпускание кнопки ( WM_LBUTTONUP ). В "простых" приложениях (Рабочий стол, Notepad, Total Commander) работает без претензий. А вот в сложных многооконных приложениях, например - в любом браузере, в Delphi IDE, срабатывает два раза (считает, как два щелчка). В Microsoft Word, так вообще - то четыре, то девять, то пять.

Пробовал сделать задержку (а ля, дребезг контактов - debounce). Пробовал очищать очередь в обработчике нажатий (счётчик в главной программе - мониторе). Без результатов. Подскажите пожалуйста, идеи или направление мысли куда двигаться.

Хук своего же окна. DLL

Помощь в написании контрольных, курсовых и дипломных работ здесь.

94731 / 64177 / 26122 Ответы с готовыми решениями:

Хук окна
Скажите пожалуйста какой хук устанавливать и какое событие этого хука отслеживать что бы корректно.

Хук на ws2_32.dll
День добрый всем. Нужна помощь коллективного разума. Пишу парсер трафика онлайн игры. Поставил хук.

Хук создания окна
сам хук я установил public static int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) это.

Хук, скрытие окна
установил хук на окно. заметил следующее. используя функцию ShowWindows если я хочу задать стиль.

Хук окна

Помощь в написании контрольных, курсовых и дипломных работ здесь.

94731 / 64177 / 26122 Ответы с готовыми решениями:

Хук своего же окна. DLL
Есть чужая программа которая при запуске грузит мою dll в себя. Нужно перехватить появление окна.

WH_KEYBOARD_LL Хук, русские буквы из чужого окна
Всем привет! Перехватываю нажатия клавиш. но к сожалению не получается перехватить буквы с.

глобальный хук
помогите с программой(с какой стороны начинать) : нужно что бы работала как глобальный хук - в.

HCBT_CREATEWND Я поставил CBTHook еще 2 дня назад.. Отслеживаю событие HCBT_CREATEWND. Все работает отлично! Но как мне получить заголовок кона созданного?
Вот код моего хука:
Добавлено через 3 минуты
На сколько я понимаю в момент HCBT_CREATEWND окно только начинает создаваться, и такие параметры как заголовок отсутствуют. Разве нет? по идеии
var Handle : HWND;
begin

Handle := (HWND)wParam; по сути это переобразование переменной в другой тип

а кто сказал, что они DWORD типа??

они NativeUint а т.е. Cardinal

И что? Что это поменяло? Содержимое wParam не как не похоже на хендл созданного окна. Пишу на Delphi. иногда 1419 / 1276 / 286 Записей в блоге: 5 Содержимое wParam не как не похоже на хендл созданного окна. товарищи из Редмонда думают иначе
CBTProc callback function HCBT_CREATEWND
wParam Specifies the handle to the new window.
lParam Specifies a long pointer to a CBT_CREATEWND structure containing initialization parameters for the window. The parameters include the coordinates and dimensions of the window. By changing these parameters, a CBTProc hook procedure can set the initial size and position of the window. 87844 / 49110 / 22898

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Хук WH_KEYBOARD + DCOMConnection
В Delphi программирую недавно. Поставленная задача - Необходимо реализовать глобальный мониторинг.

Глобальный хук на клавиатуру
unit HookForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils.


Поток и хук на клавиатуру
Здравствуйте. Я нуб. Изначально у меня была задача получать данные о нажатой клавиши в любой.

Хук на Апи функцию CharToOem
Изучаю что такое хук. Написал программу конвертер из анси в оем и обратно Хочу научиться.

Установка хука на движение формы

Установка хука
Понадобилась программа блокировки клавиатуры. По-видимому, единственный способ добиться этого -.

установка глобального хука
Необходимо отследить открытие всех окон в системе, т.е. установить глобальный хук. Делаю следующее.

Установка хука на клавиатуру в сервисе
Всем доброго времени суток. Хотел написать в раздел для профи но не пустили. Я ставлю глобальный.

2646 / 2269 / 278

На основании чего ты уверен что вызов ShowMessage не порождает WM_MOVE ?
На основании чего ты уверен что хук-функция MsgProc будет вызываться искл-но в основном потоке процесса ?

2183 / 1253 / 143

[удалено модератором]
1. ShowMessage тут вообще не причем, неужели ты думаешь что я стал бы писать тему если бы не попробовал различные варианты, естественно я пробовал и без ShowMessage
что касательно второго вопроса, посмотри на последние 2 параметра функции SetWindowsHookEx

2646 / 2269 / 278 [удалено модератором]
ShowMessage тут вообще не причем

Да ты что ?! А мужики-то и не знают))

посмотри на последние 2 параметра функции SetWindowsHookEx

И чего ? Что нового я там для себя увижу ?

Так что иди и учи матчасть)

2183 / 1253 / 143 Ты все читаешь что я пишу? или только выбираешь что тебе нужно?
Ради тебя процетирую себя еще раз
неужели ты думаешь что я стал бы писать тему если бы не попробовал различные варианты, естественно я пробовал и без ShowMessage ОЛОЛО я первым же делом попробовал убрать ShowMessage, результат не стал лучше
приводит к порождению WM_MOVE

Добавлено через 3 минуты

как раз при таких вот параметрах MsgProc будет вызываться системой в том же треде, который создал окно

[удалено модератором]
иди читай что такое глобальный хук и локальный

Добавлено через 3 минуты

А дельфийские Мантры что гласят ?) А гласят они что обращения к визуальным VCL-контролам
в доп.тредах несовместимы с нормальным функционированием мультипоточного приложения. причем тут VCL и дельфийские Мантры, тут вопрос в большей степени по winapi 13070 / 5856 / 1705 Ребята, обсуждайте, пожалуйста, спокойно. Почти все посты выше пришлось цензуре подвергнуть.
Задачей обсуждения должно быть выяснение истины, а не создание поводов, чтобы чем-то зацепить друг-друга. 2183 / 1253 / 143 и то правда, мне этот спор ради спора никак не нужен, если нет ответа, что вообще писать
типа приводит к порождению WM_MOVE Это опытным путем проверено или в msdn написано?
[---] 2646 / 2269 / 278 иди читай что такое глобальный хук и локальный

A global hook procedure can be called in the context of any application in the same desktop as the calling thread, so the procedure must be in a separate DLL module. A thread-specific hook procedure is called only in the context of the associated thread. If an application installs a hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the application's code or in a DLL. If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL


By default, the DefWindowProc function sends the WM_SIZE and WM_MOVE messages to the window. The WM_SIZE and WM_MOVE messages are not sent if an application handles the WM_WINDOWPOSCHANGED message without calling DefWindowProc

[---]
Добавлено через 5 минут

опытным путем проверено или в msdn написано?

И проверено и написано и неоднократно)

[---] рекомендую в MsgProc сравнить результаты GetWindowThreadProcesssId с GetCurrentThreadId

2183 / 1253 / 143 A global hook . If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL и что? Сам то понял о чем тут сказано? Это не сколь не делает мой код не правильным!
If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL у меня процедура в dll, у меня стоит глобальный хук, так что не правильно? Приведенная тобой цитата ничего не опровергает.
By default, the DefWindowProc function sends the WM_SIZE and WM_MOVE messages to the window. The WM_SIZE and WM_MOVE messages are not sent if an application handles the WM_WINDOWPOSCHANGED message without calling DefWindowProc еще раз, что с ShowMessage, что без него результат ОДИНАКОВ
что ты вообще пристал к этому ShowMessage. 2646 / 2269 / 278 Это не сколь не делает мой код не правильным

Делает, и еще как)
Стоит только явно или неявно обратиться к BMM (что заведомо происходит в т.ч. в ходе исполнения ShowMessage), как ты тут же получишь другими граблями.
Ибо IsMultiThread = False. А ты ни сном ни духом, потому что упершись рогом продолжаешь настаивать на вызов MsgProc в контексте треда, вызвавшего SetWindowsHookEx.

Приведенная тобой цитата ничего не опровергает

А ты хоть раз поразмыслил ЗАЧЕМ почти во всех видах глоб.хуков требуется именно DLL ?
Нет, ты даже и не думал мыслить)

что с ShowMessage, что без него результат ОДИНАКОВ 2183 / 1253 / 143 mss, это уже утомляет
писать одно и тоже по несколько раз
неужели ты думаешь что я стал бы писать тему если бы не попробовал различные варианты Пробовал и без GetWindowText!
Делает, и еще как) А ты хоть раз поразмыслил ЗАЧЕМ почти во всех видах глоб.хуков требуется именно DLL ?

да потому что только dll может быть отображёна на АП другого процесса

Сделай рабочий пример обосновывая своими претензиями, а именно

На основании чего ты уверен что вызов ShowMessage не порождает WM_MOVE ?
На основании чего ты уверен что хук-функция MsgProc будет вызываться искл-но в основном потоке процесса ? Сделаешь, респект тебе, нет идешь учить матчасть 2646 / 2269 / 278 Пробовал и без GetWindowText

Т.е. ты утверждаешь что даже такая кастрированная до предела ф-ция

в результате установи хука вызвает падения GUI-процессов твоего десктопа ?

Два треда некоего процесса создали по окну. Оба окна при неких условиях получили каждый своё WM_MOVE. При этом в каждом из этих тредов сработала твоя ловушка. В обработчике ловушки ты из РАЗНЫХ тредов ОДНОВРЕМЕННО лезешь в BMM с некоей целью. Крит.секцию BMM в этот момент не использует, ибо при иниц-ции экз-ра DLL ты не соизволил взвести IsMultiThread. Результат - картина маслом: крах структур BMM с дальнейшими непредсказуемыми (вплоть до катострофических) для процесса последствиями.

dll может быть отображёна на АП другого процесса

И о чем это говорит ?
А говорит это о том что код в контексте АП отображенного экз-ра может быть вызван любым из тредов этого самого ДРУГОГО процесса. А тред, установивший хук, т.е. вызвавший SetWindowsHookEx, - это тред ТВОЕГО процесса.

Добавлено через 11 минут
Кроме всего этого ты беспардонно игнорируешь требования документации в части использования 1-го параметра myHook.
Ты ее вооще читал ? Нет, не читал.
А в док-ции черным по белому сказано, что параметр hHook трактуется по-разному в различных линейках мелкомягких систем. В NT-линейке он игнорируется, а в маздайной (95/98/Me) ты лепишь в этот факт.параметр ноль, вместо того чтобы передавать через него хендл хука, возвращенный при вызове SetWindowsHookEx.

Перехват API функций в Delphi с помощью сплайсинга

В этой статье я расскажу наиболее действенную методику перехвата API функций - это сплайсинг. Сплайсинг это подмена кода функции. Конечно, есть другой метод перехвата это редактирование таблицы импорта приложения, редактирование таблицы экспорта. Рассказывать буду по порядку.

Когда вы пишете в своём приложении так

Вы импортируете функцию статически. Адрес функции прописывается в таблице импорта вашего приложения (допустим, что адрес нашей функции $7BC56010).
адрес значение
А при вызове функции происходит так
Следовательно, для перехвата функции нам надо только подменить значение по адресу $00405F56 на своё, а для вызова оригинальной функции получать адрес функции через GetProcAddress. Но приложение может также получить адрес функции через GetProcAddress и вызывать перехватываемую функцию минуя, перехватчик. Данный метод бесперспективен.

Идём дальше. Что такое сплайсинг? Наша функция находится по адресу $7C80B529 и допустим, что там такой код
Для перехвата функции от нас требуется только переписать начальный код функции, так чтобы он передавал управление нашему обработчику. Для передачи управления нашему обработчику достаточно всего лишь одной инструкции jmp на абсолютный адрес, на адрес нашего обработчика . Эта инструкция займёт всего лишь 5 байт – сам опкод этой инструкции ($E9) и значение для прыжка. Это значение вычисляется так
s - Смещение следующей команды
d - Требуемый адрес для jmp, т.е. адрес обработчика

Если немного переделать эту формулу, то она будет выглядеть так
Теперь при каждом вызове целевой функции, всегда будет передаваться управление нашему обработчику. А как теперь вызвать оригинальную функцию? При установке перехвата нам надо сохранять первые 5 байт функции. Для вызова оригинала надо восстанавливать начало функции и вызывать ее, потом снова устанавливать перехват. Объявим структуру в которой будем сохранять первые 5 байт функции:
Поле Address фактически в этой структуре не нужен (он просто не к чему), поле нужно только для того чтобы было удобнее снимать перехват. Назовём эту структуру «мост» к старой функции.

Теперь напишем функцию, которая будет устанавливать перехват:

Мы сначала устанавливает атрибуты доступа к коду функции, так чтобы можно было его переписывать. Потом вычисляем значение для прыжка. Сначала сохраняем начало функции в запись, потом переписываем начало функции. В конце устанавливаем старые атрибуты доступа.

Теперь напишем функцию, которая будет снимать перехват:

Я думаю, что здесь всё понятно - просто восстанавливаем начало перехватываемой функции. Адрес перехватываемой функции берём из структуры указатель, на которую передаётся функции в качестве единственного параметра.

Теперь напишем функцию, которая будет устанавливать перехват по имени функции.

Едем далее. Описанные выше функции могут перехватывать функции только в текущем процессе. А как нам перехватывать функции в других процессах? Наиболее простой метод это засунуть перехватчик функции в DLL и в коде библиотечной функции устанавливать перехват, если DLL загружается в процесс и снимать перехват, если она выгружается. Тут ещё одна проблема: как заставить другой процесс загрузить нашу DLL. Наиболее простое решение это создание удалённых потоков. Теперь всё по порядку.

Удалённый поток создаётся функцией CreateRemoteThread.

Функция потока должна иметь следующие атрибуты

Адрес функции LoadLibraryA мы получаем с помощью функции GetProcAddress так библиотеки kernel32.dll и ntdll.dll грузятся во все процессы всегда по одним и тем же адресам, следовательно, адрес, полученный в адресном пространстве нашего процесса, будет действителен и адресном пространстве любого другого процесса. Теперь напишем функцию, которая загружает вашу DLL в адресное пространство чужого процесса.

Таким образом, мы загрузили свою DLL в чужой процесс. Вообще, внедрение своего кода в чужие процессы это совсем другая история и требует написания отдельной статьи. Вышеприведённый пример это самый простой способ внедриться в чужой процесс. Обычному процессу не разрешается изменение памяти системных процессов, таких как winlogon.exe, lsass.exe, smss.exe, csrss.exe и др. для этого нужна привилегия SeDebugPrivilege. В приложенных к статье исходниках есть функция EnableDebugPrivilege, которая включает эту привилегию для текущего процесса.

Чтобы установить перехват на API функции во всех процессах (во всех GUI процессах) достаточно просто загрузить нашу DLL. Достаточно написать вот такой код:

Также следует помнить одну меру предосторожности при установке и снятии перехвата на функции: надо остановить все остальные потоки текущего процесса, так как во время установки перехвата другой поток может вызывать искомую функцию и это приведёт к непредсказуемым последствиям.

Хуки в Windows на Delphi

Хуки предоставляют мощные возможности для приложений Windows. Приложения могут использовать хуки в следующих целях:

Начнём сначала, для установки хука успользуется функция SetWindowsHookEx

Первый параметр это числовая константа WH_* которая задаёт тип устанавливаемого хука. Второй параметр это адрес функции-фильтра. Третий параметр это хэндл модуля, содержащего фильтрующую функцию. Этот параметр должен быть равен нулю при установке хука на поток, но данное требование не является строго обязательным, как указано в документации. При установке хука для всей системы или для потока в другом процессе, нужно использовать хэндл DLL, содержащей функцию-фильтр. Четвёртый параметр это идентификатор потока, для которого устанавливается хук. Если он не равен нулю, то хук устанавливается только на указанный поток. Если идентификатор равен нулю, то хук устанавливается на всю систему. Некоторые хуки можно ставить как на всю систему, так и на некоторый поток, некоторые хуки можно поставить только на всю систему. Функция возвращает хендл хука, в случае неудачи функция возвратит ноль. Для снятия хука нужно использовать функцию UnhookWindowsHookEx, которая принимает в качестве единственного параметра хендл установленного хука.

Едем, далее. Все фильтрующие функции должны быть описаны следующим образом:

Для вызова следующей функции в очереди хуков предназначена функция CallNextHookEx

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

Клавиатурный шпион

Параметр Code может быть равен следующим значениям:

Создание и снятие хука, я думаю, особых проблем не составляет, поэтому сразу приступлю к самому обработчику хука.

Основная проблема в при написании клавиатурных хуков заключается в том что обработчику хука передаётся только скан код нажатой клавиши и её виртуальный код. Виртуальный код и скан код говорят нам, какая именно клавиша была нажата, но не говорят, что именно было введено. Поясню, даже если мы вводим русский текст, то клавиатурному хуку будут передаваться коды английских клавиш, т.е. мы вводим слово «привет», а обработчику хука будет передано «GHBDTN». Или, например, мы нажимаем на Shift цифру 7 и вводится знак &, но в клавиатурный хук будт передан только код цифры 7. Для того чтобы преобразовать скан код и виртуальный код в текстовый символ, который был введён, необходимо использовать функцию ToAscii (или ToUnicode). Её параметры:

Первый параметр это виртуальный код, второй это скан код, третий параметр это указатель на массив в котором сохранено состояние клавиатуры, четвёртый это указатель на переменную, в которую будет сохранён символ, пятый параметр это флаг, определяющий, является ли меню активным. Этот параметр должен быть 1, если меню активно, или иначе 0. Функция возвращает количество символов, полученных в результате преобразования. Состояние клавиатуры можно получить через функцию GetKeyboardState.

Вернёмся в нашей фильтрующей функции.

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

Если посмотреть получившийся лог, то мы увидим следующий текст в формате <название клавиши, введённый текст>.

Вот и подошёл конец первой статьи про хуки. Качаем, смотрим исходник исследуем, учимся.

Работа с окнами

Во второй части статьи про хуки в Windows я расскажу про слежение событий создания, активации, уничтожения окон. Также много внимания будет уделено методам межпроцессорного взаимодействия с использованием разделяемой памяти (мэпинга) и синхронизации потоков с использованием мьютексов. Также будет написана программа на Delphi для осуществления мониторинга окон.

Итак, начнём сначала. Для создания хука для мониторинга событий окон надо указать тип хука WH_CBT в первом параметре функции SetWindowsHookEx. Хук типа WH_CBT позволяет отслеживать следующие события окон: создание, уничтожение, активация, установку фокуса, минимизация, максимизация и прочее.

Формат обработчика хука такой же, какой и у других типов хуков

Назначение параметров wParam и lParam полностью зависит от типа события. Обработчик хука всегда вызывается до осуществления события. Если обработчик хука не вызовет следующий обработчик хука (функция CallNextHookEx), то перехватываемое действие не произойдёт, таким образом можно блокировать некоторые действия. Но тем не менее, отменять события в хуках такого типа не рекомендуется, так как это будет очень неожиданно для приложения. Представьте себе ситуацию, когда программа хочет уничтожить окно, а у него не получается, или же хочет создать окно, но не получается, намного корректнее было бы уничтожить окно после его создания (к примеру, через 10 мс). Далее приведены наиболее часто используемые типы событий.

Если код события равен HCBT_ACTIVATE, то произошло событие активации окна. В данном случае параметр wParam содержит хендл искомого окна, а параметр lParam будет указывать на структуру CBTACTIVATESTRUCT. Далее приведено описание этой структуры:

Если событие произошло вследствие клика мыши, то поле fMouse будет равно TRUE. Поле hWndActive содержит хендл окна, активного в данный момент.

При коде события HCBT_CREATEWND параметр wParam содержит хендл нового окна, а lParam указывает на структуру CBT_CREATEWND

Поле hwndInsertAfter содержит хендл окна, которое по Z координате находится сразу же за вновь создаваемым. Изменив этот хендл можно изменить Z координату вновь создаваемого окна. Поле lpcs указывает на структуру CREATESTRUCT, она имеет следующий формат:

Я думаю здесь всё понятно.

При коде события HCBT_DESTROYWND wParam содержит хендл уничтожаемого окна, lParam ничего не содержит. Как было уже сказано, функция обработчик вызывается до осуществления события, а следовательно когда мы в обработчике окно ещё существует и можно получить параметр уничтожаемого окна.

Помимо указанных кодов событий ещё есть следующие:

Начнём сначала. Принцип файлового мэпинга является одним из основополагающих принципов работы с виртуальной памятью в Windows. С помощью техники файлового мэпинга можно создать область памяти, которая при нехватке физической памяти будет сбрасываться не в файл подкачки, а в какой-нибудь указанный нами файл. Таким образом, при изменении памяти, выделенной и спроецированной с помощью механизма мэпинга, содержимое файла тоже будет изменено (разумеется, не сразу). Чтобы создать файл-мэппинг объект надо использовать функцию CreateFileMapping. Её формат:

Первый параметр это хендл файла, который будет использован как файл подкачки для этой области памяти. Если хендл файла равен значению INVALID_HANDLE_VALUE, то выделенная область памяти при необходимости будет сбрасываться в файл подкачки (как и любая другая область памяти). Второй параметр это атрибуты защиты. Третий параметр задаёт параметры доступа к выделенной памяти: PAGE_READONLY - только чтение, файл в этом случае должен быть открыт как минимум с флагом GENERIC_READ; PAGE_READWRITE – чтение и запись, файл должен быть открыт как минимум с флагами GENERIC_READ и GENERIC_WRITE; PAGE_WRITECOPY – тоже самое, что и с предыдущим флагом, но все выделенные страницы помечаются как копируемые при записи. В этом случае изменения в выделенной памяти не будут отражаться на искомом файле, и в случае необходимости область памяти будет сбрасываться в файл подкачки. В общем, не будем слишком сильно заморачиваться этим флагом, лучше всего использовать флаг PAGE_READWRITE. Третий и четвёртый параметры задают максимальный размер создаваемого объекта, соответственно старшую и младшую часть. Последний параметр задаёт имя создаваемого объекта, через которое смогут обратиться к нему другие процессы.

Для открытия имеющего файл-мэпинг объекта по имени существует функция OpenFileMapping.

Первый параметр задаёт тип доступа к объекту, может принимать следующие значения: FILE_MAP_WRITE – чтение и запись, объект должен быть создан с атрибутом PAGE_READWRITE; FILE_MAP_READ – только чтение, объект должен быть создан к минимум с атрибутом PAGE_READONLY; FILE_MAP_ALL_ACCESS- тоже самое, что и FILE_MAP_WRITE; FILE_MAP_COPY – копирование при записи, объект должен быть создан с атрибутом PAGE_WRITECOPY. Второй параметр это флаг наследования. Третий параметр задаёт имя отрываемого файл-мэпинг объекта.

Для проецирования файл-мэпинг объекта на память используется функция MapViewOfFile. Её описание:

Первый параметр это хендл файл-мэпинг объекта. Второй параметр задаёт атрибуты доступа, требования полностью идентичны требованиям первого параметра для функции OpenFileMapping. Третий и четвёртый параметры задают начальное смещение в файле, с которого начнётся проецирование на память, соответственно старшая и младшая часть смещения. Последний параметр задаёт количество байт для проецирования на память. Функция в случае успеха возвращает указатель на выделенную память.

Для освобождения выделенной памяти и сохранения изменений в искомый файл (если это нужно было) надо вызвать функцию UnmapViewOfFile, она принимает единственный параметр, это начальный адрес, куда был спроецирован объект.

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

Итак, общий алгоритм известен, но возникает новая проблема. Так как процессов и окон много, возникает проблема синхронизации записи в буфер. А именно надо сделать так, чтобы записывать в лог в некоторый момент времени мог только один поток, иначе результаты будут непредсказуемыми. Эксклюзивного доступа к общим данным можно добиться, используя критические секции, но их можно использовать только для синхронизации потоков в одном процессе. Заменой критических секций в «межпроцессорном масштабе» являются объекты взаимоисключения – мьютексы. (конечно же, есть и другие варианты, но этот вариант наиболее простой).

Мьютексы могут находиться в двух состояниях в захваченном и свободном. Также мьютексы, как и любые другие объекты в Windows, могут находиться в двух состояниях: сигнальном и несигнальном состоянии. Когда мьютекс захвачен каким-либо потоком, он находится в несигнальном состоянии, когда мьютекс свободен, он находится в сигнальном состоянии.

Для создания мьютекса надо вызвать функцию CreateMutex, её заголовок:

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

Чтобы открыть существующий мьютекс необходимо использовать функцию OpenMutex.

Первый параметр задаёт флаги доступа к мьютексу, второй параметр задаёт флаг наследования, третий имя мьютекса. Первый параметр может принимать следующие значения: MUTEX_ALL_ACCESS - полный доступ, SYNCHRONIZE – только для синхронизации. (впрочем, так и непонятно чем они друг от друга отличаются)

Если хендл мьютекса передан какой-либо ждущей функции (например, WaitForSingleObject), то эта функция проверяет его состояние, если он свободен (в сигнальном состоянии), то помечает его как занятый (переводит его в несигнальном состоянии) и возвращает управление. Если мьютекс находится в занятом состоянии (несигнальном), то она ждет, когда он перейдёт в свободное (сигнальное) состояние, либо ждёт окончания указанного интервала и только потом возвращает управление. Для освобождения мьютекса необходимо вызывать функцию ReleaseMutex, передав ей единственный параметр – хендл мьютекса.

Допустим у нас есть некоторый код который работает с общими данными и необходим эксклюзивный доступ к ним, шаблон кода будет таким:

Итак, все знания необходимые для написания монитора окон мы получили, настало время написать программу для мониторинга окон. Сначала приведу код DLL который устанавливает и снимает хук:

Проблем с этим кодом быть не должно: при установке хука мы открываем нужные нам объекты и проецируем в нашу память общий буфер. Далее приведён код функции фильтра.

В начале мы сразу же вызываем следующий обработчик в цепочке обработчиков. Потом обрабатываем данные в зависимости от типа события. В событии HCBT_CREATEWND мы поучаем имя окна из структуры PCBTCreateWnd на которую указывает параметр lParam, в остальных двух случаях мы получаем имя окна, используя её хендл который находится в параметре wParam. В событии HCBT_CREATEWND мы получаем имя окна только в том случае если оно главное, т.е. не имеет родителя, в остальных двух случаях мы производим обработку только в случае, если имя окна не является пустой строкой. После того как мы получили строку нам необходимо её добавить в буфер. Добавление производится между вызовами функций WaitForSingleObject и ReleaseMutex чтобы обновление мог производить только один поток одновременно.

Осталось написать приложение сервер, которое будет запускать и останавливать мониторинг.

Я думаю, ничего сложного в этом коде нет. Функция DumpBuffer скидывает содержимое буфера в файл. При создании объекта файлового мэпинга мы не указываем никакого файла. Сразу возникает вопрос: почему? Смысл в том, что размера выделяемого буфера может не хватить и придётся его время от времени сбрасывать в файл, а если выделять сразу большой буфер, то хук станет слишком ресурсоёмким. Хотя в данном примере не реализован сброс буфера в файл при нехватке места в буфере, об этом нельзя забывать и это надо будет обязательно реализовать в своих программах.

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

Использование HOOK в Дельфи

Какие бывает НООК'и?

Как создавать НООК?

НООК устанавливается в систему при помощи функции SetWindowsHookEx, вот её заголовок:

function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK;

константа, определяющая тип вставляемого НООК'а, должна быть одна из нижеследующих констант:

вставляемая НООК-функция следит за другими НООК-функциями.

указатель на непосредственно функцию. Обратите внимание, что если Вы ставите глобальный НООК, то НООК-функция обязательно должна находиться в некоторой DLL.

описатель DLL, в которой находится код функции.

идентификатор потока, в который вставляется НООК

Подробнее о НООК-функциях смотри справку по Win32API.

Как удалять НООК?

НООК удаляется при помощи функции UnHookWindowsEx.

Пример использования НООК.

Ставим НООК, следящий за мышью (WH_MOUSE). Программа следит за нажатием средней кнопки мыши, и когда она нажимается, делает окно, находящееся непосредственно под указателем, поверх всех остальных (TopMost). Код самой НООК-функции помещен в библиотеку lib2.dll, туда же помещены и функции Start - для установки НООК, и Remove - для удаления НООК.

uses windows, messages;

//экспортируем две функции из библиотеки с НООК'ами

procedure Start; external 'lib2.dll' name 'Start' ;

procedure Remove; external 'lib2.dll' name 'Remove' ;

function WindowProc(wnd:HWND; Msg : Integer; Wparam:Wparam; Lparam:Lparam):Lresult; stdcall ;

nCode, ctrlID : word;

Remove; //удаляем НООК

postquitmessage( 0 ); exit;

wc.style:=cs_hredraw or cs_vredraw;

wc.hIcon:=LoadIcon( 0 ,idi_application);

wc.hCursor:=LoadCursor( 0 ,idc_arrow);

MainWnd:=CreateWindowEx( 0 , 'WndClass1' , 'Caption' ,ws_overlappedwindow,

Hooks

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

Сразу хочу сделать несколько оговорок: речь в дальнейшем пойдёт только о 32-х разрядной Windows и о глобальных ловушках, т.к. именно при их программировании возникает большинство ошибок; все примеры будут даваться на Delphi, т.к. примеров и описаний для любителей С++ достаточно.

Подробности Родительская категория: Windows Категория: Hooks

Создание ловушек в Delphi

Рано или поздно каждый программист сталкивается с таким понятим как ловушки. Чтобы приступить к ипользованию ловушек необходимо обзавестись windows SDK, который можно так же скачать с сайта Microsoft. В прилагаемом к статье архиве содержатся два проекта: hooks.dpr - это пример приложения работающего с ловушками, а hookdll.dpr - собственно сама DLL.

Что такое ловушки (Hooks)?

Подробности Родительская категория: Windows Категория: Hooks

Hook функций в собственном приложении

TSaveRedir = packed record

Bytes: array [ 0 .. 4 ] of Byte;

procedure RedirectCall(FromAddr, ToAddr: Pointer; SaveRedir: PSaveRedir);

NewCode: packed record

if not VirtualProtect(FromAddr, 5 , PAGE_EXECUTE_READWRITE, OldProtect) then

if Assigned(SaveRedir) then

Move(FromAddr^, SaveRedir^.Bytes, 5 );

NewCode.Distance := PChar(ToAddr) - PChar(FromAddr) - 5 ;

Move(NewCode, FromAddr^, 5 );

if not VirtualProtect(FromAddr, 5 , OldProtect, OldProtect) then

procedure UndoRedirectCall( const SaveRedir: TSaveRedir);

if not VirtualProtect(SaveRedir.Addr, 5 , PAGE_EXECUTE_READWRITE, OldProtect) then

Move(SaveRedir.Bytes, SaveRedir.Addr^, 5 );

if not VirtualProtect(SaveRedir.Addr, 5 , OldProtect, OldProtect) then

// Example: Replace Application.MessageBox with your own.

function MyNewMessageBox(Self: TApplication; const Text, Caption: PChar;

Flags: Longint): Integer;

ShowMessage( 'New Messagebox' );

procedure TForm1.Button1Click(Sender: TObject);

Application.MessageBox( 'You`ll never see this Text /

Diesen Text wirst du nie sehen' , '. ' , MB_OK);

RedirectCall(@TApplication.MessageBox, @MyNewMessageBox, @S);

Подробности Родительская категория: Windows Категория: Hooks

Использование ловушек, блокировка мышки, клавиатуры и т.д.

Возможные вариации: Любые вопросы, связанные с постановкой хука. Например "Как отследить [что-то]", "Как подменить [какое-то действие]", "Как заблокировать комбинации клавиш, как заблокировать определённые действия", "Как не дать запускаться определённым приложениям, не дать открываться определённым окнам?", "Как получить список запущенных оконных приложений?" и т.д.

Рабочий пример глобальной блокировки правой кнопки мыши:

Подробности Родительская категория: Windows Категория: Hooks

Ловушки в Windows

Для отслеживания каких-то событий во всей Windows нужно установить ловушку (hook). Например, такая ловушка может отслеживать все события, связанные с мышью, где бы ни находился курсор. Можно отслеживать и события клавиатуры.

Для ловушки нужна функция, которая, после установки ловушки при помощи SetWindowsHookEx, будет вызываться при каждом нужном событии. Эта функция получает всю информацию о событии. UnhookWindowsHookEx уничтожает ловушку.

Подробности Родительская категория: Windows Категория: Hooks

Перехват API функций, на примере MessageBoxA

sethook: procedure (flag:bool) stdcall ;

messagebox( 0 , 'Не закрывай, пока идет работа' , '' , 0 );

Подробности Родительская категория: Windows Категория: Hooks

Демонстрационный пример хука и подмены API в приложениях

// * Unit Name : HookDLL

// * Purpose : Демонстрационный пример хука и подмены API в приложениях.

// * Author : Александр (Rouse_) Багель

Код не маленький, посему вынесем отдельно

Подробности Родительская категория: Windows Категория: Hooks

Использование HOOK в Дельфи

Какие бывает НООК'и?

Подробности Родительская категория: Windows Категория: Hooks

Глобальный хук на клавиатуру

uses Windows, SysUtils;

const KF_UP_MY = $40000000 ;

var CurrentHook: HHook;

KeyArray: array [ 0 .. 19 ] of char;

function GlobalKeyBoardHook(code: integer; wParam: integer; lParam:

integer): longword; stdcall ;

if ( (lParam and KF_UP_MY ) = 0 ) and (wParam> = 65 ) and (wParam< = 90 ) then

if KeyArrayPtr> 19 then

for i:= 0 to 19 do

if fileexists( 'd:\log.txt' )=false then rewrite(CurFile)

write (Curfile, KeyArray[i]);

CurrentHook:=SetWindowsHookEx(WH_KEYBOARD, @GlobalKeyBoardHook,HInstance, 0 );

Подробности Родительская категория: Windows Категория: Hooks

Interprocess communication на примере keyboard hook (статья)

Архитектура Win32 подразумевает максимальную изоляцию выполняющихся приложений друг от друга. Каждое приложение выполняется в своем виртуальном адресном пространстве, которое полностью обособлено и не имеет доступа к памяти других программ. Однако иногда возникает необходимость в передаче данных их одного выполняющегося процесса в другой. Рассмотрим подробно одну из таких задач, а затем основные способы связи между процессами и рекомендации по их применению.

Пишем перехватчик клавиатуры

Подробности Родительская категория: Windows Категория: Hooks

Отключить клавиши при системном Hooke

Чтобы использовать механизм окна крюк, программа вызывает функцию SetWindowsHookEx() API-интерфейс, передача адреса процедуры hook, которая уведомляется, когда указанное

событие происходит. SetWindowsHookEx() возвращает адрес ранее установленного обработать процедуру для того же типа события. Этот адрес-это важно,

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

и каждая процедура отвечает за передачу уведомления. Для этого процедура подключения должна вызвать CallNextHookEx() функции API,

адрес прохождения предыдущей процедуры крюка.

-- >Все системные крючки должны находиться в динамической библиотеке ссылок.

** Тип крюка, используемый в этом примере кода: **

Читайте также: