Мы рассмотрим некоторые наиболее простые виды ловушек, перехватывающих сообщения окон. По глобальности действия рассмотренные нами ловушки являются устанавливаемыми на отдельный поток: при ошибке в функции-ловушке это безопаснее для системы.
Начинается создание ловушки с написания собственно функции-ловушки, имеющей следующий прототип:
function HookProc(code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT stdcall;
Параметр code используется для обозначения тех случаев, когда функция ловушки должна вызвать специальную API-функцию CallNextHookEx и вернуть значение, возвращенное ею. Назначения параметров wParamи lParam этой функции сильно зависят от того, для реакции на какое именно событие ловушка используется.
Для регистрации ловушки используется API-функция SetWindowsHookEx, имеющая следующий прототип:
function SetWindowsHookEx(idHook: Integer; //Тип ловушки
lpfn: TFNHookProc; //Адрес функции-ловушки
hmod: HINST; //Используемый модуль, в котором
//расположена функция ловушки
dwThreadId: DWORD //Идентификатор потока, для
//которого создается ловушка
): HHOOK; stdcall;
В случае успешного создания ловушки функция SetWindowsHookEx возвращает дескриптор новой ловушки (ненулевое значение).
Для удаления ловушки используется функция UnhookWindowsHookEx, принимающая единственный параметр – дескриптор ловушки, возвращенный функцией SetWindowsHookEx. Причем удаление ловушки нужно производить обязательно, поэтому по крайней мере при закрытии приложения не следует забывать вызывать функцию UnhookWindowsHookEx.
Теперь несколько слов о функции CallNextHookEx. Ее объявление имеет следующий вид:
function CallNextHookEx(hhk: HHOOK; nCode: Integer;
wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
В чем важность этой функции? Она предназначена для продолжения передачи сообщения по цепочке ловушек (ведь одновременно несколько приложений могут создать несколько ловушек). Вызов этой функции настоятельно рекомендуется осуществлять в любом случае (независимо от значения параметра code функции-ловушки), только если целью не стоит блокирование других ловушек.
Виды ловушек
Приведем список некоторых простых типов ловушек, а именно констант из модуля Windows, их обозначающих и передаваемых в функцию SetWindowsHookEx:
• WH_CALLWNDPROC – функция ловушки вызывается каждый раз до вызова функции обработки сообщений окон, созданных наблюдаемым потоком;
• WH_CALLWNDPROCRET – вызывается каждый раз при возврате из функции обработки сообщений окон наблюдаемого потока;
• WH_KEYBOARD – функция ловушки вызывается перед обработкой сообщений WM_KEYDOWN и WM_KEYUP оконной функцией исследуемого потока;
• WH_MOUSE – вызывается перед обработкой оконной функцией наблюдаемого потока сообщений от манипулятора «мышь».
Рассмотрим, какое значение имеют параметры 1 Par am и wParam функции-ловушки в каждом из перечисленных случаев.
Перехват вызова оконной функции
Итак, для ловушки WH_CALLWNDPROC, которая, кстати, используется в рассматриваемом далее приложении, два последних параметра функции-ловушки трактуются следующим образом:
• wParam – равен нулю, если сообщение послано в окно тем же потоком, в котором исполняется функция ловушки, и не равен нулю, если сообщение послано другим потоком;
• lParam – указатель на структуру TCWPStruct, содержащую информацию о сообщении, которое передано окну (и будет передано в оконную функцию).
Объявление структуры TCWPStruct с описанием ее полей выглядит следующим образом:
type TCWPStruct = packed record
lParam: LPARAM; //Параметр сообщения
wParam: WPARAM; //Параметр сообщения
message: UINT; //Код сообщения
hwnd: HWND; //Окно, которому адресовано сообщение
end;
Ниже приводится пример преобразования параметра lParam функции ловушки к указателю на структуру с последующей проверкой кода сообщения (фрагмент программы):
var hook_data : hook_data: ^TCWPStruct;
begin
hook_data := Pointer(lParam);
if hook_data^.message = WM_SIZE then
begin
//Реагируем на изменение размера окна
end;
end;
Получение доступа к данным, передаваемым в остальные функции ловушки (а именно, несложное преобразование типов у операции с указателем), осуществляется аналогичным образом, поэтому более демонстрироваться не будет.
Перехват возврата из оконной процедуры