Электронная библиотека » Александр Чиртик » » онлайн чтение - страница 19


  • Текст добавлен: 10 ноября 2013, 00:08


Автор книги: Александр Чиртик


Жанр: Программирование, Компьютеры


сообщить о неприемлемом содержимом

Текущая страница: 19 (всего у книги 24 страниц)

Шрифт:
- 100% +
Изменение оконных стилей

Изменять стили окна «на лету» ничуть не сложнее, чем их определять: с помощью API-функций GetWindowLong и SetWindowsLong. Пример кода добавления флага, обозначение которого выбрано в списке доступных стилей, приведен в листинге 10.9.

Листинг 10.9. Добавление оконного стиля

procedure TfrmWindowProp.cmbAddStyleClick(Sender: TObject);

var

style: DWORD;

addstyle: DWORD;

begin

if lstAvailStyle.ItemIndex = –1 then Exit;

//Удаление выбранного стиля окна

//..определяем, какой стиль удалить

addstyle:=

styles[GetStyleIndex(lstAvailStyle.ItemIndex, False)].value;

//..вычисляем и устанавливаем новое значение стиля окна

style:= GetWindowLong(wnd, GWL_STYLE);

style:= style or addstyle;

SetWindowLong(wnd, GWL_STYLE, style);

//..перерисуем все окна

InvalidateRect(0, nil, True);

//Обновим список стилей окна

LoadWindowStyle();

end;


Удаление флага стиля производится аналогично добавлению – просто к битам стиля окна применяется другая операция (листинг 10.10).

Листинг 10.10. Удаление оконного стиля

procedure TfrmWindowProp.cmbDelStyleClick(Sender: TObject);

var

style: DWORD;

delstyle: DWORD;

begin

if lstStyle.ItemIndex = –1 then Exit;

//Удаление выбранного стиля окна

//..определяем, какой стиль удалить

delstyle:= styles[GetStyleIndex(lstStyle.ItemIndex, True)].value;

//..вычисляем и устанавливаем новое значение стиля окна

style:= GetWindowLong(wnd, GWL_STYLE);

style:= style and not delstyle;

SetWindowLong(wnd, GWL_STYLE, style);

//..перерисуем все окна

InvalidateRect(0, nil, True);

//Обновим список стилей окна

LoadWindowStyle();

end;


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

Удаление и добавление дополнительных (расширенных) оконных стилей выполняется аналогично, только при этом используется массив exstyles, функция GetExStylelndex и константа GWL_EXSTYLE, передаваемая в функции GetWindowLong и SetWindowLong.

Что же за функция GetStylelndex, используемая в листинге 10.10? Эта функция позволяет определять положение в массиве styles стиля, выбранного в списке доступных или используемых стилей (верхний список) (листинг 10.11).

Листинг 10.11. Определение положения записи о нужном стиле

function TfrmWindowProp.GetStyleIndex(listIndex: Integer; used: Boolean)

:Integer;

var

i, count: Integer;

begin

count:= 0;

for i:= 0 to 17 do

if styles[i].used = used then

begin

if count = listIndex then

begin

//Нашли

GetStyleIndex:= i;

Exit;

end;

Inc(count);

end;

GetStyleIndex:= 0;

end;


Функция GetStyleIndex принимает в качестве своих параметров номер строки в соответствующем списке и логическое значение, от истинности или ложности которого зависит, используемые или неиспользуемые стили будут подсчитываться внутри функции.

Применение функции GetStyleIndex и введение в структуру StyleInfo поля used несколько усложняет алгоритм работы с массивом стилей, однако позволяет избавиться от постоянного перемещения данных, например, из массива доступных стилей в массив используемых. К тому же, в противном случае пришлось бы использовать по два массива для обычных и дополнительных оконных стилей.

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

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

Рис. 10.5. Форма перехвата сообщений


Показанная на рис. 10.5 форма носит имя frmMessages.

Перехватчик сообщений состоит из двух частей: части программы (EXE), отвечающей за построение фильтра сообщений и обработку перехваченных сообщений, и ловушки, заключенной в DLL (hookhook.dll).

Взаимодействие ловушки и EXE-файла построено по следующей схеме.

1. Из приложения вызываются функции создания и удаления ловушки (расположенные в DLL).

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

Однако ловушка ведь предназначена для работы в другом процессе, а если так, то как ей дать знать, какому именно окну посылать сообщения? Для этого и используется именованная проекция файла в память, в которой сохраняются данные, необходимые для ловушки. В проекции же файла ловушка также сохраняет информацию о перехваченном сообщении (код и параметры сообщения). Эта информация используется приложением, ведущим слежение.

Данные в проекции файла хранятся в виде записи THookInfo, объявленной в модуле HookData. В этом же модуле объявлена константа с именем проекции файла, код сообщения WM_SPY_NOTIFY (листинг 10.12) и две служебные переменные, использование которых будет пояснено позже.

Листинг 10.12. Содержимое файла HookData.pas

type

//Структура (запись), которая хранится в разделяемом файле и

//используется для передачи данных между процессами

THookInfo = record

wnd: HWND; //Окно, за которым ведется наблюдение

hook_handle: HHOOK; //Дескриптор ловушки

spy_wnd: HWND; //Окно, уведомляемое о перехвате сообщения

//Следующие поля заполняются при перехвате сообщения

mess: UINT;

wParam: WPARAM;

lParam: LPARAM;

end;

var

//Указатель на разделяемую область памяти

hook_info: ^THookInfo;

//Дескриптор проекции файла в память

hFile: THandle;

const

//Имя япроекции иифайла

strFileMapName = 'TricksDelphi_WinSpy_Mapping';

//Сообщение для уведомления окна-шпиона

WM_SPY_NOTIFY = WM_USER + 1;


Построение фильтра и обработка перехваченных сообщений

Теперь вернемся к приложению-шпиону, а точнее, к той его части, которая отвечает за работу формы, показанной на рис. 10.5.

Начнем с самого простого – управления фильтром сообщений. Оно построено по тому же принципу, что и управление списками оконных стилей (форма свойств окна, рассмотренная ранее).

Структура, хранящая информацию о сообщении, выглядит следующим образом:


type MessageInfo = record

value: DWORD; //Код сообщения

name: String; //Название сообщения

used: Boolean; //Служебное поле

end;


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

Листинг 10.13. Сообщения, поддерживаемые программой

const

mess_firsttt= 0;

mess_lasttt= 15;

var

messages_list: array[ mess_first .. mess_last] ofMessageInfoo=

(

(value: WM_DESTROY; name: 'WM_DESTROY'; used: False),

(value: WM_MOVE; name: 'WM_MOVE'; used: False),

(value: WM_SIZE; name: 'WM_SIZE'; used: False),

(value: WM_ACTIVATE; name: 'WM_ACTIVATE'; used: False),

(value: WM_SETFOCUS; name: 'WM_SETFOCUS'; used: False),

(value: WM_KILLFOCUS; name: 'WM_KILLFOCUS'; used: False),

(value: WM_ENABLE; name: 'WM_ENABLE'; used: False),

(value: WM_SETTEXT; name: 'WM_SETTEXT'; used: False),

(value: WM_GETTEXT; name: 'WM_GETTEXT'; used: False),

(value: WM_PAINT; name: 'WM_PAINT'; used: False),

(value: WM_CLOSE; name: 'WM_CLOSE'; used: False),

(value: WM_QUIT; name: 'WM_QUIT'; used: False),

(value: WM_SIZING; name: 'WM_SIZING'; used: False),

(value: WM_MOVING; name: 'WM_MOVING'; used: False),

(value: WM_NOTIFY; name: 'WM_NOTIFY'; used: False),

(value: WM_NCHITTEST; name: 'WM_NCHITTEST'; used: False)

);


Загрузка фильтра (выбранных и невыбранных сообщений в соответствующие списки) выполняется достаточно просто (листинг 10.14).

Листинг 10.14. Загрузка фильтра сообщений

proceduree TfrmMessages.LoadFilter();

var

i: Integer;

begin

//Загрузка фильтра сообщений

lstAvailMessages.Clear();

lstSelMessages.Clear();

for i:= mess_first to mess_last do

if messages_list[i].used then

//Сообщение перехватывается

lstSelMessages.Items.Add(messages_list[i].name)

else

lstAvailMessages.Items.Add(messages_list[i].name);

end;


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

Листинг 10.15. Инициализация формы

procedureTfrmMessages.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;


Реализация функции GetMessageIndex, используемой в листинге 10.16, приведена в листинге 10.17.

Листинг 10.17. Функция GetMessageIndex

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. Запуск и остановка перехвата сообщений

procedureTfrmMessages.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;


Как можно увидеть из приведенного кода, вся сложность на стороне приложения-шпиона состоит в создании и удалении проекции файла, а также в вызове двух экспортируемых из библиотеки hook.dll функций. Эти функции подключаются следующим объявлением:


function InstallHook(wnd: HWND; spy: HWND): Boolean stdcall;

external 'hookhook.dll'name 'InstallHook';

function RemoveHook(): Boolean stdcall;

external 'hookhook.dll'name 'RemoveHook';


Для обработки сообщения WM_SPY_NOTIFY, посылаемого ловушкой, переопределена оконная процедура формы frmMessages (листинг 10.19).

Листинг 10.19. Обработка сообщения WMSPYNOTIFY

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;


Ловушка

Теперь обратимся к реализации самой ловушки. По рассмотренным ранее причинам ловушка размещена в отдельной библиотеке (hookhook. dll на прилагаемом к книге диске в папке с номером главы). Если вы не знакомы с созданием DLL средствами Delphi, ознакомьтесь с приведенными далее краткими сведениями.

Среда программирования Delphi замечательна тем, что позволяет простым образом выполнять довольно сложные вещи (правда, и при использовании сред разработки, скрывающих меньшее количество сложных деталей, например, Visual C++, создание DLL не является особенно сложной задачей).

Итак, для создания DLL в простейшем, то есть данном случае, достаточно выполнить следующие действия.

1. Создать соответствующий проект (откройте меню File ► New ► Other и на вкладке New выберите тип проекта DLL Wizard) (рис. 10.6).

Рис. 10.6. Создание проекта DLL


2. В DPR-файле получившегося проекта реализовать функции, которые предполагается экспортировать.

3. Объявить функции, которые нужно экспортировать с помощью ключевого слова exports (листинг 10.20).

Структура 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;

hFilee e:= 0;

end.


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

Теперь обратимся к реализации экспортируемых функций InstallHook и RemoveHook. Как вы помните, только эти две функции вызываются из программы-шпиона. Функция установки ловушки представлена в листинге 10.21.

Листинг 10.21. Установка (создание) ловушки

function InstallHook (wnd: HWND; spy: HWND): Booleanstdcall;

begin

//Открываем проекцию файла (области файла подкачки)

if not GetFileMapping() then

begin

//Не удалось спроецировать файл в память

InstallHook:= False;

Exit;

end;

//Сохраняем данные, необходимые для работы ловушки

hook_info^.wnd:= wnd;

hook_info^.spy_wnd:= spy;

//Создаем ловушку

if (GetWindowThreadProcessId(wnd) <> 0)

then

hook_info^.hook_handle:=

SetWindowsHookEx(WH_CALLWNDPROC, WndProcHook,

hInstance,, GetWindowThreadProcessId(wnd))

else

//Создание ловушки для потоков данного приложения было бы фатальным

hook_info^.hook_handle:= 0;

InstallHook:= hook_info^.hook_handle <> 0;

//Освободим проекцию файла

ReleaseFileMapping();

end;


Функция InstallHook использует глобальную переменную-указатель hook_info, которая объявлена в модуле HookData. Функция GetFileMapping, также используемая в листинге 10.21, связывает указатель hook_info с областью памяти, на которую проецируется файл. Соответственно, процедура ReleaseFileMapping отменяет проецирование файла в память (после этого использовать указатель hook_info нельзя).

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

Работа с проецируемым файлом в ловушке будет рассмотрена чуть позже. Сейчас же обратимся к функции удаления ловушки, реализация которой приведена в листинге 10.22.

Листинг 10.22. Удаление ловушки

function RemoveHook(): Boolean stdcall;

begin

if GetFileMapping() then

begin

if hook_info^.hook_handle <> 0 then

//Удаляем ловушку

UnhookWindowsHookEx(hook_info^.hook_handle);

//Закрываем проекцию файла

ReleaseFileMapping();

RemoveHook:= True;

end

else

RemoveHook:= False;

end;


Здесь все просто и не требует подробного пояснения. Далее рассмотрим так часто используемые функцию GetFileMapping и процедуру ReleaseFileMapping, работающие с проекцией файла в память. Данная функция, код которой приведен в листинге 10.23, открывает проекцию файла в память и связывает указатель hook_ info с областью памяти, отведенной для проекции файла.

Листинг 10.23. Открытие проекции файла

function GetFileMapping(): Boolean;

begin

//Пытаемся открыть проекцию файла

hFile:= OpenFileMapping(FILE_MAP_WRITE, False,

PAnsiChar(strFileMapName));

//Получаем адрес разделяемой памяти

hook_info:= MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0,

SizeOf(THookInfo));

GetFileMapping:= hook_info <> nil;

end;


Способ реализации процедуры ReleaseFileMapping, обратной по своему назначению функции GetFileMapping, показан в листинге 10.24.

Листинг 10.24. Освобождение проекции файла

procedure ReleaseFileMapping();

begin

UnmapViewOfFile(hook_info);

hook_info:= nil;

CloseHandle(hFile);

hFile:= 0;

end;


Функция GetFileMapping и процедура ReleaseFileMapping используют дополнительно глобальную переменную hFile (тип THandle), объявленную в модуле HookData.

Наконец, пришла очередь рассмотреть функцию-ловушку. Код ее реализации приведен в листинге 10.25.

Листинг 10.25. Функция-ловушка

function WndProcHook(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;

var

hook_data: ^TCWPStruct;

begin

//Получим доступ к проекции файла

if not GetFileMapping() then

begin

//Не удалось получить доступ к проекции файла. Ценой потери

//сообщений не дадим возникнуть ошибкам доступа к памяти

WndProcHook:= 0;

Exit;

end;

if code < 0 then

begin

WndProcHook:= CallNextHookEx(hook_info^.hook_handle, code, wParam,

lParam);

//Освободим проекцию файла

ReleaseFileMapping();

Exit;

end;

//Можно обрабатывать сообщение

hook_data:= Pointer(lParam);

//Обрабатываем только сообщения нужного окна

if hook_data^.hwnd = hook_info^.wnd then

begin

//Заполняем поля структуры в общей области памяти и посылаем

//сообщение окну-шпиону

hook_info^.mess:= hook_data^.message;

hook_info^.wParam:= hook_data^.wParam;

hook_info^.lParam:= hook_data^.lParam;

PostMessage(hook_info^.spy_wnd, WM_SPY_NOTIFY, 0, 0);

end;

//Передаем сообщение для дальнейшей обработки

WndProcHook:= CallNextHookEx(hook_info^.hook_handle, code, wParam,

lParam);

//Освободим проекцию файла

ReleaseFileMapping();

end;


Код функции WndProc достаточно прост, поэтому подробно его работа рассматриваться не будет. Вместо этого поясню, для чего все же GetFileMapping и ReleaseFileMapping вызываются при обработке каждого перехваченного сообщения.

Дело в том, что загрузка DLL в адресное пространство другого процесса отличается от штатной загрузки библиотеки, например, с помощью функции LoadLibrary: не вызывается код инициализации. Следовательно, невозможно, например, обнулить указатель hook_info или установить какой-либо другой признак того, была ли открыта проекция файла. Велика вероятность того, что без отсутствия ручной инициализации указатель hook_info не будет равен нулю. Как тогда определить, связан ли этот указатель с областью памяти, в которую спроецирован файл?

Можно было бы, конечно, завести 64– или более битную переменную, которой присваивалось бы «магическое» число при первой инициализации указателя hook_ info. Однако в таком случае работоспособность данной программы носила бы вероятностный характер. Речь не идет о том, что в приведенном примере ловушка реализована оптимальным образом, просто альтернатива с использованием GetFileMapping и ReleaseFileMapping при написании программы показалась наиболее простой и легко поддающейся объяснению.

Глава 11
Сетевое взаимодействие

• Краткое описание сетевых компонентов

• Простой обмен данными

• Слежение за компьютером по сети

• Многопользовательский разговорник


Организация надежного сетевого взаимодействия между приложениями или компонентами одного приложения зачастую является задачей довольно сложной даже для программиста со значительным опытом работы. Это правда, если пытаться самостоятельно использовать API сетевого взаимодействия, предоставляемый операционной системой (в данном случае – Windows). Однако при использовании компонентов Delphi, в которых уже реализованы рутинные операции по созданию соединений, пересылке данных, контролю ошибок и т. д., программирование сетевых приложений становится не только простым, но и увлекательным занятием.

В данной главе будет рассмотрено несколько примеров создания несложных сетевых приложений, построенных с использованием архитектуры «клиент-сервер».


Страницы книги >> Предыдущая | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Следующая
  • 0 Оценок: 0

Правообладателям!

Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.


Популярные книги за неделю


Рекомендации