Файл hlpmore/hlpmore.cpp
#define STRICT #include <windows.h>
#include <mem.h>
#include <time.h>
#include "hlpmore.h"
BOOL RegisterEWclass(HMODULE hModule);
extern "C" LRESULT CALLBACK _export EWWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
char const szClassName[] = "EWHlpMoreClass";
// ======================================================== // Функция LibMain // Получает управление только один раз при // загрузке DLL-библиотеки в память // ======================================================== #pragma argsused int FAR PASCAL LibMain(HINSTANCE hModule, WORD wDataSegment, WORD wHeapSize, LPSTR lpszCmdLine) { // После инициализации локальной области данных // функция LibEntry фиксирует сегмент данных. // Его необходимо расфиксировать. if(wHeapSize != 0) // Расфиксируем сегмент данных UnlockData(0);
// Регистрируем класс для встроенного окна if(!RegisterEWclass(hModule)) return FALSE; else // Возвращаем TRUE. Это означает, что инициализация // DLL-библиотеки выполнена успешно return TRUE; }
// ======================================================== // Функция WEP // Получает управление только один раз при // удалении DLL-библиотеки из памяти // ======================================================== #pragma argsused int FAR PASCAL WEP(int bSystemExit) { return 1; }
// ======================================================== // Функция MsgBox // Выводит на экран диалоговую панель с сообщением // ======================================================== extern "C" void FAR PASCAL _export MsgBox(HWND hwnd, LPSTR szMsg) { MessageBox(hwnd, szMsg, "Message from DLL", MB_OK);
}
// ======================================================== // Функция RegisterEWclass // Регистрация класса для встроенного окна // ======================================================== BOOL RegisterEWclass(HMODULE hModule) { ATOM aWndClass; WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
// Класс окна доступен для всех приложений wc.style = CS_GLOBALCLASS;
wc.lpszMenuName = NULL; wc.lpfnWndProc = (WNDPROC) EWWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hModule; wc.hIcon = NULL; wc.hCursor = LoadCursor(hModule, "AppCursor");
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = (LPSTR)szClassName;
aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}
// ======================================================== // Функция EWWndProc // Функция встроенного окна // ======================================================== extern "C" LRESULT CALLBACK _export EWWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rc; TEXTMETRIC tm; int cxChar, cyChar; QCI qci; QRI qri; static BYTE szCustomData[80]; static BYTE szBuf[80];
#define CLOCK_TIMER 1
switch (msg) { // Создание встроенного окна case WM_CREATE: { // Копируем строку параметров qci = (QCI)((CREATESTRUCT FAR *)lParam)->
lpCreateParams; if(lstrlen(qci->
szAuthorData) < 80) lstrcpy(szCustomData, qci->
szAuthorData);
// Добавляем к встроенному окну рамку SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd,GWL_STYLE) | WS_BORDER);
// Создаем таймер для часов SetTimer(hwnd, CLOCK_TIMER, 1000, NULL);
return 0; }
// Сообщение от таймера case WM_TIMER: { // Перерисовываем встроенное окно InvalidateRect(hwnd, NULL, FALSE);
return 0; }
// Обработчик сообщения WM_PAINT case WM_PAINT: { int nBufSize; time_t t; struct tm *ltime; RECT rc;
hdc = BeginPaint(hwnd, &ps);
// Определяем время и его отдельные компоненты time(&t);
ltime = localtime(&t);
// Подготавливаем буфер, заполняя его // строкой с текущим временем nBufSize = wsprintf(szBuf, "%02d:%02d:%02d", ltime->
tm_hour, ltime->
tm_min, ltime->
tm_sec);
// Выбираем шрифт с фиксированной шириной букв SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
// Получаем координаты и размер окна GetClientRect(hwnd, &rc);
// Выбираем цвет текста для часов SetTextColor(ps.hdc, RGB(0,100,0));
// Выводим время в центре окна DrawText(hdc, (LPSTR)szBuf, nBufSize, &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);
EndPaint(hwnd, &ps);
return 0; }
// Обработчик этого сообщения должен определить // размеры встроенного окна case EWM_QUERYSIZE: { // Определяем метрики фиксированного шрифта hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &tm);
cxChar = tm.tmMaxCharWidth; cyChar = tm.tmHeight + tm.tmExternalLeading; ReleaseDC(hwnd, hdc);
// Сохраняем размеры встроенного окна ((LPPOINT)lParam)->
x = 10 * cxChar; ((LPPOINT)lParam)->
y = 2 * cyChar;
return 1; }
// В ответ на это сообщение функция встроенного окна // должна вернуть представление содержимого окна // в виде текста или битового изображения case EWM_RENDER: { long lPtr = 0l;
switch(wParam) { // Представление содержимого в виде текста case CF_TEXT: { // Заказываем глобальный блок памяти HGLOBAL hglb = GlobalAlloc(GMEM_MOVEABLE, 50);
if(hglb) { // Фиксируем блок памяти LPSTR lpszTempBuf = (LPSTR)GlobalLock(hglb);
// Копируем строку, содержащую текущее время lstrcpy(lpszTempBuf,"\r\n");
lstrcat(lpszTempBuf,szBuf);
lstrcat(lpszTempBuf,"\r\n");
// Расфиксируем блок памяти GlobalUnlock(hglb);
// Возвращаем идентификатор блока памяти, // содержащего текстовое представление lPtr = (long)hglb; } break; }
// Представление содержимого // в виде битового изображения case CF_BITMAP: { POINT pt; RECT rc; HBITMAP hbmp, hbmpOld; HBRUSH hbrush;
// Сохраняем указатель на структуру RENDERINFO qri = (QRI)lParam;
// Создаем контекст отображения, совместимый // с экраном монитора hdc = CreateCompatibleDC(NULL);
// Определяем размеры встроенного окна SendMessage(hwnd, EWM_QUERYSIZE, (WPARAM)hdc, (long)(LPPOINT)&pt);
// Создаем битовое изображение, имеющее размеры, // равные размерам встроенного окна rc.left = rc.top = 0; rc.right = pt.x; rc.bottom = pt.y; hbmp = CreateCompatibleBitmap(qri->
hdc, pt.x, pt.y);
// Выбираем битовое изображение // в контекст отображения hbmpOld = (HBITMAP)SelectObject(hdc,hbmp);
// Закрашиваем битовое изображение цветом фона hbrush = CreateSolidBrush(GetBkColor(hdc));
FillRect(hdc, &rc, hbrush);
DeleteObject(hbrush);
// Рисуем рамку по периметру битового изображения Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
// Рисуем время в центре битового изображения DrawText(hdc, (LPSTR)szBuf, lstrlen(szBuf), &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);
// Выбираем старое битовое изображение hbmp = (HBITMAP)SelectObject(hdc, hbmpOld);
DeleteDC(hdc);
// Возвращаем идентификатор созданного // битового изображения lPtr = (long)hbmp;
break; } }
return lPtr; }
// Обрабатываем сообщение от левой клавиши мыши case WM_LBUTTONDOWN: { if(IDYES == MessageBox(hwnd, "Запустить clock.exe?", "Help Sample", MB_YESNO | MB_ICONQUESTION)) WinExec("clock.exe", SW_SHOW);
#define IDN_CLOCK 200
// Во временном окне отображаем раздел справочной // системы с номером контекста, равным IDN_CLOCK WinHelp(hwnd, "hlpmore.hlp", HELP_CONTEXTPOPUP, (DWORD)IDN_CLOCK);
return 0; }
// При удалении встроенного окна уничтожаем таймер case WM_DESTROY: { KillTimer(hwnd, CLOCK_TIMER);
return 0; }
default: break; } return DefWindowProc(hwnd, msg, wParam, lParam);
}
Функция WinMain получает управление при загрузке DLL-библиотеки в память. Ее основная задача - регистрация класса встроенного окна, для чего вызывается функция RegisterEWclass, определенная в этой же библиотеке.
Функция RegisterEWclass выполняет обычную процедуру регистрации класса, однако при регистрации используется стиль CS_GLOBALCLASS. Класс, зарегистрированный с этим стилем, доступен для использования всеми запущенными приложениями - это глобальный класс.
Далее в исходном тексте DLL-библиотеки определена функция MsgBox, которая не имеет никакого отношения к встроенным окнам и используется как дополнительная макрокоманда, вызываемая при помощи ссылки из раздела оглавления. Задача функции MsgBox заключается в отображении сообщения, полученного ей через второй параметр.
Функция EWWndProc выполняет роль функции встроенного окна. Она обрабатывает все сообщения, поступающие от приложения winhelp.exe при создании, в процессе работы и при удалении встроенного окна.
Обработчик сообщения WM_CREATE копирует строку параметров, добавляет к окну рамку и создает таймер с периодом работы 1 сек.
Каждую секунду от таймера в функцию встроенного окна приходит сообщение WM_TIMER. Обработка этого сообщения заключается в полной перерисовке содержимого встроенного окна.
Обработчик сообщения WM_PAINT вызывается при создании окна и затем периодически, раз в секунду. Он рисует в центре окна текстовую строку текущего времени.
Обработчик сообщения EWM_QUERYSIZE возвращает размер окна, определяя его на основании метрик фиксированного системного шрифта.
Как мы уже говорили, когда приложению winhelp.exe требуется получить текстовое или графическое представление содержимого встроенного окна, оно посылает функции окна сообщение EWM_RENDER.
При запросе текстового представления наше приложение предоставляет идентификатор блока, содержащего текстовую строку, в которой записано текущее время. При запросе графического представления приложение создает битовое изображение и рисует на нем тот же самый текст, обводя его рамкой по периметру окна.
Все выполняемые при этом действия были описаны нами ранее в предыдущих томах "Библиотеки системного программиста", поэтому они должны быть понятны без дополнительных объяснений.
Обратим ваше внимание на обработчик сообщения WM_LBUTTONDOWN.
Этот обработчик был определен для обеспечения возможности использования встроенного окна для организации чувствительной точки (в исходном тексте раздела встроенное окно нельзя использовать для организации ссылки или запуска макрокоманд).
Когда пользователь расположит курсор мыши над поверхностью встроенного окна и нажмет левую клавишу мыши, функция встроенного окна получит обычное сообщение WM_LBUTTONDOWN. Обработчик этого сообщения выводит диалоговую панель с предложением запустить приложение clock.exe, при необходимости запускает это приложение, а затем вызывает функцию WinHelp.
Функция WinHelp используется для организации гипертекстовой ссылки на раздел с номером контекста IDN_CLOCK.Раздел будет отображен во временном окне.
Идентификаторы констант, а также все необходимые структуры данных определены в файле hlpmore.h (листинг 4.8).