Графический интерфейс GDI в Microsoft Windows© Александр Фролов, Григорий ФроловТом 14, М.: Диалог-МИФИ, 1993, 288 стр. 6.4. Приложение PRNFILEДля иллюстрации всего сказанного выше мы немного изменили приложение TEDIT, описанное в 12 томе "Библиотеки системного программиста", добавив в него возможность печати. В главном окне этого простейшего редактора текста появилась кнопка "Print", с помощью которой вы можете распечатать текст на любом установленном в системе принтере (рис. 6.5).
Рис. 6.5. Главное окно приложения PRNFILE Исходный основного файла приложения приведен в листинге 6.1. Листинг 6.1. Файл prnfile/prnfile.cpp
// ----------------------------------------
// Редактор текстовых файлов с возможностью печати
// ----------------------------------------
#define STRICT
#include <windows.h>
#include <commdlg.h>
#include <mem.h>
#include <string.h>
#include <stdlib.h>
// Идентификатор редактора текста
#define ID_EDIT 1
// Идентификаторы кнопок
#define ID_NEW 2
#define ID_OPEN 3
#define ID_SAVE 4
#define ID_PRINT 5
#define ID_EXIT 6
// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
HFILE OpenFile(void);
HFILE OpenSaveFile(void);
int PrintFile(HWND, NPSTR, WORD);
// Имя класса окна
char const szClassName[] = "TEditAppClass";
// Заголовок окна
char const szWindowTitle[] = "Text Editor";
// Идентификатор копии приложения
HINSTANCE hInst;
// Флаг изменений в тексте
BOOL bUpdate;
// =====================================
// Функция WinMain
// =====================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg; // структура для работы с сообщениями
HWND hwnd; // идентификатор главного окна приложения
// Инициализируем приложение
if(!InitApp(hInstance))
return FALSE;
// Сохраняем идентификатор копии приложения
// в глобальной переменной
hInst = hInstance;
// После успешной инициализации приложения создаем
// главное окно приложения
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.style = 0;
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)
{
// Идентификатор редактора текста
static HWND hEdit;
// Идентификаторы кнопок
static HWND hButtNew;
static HWND hButtOpen;
static HWND hButtSave;
static HWND hButtPrint;
static HWND hButtExit;
// Идентификаторы файлов
static HFILE hfSrcFile, hfDstFile;
switch (msg)
{
case WM_CREATE:
{
// Создаем редактор текста
hEdit = CreateWindow("edit", NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER |
WS_HSCROLL | WS_VSCROLL |
ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
ES_MULTILINE,
0, 0, 0, 0,
hwnd, (HMENU) ID_EDIT, hInst, NULL);
// Устанавливаем максимальную длину
// редактируемого текста, равную 32000 байт
SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);
// Сбрасываем флаг обновления текста
bUpdate = FALSE;
// Создаем кнопки
hButtNew = CreateWindow("button", "New",
WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,
0, 0, 80, 20,
hwnd, (HMENU) ID_NEW, hInst, NULL);
hButtOpen = CreateWindow("button", "Open",
WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,
80, 0, 80, 20,
hwnd, (HMENU) ID_OPEN, hInst, NULL);
hButtSave = CreateWindow("button", "Save",
WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,
160, 0, 80, 20,
hwnd, (HMENU) ID_SAVE, hInst, NULL);
hButtPrint = CreateWindow("button", "Print",
WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,
240, 0, 80, 20,
hwnd, (HMENU) ID_PRINT, hInst, NULL);
hButtExit = CreateWindow("button", "Exit",
WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,
320, 0, 80, 20,
hwnd, (HMENU) ID_EXIT, hInst, NULL);
return 0;
}
case WM_SIZE:
{
// Устанавливаем размер органа управления
// (текстового редактора) в соответствии
// с размерами главного окна приложения
MoveWindow(hEdit, 0, 20, LOWORD(lParam),
HIWORD(lParam) - 20, TRUE);
return 0;
}
// Когда главное окно приложения получает
// фокус ввода, отдаем фокус редактору текста
case WM_SETFOCUS:
{
SetFocus(hEdit);
return 0;
}
case WM_COMMAND:
{
// Обработка извещений текстового редактора
if(wParam == ID_EDIT)
{
// Ошибка
if(HIWORD(lParam) == EN_ERRSPACE)
{
MessageBox(hwnd, "Мало памяти",
szWindowTitle, MB_OK);
}
// Произошло изменение в редактируемом
// тексте
else if(HIWORD(lParam) == EN_UPDATE)
{
// Устанавливаем флаг обновления текста
bUpdate = TRUE;
}
return 0;
}
// Нажата кнопка сохранения текста
else if(wParam == ID_SAVE)
{
WORD wSize;
HANDLE hTxtBuf;
NPSTR npTextBuffer;
// Открываем выходной файл
hfDstFile = OpenSaveFile();
if(!hfDstFile) return 0;
// Определяем размер текста
wSize = GetWindowTextLength(hEdit);
// Получаем идентификатор блока памяти,
// в котором находится редактируемый текст
hTxtBuf =
(HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);
// Фиксируем блок памяти и получаем указатель
// на него
npTextBuffer = (NPSTR)LocalLock(hTxtBuf);
// Записываем содержимое блока памяти в файл
if(wSize !=
_lwrite(hfDstFile, npTextBuffer, wSize))
{
// При ошибке закрываем файл и выдаем сообщение
_lclose(hfDstFile);
MessageBox(hwnd, "Ошибка при записи файла",
szWindowTitle, MB_OK);
return 0;
}
// Закрываем файл
_lclose(hfDstFile);
// Расфиксируем блок памяти
LocalUnlock(hTxtBuf);
// Так как файл был только что сохранен,
// сбрасываем флаг обновления
bUpdate = FALSE;
SetFocus(hEdit);
return 0;
}
// Создание нового файла
else if(wParam == ID_NEW)
{
// Проверяем флаг обновления
if(bUpdate)
{
if(IDYES == MessageBox(hwnd,
"Файл был изменен. Желаете сохранить?",
szWindowTitle, MB_YESNO | MB_ICONQUESTION))
return 0;
}
// Сбрасываем содержимое текстового редактора
SetWindowText(hEdit, "\0");
// Сбрасываем флаг обновления
bUpdate = FALSE;
SetFocus(hEdit);
return 0;
}
// Загрузка файла для редактирования
else if(wParam == ID_OPEN)
{
LPSTR lpTextBuffer;
DWORD dwFileSize, dwCurrentPos;
// Проверяем флаг обновления
if(bUpdate)
{
if(IDYES == MessageBox(hwnd,
"Файл был изменен. Желаете сохранить?",
szWindowTitle, MB_YESNO | MB_ICONQUESTION))
return 0;
}
// Открываем входной файл.
hfSrcFile = OpenFile();
if(!hfSrcFile) return 0;
// Определяем размер файла
dwCurrentPos = _llseek(hfSrcFile, 0L, 1);
dwFileSize = _llseek(hfSrcFile, 0L, 2);
_llseek(hfSrcFile, dwCurrentPos, 0);
// Размер файла не должен превосходить 32000 байт
if(dwFileSize >= 32000)
{
_lclose(hfSrcFile);
MessageBox(hwnd, "Размер файла больше 32000 байт",
szWindowTitle, MB_OK);
return 0;
}
// Заказываем память для загрузки файла
lpTextBuffer = (LPSTR)malloc(32000);
if(lpTextBuffer == NULL)
return 0;
// Загружаем текст из файла в буфер
_lread(hfSrcFile, lpTextBuffer, dwFileSize);
// Закрываем буфер двоичным нулем
lpTextBuffer[(WORD)dwFileSize] = '\0';
// Закрываем файл
_lclose(hfSrcFile);
// Переносим содержимое буфера в
// текстовый редактор
SetWindowText(hEdit, lpTextBuffer);
// Освобождаем буфер
free((void *)lpTextBuffer);
// сбрасываем флаг обновления
bUpdate = FALSE;
SetFocus(hEdit);
return 0;
}
// ------------------------------------------
// Печать текста
// ------------------------------------------
else if(wParam == ID_PRINT)
{
WORD wSize;
HANDLE hTxtBuf;
NPSTR npTextBuffer;
// Определяем размер текста
wSize = GetWindowTextLength(hEdit);
// Получаем идентификатор блока памяти,
// в котором находится редактируемый текст
hTxtBuf =
(HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);
// Фиксируем блок памяти и получаем указатель
// на него
npTextBuffer = (NPSTR)LocalLock(hTxtBuf);
PrintFile(hwnd, npTextBuffer, wSize);
// Расфиксируем блок памяти
LocalUnlock(hTxtBuf);
SetFocus(hEdit);
return 0;
}
else if(wParam == ID_EXIT)
{
// Проверяем флаг обновления
if(bUpdate)
{
if(IDYES == MessageBox(hwnd,
"Файл был изменен. Желаете сохранить?",
szWindowTitle, MB_YESNO | MB_ICONQUESTION))
return 0;
}
// Посылаем в функцию главного окна
// сообщение WM_CLOSE
SendMessage(hwnd, WM_CLOSE, 0, 0L);
return 0;
}
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// -------------------------------
// Функция OpenFile
// Сохранение файла
// -------------------------------
HFILE OpenFile(void)
{
// Структура для выбора файла
OPENFILENAME ofn;
// Буфер для записи пути к выбранному файлу
char szFile[256];
// Буфер для записи имени выбранного файла
char szFileTitle[256];
// Фильтр расширений имени файлов
char szFilter[256] =
"Text Files\0*.txt;*.doc\0Any Files\0*.*\0";
// Идентификатор открываемого файла
HFILE hf;
// Инициализация имени выбираемого файла
// не нужна, поэтому создаем пустую строку
szFile[0] = '\0';
// Записываем нулевые значения во все поля
// структуры, которая будет использована для
// выбора файла
memset(&ofn, 0, sizeof(OPENFILENAME));
// Инициализируем нужные нам поля
// Размер структуры
ofn.lStructSize = sizeof(OPENFILENAME);
// Идентификатор окна
ofn.hwndOwner = NULL;
// Адрес строки фильтра
ofn.lpstrFilter = szFilter;
// Номер позиции выбора
ofn.nFilterIndex = 1;
// Адрес буфера для записи пути
// выбранного файла
ofn.lpstrFile = szFile;
// Размер буфера для записи пути
// выбранного файла
ofn.nMaxFile = sizeof(szFile);
// Адрес буфера для записи имени
// выбранного файла
ofn.lpstrFileTitle = szFileTitle;
// Размер буфера для записи имени
// выбранного файла
ofn.nMaxFileTitle = sizeof(szFileTitle);
// В качестве начального каталога для
// поиска выбираем текущий каталог
ofn.lpstrInitialDir = NULL;
// Определяем режимы выбора файла
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
| OFN_HIDEREADONLY;
// Выбираем входной файл
if (GetOpenFileName(&ofn)) {
// Открываем выбранный файл
hf = _lopen(ofn.lpstrFile, OF_READ);
// Возвращаем идентификатор файла
return hf;
}
// При отказе от выбора возвращаем
// нулевое значение
else return 0;
}
// -------------------------------
// Функция OpenSaveFile
// Выбор файла для редактирования
// -------------------------------
HFILE OpenSaveFile(void)
{
OPENFILENAME ofn;
char szFile[256];
char szFileTitle[256];
char szFilter[256] =
"Text Files\0*.txt\0Any Files\0*.*\0";
HFILE hf;
szFile[0] = '\0';
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_HIDEREADONLY;
// Выбираем выходной файл
if (GetSaveFileName(&ofn)) {
// При необходимости создаем файл
hf = _lcreat(ofn.lpstrFile, 0);
return hf;
}
else return 0;
}
Подробное описание этого файла вы найдете в 12 томе, здесь же для экономии места мы расскажем только о фрагменте, выполняющем печать. Когда вы нажимаете кнопку "Print", соответствующий обработчик определяет размер текста, загруженного в редактор, вызывая функцию GetWindowTextLength: wSize = GetWindowTextLength(hEdit); Далее он получает адрес блока памяти, содержащий текст, фиксируя его: hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L); npTextBuffer = (NPSTR)LocalLock(hTxtBuf); После этого вызывается функция печати PrintFile, определенная в файле print.cpp (листинг 6.2): PrintFile(hwnd, npTextBuffer, wSize); В качестве параметров этой функции передаются идентификатор окна приложения, адрес буфера, содержащего печатаемый текст, и размер этого буфера в байтах. После выполнения печати буфер расфиксируется, после чего редактор текста получает фокус ввода: LocalUnlock(hTxtBuf); SetFocus(hEdit); return 0; Все функции, предназначенные для работы с принтером, мы вынесли в отдельный файл (листинг 6.2). Листинг 6.2. Файл prnfile/print.cpp
// ----------------------------------------------------
// Функции для работы с принтером
// ----------------------------------------------------
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <string.h>
#include "prnfile.hpp"
// Прототипы функций
BOOL CALLBACK _export
AbortDlgFunc(HWND hdlg, UINT msg,
WPARAM wParam, LPARAM lParam);
BOOL CALLBACK _export AbortFunc(HDC hdc, int nCode);
HDC GetPrinterDC(HWND);
// Внешние глобальные переменные
extern HWND hdlgAbort;
extern BOOL fAbort;
BOOL fAbort = FALSE;
HWND hdlgAbort = 0;
static PRINTDLG pd;
// ----------------------------------------------------
// Функция PrintFile
// Печать файла
// ----------------------------------------------------
BOOL PrintFile(HWND hwnd, NPSTR npBuff, WORD wSize)
{
HDC hdc;
int cyPage;
int cyChar, yPos, nLength;
int i;
WORD wCurPos = 0;
TEXTMETRIC tm;
ABORTPROC lpAbortFunc;
BOOL fDone;
char abBuffer[256];
DOCINFO docinfo;
DLGPROC lpAbortDlgFunc;
HINSTANCE hInst;
int rc;
// Получаем контекст устройства для принтера
hdc = GetPrinterDC(hwnd);
// Определяем разрешение принтера по вертикали
cyPage = GetDeviceCaps(hdc, VERTRES);
// Определяем метрики текста
GetTextMetrics(hdc, &tm);
// Вычисляем высоту шрифта
cyChar = tm.tmHeight + tm.tmExternalLeading;
// Создаем переходник для функции AbortFunc
hInst = GetWindowInstance(hwnd);
lpAbortFunc =
(ABORTPROC)MakeProcInstance((FARPROC)AbortFunc, hInst);
// Устанавливаем функцию AbortProc
rc = SetAbortProc(hdc, lpAbortFunc);
if(rc <= 0)
{
DeleteDC(hdc);
return FALSE;
}
// Создаем переходник для функции диалога
lpAbortDlgFunc =
(DLGPROC)MakeProcInstance((FARPROC)AbortDlgFunc, hInst);
// Создаем диалог для отмены печати
hdlgAbort = CreateDialogParam (
hInst, MAKEINTRESOURCE(IDD_ABORT),
hwnd, lpAbortDlgFunc, NULL) ;
if(!hdlgAbort)
{
FreeProcInstance((FARPROC)lpAbortFunc);
DeleteDC(hdc);
return FALSE;
}
// Отображаем созданную диалоговую панель
ShowWindow(hdlgAbort, SW_SHOWNORMAL);
UpdateWindow(hdlgAbort);
// Переводим окно приложения в неактивное
// состояние
EnableWindow(hwnd, FALSE);
// Заполняем структуру docinfo
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = NULL;
docinfo.lpszOutput = NULL;
// Начинаем печать документа
rc = StartDoc(hdc, &docinfo);
if(rc <= 0)
{
DestroyWindow(hdlgAbort);
FreeProcInstance((FARPROC)lpAbortFunc);
FreeProcInstance((FARPROC)lpAbortDlgFunc);
DeleteDC(hdc);
return FALSE;
}
// Флаг завершения печати документа
fDone = FALSE;
// Цикл печати страниц документа
while(!fDone && !fAbort)
{
// Начинаем печать страницы документа
StartPage(hdc);
// Начальная позиция по вертикали
yPos = 0;
// Цикл по строкам страницы
while(yPos + cyChar < cyPage)
{
// Проверка завершения печати страницы
if(wCurPos > wSize)
{
fDone = TRUE;
break;
}
i=0;
nLength = 0;
// Цикл по строке
// Копируем строку в буфер abBuffer
while((npBuff[wCurPos] != 0x0d) &&
(wCurPos < wSize))
{
abBuffer[i] = npBuff[wCurPos];
i++;
wCurPos++;
nLength++;
}
// Рисуем одну строку текста
TextOut(hdc, 0, yPos, abBuffer, nLength);
// Переходим к следующей строке
wCurPos += 2;
yPos += cyChar ;
}
// Инициируем печать страницы
rc = EndPage(hdc);
if(rc < 0)
{
fAbort = TRUE;
break;
}
}
// При аварийном завершении печати вызываем
// функцию AbortDoc, при нормальном - EndDoc
if(fAbort)
AbortDoc(hdc);
else
EndDoc(hdc);
// Активизируем главное окно приложения
EnableWindow(hwnd, TRUE);
// Удаляем диалоговую панель
DestroyWindow(hdlgAbort);
// Освобождаем ресурсы
FreeProcInstance((FARPROC)lpAbortFunc);
FreeProcInstance((FARPROC)lpAbortDlgFunc);
DeleteDC(hdc);
return TRUE ;
}
// ----------------------------------------------------
// Функция AbortDlgFunc
// Функция диалога для диалоговой панели,
// позволяющей прервать процесс печати
// ----------------------------------------------------
#pragma argsused
BOOL CALLBACK _export
AbortDlgFunc(HWND hdlg,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
// Инициализируем флаги
case WM_INITDIALOG:
{
fAbort = FALSE;
hdlgAbort = hdlg;
return TRUE;
}
case WM_COMMAND:
{
// Устанавливаем флаг аварийного завершения печати
if (wParam == IDOK || wParam == IDCANCEL)
{
fAbort = TRUE;
return TRUE;
}
return FALSE;
}
case WM_DESTROY:
{
hdlgAbort = 0;
return FALSE;
}
}
return FALSE;
}
// ----------------------------------------------------
// Функция AbortFunc
// Обеспечивает возможность работы других
// приложений во время печати
// ----------------------------------------------------
#pragma argsused
BOOL CALLBACK _export
AbortFunc(HDC hdc, int nCode)
{
MSG msg;
// Второй цикл обработки сообщений
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if(!hdlgAbort || !IsDialogMessage (hdlgAbort, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(!fAbort);
}
// ----------------------------------------------------
// Функция GetPrinterDC
// Выводит на экран диалоговую панель "Print",
// с помощью которой можно выбрать принтер.
// Возвращает идентификатор контекста для
// выбранного принтера
// ----------------------------------------------------
HDC GetPrinterDC(HWND hwnd)
{
BOOL fResult;
// Инициализируем структуру PRINTDLG
memset(&pd, 0, sizeof(PRINTDLG));
pd.lStructSize = sizeof(PRINTDLG);
pd.hwndOwner = hwnd;
pd.Flags = PD_RETURNDC;
// Отображаем диалоговую панель
fResult = PrintDlg(&pd);
// При необходимости освобождаем память, полученную
// функцией PrintDlg для структур DEVMODE и DEVNAMES
if(pd.hDevMode != 0)
GlobalFree (pd.hDevMode);
if(pd.hDevNames != 0)
GlobalFree (pd.hDevNames);
// В случае успешного завершения возвращаем
// контекст принтера
if(fResult)
return pd.hDC;
else
return 0;
}
Функция PrintFile выполняет печать файла, загруженного в текстовый редактор. Она получает контекст печати, вызывая функцию GetPrinterDC, определенную в этом же файле. Функция GetPrinterDC выводит на экран стандартную диалоговую панель "Print", позволяющую выбрать принтер и задать параметры для выбранного принтера. Далее определяется разрешение принтера по вертикали, метрики текста для шрифта, выбранного в контекст принтера, вычисляется высота шрифта. Вся эта информация необходима для правильного расположения строк текста на листе бумаги. После этого функция PrintFile создает переходник для функции отмены печати и подключает последнюю, вызывая функцию SetAbortProc. Далее создается переходник для функции диалога отмены печати, затем создается и выводится на экран диалоговая панель отмены печати. Шаблон диалоговой панели определен в файле ресурсов и содержит единственную кнопку с надписью "Cancel". После отображения этой панели главное окно приложения переводится в неактивное состояние для передачи фокуса ввода диалоговой панели отмены печати. Перед началом печати заполняется структура DOCINFO и вызывается функция StartDoc. В цикле печати страниц документа выполняется проверка глобального флага отмены печати, а также флага завершения печати fDone. Перед началом печати каждой страницы вызывается функция StartPage. Далее выполняется построчное копирование текста из буфера текстового редактора в буфер abBuffer с последующим выводом содержимого этого буфера на принтер функцией TextOut. Печать страницы выполняется после вызова функции EndPage. При нормальном завершении процесса печати вызывается функция EndDoc, а при аварийном - AbortDoc. После нормального или аварийного завершения печати документа активизируется главное окно приложения, а диалоговая панель отмены печати удаляется. Вслед за этим освобождаются созданные переходники и контекст принтера. Идентификатор диалоговой панели определен в файле prnfile.hpp (листинг 6.3). Листинг 6.3. Файл prnfile/prnfile.hpp #define IDD_ABORT 25 Шаблон диалоговой панели отмены печати определен в файле описания ресурсов приложения (листинг 6.4). Листинг 6.4. Файл prnfile/prnfile.rc
#include <g:\tcwin\include\windows.h>
#include "prnfile.hpp"
IDD_ABORT DIALOG 50, 30, 89, 43
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Печать..."
BEGIN
CONTROL "Cancel", IDCANCEL, "BUTTON",
WS_GROUP, 29, 23, 32, 14
CTEXT "Cancel - отмена печати",
-1, -1, 8, 90, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
END
Файл определения модуля приведен в листинге 6.5. Листинг 6.5. Файл prnfile/prnfile.def ; ============================= ; Файл определения модуля ; ============================= NAME PRNFILE DESCRIPTION 'Приложение PRNFILE, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple |

