Создание ловушки клавиатуры в Borland C++ Builder 6
5августа 2004
Создадим программу, которая
будет отслеживать нажатие клавиш ALT+F12 и делать видимой главную форму. При
создании этого проекта вы научитесь создавать и обрабатывать сообщения с помощью
карты сообщений (таблицы отображения сообщений), делать невидимой главную форму
приложения и собственно создавать и устанавливать ловушки. При создании ловушек (Hook’s) помните два основных
правила:
Процедура заглушки должна располагаться в DLL;
Процедура заглушки должна заканчиваться вызовом функции
CallNextHookEx().
Создайте новую группу
приложений (ProjectGroup) и добавьте в нее библиотеку DLL. Для этого
воспользуйтесь мастером создания DLL. В мастере создания DLL оставьте все
настройки по умолчанию (C++, Use VCL, Multi Threaded). Добавьте новое
приложение. Перейдите в проект DLL и добавьте новый текстовый файл, который
сохраните под именем Unit1.h. Сохраните группу. Теперь перейдем непосредственно
к созданию кода.
Перейдите в окно редактора кода Unit1.h и введите следующий код:
#ifndef Unit1H
#define Unit1H
#endif
заголовочный файл для нашей DLL готов. Группа директив препроцессора работает
следующим образом: если определено имя __DLL__, то DLL_EXP раскрывается в
__declspec(dllexport). Если имя __DLL__ не определено, DLL_EXP раскрывается в
__declspec(dllimport). Имя __DLL__ автоматически определяется в Borland C++
Builder при создании проекта DLL, и не определяется при создании проекта
приложения. Этот заголовок можно использовать как для DLL, так и для приложений.
При построении DLL все классы и функции, объявленные с DLL_EXP, будут
экспортированы, а при построении вызывающего приложения, все функции и классы,
объявленные с DLL_EXP, будут импортированны. Перейдите в редактор кода файла Unit1.cpp. Можете
удалить закомментированные строки, которые предупреждают о необходимости
добавления библиотеки MEMMGR.LIB и использовании BORLNDMM.DLL в случае работы со
статической RTL. В нашем проекте мы не используем статическую RTL. Добавьте
строчку с подключением заголовочного файла Unit1.h, пропустите функцию
DllEntryPoint() и добавьте следующий код:
LRESULT CALLBACK DLL_EXP KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam)
{ if(nCode < 0 || nCode != HC_ACTION) return CallNextHookEx(hhk, nCode, wParam,
lParam); if(wParam == (VK_F12)) { if(lParam & 0x80000000 || lParam & 0x40000000) return CallNextHookEx(hhk, nCode, wParam,
lParam); if((lParam >> 29) & 1) { hWnd = FindWindow("TForm2", NULL); SendMessage(hWnd, MYMESSAGE, 0, 0); } } return CallNextHookEx(hhk, nCode, wParam, lParam);
}.
В функцию ловушки передаются следующие параметры:
nCode – определяет порядок обработки сообщения;
wParam – код виртуальной клавиши;
lParam – дополнительная информация: счетчик нажатий, скэн-код и др. (рисунок 1).
Рисунок 1
Нас интересуют параметры wParam
и lParam. Если wParam содержит VK_F12, то есть нажата клавиша F12, идет проверка
на удержание клавиш. Последний оператор if проверяет нажатие клавиши ALT. В
операторном блоке этого if ищется наша главная форма и ей посылается сообщение.
Так как наша функция является функцией обратного вызова она возвращает
управление в следующую ловушку, то есть как было сказано выше, вызывает функцию
CallNextHookEx(). Ловушка готова. Далее переходим к написанию кода главного
приложения.
Активизируйте Project2 и на форме разместите 3 компонента Button и компонент
Label. Задайте свойство Caption у Button1 “Set Hook’s”, у Button2 “Unhook’s”, у
Button3 “Hide” и удалите у Label1. Установите свойство Enabled в false у Button2
и Button3. В обработчик OnClick компонента Button1 добавьте следующий код:
hMod = LoadLibrary("Project1.dll");
lpfn = (HOOKPROC)GetProcAddress(hMod, "KeyboardHook");
hhk = SetWindowsHookEx(WH_KEYBOARD, lpfn, hMod, 0);
if(hhk == NULL)
{ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); MessageBox(NULL, (LPTSTR)lpMsgBuf, "Не могу
установить ловушку клавиатуры", MB_OK | MB_ICONERROR); LocalFree(lpMsgBuf);
}
else
{ Button1->Enabled = false; Button2->Enabled = true; Button3->Enabled = true; Label1->Caption = "Ловушка установлена.\nИспользуйте
ALT+F12";
}. Первая строка кода загружает в память нашу DLL с
функцией ловушки. Вторая получает адрес функции ловушки. Третья строка
устанавливает нашу ловушку. Если ловушка не установлена выводиться сообщение об
ошибке. В обработчик OnClick компонента Button2 добавьте
код:
if(UnhookWindowsHookEx(hhk))
{ Button1->Enabled = true; Button2->Enabled = false; Button3->Enabled = false; Label1->Caption = "Ловушка выгружена.";
}
FreeLibrary(hMod);
который удаляет нашу заглушку из цепочки заглушек операционной системы и
выгружает нашу DLL из памяти. Теперь осталось написать код для скрытия нашего
приложения. В обработчик OnClick компонента Button3 добавьте строку кода:
Form2->Hide(), в событие OnHide формы добавьте код: ShowWindow(Application->Handle, SW_HIDE); Application->MainForm->Visible = false;,
а в событие OnShow добавьте код: ShowWindow(Application->Handle, SW_SHOW); Application->MainForm->Visible = true;. Наше приложение почти готово, осталось написать
код для обработки сообщения MYMESSAGE, которое было определено в заголовочном
файле DLL. Для обработки нашего сообщения необходимо создать карту сообщений.
Перейдите в редактор кода заголовочного файла формы. Добавьте код для
подключения заголовочного файла DLL – #include "Unit1.h". В общий раздел класса
формы добавьте следующий код:
HINSTANCE hMod;
LPVOID lpMsgBuf;
void __fastcall OnMyMessage(TMessage& Msg);
BEGIN_MESSAGE_MAP MESSAGE_HANDLER(MYMESSAGE, TMessage, OnMyMessage)
END_MESSAGE_MAP(TForm). Первая строка кода объявляет переменную для
хранения хэндла библиотеки DLL, вторая переменную для хранения сообщения об
ошибке, третья строка объявляет функцию, которая будет обрабатывать наше
сообщение (обработчик сообщения). Далее следует карта сообщений (таблица
отображения сообщений), которая связывает некоторое сообщение Windows (в нашем
случае MYMESSAGE) с определенной функцией в нашем коде (OnMyMessage). Карта
сообщений всегда располагается в конце определения класса. Она начинается с
BEGIN_MESSAGE_MAP и заканчивается END_MESSAGE_MAP. В END_MESSAGE_MAP передается
единственный параметр, имя базового класса. Между этими элементами располагается
одно или несколько объявлений MESSAGE_HANDLER (обработчиков). Для компонентов
VCL используется VCL_ MESSAGE_HANDLER. Вы наверно догадались, что это
макроопределения. Компилятор раскрывает макросы таблицы сообщений в функцию
Dispatch(). В общем случае эта функция выглядит следующим образом:
vitrual void __fastcall Dispatch(void *Message)
{ switch((Pmessage)Message)->Msg) { case UserMsg1 : HandlerMsg1((StructMsg1&) *Message); break; case UserMsg2 : HandlerMsg2((StructMsg2&) *Message); break; … default: Name_of_Base_Class::Dispatch(Message); break; }
} Рассмотрим более подробно параметры макроса
MESSAGE_HANDLER, который имеет следующий синтаксис: MESSAGE_HANDLER(<UserMsg>, <StructMsg>, <HandlerMsg>). UserMsg – сообщение Windows или определенное вами,
которое вы хотите обработать в нашем случае сообщение MYMESSAGE, определенное в
Unit1.h). StructMsg – имя структуры, которая будет содержать
параметры сообщения после его раскрытия VCL. Эта структура передается
обработчику сообщения. HandlerMsg – фактическое имя функции, которая
обрабатывает сообщение.
Функция обработки сообщения (обработчик сообщения) – это функция, которая будет
вызываться каждый раз, когда ваше приложение получает обрабатываемое сообщение.
Эта функция имеет один параметр – структуру, раскрывающую обрабатываемое
сообщение. Для нашего примера функция обработки сообщения выглядит следующим
образом:
void __fastcall TForm2::OnMyMessage(TMessage& Msg)
{ Form2->Show();
}. Добавьте ее в Unit2.cpp. Выполните команду “Build
All Projects”, закройте Borland C++ Builder и запустите Project2.exe на
выполнение.