FreeBASIC в России

   

 Главная   Исходники   Русскоязычный форум   Полезные файлы   Пользователи   Поиск

 

 FB Wiki   Уроки   Скачать FreeBASIC   Проекты   Ссылки   Загрузить   Каталог ссылок

 
 

 Замечания и предложения по работе сайта оставляйте здесь.

Сейчас на сайте:

Вы вошли как: Guest   Регистрация   

Логин:     Пароль:     

Создай бесплатно WebMoney кошелёк на своём мобильном всего за 5 минут

 

Делаем dll своими руками

Хэндл окна

Графические средства языка

Точка пересечения отрезков

Окна без рамки

Физика поведения ядра

Win32 API Введение

►Урок 1 Win32 API

►Урок 2 Win32 API

►Урок 3 Win32 API

►Урок 4 Win32 API

►Урок 5 Win32 API

►Урок 6 Win32 API

►Урок 7 Win32 API

►Урок 8 Win32 API

►Урок 9 Win32 API

►Урок 10 Win32 API

►Урок 11 Win32 API

►Урок 12 Win32 API

►Урок 13 Win32 API

►Урок 14 Win32 API

►Урок 15 Win32 API

►Урок 16 Win32 API

►Урок 17 Win32 API

►Урок 18 Win32 API

►Урок 19 Win32 API

►Урок 20 Win32 API

►Урок 21 Win32 API

►Урок 22 Win32 API

►Урок 23 Win32 API

►Урок 24 Win32 API

Ассоциация файлов

Определить OS

Выводит имя компьютера

 

 

 

Win32 API. Урок 24.  Windows-хуки.

Опытом поделился и оптимизировал под FreeBASIC:   15.01.2011

В этом туториале мы изучим хуки. Это очень мощная техника. С их помощью вы сможете вмешиваться в другие процессы и иногда менять их поведение.

Теория:

Хуки Windows можно считать одной из самых мощных техник. С их помощью вы можете перехватывать события, которые случатся внутри созданного вами или кем-то другим процесса. Перехватывая что-либо, вы сообщаете Windows о фильтрующей функции, также называющейся функцией перехвата, которая будет вызываться каждый раз, когда будет происходить интересующее вас событие. Есть два вида хуков: локальные и удаленные.

• Локальные хуки перехватывают события, которые случаются в процессе, созданном вами.
• Удаленные хуки перехватывают события, которые случаются в других процессах. Есть два вида удаленных хуков:

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

При установке хуков, помните, что они оказывают отрицательное воздействие на быстродействие системы. Особенно в этом отличаются системные. Так как все требуемые события будут проходить через вашу функцию, ваша система может значительно потерять в быстродействии.

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

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

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

Существует 14 типов хуков:

• WH_CALLWNDPROC - хук вызывается при вызове SendMessage.
• WH_CALLWNDPROCRET - хук вызывается, когда возвращается SendMessage.
• WH_GETMESSAGE - хук вызывается, когда вызывается GetMessage или peekMessage.
• WH_KEYBOARD - хук вызывается, когда GetMessage или PeekMessage получают WM_KEYUP или WM_KEYDOWN из очереди сообщений.
• WH_MOUSE - хук вызывается, когда GetMessage или peekMessage получают сообщение от мыши из очереди сообщений.
• WH_HADRWARE - хук вызывается, когда GetMessage или peekMessage получают хардварное сообщение, не относящееся к клавиатуре или мыши.
• WH_MSGFILTER - хук вызывается, когда диалоговое окно, меню или скролбар готовятся к обработке сообщения. Этот хук - локальный. Он создан специально для тех объектов, у которых свой внутренний цикл сообщений.
• WH_SYSMSGFILTER - то же самое WH_MSGFILTER, но системный.
• WH_JOURNALRECORD - хук вызывается, когда Windows получает сообщение из очереди хардварных сообщений.
• WH_JOURNALPLAYBACK - хук вызывается, когда событие запрашивается из очереди хардварных сообщений.
• WH_SHELL - хук вызывается, когда происходит что-то интересное и связанное с оболочкой, например, когда таскбару нужно перерисовать кнопку.
• WH_CBN - хук используется специально для CBT.
• WH_FOREGROUND - такие хуки используются Windows. Обычным приложениям от них пользы немного.
• WH_DEBUG - хук используется для отладки хук-процедуры.

Теперь, когда мы немного подучили теорию, мы можем перейти к тому, как, собственно, устанавливать/снимать хуки.

Чтобы установить хук, вам нужно вызвать функцию SetWindowsHookEx, имеющую следующий синтаксис:

function SetWindowsHookEx _
(byval HookType as integer, _
byval pHookproc as HOOKPROC, _
byval hInstance as HINSTANCE, _
byval ThreadID as DWORD) as HHOOK

• HookType - это одно из значений, перечисленных выше (WH_MOUSE, WH_KEYBOARD и т.п.).
• pHookproc - это адрес хук-процедуры, которая будет вызвана для обработки сообщений от хука. Если хук является удаленным, он должен находиться в DLL. Если нет, то он должен быть внутри процесса.
• hInstance - это хэндл DLL, в которой находится хук-процедура. Если хук локальный, тогда это значение должно быть равно NULL.
• ThreadID - это ID треда, на который вы хотите поставить хук. Этот параметр определяет является ли хук локальным или удаленным. Если этот параметр равен NULL, Windows будет считать хук системным и удаленным, который затрагивает все треды в системе. Если вы укажете ID одного из тредов вашего собственного процесса, хук будет локальным. Если вы укажете ID треда из другого процесса, то хук будет тредоспециализированным и удаленным. Из этого правила есть два исключения: WH_JOURNALRECORD и WH_JOURNALPLAYBACK - это всегда локальные системные хуки, которым не нужно быть в DLL. Также WH_SYSMSGFILTER - это всегда системный удаленный хук. Фактически он идентичен хуку WH_MSGFILTER при ThreadID равным 0.

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

Вы можете деинсталлировать хук, вызвав UnhookWindowsHookEx, которая принимает только один параметр - хэндл хука, который нужно деинсталлировать. Если вызов успешен, он возвращает ненулевое значение. Иначе он возвратит NULL.

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

function HookProc(byval nCode as integer,byval wParam as WPARAM,byval lParam as LPARAM) as LRESULT

• nCode задает код хука.
• wParam и lParam содержат дополнительную информацию о событие.

Вместо HookProc будет имя вашей хук-процедуры. Вы можете назвать ее как угодно, главное чтобы ее прототип совпадал с вышеприведенным. Интерпретация nCode, wparam и lparam зависит от типа установленного хука, так же, как и возвращаемое хук-процедурой значение. Например:

WH_CALLWNDPROC
• nCode может иметь значение HC_ACTION - это означает, что окну было послано сообщение.
• wParam содержит посланное сообщение, если он не равен нулю, lParam указывает на структуру CWPSTRUCT.
• возвращаемое значение: не используется, возвращайте ноль.

WH_MOUSE
• nCode может быть равно HC_ACTION или HC_NOREMOVE.
• wParam содержит сообщение от мыши.
• lParam указывает на структуру MOUSEHOOKSTRUCT.
• возвращаемое значение: ноль, если сообщение должно быть обработано. 1, если сообщение должно быть пропущено.

Вы должны обратиться к вашему справочнику по Win32 API за подробным описанием значение параметров и возвращаемых значений хука, который вы хотите установить.

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

function CallNextHookEx _
(byval hHook as HHOOK, _
byval nCode as integer, _
byval wparam as WPARAM, _
byval lparam as LPARAM) as LRESULT

• hHook - хэндл вашего хука. Функция использует этот хук для того, чтобы определить, какой хук надо вызвать следующим.
• nCode, wParam и lParam - вы передаете соответствующие параметры, полученные от Windows.

Важная деталь относительно удаленных хуков: хук-процедура должна находиться в DLL, которая будет промэппирована в другой процесс. Когда Windows мэппирует DLL в другой процесс, секция данных мэппироваться не будет. То есть, все процессы разделяют одну копию секции кода, но у них будет своя личная копия секции кода DLL! Это может стать большим сюрпризом для непредупрежденного человека. Вы можете подумать, что при сохранении значения в переменную в секции данных DLL, это значение получат все процессы, загрузившие DLL в свое адресное пространство. На самом деле, это не так. В обычной ситуации, такое поведение правильно, потому что это создает иллюзию, что у каждого процесса есть отдельная копия DLL. Но не тогда, когда это касается хуков Windows. Нам нужно, чтобы DLL была идентична во всех процессах, включая данные. решение: вы должны пометить секцию данных как разделяемую. Это можно сделать, указав атрибуты секции. ниже будет показан пример, как во FreeBasic открыть секцию .bss.

Пример:

Есть два модуля: один - это основная программа с GUI'ем, а другая - это DLL, которая устанавливает/снимает хук.

;---------------------------------------------
; Исходный код основной программы
;---------------------------------------------


файл mouse.bas

Скопировать данный код в буфер обмена

файл mouse.bi

Скопировать данный код в буфер обмена

файл mouse.rc

Скопировать данный код в буфер обмена

исходный код dll

файл mhook.bas

Скопировать данный код в буфер обмена

файл mhook.bi

Скопировать данный код в буфер обмена

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

файл bsspatch.bas

Скопировать данный код в буфер обмена

использование:
bsspatch [имя dll]
например:
bsspatch mhook.dll
проверено на fbc0.20

Анализ:

Пример отобразит диалоговое окно с тремя edit control'ами, которые будут заполнены именем класса, хэндлом окна и адресом процедуры окна, ассоциированное с окном под курсором мыши. Есть две кнопки - Hook и Exit. Когда вы нажимаете кнопку Hook, программа перехватывает сообщения от мыши и текст на кнопке меняется на Unhook. Когда вы двигаете курсор мыши над каким-либо окном, информация о нем отобразится в окне программы. Когда вы нажмете кнопку Unhook, программа уберет установленный hook.

Основная программа использует диалоговое окно в качестве основного. Она определяет специальное сообщение - WM_MOUSEHOOK, которая будет использоваться между основной программой и DLL с хуком. Когда основная программа получает это сообщение, wparam содержит хэндл окна, над которым находится курсор мыши. Конечно, это было сделано произвольно. Я решил слать хэндл в wparam, чтобы было проще. Вы можете выбрать другой метод взаимодействия между основной программой и DLL с хуком.

if HookFlag = FALSE then
if InstallHook(hDlg) <> NULL then
HookFlag = TRUE
SetDlgItemText _
(hDlg, _
IDC_HOOK, _
@UnhookText)

Программа пользуется флагом, HookFlag, чтобы отслеживать состояние хука.

Он равен FALSE, если хук не установлен, и TRUE, если установлен.

Когда пользователь нажмет кнопку hook, программа проверяет, установлен ли уже хук. Если это так, она вызывает функцию InstallHook из DLL. Заметьте, что мы передаем хэндл основного диалогового окна в качестве параметра функции, чтобы хук-DLL могла посылать сообщения WM_MOUSEHOOK верному окну, то есть нашему.

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


function InstallHook alias "InstallHook" (byval hwnd as HWND) as HHOOK export
hWnd_mhook = hwnd
hHook = SetWindowsHookEx _
(WH_MOUSE, _
cPtr(HOOKPROC,@MouseProc), _
GetModuleHandle("mhook.dll"), _
NULL)
function = hHook
end function

Функция InstallHook сама по себе очень проста. Она сохраняет хэндл окна, переданный ей в качестве параметра, в глобальную переменную hWnd_mhook. Затем она вызывает
SetWindowsHookEx, чтобы установить хук на мышь и получить хэндл dll. Возвращенное значение сохраняется в глобальную переменную hHook, чтобы в будущем передать ее UnhookWindowsHookEx.

После того, как вызван SetWindowsHookEx, хук начинает работать. Всякий раз, когда в системе случается мышиное событие, вызывается Mouseproc (ваша хук-процедура).

function MouseProc _
(byval nCode as integer,byval wParam as WPARAM, _
byval lParam as LPARAM) as LRESULT
dim hw as HWND
function = 0
CallNextHookEx _
(hHook, _
nCode, _
wparam,lparam)
hw = WindowFrompoint _
(cPtr(LPMOUSEHOOKSTRUCT, LPARAM)->pt)
postMessage _
(hWnd_mhook, _
WM_MOUSEHOOK, _
cPtr(WPARAM,hw), _
0)
end function

Сначала вызывается CallNextHookEx, чтобы другие хуки также могли обработать событие мыши. После этого, она вызывает функцию WindowFrompoint, чтобы получить хэндл окна, находящегося в указанной координате экрана. Заметьте, что мы используем структуру POINT, являющуюся членом структуры MOUSEHOOKSTRUCT, на которую указывает lparam, то есть координату текущего местонахождения курсора. После этого, мы посылаем хэндл окна основной программы через сообщение WM_MOUSEHOOK.
Вы должны помнить: вам не следует использовать SendMessage в хук-процедуре, так как это может вызвать "подвисы", поэтому рекомендуется использовать PostMessage.
Структура MOUSEHOOKSTRUCT определена ниже:

type MOUSEHOOKSTRUCT
pt as POINT
hwnd as HWND
wHitTestCode as UINT
dwExtraInfo as DWORD
end type

• pt - это текущая координата курсора мыши.
• hwnd - это хэндл окна, которое получает сообщения от мыши. Это обычно окно под курсором мыши, но не всегда. Если окно вызывает SetCapture, сообщения от мыши будут перенаправлены этому окну. По этой причине я не использую параметр hwnd этой структуры, а вызываю вместо этого WindowFrompoint.
• wHitTestCode дает дополнительную информацию о том, где находится курсор мыши. Полный список значений вы можете получить в вашем справочнике по Win32 API в разделе сообщения WM_NCHITTEST.
• dwExtraInfo содержит дополнительную информацию, ассоциированную с сообщением. Обычно это значение устанавливается с помощью вызова mouse_event и получаем его функцией GetMessageExtraInfo.

Когда основное окно получает сообщение WM_MOUSEHOOK, оно использует хэндл окна в wparam'е, чтобы получить информацию об окне.

case WM_MOUSEHOOK
GetDlgItemText _
(hDlg, _
IDC_HANDLE, _
@buffer1, _
128)
wsprintf _
(@buffer, _
@template, _
wparam)
if lstrcmpi _
(@buffer, _
@buffer1) _
<> 0 then
SetDlgItemText _
(hDlg, _
IDC_HANDLE, _
@buffer)
end if
GetDlgItemText _
(hDlg, _
IDC_CLASSNAME, _
@buffer1, _
128)
GetClassName _
(cPtr(HWND,wparam), _
@buffer, _
128)
if lstrcmpi _
(@buffer, _
@buffer1) _
<> 0 then
SetDlgItemText _
(hDlg, _
IDC_CLASSNAME, _
@buffer)
end if
GetDlgItemText _
(hDlg, _
IDC_WNDPROC, _
@buffer1, _
128)
cl = GetClassLong _
(cPtr(HWND,wparam), _
GCL_WNDPROC)
wsprintf _
(@buffer, _
@template, _
cl)
if lstrcmpi _
(@buffer, _
@buffer1) _
<> 0 then
SetDlgItemText _
(hDlg, _
IDC_WNDPROC, _
@buffer)
end if

Чтобы избежать мерцания, мы проверяем, не идентичны ли текст в edit control'ах с текстом, который мы собираемся ввести. Если это так, то мы пропускаем этот этап.

Мы получаем имя класса с помощью вызова GetClassName, адрес процедуры с помощью вызова GetClassLong со значением GCL_WNDPROC, а затем форматируем их в строки и помещаем в соответствующие edit control'ы.

UninstallHook()
SetDlgItemText _
(hDlg, _
IDC_HOOK, _
@hookText)
HookFlag = FALSE
SetDlgItemText(hDlg,IDC_CLASSNAME,NULL)
SetDlgItemText(hDlg,IDC_HANDLE,NULL)
SetDlgItemText(hDlg,IDC_WNDPROC,NULL)

Когда юзер нажмет кнопку Unhook, программа вызовет функцию UninstallHook в хук-DLL. UninstallHook всего лишь вызывает UnhookWindowsHookEx. После этого, она меняет текст кнопки обратно на "Hook", HookFlag на FALSE и очищает содержимое edit control'ов.

не забудьте после компиляции dll, выполнить следующую команду:
bsspatch mhook.dll

Секция .bss помечается как разделяемая, чтобы все процессы разделяли секцию неинициализируемых данных хук-DLL. Без этой опции, ваша DLL функционировала бы неправильно.

[C] Iczelion, пер. Aquila.

 

 

Прокомментировать

Ваше имя:  

Ваш e-mail:  

Сообщение:



Введите код: 

 

 

   Спасибо можно сказать, посетив любую ссылку:

 

 

© 2010-2012

DEPOzit (Попов Денис Владимирович)

WebMoney кошелёк: WMID#302963000004

ICQ:279786014 или R549103331586   Z116647355686