Excel. Трюки и эффекты
Шрифт:
При обращении к форме f rmMessages, кроме загрузки фильтра, нужно произвести некоторые дополнительные действия. Поэтому работа с этой формой начинается так же, как и в случае формы свойств окна, с вызова ее специального метода (листинг 10.15).
Листинг 10.15.
Инициализация формы
procedure TfrmMessages.ShowMessages(wnd: HWND);
begin
self.wnd := wnd;
LoadFilter;
ShowModal;
end;
При нажатии кнопок > (выбрать) и < (отменить выбор) происходит перемещение сообщений между списками фильтра (листинг 10.16).
Листинг 10.16.
Перемещение
procedure TfrmMessages.cmbAddMessageClick(Sender: TObject);
var
i: Integer;
begin
if lstAvailMessages.SelCount = 0 then Exit;
//Включение выбранных сообщений в список перехватываемых
for i := lstAvailMessages.Count – 1 downto 0 do
if lstAvailMessages.Selected[i] then
messages_list[GetMessageIndex(i, False)].used := True;
//Отобразим изменения в списках
LoadFilter;
end;
procedure TfrmMessages.cmDelMessageClick(Sender: TObject);
var
i: Integer;
begin
if lstSelMessages.SelCount = 0 then Exit;
//Исключение выбранных сообщений из списка перехватываемых
for i := lstSelMessages.Count – 1 downto 0 do
if lstSelMessages.Selected[i] then
messages_list[GetMessageIndex(i, True)].used := False;
//Отобразим изменения в списках
LoadFilter;
end;
Функция GetMessagelndex, используемая в листинге 10.16, реализована следующим образом (листинг 10.17).
Листинг 10.17.
Преобразование номера сообщения в списке в номер сообщения в массиве messages_list
function TfrmMessages.GetMessageIndex(listIndex: Integer;
used: Boolean):Integer;
var
i, count: Integer;
begin
count := 0;
for i := mess_first to mess_last do
if messages_list[i].used = used then
begin
if count = listIndex then
begin
//Нашли
GetMessageIndex := i;
Exit;
end;
Inc(count);
end;
GetMessageIndex := 0;
end;
Теперь обратимся к реализации главной функции, выполняемой формой: использованию ловушки. Итак, слежение за выбранным в дереве окном (дескриптор его сохранен в поле wnd при инициализации формы) начинается и заканчивается при нажатии кнопки cmbStart. Обработчик нажатия этой кнопки приведен в листинге 10.18.
Листинг 10.18.
Запуск/остановка перехвата сообщений
procedure TfrmMessages.cmbStartClick(Sender: TObject);
begin
if cmbStart.Caption <> \'Остановить\' then
begin
//Начинаем слежение
lvwMessages.Clear;
//Создаем проекцию файла
hFile := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
PAGE_READWRITE,
0, SizeOf(THookInfo),
strFileMapName);
hook_info := MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0,
SizeOf(THookInfo));
//Создание ловушки
if InstallHook(wnd, frmMessages.Handle) then
cmbStart.Caption := \'Остановить\'
else
begin
//При ошибке удалим проекцию файла
UnmapViewOfFile(hook_info);
hook_info := nil;
CloseHandle(hFile);
hFile := 0;
MessageBox(Handle, \'Ошибка при создании ловушки\',
PAnsiChar(Application.Title), MB_ICONEXCLAMATION);
end;
end
else
begin
//Заканчиваем слежение (удаляем ловушку и проекцию файла)
RemoveHook;
UnmapViewOfFile(hook_info);
hook_info := nil;
CloseHandle(hFile);
hFile := 0;
cmbStart.Caption := \'Начать слежение\
end;
end;
Как можно увидеть, вся сложность на стороне приложения-шпиона состоит в создании/удалении проекции файла и
function InstallHook(wnd: HWND; spy: HWND): Boolean stdcall;
external \'hook\hook.dll\' name \'InstallHook\
function RemoveHook: Boolean stdcall;
external \'hook\hook.dll\' name \'RemoveHook\
Для обработки сообщения WM_SPY_NOTIFY, посылаемого ловушкой, переопределена оконная процедура формы f rmMessages (листинг 10.19).
Листинг 10.19.
Обработка сообщения WM_SPY_NOTIFY
procedure TfrmMessages.WndProc(var Message: TMessage);
var
item: TListItem;
i: Integer;
begin
if (Message.Msg = WM_SPY_NOTIFY) and (hook_info <> nil) then
begin
//Обрабатываем уведомление о приходе сообщения в наблюдае-
мое окно
for i := mess_first to mess_last do
if (messages_list[i].value = hook_info^.mess) and
messages_list[i].used then
begin
//Сообщение выбрано в фильтре – добавим запись в список
item := lvwMessages.Items.Add;
item.Caption := messages_list[i].name;
item.SubItems.Add(IntToStr(hook_info^.wParam));
item.SubItems.Add(IntToStr(hook_info^.lParam));
end;
end
else
inherited WndProc(Message);
end;
Ловушка
Теперь обратимся к реализации самой ловушки. По рассмотренным ранее причинам ловушка размещена в отдельной DLL (hook\hook.dll на прилагаемом к книге диске в папке с номером главы). На случай, если вы не знакомы с созданием DLL средствами Delphi, приведем краткие сведения.
Среда программирования Delphi замечательна тем, что позволяет просто делать довольно сложные вещи. Хотя и при использовании сред разработки, скрывающих меньшее количество сложных деталей, например Visual C++, создание DLL не является очень сложной задачей. Итак, для создания DLL в простейшем, то есть нашем, случае достаточно выполнить следующие действия.
1. Создать соответствующий проект (с помощью команды меню FiLe → New → Other, тип проекта – DLL Wizard) (рис. 10.6).
2. В DPR-файле получившегося проекта реализуем функции, которые предполагается экспортировать.
3. Объявляем, какие функции нужно экспортировать с помощью ключевого слова exports (листинг 10.20).
Рис. 10.6. Создание проекта DLL
Структура DLL ловушки, реализованной в нашем примере, приведена в листинге 10.20.
Листинг 10.20.
DLL ловушки без реализации функций
library hook;
uses
Windows,
HookData;
//****************************************************
//Экспортируемые функции
function InstallHook(wnd: HWND; spy: HWND): Boolean stdcall;
forward;
function RemoveHook: Boolean stdcall; forward;
exports
InstallHook,
RemoveHook;
//****************************************************
…
begin
hook_info := nil;
hFile := 0;
end.
Код после begin является кодом инициализации библиотеки (выполняется при загрузке DLL в память процесса). Правда, как показали многочисленные эксперименты, проведенные во время написания и отладки ловушки, код этот не выполняется при загрузке DLL ловушки в адресное пространство другого процесса.
Теперь обратимся к реализации экспортируемых функций InstallHook, а также RemoveHook. Как вы помните, только эти две функции вызываются из программы-шпиона. Начнем с функции установки ловушки (листинг 10.21).