- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 32 бита
- •Выделение памяти
- •Библиотечные функции C
- •Фундаментальное выделение памяти в Windows 95
- •Перемещаемая память
- •Удаляемая память
- •Другие функции и флаги
- •Хорошо ли это?
- •Функции работы с "кучей"
- •Файловый ввод/вывод
- •Старый путь
- •Отличия Windows 95
- •Ввод/вывод с использованием файлов, проецируемых в память
- •Режимы многозадачности
- •Многозадачность в DOS
- •Невытесняющая многозадачность
- •Решения, использующие многопоточность
- •Многопоточная архитектура
- •Коллизии, возникающие при использовании потоков
- •Преимущества Windows
- •Новая программа! Усовершенствованная программа! Многопоточная!
- •Многопоточность в Windows 95
- •И снова случайные прямоугольники
- •Задание на конкурсе программистов
- •Решение с использованием многопоточности
- •О пользе использования функции Sleep
- •Синхронизация потоков
- •Критический раздел
- •Объект Mutex
- •Программа BIGJOB1
- •Объект Event
- •Локальная память потока
- •Печать, буферизация и функции печати
- •Контекст принтера
- •Формирование параметров для функции CreateDC
- •Измененная программа DEVCAPS
- •Вызов функции PrinterProperties
- •Проверка возможности работы с битовыми блоками (BitBlt)
- •Программа FORMFEED
- •Печать графики и текста
- •Каркас программы печати
- •Прерывание печати с помощью процедуры Abort
- •Реализация процедуры прерывания
- •Добавление диалогового окна печати
- •Добавление печати к программе POPPAD
- •Обработка кодов ошибок
- •Техника разбиения на полосы
- •Разбиение на полосы
- •Реализация разбиения страницы на полосы
- •Принтер и шрифты
- •Глава 16 Буфер обмена
- •Простое использование буфера обмена
- •Стандартные форматы данных буфера обмена
- •Передача текста в буфер обмена
- •Получение текста из буфера обмена
- •Открытие и закрытие буфера обмена
- •Использование буфера обмена с битовыми образами
- •Метафайл и картина метафайла
- •Более сложное использование буфера обмена
- •Использование нескольких элементов данных
- •Отложенное исполнение
- •Нестандартные форматы данных
- •Соответствующая программа просмотра буфера обмена
- •Цепочка программ просмотра буфера обмена
- •Функции и сообщения программы просмотра буфера обмена
- •Простая программа просмотра буфера обмена
- •Основные концепции
- •Приложение, раздел и элемент
- •Типы диалогов
- •Символьные строки и атомы
- •Программа сервер DDE
- •Программа DDEPOP1
- •Сообщение WM_DDE_INITIATE
- •Оконная процедура ServerProc
- •Функция PostDataMessage программы DDEPOP1
- •Сообщение WM_DDE_ADVISE
- •Обновление элементов данных
- •Сообщение WM_DDE_UNADVISE
- •Сообщение WM_DDE_TERMINATE
- •Программа-клиент DDE
- •Инициирование диалога DDE
- •Сообщение WM_DDE_DATA
- •Сообщение WM_DDE_TERMINATE
- •Управляющая библиотека DDE
- •Концептуальные различия
- •Реализация DDE с помощью DDEML
- •Элементы MDI
- •Windows 95 и MDI
- •Пример программы
- •Три меню
- •Инициализация программы
- •Создание дочерних окон
- •Дополнительная информация об обработке сообщений в главном окне
- •Дочерние окна документов
- •Освобождение захваченных ресурсов
- •Сила оконной процедуры
- •Основы библиотек
- •Библиотека: одно слово, множество значений
- •Пример простой DLL
- •Разделяемая память в DLL
- •Библиотека STRLIB
- •Точка входа/выхода библиотеки
- •Программа STRPROG
- •Работа программы STRPROG
- •Разделение данных между экземплярами программы STRPROG
- •Некоторые ограничения библиотек
- •Динамическое связывание без импорта
- •Библиотеки, содержащие только ресурсы
- •Глава 20 Что такое OLE?
- •Основы OLE
- •Связь с библиотеками OLE
- •Расшифровка кода результата
- •Интерфейсы модели составного объекта (COM-интерфейсы)
- •Услуги интерфейса IUnknown
- •Является ли OLE спецификацией клиент/сервер?
- •Сервер закрытого компонента
- •IMALLOC.DLL
- •Теперь о макросах
- •Услуги, предоставляемые интерфейсом IUnknown
- •Клиент закрытого компонента
- •Сервер открытого компонента
- •Назначение реестра
- •Способы генерации и использования идентификаторов CLSID
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
70
Если вы уже писали простые программы для прогона бумаги в MS-DOS, то знаете, что для большинства принтеров ASCII-кодом для прогона бумаги является число 12. Почему бы просто не открыть порт принтера с помощью функции GDI OpenPrinter и затем вывести в качестве выходных данных ASCII-код 12 с помощью функции write? Конечно, ничто не мешает нам сделать это. Если необходимо определить параллельный или последовательный порт, к которому подключен принтер, то следует использовать функцию GetPrinter с идентификатором принтера, полученным в качестве возвращаемого значения функции OpenPrinter, и считать идентификатор порта из структуры PRINTER_INFO_5. Затем необходимо определить, не используется ли принтер в данный момент другой программой. (Чтобы прогон бумаги не случился при печати какого-нибудь документа.) И наконец, необходимо определить, действительно ли ASCII-код 12 является управляющим символом прогона бумаги в подключенном принтере. Он является таковым, но не для всех принтеров. Например, команда прогона бумаги в PostScript равна не 12, а ключевому слову showpage.
Короче говоря, не пытайтесь даже думать о том, чтобы пойти в обход Windows; лучше разберитесь с функциями Windows для печати.
Печать графики и текста
Печать из программы для Windows обычно представляет собой большее число действий, чем это показано в программе FORMFEED. Давайте напишем программу, которая печатает одну страницу текста и графики. Начнем также, как в программе FORMFEED, а затем кое-что добавим. Мы рассмотрим четыре версии этой программы PRINT1, PRINT2, PRINT3 и PRINT4. Чтобы избежать бесполезного дублирования, в каждой из этих программ будут использоваться функции из файла PRINT.С, который представлен на рис. 15.5.
PRINT.C
/*------------------------------------------------------------------- |
|
PRINT.C -- |
Common routines for Print1, Print2, Print3, and Print4 |
------------------------------------------------------------------- |
*/ |
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL PrintMyPage(HWND);
extern HINSTANCE |
hInst; |
extern char |
szAppName[]; |
extern char |
szCaption[]; |
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
PSTR szCmdLine, int iCmdShow) |
|
{ |
|
|
HWND |
hwnd; |
|
MSG |
msg; |
|
WNDCLASSEX wndclass; |
|
|
wndclass.cbSize |
= sizeof(wndclass); |
|
wndclass.style |
= CS_HREDRAW | CS_VREDRAW; |
|
wndclass.lpfnWndProc |
= WndProc; |
|
wndclass.cbClsExtra |
= 0; |
|
wndclass.cbWndExtra |
= 0; |
|
wndclass.hInstance |
= hInstance; |
|
wndclass.hIcon |
= LoadIcon(NULL, IDI_APPLICATION); |
|
wndclass.hCursor |
= LoadCursor(NULL, IDC_ARROW); |
|
wndclass.hbrBackground |
=(HBRUSH) GetStockObject(WHITE_BRUSH); |
|
wndclass.lpszMenuName |
= NULL; |
|
wndclass.lpszClassName |
= szAppName; |
|
wndclass.hIconSm |
= LoadIcon(NULL, IDI_APPLICATION); |
RegisterClassEx(&wndclass);
hInst = hInstance;
hwnd = CreateWindow(szAppName, szCaption, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
71
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
HDC GetPrinterDC(void)
{
PRINTER_INFO_5 pinfo5[3];
DWORD dwNeeded, dwReturned;
if(EnumPrinters(PRINTER_ENUM_DEFAULT, NULL, 5,(LPBYTE) pinfo5, sizeof(pinfo5), &dwNeeded, &dwReturned))
return CreateDC(NULL, pinfo5[0].pPrinterName, NULL, NULL);
return 0; // EnumPrinters failed, so return null hdc
}
void PageGDICalls(HDC hdcPrn, int cxPage, int cyPage)
{
static char szTextStr[] = "Hello, Printer!";
Rectangle(hdcPrn, 0, 0, cxPage, cyPage);
MoveToEx(hdcPrn, 0, 0, NULL);
LineTo (hdcPrn, cxPage, cyPage);
MoveToEx(hdcPrn, cxPage, 0, NULL);
LineTo (hdcPrn, 0, cyPage);
SaveDC(hdcPrn);
SetMapMode |
(hdcPrn, |
MM_ISOTROPIC); |
SetWindowExtEx |
(hdcPrn, |
1000, 1000, NULL); |
SetViewportExtEx(hdcPrn, |
cxPage / 2, -cyPage / 2, NULL); |
|
SetViewportOrgEx(hdcPrn, |
cxPage / 2, cyPage / 2, NULL); |
Ellipse(hdcPrn, -500, 500, 500, -500);
SetTextAlign(hdcPrn, TA_BASELINE | TA_CENTER);
TextOut(hdcPrn, 0, 0, szTextStr, sizeof(szTextStr) - 1);
RestoreDC(hdcPrn, -1);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static int HDC
HMENU
PAINTSTRUCT
switch(msg)
{
case WM_CREATE :
hMenu = GetSystemMenu(hwnd, FALSE); AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, 0, 1, "&Print"); return 0;
72
case WM_SIZE :
cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0;
case WM_SYSCOMMAND : if(wParam == 1)
{
if(PrintMyPage(hwnd))
MessageBox(hwnd, "Could not print page!", szAppName, MB_OK | MB_ICONEXCLAMATION);
return 0;
}
break;
case WM_PAINT :
hdc = BeginPaint(hwnd, &ps);
PageGDICalls(hdc, cxClient, cyClient);
EndPaint(hwnd, &ps); return 0;
case WM_DESTROY : PostQuitMessage(0); return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Рис. 15.5 Файл PRINT.С программ PRINT1, PRINT2, PRINT3 и PRINT4
В файле PRINT.С имеются функции WinMain, WndProc, GetPrinterDC и PageGDICalls. Для последней функции необходим описатель контекста принтера и две переменных, содержащих ширину и высоту страницы принтера. Функция PageGDICalls рисует прямоугольник, который ограничивает всю страницу, две линии между противоположными углами страницы, эллипс в середине страницы (его диаметр вдвое меньше ширины и высоты страницы) и текст "Hello, Printers!" в центре эллипса.
При обработке сообщения WM_CREATE функция WndProc добавляет к системному меню опцию Print. Выбор этой опции приводит к вызову функции PrintMyPage, которую мы будем совершенствовать по мере перехода к очередной версии программы. Возвращаемым значением функции PrintMyPage является TRUE (ненулевое значение), если в процессе печати имела место ошибка, в противном случае ее возвращаемым значением будет FALSE. Если функция PrintMyPage возвращает TRUE, то WndProc выводит на экран окно сообщения об ошибке.
Каркас программы печати
Программа PRINT1, представленная на рис. 15.6, является первой версией программы печати. После компиляции программы PRINT1, ее можно запустить и затем выбрать из системного меню опцию Print. Если в переменной TEMP окружения MS-DOS указан жесткий диск (или если переменная TEMP не задана), то вы заметите некоторую дисковую активность, когда модуль GDI сохраняет выходные данные для печати во временном файле. После окончания работы программы PRINT1, спулер должен начать передачу файла с диска на принтер.
Рассмотрим программу в файле PRINT1.С. Если функция PrintMyPage не может получить для принтера описатель контекста, ее возвращаемым значением будет TRUE, и WndProc выведет на экран окно сообщения об ошибке. Если функция благополучно получает описатель контекста принтера, то она, с помощью вызова функции GetDiviceCaps, определяет горизонтальный и вертикальный размер страницы в пикселях:
xPage = GetDeviceCaps(hdcPrn, HORZRES);
yPage = GetDeviceCaps(hdcPrn, VERTRES);
Эти размеры представляют собой не размеры страницы, а скорее, размеры области печати на ней. Не считая этого вызова, структура кодов функции PrintMyPage программы PRINT1 такая же, как в программе FORMFEED, за исключением того, что в программе PRINT1 вызов функции PageGDICalls происходит между вызовами функций StartPage и EndPage. Только в том случае, если вызовы функций StartDoc, StartPage и EndPage прошли удачно, программа PRINT1 вызывает функцию печати EndDoc.
73
PRINT1.MAK
#----------------------
# PRINT1.MAK make file
#----------------------
print1.exe : print.obj print1.obj
$(LINKER) $(GUIFLAGS) -OUT:print1.exe print.obj print1.obj \ $(GUILIBS) winspool.lib
print.obj : print.c
$(CC) $(CFLAGS) print.c
print1.obj : print1.c
$(CC) $(CFLAGS) print1.c
PRINT1.C
/*--------------------------------------- |
|
|
|
PRINT1.C |
-- Bare Bones Printing |
||
|
|
(c) Charles Petzold, 1996 |
|
--------------------------------------- |
|
|
*/ |
#include <windows.h> |
|
||
HDC GetPrinterDC(void); |
// in PRINT.C |
||
void PageGDICalls(HDC, int, int); |
|||
HINSTANCE hInst; |
|
||
char |
szAppName[] = "Print1"; |
||
char |
szCaption[] = "Print Program 1"; |
||
BOOL PrintMyPage(HWND hwnd) |
|||
{ |
|
|
|
static DOCINFO di |
= { sizeof(DOCINFO), "Print1: Printing", NULL }; |
||
BOOL |
|
bError = FALSE; |
|
HDC |
|
hdcPrn; |
|
int |
|
xPage, yPage; |
if(NULL ==(hdcPrn = GetPrinterDC())) return TRUE;
xPage = GetDeviceCaps(hdcPrn, HORZRES);
yPage = GetDeviceCaps(hdcPrn, VERTRES);
if(StartDoc(hdcPrn, &di) > 0)
{
if(StartPage(hdcPrn) > 0)
{
PageGDICalls(hdcPrn, xPage, yPage);
if(EndPage(hdcPrn) > 0) EndDoc(hdcPrn);
else
bError = TRUE;
}
}
else
bError = TRUE;
DeleteDC(hdcPrn); return bError;
}
Рис. 15.6 Программа PRINT1