Операционная система Microsoft Windows 3.1 для программиста -том 3

         

Файл winhook/winhook.cpp


// ================================================ // Приложение WINHOOK // Простейший руссификатор клавиатуры // для Microsoft Windows // Работает совместно с DLL-библиотекой kbhook.dll // ================================================

#define STRICT #include <windows.h>
#include <windowsx.h>
#include <dos.h>
#include <mem.h>

#include "kbhook.hpp"

// ---------------------------------------------------- // Прототипы функций // ----------------------------------------------------

extern "C" void WINAPI _export SetKbHook(HWND hwnd);

extern "C" void WINAPI _export RemoveKbHook(void);

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// ---------------------------------------------------- // Глобальные переменные // ----------------------------------------------------

char const szClassName[] = "WINHOOKAppClass"; char const szWindowTitle[] = "WINHOOK Application";

TEXTMETRIC tm; int cxChar, cyChar; RECT rc; static BOOL bCyrillic = FALSE;

// ===================================== // Функция WinMain // ===================================== #pragma argsused

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения

// Можно запускать только одну копию приложения if(hPrevInstance) return 0;

// Инициализация копии приложения if(!InitApp(hInstance)) return FALSE;

// Получаем координаты окна Desktop. // Это окно занимает всю поверхность экрана // и на нем расположены все остальные окна GetWindowRect(GetDesktopWindow(), &rc);

// Создаем временное окно с толстой // рамкой для изменения размера, но без // заголовка и системного меню. // При создании окна указываем произвольные // размеры окна и произвольное расположение hwnd = CreateWindow( szClassName, szWindowTitle, WS_POPUPWINDOW | WS_THICKFRAME, 100, 100, 100, 100, 0, 0, hInstance, NULL);




if(!hwnd) return FALSE;

// Передвигаем окно в правый нижний // угол экрана и делаем его самым // верхним, т. е. это окно будет // всегда находиться над другими окнами SetWindowPos(hwnd, HWND_TOPMOST, rc.right - cxChar * 15, rc.bottom - cyChar * 3, cxChar * 10, cyChar * 2, 0);

// Отображаем окно в новом месте ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg);
} return msg.wParam; }

// ===================================== // Функция InitApp // =====================================

BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна

memset(&wc, 0, sizeof(wc));
wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szClassName;

aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}

// ===================================== // Функция WndProc // =====================================

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps;

switch (msg) { case WM_CREATE: { // Устанавливаем перехватчики SetKbHook(hwnd);

// Получаем контекст отображения hdc = GetDC(hwnd);

// Выбираем шрифт с фиксированной шириной букв SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

// Заполняем структуру информацией // о метрике шрифта, выбранного в // контекст отображения GetTextMetrics(hdc, &tm);

// Запоминаем значение ширины для // самого широкого символа cxChar = tm.tmMaxCharWidth;

// Запоминаем значение высоты букв с // учетом межстрочного интервала cyChar = tm.tmHeight + tm.tmExternalLeading;

ReleaseDC(hwnd, hdc);
return 0; }

// Для обеспечения возможности перемещения // окна, не имеющего заголовка, встраиваем // свой обработчик сообщения WM_NCHITTEST case WM_NCHITTEST: { long lRetVal;



// Вызываем функцию DefWindowProc и проверяем // возвращаемое ей значение lRetVal = DefWindowProc(hwnd, msg, wParam, lParam);

// Если курсор мыши находится на одном из // элементов толстой рамки, предназначенной // для изменения размера окна, возвращаем // неизмененное значение, полученное от // функции DefWindowProc if(lRetVal == HTLEFT lRetVal == HTRIGHT lRetVal == HTTOP lRetVal == HTBOTTOM lRetVal == HTBOTTOMRIGHT lRetVal == HTTOPRIGHT lRetVal == HTTOPLEFT lRetVal == HTBOTTOMLEFT) { return lRetVal; }

// В противном случае возвращаем значение // HTCAPTION, которое соответствует // заголовку окна. else { return HTCAPTION; } }

case WM_DESTROY: { // Перед завершением работы приложения // удаляем перехватчики RemoveKbHook();

PostQuitMessage(0);
return 0; }

// Это сообщение приходит от DLL-библиотеки // при переключении раскладки клавиатуры case WM_KBHOOK: { // Получаем флаг раскладки bCyrillic = (BOOL)wParam;

// Выдаем звуковой сигнал MessageBeep(0);

// Перерисовываем окно приложения InvalidateRect(hwnd, NULL, FALSE);
return 0; }

case WM_PAINT: { BYTE szBuf[10]; RECT rc;

// Получаем контекст отображения hdc = BeginPaint(hwnd, &ps);

// Выбираем шрифт с фиксированной шириной букв SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

// Получаем координаты и размер окна GetClientRect(hwnd, &rc);

// В зависимости от состояния флага раскладки // клавиатуры выбираем надпись для // отображения в окне if(bCyrillic) lstrcpy(szBuf, (LPCSTR)"CYRILLIC");
else lstrcpy(szBuf, (LPCSTR)"DEFAULT ");

// Выводим надпись в центре окна DrawText(hdc, (LPSTR)szBuf, 8, &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);

EndPaint(hwnd, &ps);
} } return DefWindowProc(hwnd, msg, wParam, lParam);
}

После запуска приложения функция WinMain определяет размеры окна DeskTop, которые равны размеру экрана, и создает главное окно приложения в виде временного окна с толстой рамкой для изменения размера без заголовка и системного меню (такое окно имеет стиль WS_POPUPWINDOW | WS_THICKFRAME).



Для того чтобы сделать окно "непотопляемым", а заодно и изменить его размеры, мы вызываем функцию SetWindowPos , передав ей в качестве второго параметра константу HWND_TOPMOST:

SetWindowPos(hwnd, HWND_TOPMOST, rc.right - cxChar * 15, rc.bottom - cyChar * 3, cxChar * 10, cyChar * 2, 0);

Функция SetWindowPos позволяет изменить размеры и расположение окна относительно экрана и относительно других окон:

BOOL WINAPI SetWindowPos( HWND hwnd, // идентификатор окна HWND hwndInsertAfter, // расположение окна int x, // горизонтальное положение int y, // вертикальное положение int cx, // ширина int cy, // высота UINT fuFlags);
// флаги расположения окна

Для параметра hwndInsertAfter, определяющего расположение окна относительно других окон, можно использовать следующие значения:

Значение Описание
HWND_BOTTOM Окно следует расположить под другими окнами
HWND_TOP Окно будет расположено над другими окнами
HWND_TOPMOST Окно следует расположить над всеми другими окнами, имеющими расположение HWND_TOPMOST
HWND_NOTOPMOST Окно будет расположено над всеми HWND_TOP-окнами, но под окном, имеющим расположение HWND_TOPMOST
Параметры x, y, cx и cy определяют, соответственно, горизонтальное и вертикальное расположение окна, его ширину и высоту.

Параметр fuFlags может принимать следующие значения:

Значение Описание
SWP_DRAWFRAME Следует нарисовать рамку, определенную в классе окна
SWP_HIDEWINDOW Окно будет скрыто
SWP_NOACTIVATE Окно не будет активизировано
SWP_NOMOVE Окно не будет перемещаться, при указании этого флага параметры x и y игнорируются
SWP_NOSIZE Окно не будет изменять свои размеры, параметры cx и cy игнорируются
SWP_NOREDRAW Не следует выполнять перерисовку окна. После перемещения приложение должно перерисовать окно самостоятельно
SWP_NOZORDER Не следует изменять расположение окна относительно других окон, параметр hwndInsertAfter игнорируется
SWP_SHOWWINDOW Отобразить окно
После перемещения и изменения расположения главного окна функция WinMain приложения WINHOOK отображает окно и запускает цикл обработки сообщений.



Функция главного окна приложения во время обработки сообщения WM_CREATE вызывает функцию SetKbHook, которая определена в созданной нами для этого приложения DLL-библиотеке kbhook.dll (исходные тексты этой библиотеки будут приведены ниже). Функция SetKbHook устанавливает два фильтра - типа WH_KEYBOARD и WH_GETMESSAGE. В качестве параметра этой функции передается идентификатор главного окна приложения WINHOOK. Когда пользователь переключает раскладку клавиатуры, DLL-библиотека, пользуясь этим идентификатором, пришлет в функцию окна приложения WINHOOK сообщение с кодом WM_KBHOOK.

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

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

Перед завершением работы приложение WINHOOK удаляет установленные ранее фильтры, для чего при обработке сообщения WM_DESTROY вызывается функция RemoveKbHook, определенная в DLL-библиотеке kbhook.dll.

Обработчик сообщения WM_KBHOOK, определенного в нашем приложении и в DLL-библиотеке, определяет номер используемой раскладки клавиатуры через параметр wParam. Полученное значение записывается в переменную флагов раскладки bCyrillic. Если этот флаг сброшен, используется стандартная раскладка клавиатуры, если установлен - дополнительная с символами кириллицы.

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

Перерисовку окна выполняет обработчик сообщения WM_PAINT. С помощью функции DrawText он пишет название раскладки клавиатуры (DEFAULT или CYRILLIC) в центре главного окна приложения.

Сообщение WM_KBHOOK определено в include-файле winhook.hpp (листинг 3.10).


Содержание раздела