Банк рефератов содержит более 364 тысяч рефератов, курсовых и дипломных работ, шпаргалок и докладов по различным дисциплинам: истории, психологии, экономике, менеджменту, философии, праву, экологии. А также изложения, сочинения по литературе, отчеты по практике, топики по английскому.
Полнотекстовый поиск
Всего работ:
364150
Теги названий
Разделы
Авиация и космонавтика (304)
Административное право (123)
Арбитражный процесс (23)
Архитектура (113)
Астрология (4)
Астрономия (4814)
Банковское дело (5227)
Безопасность жизнедеятельности (2616)
Биографии (3423)
Биология (4214)
Биология и химия (1518)
Биржевое дело (68)
Ботаника и сельское хоз-во (2836)
Бухгалтерский учет и аудит (8269)
Валютные отношения (50)
Ветеринария (50)
Военная кафедра (762)
ГДЗ (2)
География (5275)
Геодезия (30)
Геология (1222)
Геополитика (43)
Государство и право (20403)
Гражданское право и процесс (465)
Делопроизводство (19)
Деньги и кредит (108)
ЕГЭ (173)
Естествознание (96)
Журналистика (899)
ЗНО (54)
Зоология (34)
Издательское дело и полиграфия (476)
Инвестиции (106)
Иностранный язык (62792)
Информатика (3562)
Информатика, программирование (6444)
Исторические личности (2165)
История (21320)
История техники (766)
Кибернетика (64)
Коммуникации и связь (3145)
Компьютерные науки (60)
Косметология (17)
Краеведение и этнография (588)
Краткое содержание произведений (1000)
Криминалистика (106)
Криминология (48)
Криптология (3)
Кулинария (1167)
Культура и искусство (8485)
Культурология (537)
Литература : зарубежная (2044)
Литература и русский язык (11657)
Логика (532)
Логистика (21)
Маркетинг (7985)
Математика (3721)
Медицина, здоровье (10549)
Медицинские науки (88)
Международное публичное право (58)
Международное частное право (36)
Международные отношения (2257)
Менеджмент (12491)
Металлургия (91)
Москвоведение (797)
Музыка (1338)
Муниципальное право (24)
Налоги, налогообложение (214)
Наука и техника (1141)
Начертательная геометрия (3)
Оккультизм и уфология (8)
Остальные рефераты (21697)
Педагогика (7850)
Политология (3801)
Право (682)
Право, юриспруденция (2881)
Предпринимательство (475)
Прикладные науки (1)
Промышленность, производство (7100)
Психология (8694)
психология, педагогика (4121)
Радиоэлектроника (443)
Реклама (952)
Религия и мифология (2967)
Риторика (23)
Сексология (748)
Социология (4876)
Статистика (95)
Страхование (107)
Строительные науки (7)
Строительство (2004)
Схемотехника (15)
Таможенная система (663)
Теория государства и права (240)
Теория организации (39)
Теплотехника (25)
Технология (624)
Товароведение (16)
Транспорт (2652)
Трудовое право (136)
Туризм (90)
Уголовное право и процесс (406)
Управление (95)
Управленческие науки (24)
Физика (3463)
Физкультура и спорт (4482)
Философия (7216)
Финансовые науки (4592)
Финансы (5386)
Фотография (3)
Химия (2244)
Хозяйственное право (23)
Цифровые устройства (29)
Экологическое право (35)
Экология (4517)
Экономика (20645)
Экономико-математическое моделирование (666)
Экономическая география (119)
Экономическая теория (2573)
Этика (889)
Юриспруденция (288)
Языковедение (148)
Языкознание, филология (1140)

Реферат: Хуки и DLL

Название: Хуки и DLL
Раздел: Рефераты по информатике, программированию
Тип: реферат Добавлен 08:04:06 19 марта 2007 Похожие работы
Просмотров: 158 Комментариев: 2 Оценило: 0 человек Средний балл: 0 Оценка: неизвестно     Скачать

Хуки и DLL

Dr. Joseph M. Newcomer

Перевод: Алексей Остапенко

Существует большая неразбериха по поводу установки и использования глобальных хуков.

ПРИМЕЧАНИЕ

Возможно стоит упомянуть, что Камбалы (прим. переводчика: Flounder - псевдоним автора) в целом не одобряют использование крючков (hooks), но такие крючки (хуки) кажутся допустимыми.

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

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

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

Концепция разделенных адресных пространств трудна для понимания. Позвольте мне продемонстрировать ее на картинке.

Здесь мы имеем три процесса. Ваш Процесс показан слева (Your Process). У DLL есть сегменты кода (Code), данных (Data) и разделяемый сегмент (Shared), как его создать мы обсудим позже. Теперь, если перехватывающая DLL вызывается для перехвата события в Процессе A (Process A), она отображается в адресное пространство Процесса A, как указано. Код является разделяемым, поэтому адреса в Процессе A ссылаются на те же страницы памяти, что и адреса в Вашем Процессе. По совпадению страницы памяти оказались отображенными в Процесс A по тем же самым виртуальным адресам, т.е. адресам, которые видит Процесс A. Процесс A также получает свою собственную копию сегмента данных, поэтому все что видит Процесс A в секции "Data", полностью принадлежит ему и не может повлиять на любой другой процесс (или быть измененным любым другим процессом!). Однако, фокус который заставляет все это работать заключается в разделяемом сегменте данных, показанном здесь красным цветом. Страницы, адресуемые Вашим Процессом в точности те же страницы памяти, что и адресуемые в Процессе A. Заметим, что по совпадению эти страницы оказались в адресном пространстве Процесса A в точности на тех же виртуальных адресах, что и в Вашем Процессе. Если бы вы сидели за отладкой Вашего Процесса и Процесса A одновременно (что вы можете делать, запустив две копии VC++!) и смотрели бы по адресу &something, находившемуся в разделяемом сегменте данных, и смотрели бы по нему в Вашем Процессе и затем по тому же адресу &something в Процессе A, вы бы увидели в точности одни и те же данные и даже по тому же самому адресу. Если бы вы использовали отладчик для изменения или отслеживали изменения программой значения something, то вы могли бы перейти к другому процессу, исследовать его и увидеть, что новое значение появилось также и здесь.

Но вот облом: одинаковый адрес - это совпадение. Это совпадение абсолютно и однозначно не гарантируется. Посмотрите на Процесс B. Когда событие перехвачено в Процессе B, в него отображается DLL. Но адреса, занимаемые ею в Вашем Процессе и Процессе A, не доступны в адресном пространстве Процесса B. Поэтому происходит перемещение кода на другой адрес в Процессе B. Код в порядке; ему действительно безразлично по какому адресу он исполняется. Адреса данных подправлены так, чтобы ссылаться на новое положение данных, и даже разделяемые данные отображены в другое множество адресов, таким образом к ним обращаются по-другому. Если бы вы использовали отладчик с Процессом B и посмотрели бы на адрес &something в разделяемой области, вы бы обнаружили, что адрес something был бы другим, но содержимое something было бы тем же самым; выполнение изменения содержимого в Вашем Процессе или в Процессе A немедленно сделало бы это изменение видимым в Процессе B, хотя Процесс B и видит его по другому адресу. Это то же самое место физической памяти. Виртуальная память - это отображение между адресами, видимыми вами, как программистом, и физическими страницами памяти, которые в действительности содержит ваш компьютер.

Хотя я и назвал одинаковое расположение совпадением, "совпадение" частично умышленно; Windows пытается отображать библиотеки в те же самые виртуальные области, что и у других экземпляров одной и той же библиотеки, всякий раз, когда это возможно. Она пытается. Ей может не удаться это сделать.

ПРИМЕЧАНИЕ

Если вы знаете немного больше (достаточно, чтобы это представляло опасность), вы можете сказать: "Ага! Я могу переместить (rebase) мою DLL так, что она загружается по не конфликтующему адресу, и я смогу проигнорировать эту особенность". Это отличный пример того, как малые знания могут представлять серьезную опасность. Вы не можете гарантировать что такая DLL будет работать с любым возможным исполняемым модулем, который может быть когда-либо запущен на вашей машине! Поскольку это DLL глобального хука, она может быть вызвана из Word, Excel, Visio, VC++ и шести тысяч приложений, о которых вы никогда не слышали, но можете когда-либо запустить или может запустить ваш клиент. Поэтому забудьте об этом. Не пытайтесь перемещать. В конце концов, вы проиграете. Обычно, в самое неподходящее время с самым важным вашим клиентом (например, с обозревателем вашего продукта из журнала или с вашим очень богатым заказчиком, который уже обеспокоен другими ошибками, которые у вас могут быть...). Считайте, что разделяемый сегмент данных "перемещаем". Если вы не понимаете этот параграф, значит вы знаете недостаточно много, чтобы это представляло опасность. И вы можете спокойно его проигнорировать.

У перемещения есть и другие последствия. Если в DLL вы сохранили указатель на callback-функцию в контексте Вашего Процесса, то для DLL бессмысленно вызывать ее в Процессе A или Процессе B. Этот адрес приведет к передаче управления в указываемую им область, что нормально, но эта передача произойдет в адресное пространство Процесса A или Процесса B, что совершенно бесполезно, не говоря уже о том, что почти наверняка фатально.

Это также означает, что вы не можете использовать в своей DLL ничего из MFC. Она не может быть ни MFC DLL, ни MFC Extension DLL. Почему? Потому, что она будет вызывать функции MFC. А где они? Ну, они в вашем адресном пространстве. А не в адресном пространстве Процесса A, написанного на Visual Basic, или Процесса B, написанного на Java. Таким образом, вы должны написать DLL на чистом C, и я бы рекомендовал совсем не использовать библиотеку времени исполнения C (CRT). Вы должны использовать только API. Используйте lstrcpy вместо strcpy или tcscpy, lstrcmp вместо strcmp или tcscmp, и т.д.

Существует множество решений для организации взаимодействия вашей DLL и ее управляющего сервера. Одно из решений заключается в использовании ::PostMessage или ::SendMessage (заметим, что здесь я ссылаюсь на вызовы чистого API, а не вызовы MFC!). Там, где возможно использовать вызов ::PostMessage, лучше используйте его, а не ::SendMessage, т.к. иначе вы можете получить опасные тупиковые ситуации. Если Ваш Процесс в итоге останавливается, все остальные процессы в системе остановятся, т.к. все заблокированы на вызове ::SendMessage, который никогда не возвратится, и вы просто вывели всю систему из строя с возможностью серьезной потери данных в важных для пользователя приложениях. Это Совершенно Однозначно Не Хорошая Ситуация.

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

Вы не можете возвратить указатель из вызовов ::SendMessage и ::PostMessage (мы забудем про возможность передавать обратно относительные указатели в разделяемую область памяти; это также выходит за рамки этой статьи). Это из-за того, что любой указатель, который вы можете создать, будет ссылаться либо на адрес в DLL (перемещенной в перехваченный процесс), либо на адрес в перехваченном процессе (Процессе A или Процессе B), и, следовательно, он будет абсолютно бесполезен в Вашем Процессе. Вы можете возвращать лишь адресно-независимую информацию в WPARAM или LPARAM.

Я сильно рекомендую использовать для таких целей Зарегистрированные Оконные Сообщения (смотрите мой обзор по Управлению Сообщениями ). Вы можете использовать макрос ON_REGISTERED_MESSAGE в MESSAGE_MAP окна, которому вы отсылаете сообщение.

Основным требованием теперь является получение HWND этого окна. К счастью, это несложно.

Первое, что вы должны сделать - это создать разделяемый сегмент данных. Это делается при помощи объявления #pragma data_seg. Выберите какое-либо хорошее мнемоническое имя для сегмента данных (оно должно быть не длиннее 8 символов). Просто чтобы подчеркнуть произвольность имени, я использовал здесь свое собственное имя. Во время преподавания я обнаружил, что если я использую имена вида .SHARE или .SHR, или .SHRDATA, то студенты полагают, что имя имеет значение. А оно не имеет значения.

#pragma data_seg(".JOE")

HANDLE hWnd = NULL;

#pragma dta_seg()

#pragma comment(linker, "/section:.JOE,rws")

Любые переменные, объявленные вами в области действия #pragma, определяющей сегмент данных, будут размещены в этом сегменте данных, при условии, что они инициализированы. Если вы не укажете инициализатор, переменные будут размещены в сегменте данных по умолчанию, и #pragma не имеет силы.

ПРИМЕЧАНИЕ

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

Директива #pragma comment вызывает добавление указанного ключа к командной строке компоновщика на этапе связывания. Вы могли бы использовать Project | Settings в VC++ и изменить командную строку компоновщика, однако трудно помнить про необходимость такого действия, когда вы перемещаете код с места на место (и обычная ошибка - забыть выбрать All Configurations при изменении установок и, таким образом, успешно отлаживать, но получить сбой в конфигурации Release). Итак, я обнаружил, что лучше всего помещать команду непосредственно в исходном файле. Заметим, что используемый текст должен соответствовать синтаксису командного ключа компоновщика. Это означает, что вы не должны включать в указанный текст пробелы, иначе компоновщик не обработает его должным образом.

Обычно вы предоставляете некоторый механизм для установки дескриптора окна. Например,

void SetWindow(HWND w)

{

hWnd = w;

}

хотя эта операция, как я покажу далее, часто совмещена с собственно установкой хука.

Пример: Мышиный Хук

заголовочный файл (myhook.h)

Здесь должны быть объявлены функции setMyHook и clearMyHook, но это требование разъяснено в моем очерке The Ultimate DLL Header File.

#define UWM_MOUSEHOOK_MSG \

_T("UMW_MOUSEHOOK-" \

"{B30856F0-D3DD-11d4-A00B-006067718D04}")

исходный файл (myhook.cpp)

#include "stdafx.h"

#include "myhook.h"

#pragma data_seg(".JOE")

HWND hWndServer = NULL;

#pragma data_seg()

#pragma comment("linker, /section:.JOE,rws")

HINSTANCE hInstance;

UINT HWM_MOUSEHOOK;

HHOOK hook;

// опережающееобъявление

static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam);

/****************************************************************

* DllMain

* Вход:

* HINSTANCE hInst: Дескрипторэкземпляра DLL

* DWORD Reason: причинавызова

* LPVOID reserved: зарезервировано

* Выход: BOOL

* TRUE при успешном завершении

* FALSE при наличии ошибок (не возвращается никогда)

* Действие:

* инициализация DLL.

****************************************************************/

BOOL DllMain(HINSTANCE hInst, DWORD Reason, LPVOID reserved)

{

switch(Reason)

{ /* причина */

//**********************************************

// PROCESS_ATTACH

//**********************************************

case DLL_PROCESS_ATTACH:

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

hInstance = hInst;

// Данный код инициализирует сообщение уведомления хука

UWM_MOUSEHOOK = RegisterWindowMessage(UWM_MOUSEHOOK_MSG);

return TRUE;

//**********************************************

// PROCESS_DETACH

//**********************************************

case DLL_PROCESS_DETACH:

// Если сервер не снял хук, снимем его, т.к. мы выгружаемся

if(hWndServer != NULL)

clearMyHook(hWndServer);

return TRUE;

} /* причина */

}

/****************************************************************

* setMyHook

* Вход:

* HWND hWnd: Окно, чей хук предстоит поставить

* Выход: BOOL

* TRUE если хук успешно поставлен

* FALSE если произошла ошибка, например, если хук

* уже был установлен

* Действие:

* Устанавливает хук для указанного окна

* Сначала устанавливает хук перехватывающий сообщения (WH_GETMESSAGE)

* Если установка прошла успешно, hWnd устанавливается в качестве

* окнасервера.

****************************************************************/

__declspec(dllexport) BOOL WINAPI setMyHook(HWND hWnd)

{

if(hWndServer != NULL)

return FALSE;

hook = SetWindowsHookEx(

WH_GETMESSAGE,

(HOOKPROC)msghook,

hInstance,

0);

if(hook != NULL)

{ /* удача */

hWndServer = hWnd;

return TRUE;

} /* удача */

return FALSE;

} // SetMyHook

/****************************************************************

* clearMyHook

* Вход:

* HWND hWnd: Окно, чей хук должен быть снят

* Выход: BOOL

* TRUE если хук успешно снят

* FALSE если вы передали неверный параметр

* Действие:

* Снимает установленный хук.

****************************************************************/

__declspec(dllexport) BOOL clearMyHook(HWND hWnd)

{

if(hWnd != hWndServer)

return FALSE;

BOOL unhooked = UnhookWindowsHookEx(hook);

if(unhooked)

hWndServer = NULL;

return unhooked;

}

/****************************************************************

* msghook

* Вход:

* int nCode: Значениекода

* WPARAM wParam: параметр

* LPARAM lParam: параметр

* Выход: LRESULT

*

* Действие:

* Если сообщение является сообщением о перемещении мыши, отправляет его

* окну сервера с координатами мыши

* Замечания:

* Функция должна быть CALLBACK-функцией, или она не будет работать!

****************************************************************/

static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam)

{

// If the value of nCode is < 0, just pass it on and return 0

// this is required by the specification of hook handlers

// Если значение nCode < 0, просто передаем его дальше и возвращаем 0

// этого требует спецификация обработчиков хуков

if(nCode < 0)

{ /* передаем дальше */

CallNextHookEx(hook, nCode,

wParam, lParam);

return 0;

} /* передаем дальше */

// Прочитайте документацию, чтобы выяснить смысл параметров WPARAM и LPARAM

// Для хука WH_MESSAGE, LPARAM определяется как указатель на структуру MSG,

// таким образом следующий код делает эту структуру доступной

LPMSG msg = (LPMSG)lParam;

// Если это сообщение о перемещении мыши, либо в клиентской (client), либо

// в не клиентской (non-client) области, мы хотим уведомить родителя о его

// возникновении. Заметим, что вместо SendMessage используется PostMessage

if(msg->message == WM_MOUSEMOVE ||

msg->message == WM_NCMOUSEMOVE)

PostMessage(hWndServer,

UWM_MOUSEMOVE,

0, 0);

// Передаем сообщение следующему хуку

return CallNextHookEx(hook, nCode,

wParam, lParam);

} // msghook

Приложение сервера

В заголовочном файле добавьте следующее в секцию protected класса:

afx_msg LRESULT OnMyMouseMove(WPARAM,LPARAM);

В фале приложения добавьте это где-нибудь в начале файла:

UINT UWM_MOUSEMOVE = ::RegisterWindowMessage(UWM_MOUSEMOVE_MSG);

Добавьте следующее в MESSAGE_MAP вне специальных комментариев //{AFX_MSG:

ON_REGISTERED_MESSAGE(UWM_MOUSEMOVE, OnMyMouseMove)

В файл приложения добавьте следующую функцию:

LRESULT CMyClass::OnMyMouseMove(WPARAM, LPARAM)

{

// ...тут что-то делаем

return 0;

}

Я написал небольшой пример для демонстрации, но поскольку я утомился создавать функцию глобального хука в n+1 раз, я сделал ему отличный пользовательский интерфейс. Кот смотрит из окна и следит за мышью. Но будьте осторожны! Подойдите достаточно близко к коту, и он схватит мышь!

Вы можете скачать этот проект и собрать его. Ключевое значение имеет подпроект DLL; остальное - это использующая ее декоративная мишура.

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

Оценить/Добавить комментарий
Имя
Оценка
Комментарии:
Где скачать еще рефератов? Здесь: letsdoit777.blogspot.com
Евгений21:50:50 18 марта 2016
Кто еще хочет зарабатывать от 9000 рублей в день "Чистых Денег"? Узнайте как: business1777.blogspot.com ! Cпециально для студентов!
15:28:25 24 ноября 2015

Работы, похожие на Реферат: Хуки и DLL

Назад
Меню
Главная
Рефераты
Благодарности
Опрос
Станете ли вы заказывать работу за деньги, если не найдете ее в Интернете?

Да, в любом случае.
Да, но только в случае крайней необходимости.
Возможно, в зависимости от цены.
Нет, напишу его сам.
Нет, забью.



Результаты(151311)
Комментарии (1844)
Copyright © 2005-2016 BestReferat.ru bestreferat@mail.ru       реклама на сайте

Рейтинг@Mail.ru