Читать интересную книгу Delphi. Трюки и эффекты - Валерий Борисок

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 39 40 41 42 43 44 45 46 47 ... 59

10.1. Ловушки Windows

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

В Windows также предусмотрен мощный механизм, позволяющий следить за некоторыми важными событиями в системе и, конечно, производить мониторинг сообщений, получаемых различными окнами. Речь идет об установке так называемых ловушек. Ловушка представляет собой функцию, вызываемую при возникновении определенного события, например перед получением каким-либо окном нового сообщения, при нажатии клавиши, записи события в системный журнал и т. д.: все зависит от того, для каких событий разработчики предусмотрели ловушки. Интересен тот факт, что в Windows предусмотрены даже ловушки для отладки других ловушек.

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

Начинается создание ловушки с написания собственно функции-ловушки, имеющей следующий прототип:

...

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;

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

Перехват возврата из оконной процедуры

Для ловушки WH_CALLWNDPROCRET параметры wParam и lParam функции-ловушки следует трактовать следующим образом:

• wParam – равен нулю, если сообщение послано другим процессом, и не равен нулю в противном случае;

• lParam – указатель на структуру TCWPRetStruct, содержащую информацию о сообщении, которое передано окну (и будет передано в оконную функцию).

Объявление структуры TCWPRetStruct с описанием ее полей выглядит следующим образом:

...

type TCWPRetStruct = packed record

lResult: LRESULT; //Значение, возвращенное оконной функцией

lParam: LPARAM; //Параметр сообщения

wParam: WPARAM; //Параметр сообщения

message: UINT; //Код сообщения

hwnd: HWND; //Дескриптор окна-получателя

end;

Перехват сообщений клавиатурного ввода

Для ловушки WH_KEYBOARD параметры wParam и lParam функции-ловушки следует трактовать следующим образом:

• wParam – код нажатой клавиши;

• lParam – первые 16 бит этого параметра означают количество повторений нажатия; старшие 16 бит используются для дополнительного описания состояния клавиатуры в момент нажатия клавиши.

Параметры wParam и lParam полностью аналогичны параметрам сообщений WM_KEYDOWN И WM_KEYUP.

Перехват сообщений от мыши

В ловушку WH_KEYBOARD в параметрах wParam и lParam передаются следующие значения:

• wParam – код сообщения мыши;

• lParam– указатель на структуру TMouseHookStruct.

Объявление структуры TMouseHookStruct с описанием полей выглядит следующим образом:

...

type TMouseHookStruct = packed record

pt: TPoint; //Экранные координаты указателя мыши

hwnd: HWND; //Дескриптор окна-получателя сообщения

wHitTestCode: UINT; //Код, возвращенный оконной функцией

//в ответ на сообщение WM_NCHITTEST

dwExtraInfo: DWORD; //Дополнительные данные

end;

Если вы забыли, какое значение имеет для окна coo6nreHHeWM_NCHITTEST, то можете вновь обратиться к гл. 1.

Расположение функции-ловушки и DLL

Теперь поговорим немного о расположении функции-ловушки.

Казалось бы, что здесь может быть такого: написал функцию в модуле, строго соответствующую приведенному ранее прототипу, передал ее адрес в функцию SetWindowsHookEx и используй ловушку. Но не так все просто. Функция ловушки может находиться в исполняемом файле только в том случае, если предполагается использовать ее для перехвата сообщений потока (потоков) того же процесса. Тогда в функцию создания ловушки в качестве параметра hmod следует передавать нулевое значение.

Если же предполагается слежение за другими приложениями (за потоками других процессов), то функция ловушки должна быть экспортируемой функцией DLL. Тогда в функцию SetWindowsHookEx передается дескриптор модуля DLL (похоже, что это адрес в адресном пространстве процесса, куда спроецирован файл DLL). Библиотека (DLL) может загружаться как при запуске приложения (если используется так называемое load-time связывание), так и динамически при помощи API-функции LoadLibrary:

...

function LoadLibrary(lpLibFileName: PChar): HMODULE; stdcall;

Функция принимает в качестве параметра путь DLL и возвращает дескриптор загруженного модуля (или 0 в случае ошибки). Если библиотека больше не нужна, то можно вызвать функцию FreeLibrary, передав в качестве единственного параметра возращенный ранее функцией LoadLibrary дескриптор модуля DLL

Возвращаясь к теме расположения ловушки зададимся вопросом: почему именно DLL? Чем плохо расположение ловушки в ЕХЕ-модуле приложения? Самое время вспомнить о том, что каждый процесс в Windows выполняется в своем собственном адресном пространстве. Поэтому адрес функции в исполняемом файле одного процесса вполне может быть адресом структуры данных где-то внутри другого процесса (рис. 10.1).

Рис. 10.1. Пример адресного пространства разных процессов

В отличие от ЕХЕ-файлов, файлы библиотек легко проецируются в адресное пространство использующего их процесса. Разместив функцию ловушки в DLL и указав дескриптор модуля этой DLL, мы предоставляем системе полную информацию для того, чтобы она смогла:

• спроецировать библиотеку с ловушкой в адресное пространство исследуемого процесса;

1 ... 39 40 41 42 43 44 45 46 47 ... 59
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Delphi. Трюки и эффекты - Валерий Борисок.

Оставить комментарий