платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 24 - Windows-хуки
Страница 1 из 1
платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 24 - Windows-хуки
Win32 API. Урок 24. Windows-хуки
В этом туториале мы изучим хуки. Это очень мощная техника. С их помощью вы сможете вмешиваться в другие процессы и иногда менять их поведение.
Теория:
Хуки 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
продолжение следует
В этом туториале мы изучим хуки. Это очень мощная техника. С их помощью вы сможете вмешиваться в другие процессы и иногда менять их поведение.
Теория:
Хуки 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
- Код:
#include "windows.bi"
#include "mhook.bi"
#include "mouse.bi"
declare Function DlgProc (byval hDlg as HWND, byval uMsg as UINT, _
byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
#define HookText "&Hook"
#define UnhookText "&Unhook"
#define template !"%lx"
dim shared hModule as HINSTANCE ' хэндл программы
dim shared hHook as HHOOK ' хэндл хука
dim shared HookFlag as uinteger = FALSE ' флаг хука
hModule = GetModuleHandle(NULL) ' получим хэндл программы
end DialogBoxParam _
(hModule, _ ' хэндл программы
cPtr(LPCSTR,IDD_MAINDLG), _ ' идентификатор диалога
NULL, _
@DlgProc, _ ' указатель на DlgProc
NULL)
Function DlgProc (byval hDlg as HWND, byval uMsg as UINT, _
byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
dim as zstring * 128 buffer
dim as zstring * 128 buffer1
dim rect as RECT ' структура RECT
dim cl as DWORD
select case uMsg ' проверяем сообщения
case WM_CLOSE ' сообщение закрытия окна
if HookFlag = TRUE then ' если флаг хука установлен
UninstallHook() ' снимаем хук
end if
EndDialog(hDlg,NULL)
case WM_INITDIALOG ' инициализация диалога
GetWindowRect _ ' получим размеры рамки ограничивающей прямоугольник заданного окна
(hDlg, _ ' хэндл диалога
@rect) ' указатель на структуру RECT
SetWindowPos _ ' установим окно
(hDlg, _ ' хэндл диалога
HWND_TOPMOST, _ ' поместим окно перед не самыми верхними окнами
rect.left, _ "x" Установим позицию с левой стороны окна, в рабочих координатах
rect.top, _ ' "y" Установим позицию верхней части окна, в рабочих координатах
rect.right, _ ' "cx" Установим ширину окна, в пикселях.
rect.bottom, _ ' "cy" Установим высоту окна, в пикселях.
SWP_SHOWWINDOW) ' отобразим окно на экране
case WM_MOUSEHOOK ' сообщение хука мыши
GetDlgItemText _ ' получим текст
(hDlg, _ ' хэндл диалога
IDC_HANDLE, _ ' идентификатор элемента edit box
@buffer1, _ ' указатель на буфер куда поместится текст
128) ' число читаемых байт
wsprintf _ ' преобразуем в строку
(@buffer, _ ' указатель на буфер
@template, _ ' указатель на шаблон
wparam)
if lstrcmpi _ ' если при сравнении строк
(@buffer, _ ' указатель на буфер
@buffer1) _ ' указатель на другой буфер
<> 0 then ' строки неравны
SetDlgItemText _ ' установим текст
(hDlg, _ ' хэндл диалога
IDC_HANDLE, _ ' идентификатор edit box
@buffer) ' указатель на буфер из которого выводится текст в диалог
end if
GetDlgItemText _ ' получим текст
(hDlg, _ ' хэндл диалога
IDC_CLASSNAME, _ ' идентификатор edit box
@buffer1, _ ' указатель на буфер куда поместится текст
128) ' число читаемых байт
GetClassName _ ' получим имя класса окна
(cPtr(HWND,wparam), _ ' хэндл окна
@buffer, _ ' указатель на буфер, куда запишется имя класса
128) ' длина буфера в байтах
if lstrcmpi _ ' если при сравнении строк
(@buffer, _ ' указатель на буфер
@buffer1) _ ' указатель на другой буфер
<> 0 then ' строки неравны
SetDlgItemText _ ' установим текст
(hDlg, _ ' хэндл диалога
IDC_CLASSNAME, _ ' идентификатор edit box
@buffer) ' указатель на буфер из которого выводится текст в диалог
end if
GetDlgItemText _ ' получим текст
(hDlg, _ ' хэндл диалога
IDC_WNDPROC, _ ' идентификатор edit box
@buffer1, _ ' указатель на буфер куда поместится текст
128) ' число читаемых байт
cl = GetClassLong _
(cPtr(HWND,wparam), _ ' хэндл окна
GCL_WNDPROC) ' извлечь адрес оконной процедуры
wsprintf _ ' преобразуем в строку
(@buffer, _ ' указатель на буфер
@template, _ ' указатель на шаблон
cl)
if lstrcmpi _ ' если при сравнении строк
(@buffer, _ ' указатель на буфер
@buffer1) _ ' указатель на другой буфер
<> 0 then ' строки неравны
SetDlgItemText _ ' установим текст
(hDlg, _ ' хэндл диалога
IDC_WNDPROC, _ ' идентификатор edit box
@buffer) ' указатель на буфер из которого выводится текст в диалог
end if
case WM_COMMAND
if hiword(wParam) = BN_CLICKED then ' если кликают кнопки
select case loword(wParam) ' продолжаем проверять идентификаторы
case IDC_EXIT ' если нажали exit
SendMessage _ ' пошлем сообщение
(hDlg, _ ' хэндл диалога
WM_CLOSE, _ ' сообщение закрытия
0,0)
case IDC_HOOK ' если нажали хоок
if HookFlag = FALSE then ' если флаг хука не установлен
if InstallHook(hDlg) <> NULL then ' если хук процедура сработала
HookFlag = TRUE ' установим флаг хука в TRUE
' изменим название кнопки hook на unhook
SetDlgItemText _ ' установим текст
(hDlg, _ ' хэндл диалога
IDC_HOOK, _ ' хэндл кнопки
@UnhookText) ' указатель на буфер с текстом
else
MessageBox(0,"не возможно установить хук","ошибка",0)
end if
else
UninstallHook() ' снимаем хук
' изменим название кнопки \unhook на hook
SetDlgItemText _ ' установим текст
(hDlg, _ ' хэндл диалога
IDC_HOOK, _ ' хэндл кнопки
@hookText) ' указатель на буфер с текстом
HookFlag = FALSE ' установим флаг хука в FALSE
' очистим все edit box'ы
SetDlgItemText(hDlg,IDC_CLASSNAME,NULL)
SetDlgItemText(hDlg,IDC_HANDLE,NULL)
SetDlgItemText(hDlg,IDC_WNDPROC,NULL)
end if
end select
end if
case else
return FALSE
end select
function = TRUE
end function
файл mouse.bi
- Код:
#define IDD_MAINDLG 101
#define IDC_CLASSNAME 1000
#define IDC_HANDLE 1001
#define IDC_WNDPROC 1002
#define IDC_HOOK 1004
#define IDC_EXIT 1005
#define IDC_STATIC -1
файл mouse.rc
- Код:
#include "mouse.bi"
IDD_MAINDLG DIALOG DISCARDABLE 0, 0, 229, 85
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Mouse Hook Demo"
FONT 8, "MS Sans Serif"
BEGIN
GROUPBOX "Window Information",IDC_STATIC,7,7,214,67
LTEXT "Class name:",IDC_STATIC,21,22,39,8
EDITTEXT IDC_CLASSNAME,69,20,139,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Handle:",IDC_STATIC,33,37,26,8
EDITTEXT IDC_HANDLE,69,36,77,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Window Proc:",IDC_STATIC,13,52,46,8
EDITTEXT IDC_WNDPROC,69,51,77,12,ES_AUTOHSCROLL | ES_READONLY
DEFPUSHBUTTON "&Hook",IDC_HOOK,159,35,50,14
PUSHBUTTON "E&xit",IDC_EXIT,159,50,50,14
END
продолжение следует
Последний раз редактировалось: electrik (Вт Май 23, 2017 4:58 pm), всего редактировалось 1 раз(а)
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 24 - Windows-хуки
платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 24 - Windows-хуки - продолжение
исходный код 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.
исходный код dll
файл mhook.bas
- Код:
#include "windows.bi"
#include "mhook.bi"
dim shared hHook as HHOOK ' хэндл хука
dim shared hWnd_mhook as HWND ' хэндл окна
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) ' x,y
postMessage _ ' вставим в очередь сообщений
(hWnd_mhook, _ ' хэндл окна
WM_MOUSEHOOK, _ ' код сообщения хука мыши
cPtr(WPARAM,hw), _ ' хэндл окна, _
0)
end function
extern "windows-ms"
function InstallHook alias "InstallHook" (byval hwnd as HWND) as HHOOK export ' процедура установки хука
hWnd_mhook = hwnd ' запишем хэндл окна
hHook = SetWindowsHookEx _ ' установим хук
(WH_MOUSE, _ ' хук мыши
cPtr(HOOKPROC,@MouseProc), _ ' указатель на хук процедуру
GetModuleHandle("mhook.dll"), _ ' хэндл dll
NULL)
function = hHook
end function
sub UninstallHook alias "UninstallHook"() export ' процедура удаления хука
UnhookWindowsHookEx _ ' удалим хук
(hHook) ' хэндл хука
end sub
end extern
файл mhook.bi
- Код:
#define WM_MOUSEHOOK WM_USER+6 ' код сообщения хука мыши
declare function MouseProc alias "MouseProc" (byval nCode as integer, _
byval wParam as WPARAM,byval lParam as LPARAM) as LRESULT
extern "Windows-MS" lib "l24_mhook"
declare function InstallHook alias "InstallHook" (byval hwnd as HWND) as HHOOK
declare sub UninstallHook alias "UninstallHook"()
end extern
исходный код патча, для того, чтобы пометить секцию .bss как разделяемая
файл bsspatch.bas
- Код:
#include "windows.bi"
#include "file.bi"
dim rFlags as uinteger ' сюда будем читать флаги из файла
dim wFlags as uinteger ' флаги для патчинга файла
dim sName as Zstring * 8 ' ну а сюда будем читать имя секции
dim cmd as string ' командная строка
' заполним флаги для патчинга, доступен в памяти для всех процессов, чтение, запись
wFlags = IMAGE_SCN_CNT_UNINITIALIZED_DATA or IMAGE_SCN_MEM_SHARED or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE
print !"\n.Bss section patcher for FreeBasic0.20\n"
cmd=command()
if not fileexists(cmd) then
if cmd = "" then
print !"Usage: bsspatch [fileName]\n"
else
print !"file not found\n"
end if
else
open cmd for binary as #1
print "Find Section .bss"
get #1,&h1F0+1,sName
if not sname = ".bss" then
print !"Section .bss not found\n"
ELSE
print "Getting flags"
get #1,&h214+1,rFlags
if rFlags = wFlags then
print !"Section .bss alriady patched\n"
else
print "Patching section .bss"
put #1,&h214+1,wFlags
close
print !"patched\n"
end if
end if
end if
использование:
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.
Последний раз редактировалось: electrik (Вт Май 23, 2017 5:07 pm), всего редактировалось 1 раз(а)
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 24 - Windows-хуки
вы можете скачать 24 мануала с исходными текстами
https://yadi.sk/d/tVY-OoCl3JSmmo
Продолжение мануалов, адаптированных уже не мной и много полезных статей можно почитать на:
http://free-basic.ru
https://yadi.sk/d/tVY-OoCl3JSmmo
Продолжение мануалов, адаптированных уже не мной и много полезных статей можно почитать на:
http://free-basic.ru
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Страница 1 из 1
Права доступа к этому форуму:
Вы не можете отвечать на сообщения