Приложение 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