Создание системных ловушек Windows на Borland C++ Builder 5
Прежде чем излагать материал я хочу заметить, что цель данной работы – показать
как пишутся ловушки Windows вообще. Подробности, по мере возможности, я буду
опускать (их можно найти в поставляемой со средой разработке справке).
Для начала определим, что именно мы хотим сделать.
Цель: написать программу, которая будет вызывающую хранитель экрана при
перемещении курсора мыши в правый верхний угол и выдавать звуковой сигнал через
встроенный динамик при переключении языка с клавиатуры.
Предполагается, что такая программа должна иметь небольшой размер. Поэтому будем
писать её с использованием только WIN API.
Понятие ловушки.
Ловушка (hook) – это механизм, который позволяет производить мониторинг
сообщений системы и обрабатывать их до того как они достигнут целевой оконной
процедуры.
Для обработки сообщений пишется специальная функция (Hook Procedure). Для начала
срабатывания ловушки эту функцию следует специальным образом «подключить» к
системе.
Если надо отслеживать сообщения всех потоков, а не только текущего, то ловушка
должна быть глобальной. В этом случае функция ловушки должна находиться в DLL.
Таким образом, задача разбивается на две части:
1. Написание DLL c функциями ловушки (их будет две: одна для клавиатуры, другая
для мыши).
2. Написание приложения, которое установит ловушку.
Написание DLL.
Создание пустой библиотеки.
С++ Builder имеет встроенный мастер по созданию DLL. Используем его, чтобы
создать пустую библиотеку. Для этого надо выбрать пункт меню File->New… В
появившемся окне надо выбрать «DLL Wizard» и нажать кнопку «Ok». В новом диалоге
в разделе «Source Type» следует оставить значение по умолчанию – «C++». Во
втором разделе надо снять все флажки. После нажатия кнопки «Ок» пустая
библиотека будет создана.
Глобальные переменные и функция входа (DllEntryPoint).
Надо определить некоторые глобальные переменные, которые понадобятся в
дальнейшем.
#define UP 1 // Состояния клавиш
#define DOWN 2
#define RESET 3
int iAltKey; // Здесь хранится состояние клавиш
int iCtrlKey;
int iShiftKey;
int KEYBLAY; // Тип переключения языка
bool bSCRSAVEACTIVE; // Установлен ли ScreenSaver
MOUSEHOOKSTRUCT* psMouseHook; // Для анализа сообшений от мыши
В функции DllEntryPoint надо написать код, подобный нижеприведённому:
if(reason==DLL_PROCESS_ATTACH) // Проецируем на адр. простр.
{
HKEY pOpenKey;
char* cResult=""; // Узнаём как перекл. раскладка
long lSize=2;
KEYBLAY=3;
RegCloseKey(pOpenKey);
}
else
MessageBox(0,"Не могу получить данные о способе"
"переключения раскладки клавиатуры",
"Внимание!",MB_ICONERROR);
//------------- Есть ли активный хранитель эрана
if(!SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,&bSCRSAVEACTIVE,0))
MessageBox(0,"Не могу получить данные об установленном"
"хранителе экрана", "Внимание!",MB_ICONERROR);
}
return 1;
Этот код позволяет узнать способ переключения языка и установить факт наличия
активного хранителя экрана. Обратите внимание на то, что этот код выполняется
только когда библиотека проецируется на адресное пространство процесса –
проверяется условие (reason==DLL_PROCESS_ATTACH). Если вас интересуют
подробности, то их можно узнать в разделе справки «Win32 Programmer's Reference»
в подразделе «DllEntryPoint».
Функция ловушки клавиатуры.
Функция ловушки в общем виде имеет следующий синтаксис:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam), где:
HookProc – имя функции,
nCode – код ловушки, его конкретные значения определяются типом ловушки,
wParam, lParam – параметры с информацией о сообщении.
В случае нашей задачи функция должна определять состояние клавиш Alt, Ctrl и
Shift (нажаты или отпущены). Информация об этом берётся из параметров wParam и
lParam (подробности в «Win32 Programmer's Reference» в подразделе «KeyboardProc»).
После определения состояния клавиш надо сравнить его со способом переключения
языка (определяется в функции входа). Если текущая комбинация клавиш способна
переключить язык, то надо выдать звуковой сигнал.
Всё это реализует примерно такой код:
LRESULT CALLBACK KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam)
{ // Ловушка клав. - биканье при перекл. раскладки
if((lParam>>31)&1) // Если клавиша нажата...
switch(wParam)
{ // Определяем какая именно
case VK_SHIFT: {iShiftKey=UP; break};
case VK_CONTROL: {iCtrlKey=UP; break};
case VK_MENU: {iAltKey=UP; break};
}
else // Если была отпущена...
switch(wParam)
{ // Определяем какая именно
case VK_SHIFT: {iShiftKey=DOWN; break};
case VK_CONTROL: {iCtrlKey=DOWN; break};
case VK_MENU: {iAltKey=DOWN; break};
}
//--------------
switch(KEYBLAY) // В зависимости от способа переключения раскладки
{
case 1: // Alt+Shift
{
if(iAltKey==DOWN && iShiftKey==UP)
{
vfBeep();
iShiftKey=RESET;
}
if(iAltKey==UP && iShiftKey==DOWN)
{
vfBeep();
iAltKey=RESET;
}
if((iAltKey==UP && iShiftKey==RESET)||(iAltKey==RESET &&
iShiftKey==UP))
{
iAltKey=RESET;
iShiftKey=RESET;
}
break;
}
//------------------------------------
case 2: // Ctrl+Shift
{
if(iCtrlKey==DOWN && iShiftKey==UP)
{
vfBeep();
iShiftKey=RESET;
}
if(iCtrlKey==UP && iShiftKey==DOWN)
{
vfBeep();
iCtrlKey=RESET;
}
if((iCtrlKey==UP && iShiftKey==RESET)||(iCtrlKey==RESET &&
iShiftKey==UP))
{
iCtrlKey=RESET;
iShiftKey=RESET;
}
}
}
return 0;
}
Звуковой сигнал выдаётся такой небольшой функцией:
void vfBeep()
{ // Биканье
MessageBeep(-1);
MessageBeep(-1); // Два раза – для отчётливости
}
Функция ловушки мыши.
Эта функция отслеживает движение курсора мыши, получает его координаты и
сравнивает их с координатами правого верхнего угла экрана (0,0). Если эти
координаты совпадают, то вызывается хранитель экрана. Для отслеживания движения
анализируется значение параметра wParam, а для отслеживания координат значение,
находящееся в структуре типа MOUSEHOOKSTRUCT, на которую указывает lParam
(подробности можно найти в «Win32 Programmer's Reference» в подразделе «MouseProc»).
Код, реализующий вышесказанное, примерно такой:
Обратите внимание, что команда на активизацию хранителя посылается в окно,
получающее сообщения от мыши: PostMessage(psMouseHook->hwnd,WM_SYSCOMMAND,SC_SCREENSAVE
,0).
Теперь, когда функции ловушек написаны, надо сделать так, чтобы они были
доступны из процессов, подключающих эту библиотеку. Для этого перед функцией
входа следует добавить такой код:
extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int,WPARAM,LPARAM);
extern "C" __declspec(dllexport) LRESULT CALLBACK MouseHook(int,WPARAM,LPARAM);
Написание приложения, устанавливающего ловушку.
Создание пустого приложения.
Для создания пустого приложения воспользоваться встроенным мастером. Для этого
надо использовать пункт меню File->New… В появившемся окне необходимо выбрать «Console
Wizard» и нажать кнопку «Ok». В новом диалоге в разделе «Source Type» следует
оставить значение по умолчанию – «C++». Во втором разделе надо снять все флажки.
По нажатию «Ок» приложение создаётся.
Создание главного окна.
Следующий этап – это создание главного окна приложения. Сначала надо
зарегистрировать класс окна. После этого создать окно (подробности можно найти в
«Win32 Programmer's Reference» в подразделах «RegisterClass» и «CreateWindow»).
Всё это делает следующий код (описатель окна MainWnd определён глобально):
BOOL InitApplication(HINSTANCE hinstance,int nCmdShow)
{ // Создание главного окна
WNDCLASS wcx; // Класс окна
wcx.style=NULL;
wcx.lpfnWndProc=MainWndProc;
wcx.cbClsExtra=0;
wcx.cbWndExtra=0;
wcx.hInstance=hinstance;
wcx.hIcon=LoadIcon(hinstance,"MAINICON");
wcx.hCursor=LoadCursor(NULL,IDC_ARROW);
wcx.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE);
wcx.lpszMenuName=NULL;
wcx.lpszClassName="HookWndClass";
Обратите внимание на то, каким образом был получен значок класса: wcx.hIcon=LoadIcon(hinstance,"MAINICON");
Для того, чтобы это получилось надо включить в проект файл ресурсов (*.res), в
котором должен находиться значок с именем "MAINICON".
Это окно никогда не появится на экране, поэтому оно имеет размеры и координаты,
устанавливаемые по умолчанию. Оконная процедура такого окна необычайно проста:
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,
LPARAM lParam)
{ // Оконная процедура
switch (uMsg)
{
case WM_DESTROY:{PostQuitMessage(0); break;}
//------------
case MYWM_NOTIFY:
{
if(lParam==WM_RBUTTONUP)
PostQuitMessage(0);
break; // Правый щелчок на значке - завершаем
}
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
Размещение значка в системной области.
Возникает естественный вопрос: если окно приложения никогда не появится на
экране, то каким образом пользователь может управлять им (например, закрыть)?
Для индикации работы приложения и для управления его работой поместим значок в
системную область панели задач. Делается это следующей функцией:
void vfSetTrayIcon(HINSTANCE hInst)
{ // Значок в Tray
char* pszTip="Хранитель экрана и раскладка"; // Это просто Hint
Для корректной работы функции предварительно нужно определить уникальный номер
значка (параметр NotIconD.uID) и его сообщение (параметр
NotIconD.uCallbackMessage). Делаем это в области определения глобальных
переменных:
#define MYWM_NOTIFY (WM_APP+100)
#define IDC_MYICON 1006
Сообщение значка будет обрабатываться в оконной процедуре главного окна (NotIconD.hWnd=MainWnd):
case MYWM_NOTIFY:
{
if(lParam==WM_RBUTTONUP)
PostQuitMessage(0);
break; // Правый щелчок на значке - завершаем
}
Этот код просто завершает работу приложения по щелчку правой кнопкой мыши на
значке.
При завершении работы значок надо удалить:
Установка и снятие ловушек.
Для получения доступа в функциям ловушки надо определить указатели на эти
функции:
LRESULT CALLBACK (__stdcall *pKeybHook)(int,WPARAM,LPARAM);
LRESULT CALLBACK (__stdcall *pMouseHook)(int,WPARAM,LPARAM);
После этого спроецируем написанную DLL на адресное пространство процесса:
hLib=LoadLibrary("SSHook.dll");
(hLib описан как HINSTANCE hLib).
После этого мы должны получить доступ к функциям ловушек:
(void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook");
(void*)pMouseHook=GetProcAddress(hLib,"MouseHook");
Теперь всё готово к постановке ловушек. Устанавливаются они с помощью функции
SetWindowsHookEx:
hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook),hLib,0);
hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook), hLib,0);
(hKeybHook и hMouseHook описаны как HHOOK hKeybHook; HOOK hMouseHook;)
Первый параметр – тип ловушки (в данном случае первая ловушка для клавиатуры,
вторая – для мыши). Второй – адрес процедуры ловушки. Третий – описатель
DLL-библиотеки. Последний параметр – идентификатор потока, для которого будет
установлена ловушка. Если этот параметр равен нулю (как в нашем случае), то
ловушка устанавливается для всех потоков.
После установки ловушек они начинают работать. При завершении работы приложения
следует их снять и отключить DLL. Делается это так:
UnhookWindowsHookEx(hKeybHook);
UnhookWindowsHookEx(hMouseHook); // Завершаем
FreeLibrary(hLib);
Функция WinMain.
Последний этап – написание функции WinMain в которой будет создаваться главное
окно, устанавливаться значок в системную область панели задач, ставиться и
сниматься ловушки. Код её должен быть примерно такой:
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
//----------------
hLib=LoadLibrary("SSHook.dll");
if(hLib)
{
(void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook");
hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook),
hLib,0); // Ставим ловушки
(void*)pMouseHook=GetProcAddress(hLib,"MouseHook");
hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook),
hLib,0);
//-------------------------------
if (InitApplication(hInstance,nCmdShow)) // Если создали главное окно
{
vfSetTrayIcon(hInstance); // Установили значок
while (GetMessage(&msg,(HWND)(NULL),0,0))
{ // Цикл обработки сообщений
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//---------------------------------- Всё – финал
UnhookWindowsHookEx(hKeybHook); // Снимаем ловушки
UnhookWindowsHookEx(hMouseHook);
FreeLibrary(hLib); // Отключаем DLL
vfResetTrayIcon(); // Удаляем значок
return 0;
}
}
return 1;
}
После написания этой функции можно смело запускать полностью готовое приложение.
HTML 5 — это грядущее обновление гипертекстового языка разметки, основного
способа создания контента для размещения его во всемирной паутине. Разработка
HTML остановилась в 1999 году, на версии HTML 4.01 и с тех пор web-содержимое
изменилось так, что текущие спецификации HTML перестали соответствовать
сегодняшним требованиям.
HTML 5 нацелен на то, чтобы увеличить функциональную совместимость HTML и
соответствовать растущим требованиям разнообразного и смешанного web-контента.
HTML 5 так же нацелен на устранение недостатков четвертой версии. В этой статье
мы взглянем на 5 новых интересных вещей в HTML 5.
Элемент управления ListView был представлен в .Net Framework 3.5 как замена
устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его
предшественник, но в тоже время лишен некоторых внутренних механизмов, что
впрочем целиком следствие из расширенной универсальности ListView. Среди отличий
ListView и GridView можно назвать и гибкую настройку разметки, что позволяет
выводить данные не только в табличном виде, но и вообще в любом каком пожелает
программист. Благодаря шаблонам ItemTemplate, EditItemTemplate,
InsertItemTeplate можно настроить внешний вид при любом из состояний ListView:
редактировании или выборе элемента.
Компания Стимулсофт предоставляет для разработчиков мощный набор инструментов
для создания отчетов для Microsoft Visual Studio .Net 2005 и 2008; эти инструменты доступны
как для Windows Forms, так и для Web Forms. Это генератор отчетов Stimulsoft Reports.Net.
Генератор отчетов Stimulsoft Reports.Net имеет ряд особенностей: простая работа с дизайнером
отчетов, полная поддержка экспорта в PDF, Word, Excel и многие другие форматы. Crystal Report
и Microsoft Reporting Service – очень хорошие программные продукты для повседневной работы,
но, если Вам необходимо создать отчеты с поддержкой кросс-табов, drill down, Ajax, штрих-кодов
и возможностью подключения одновременно более одного источника данных, то Stimulsoft Reports.Net поможет Вам
сэкономить массу времени. Также, данный генератор отчетов позволяет пользователям создавать свои собственные
отчеты любой сложности. И все эти особенности делают Stimulsoft Reports.Net хорошим выбором в
сфере программных продуктов для Business Intelligence.