Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы

         

Файл clipshow/clipshow.cpp


// ---------------------------------------- // Просмотр Clipboard // ---------------------------------------- #define STRICT #include <windows.h>
#include <mem.h>
#include "clipshow.hpp"

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawBitmap(HDC, int, int, HBITMAP);
void PrepareMetaFile(HDC, LPMETAFILEPICT, int, int);
void SizeToHiMetric(int*, int*);
void HiMetricToSize(int*, int*);
void HiMetricToSizeScaled(int*, int*, int, int);

char const szClassName[] = "ClipShowClass"; char const szWindowTitle[] = "Clipboard Show"; // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения

// Не разрешаем запуск нескольких копий приложения if(hPrevInstance) return FALSE;

if(!InitApp(hInstance)) return FALSE;

hwnd = CreateWindow( szClassName, szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL);
if(!hwnd) return FALSE;

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна memset(&wc, 0, sizeof(wc));
wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APP_ICON");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
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; RECT rc; static HWND hwndNextViewer; HGLOBAL hglbClipData; LPSTR lpszClipBuf; HBITMAP hBitmap; static HPALETTE hPal, hOldPal; static int cxClient, cyClient; HMETAFILE hmf; LPMETAFILEPICT lpmfp;

switch (msg) { case WM_CREATE: { // Наше окно становится окном просмотра Clipboard hwndNextViewer = SetClipboardViewer(hwnd);
return 0; } case WM_SIZE: { cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0; } case WM_PAINT: { hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);

// Открываем Clipboard OpenClipboard(hwnd);

// Читаем данные в формате метафайла hmf = (HMETAFILE)GetClipboardData(CF_METAFILEPICT);
if(hmf != NULL) { // Фиксируем память заголовка метафайла lpmfp = (LPMETAFILEPICT)GlobalLock(hmf);
if(lpmfp != NULL) { // Сохраняем контекст отображения SaveDC(hdc);

// Устанавливаем параметры контекста отображения // в соответствии с содержимым заголовка метафайла PrepareMetaFile(hdc, lpmfp, cxClient, cyClient);

// Проигрываем метафайл PlayMetaFile(hdc, lpmfp->
hMF);

// Восстанавливаем контекст отображения RestoreDC(hdc, -1);

// Освобождаем память заголовка метафайла GlobalUnlock(hmf);
} } else { // Читаем данные в формате CF_BITMAP hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
if(hBitmap != NULL) { // Читаем палитру hPal = (HPALETTE)GetClipboardData(CF_PALETTE);
if(hPal) { // Если Clipboard содержит палитру, // выбираем и реализуем ее hOldPal = SelectPalette(hdc, hPal, FALSE);
RealizePalette(hdc);
} // Рисуем изображение в окне DrawBitmap(hdc, 0, 0, hBitmap);
} // Читаем текстовые данные else { hglbClipData = GetClipboardData(CF_TEXT);
if(hglbClipData) { // Фиксируем блок памяти и рисуем текст lpszClipBuf = (LPSTR)GlobalLock(hglbClipData);
DrawText(hdc, lpszClipBuf, -1, &rc, DT_EXPANDTABS);



// Расфиксируем блок памяти GlobalUnlock(hglbClipData);
} } } // Закрываем Clipboard CloseClipboard();
EndPaint(hwnd, &ps);
} // Произошли изменения в цепочке окон просмотра // Clipboard case WM_CHANGECBCHAIN: { // Если идентификатор удаляемого окна просмотра // равен идентификатору следующего окна в цепочке, // запоминаем идентификатор нового следующего // окна просмотра if(wParam == (WPARAM)hwndNextViewer) hwndNextViewer = (HWND)LOWORD(lParam);

// Передаем сообщение следующему окну просмотра // по цепочке else if(hwndNextViewer) SendMessage(hwndNextViewer, msg, wParam, lParam);
return 0;

} // Содержимое Clipboard изменилось, наше приложение // должно перерисовать свое окно case WM_DRAWCLIPBOARD: { // Если наше окно не последнее в цепочке, // передаем сообщение дальше по цепочке if(hwndNextViewer) SendMessage(hwndNextViewer, msg, wParam, lParam);
InvalidateRect(hwnd, NULL, TRUE);
return 0; } case WM_COMMAND: { switch (wParam) { case CM_HELPABOUT: { MessageBox(hwnd, "Приложение CLIPSHOW\n(C) Фролов А.В., 1995", (LPSTR)szWindowTitle, MB_OK | MB_ICONINFORMATION);
return 0; } case CM_FILEEXIT: { DestroyWindow(hwnd);
return 0; } default: return 0; } } // Отслеживаем изменения палитры case WM_PALETTECHANGED: { if(hwnd == (HWND) wParam) break; } case WM_QUERYNEWPALETTE: { int nChanged;

hdc = GetDC(hwnd);
hOldPal = SelectPalette(hdc, hPal, (msg == WM_QUERYNEWPALETTE) ? FALSE : TRUE);
nChanged = RealizePalette(hdc);
SelectPalette(hdc, hOldPal, TRUE);
ReleaseDC(hwnd, hdc);
if(nChanged) InvalidateRect(hwnd, NULL, TRUE);
return nChanged; } // При завершении работы приложения изменяем // цепочку окон просмотра Clipboard, // удаляя из нее окно нашего приложения case WM_DESTROY: { ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam);
} // ===================================== // Функция DrawBitmap // ===================================== void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap) { HBITMAP hbm, hOldbm; HDC hMemDC; BITMAP bm; POINT ptSize, ptOrg;



hMemDC = CreateCompatibleDC(hDC);
hOldbm = (HBITMAP)SelectObject(hMemDC, hBitmap);
if (hOldbm) { SetMapMode(hMemDC, GetMapMode(hDC));
GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);

ptSize.x = bm.bmWidth; // ширина ptSize.y = bm.bmHeight; // высота DPtoLP(hDC, &ptSize, 1);

ptOrg.x = 0; ptOrg.y = 0; DPtoLP(hMemDC, &ptOrg, 1);

BitBlt(hDC, x, y, ptSize.x, ptSize.y, hMemDC, ptOrg.x, ptOrg.y, SRCCOPY);
SelectObject(hMemDC, hOldbm);
} DeleteDC(hMemDC);
} // ===================================== // Функция PrepareMetaFile // ===================================== void PrepareMetaFile( HDC hdc, LPMETAFILEPICT lpmfp, int cxClient, int cyClient) { int x, y;

// Устанавливаем такой режим отображения, // какой указан в заголовке метафайла SetMapMode(hdc, lpmfp->
mm);

// Для изотропного и анизотропного режима // анализируем поля xExt и yExt заголовка if(lpmfp->
mm == MM_ISOTROPIC lpmfp->
mm == MM_ANISOTROPIC) { // Если xExt равен нулю, устанавливаем // размеры изображения равными размерам // внутренней области окна if(lpmfp->
xExt == 0) SetViewportExtEx(hdc, cxClient, cyClient, NULL);

// Если xExt больше нуля, устанавливаем // размеры изображения в соответствии со значениями, // указанными в заголовке метафайла else if(lpmfp->
xExt >
0) { x = lpmfp->
xExt; y = lpmfp->
yExt;

// Выполняем преобразование из // сотых долей мм в пикселы HiMetricToSize(&x, &y);
SetViewportExtEx(hdc, x, y, NULL);
}

// Если xExt меньше нуля, сохраняем масштаб // изображения, разрешая изменения размеров else if(lpmfp->
xExt < 0) { x = -lpmfp->
xExt; y = -lpmfp->
yExt;

// Выполняем преобразование из // сотых долей мм в пикселы с учетом // размеров окна HiMetricToSizeScaled(&x, &y, cxClient, cyClient);
SetViewportExtEx(hdc, x, y, NULL);
} } // Для остальных режимов отображения устанавливаем // размеры изображения равными размерам, // указанным в заголовке метафайла else SetViewportExtEx(hdc, lpmfp->
xExt, lpmfp->
yExt, NULL);
} // ===================================== // Функция SizeToHiMetric // ===================================== void SizeToHiMetric(int *width, int *height) { HDC hDC = GetDC(0);



// Определяем количество пикселов в логическом // дюйме по горизонтали и по вертикали int dpiX = GetDeviceCaps(hDC, LOGPIXELSX);
int dpiY = GetDeviceCaps(hDC, LOGPIXELSY);

// Константа для преобразования const long HiMetricPerInch = 2540;

// Выполняем преобразование if(width) *width = int(*width * HiMetricPerInch / dpiX);
if(height) *height = int(*height * HiMetricPerInch / dpiY);

ReleaseDC(0, hDC);
} // ===================================== // Функция HiMetricToSize // ===================================== void HiMetricToSize(int *width, int *height) { HDC hDC = GetDC(0);
long int dpiX = GetDeviceCaps(hDC, LOGPIXELSX);
long int dpiY = GetDeviceCaps(hDC, LOGPIXELSY);
const long HiMetricPerInch = 2540;

if(width != NULL) *width = int(*width * dpiX / HiMetricPerInch);
if(height != NULL) *height = int(*height * dpiY / HiMetricPerInch);

ReleaseDC (0, hDC);
} // ===================================== // Функция HiMetricToSizeScaled // ===================================== void HiMetricToSizeScaled (int *width, int *height, int cxClient, int cyClient) { HDC hDC = GetDC(0);
long int dpiX = GetDeviceCaps(hDC, LOGPIXELSX);
long int dpiY = GetDeviceCaps(hDC, LOGPIXELSY);
const long HiMetricPerInch = 2540; int xPic, yPic;

// Определяем размеры в пикселах if (width != NULL) xPic = int(*width * dpiX / HiMetricPerInch);
if (height != NULL) yPic = int(*height * dpiY / HiMetricPerInch);

// Сравниваем коэффициенты масштабирования // по горизонтали и вертикали if((cxClient*100L) / xPic >
(cyClient*100L) / yPic) { // Если коэффициент масштабирования по // горизонтали больше, масштабируем // по горизонтали xPic = ((long)xPic * cyClient) / (long)yPic; yPic = cyClient; } else { // Иначе масштабируем по вертикали yPic = ((long)cxClient * yPic) / (long)xPic; xPic = cxClient; } ReleaseDC (0, hDC);

// Новые размеры *width = xPic; *height = yPic; }

Функция WinMain допускает запуск только одной копии приложения. Вы можете убрать это ограничение, однако в этом случае вам нужно будет предусмотреть способ раздельного хранения (для каждой копии приложения) идентификаторов палитр hPal и hOldPal.


Например, можно зарезервировать двойное слово в классе окна и использовать его для этой цели.

При создании главного окна приложения обработчик сообщения WM_CREATE добавляет это окно в список окон просмотра Clipboard, вызывая функцию SetClipboardViewer. Идентификатор следующего в цепочке окна просмотра сохраняется в переменной hwndNextViewer.

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

Извлечение данных из Clipboard и их рисование выполняется во время обработки сообщения WM_PAINT. При этом используются описанные нами ранее процедуры.

Вначале приложение проверяет, содержит ли Clipboard данные в формате метафайла. Если да, то данные извлекаются и отображаются в окне приложения. Если нет, проверяется формат CF_BITMAP.

Если доступен формат CF_BITMAP, выполняется попытка чтения из Clipboard палитры, сопровождающей битовое изображение DDB. Найденная палитра выбирается в контекст отображения и реализуется. Затем обработчик сообщения WM_PAINT рисует битовое изображение, вызывая функцию DrawBitmap.

В том случае, когда ни один из перечисленных выше форматов недоступен, выполняется попытка прочитать данные в текстовом формате. В случае успеха прочитанный текст рисуется в окне функцией DrawText.

После завершения работы с Clipboard приложение вызывает функцию CloseClipboard, закрывая Clipboard.

Обработчики сообщений WM_CHANGECBCHAIN и WM_DRAWCLIPBOARD были описаны ранее. Их задачей является, соответственно, отслеживание изменений в списке окон просмотра Clipboard и изменений содержимого Clipboard.

Наше приложение отслеживает также изменения в системной палитре, обрабатывая сообщения WM_PALETTECHANGED и WM_QUERYNEWPALETTE. Соответствующие алгоритмы описаны в 14 томе "Библиотеки системного программиста" в главе "Цвет и цветовые палитры".

Обработчик сообщения WM_DESTROY удаляет главное окно приложения из списка окон просмотра Clipboard, вызывая функцию ChangeClipboardChain.

Файл clipshow.hpp содержит определения идентификаторов строк меню (листинг 2.8).


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