Если содержимое этого поля указать как NULL, после возвращения из функции PrintDlg поле будет содержать идентификатор глобального блока памяти, заказанного функцией. В этом блоке памяти будет расположена структура DEVMODE, заполненная выбранными параметрами принтера.
HDC – контекст устройства или информационный контекст.
Это поле заполняется после возвращения из функции PrintDlg, если в поле Flags указано одно из значений: PD_RETURNDC или PD_RETURNIC. В первом случае возвращается контекст принтера, который можно использовать для печати, во втором – информационный контекст, который можно использовать для получения разнообразной информации о принтере.
Flags – это поле должно содержать флаги инициализации. Тут с помощью логической операции или можно указать начальное положение переключателей, отключить некоторые пункты диалога (например, печать выделенного фрагмента, если такая возможность отсутствует в программе и т.п.)
Рисунок 2.2.2 – Настройка печати
Перед вызовом функции PrintDlg() следует проинициализировать нужные поля, остальные установить в ноль:
BOOL fResult;PRINTDLG pd; ... ZeroMemory(&pd, 0, sizeof(PRINTDLG)); pd.lStructSize = sizeof(PRINTDLG);pd.hwndOwner = hWnd;pd.Flags = PD_RETURNDC; fResult = PrintDlg(&pd); if (pd.hDevMode) GlobalFree(pd.hDevMode); if (pd.hDevNames) GlobalFree(pd.hDevNames);
Необходимо обратить внимание на необходимость освобождения глобальных блоков памяти (функцией GlobalFree()), а после завершения печати и контекста принтера функцией DeleteDC(HDC hDC).
Внесем в демонстрационную программу следующие изменения:
1. Добавим прототип функции печати после прототипа функции ShadyDiagram:
void Print(HWND hWnd);
2. В оконную процедуру добавим обработчик отпускания левой кнопки мыши после обработчика функции WM_CREATE:
case WM_LBUTTONUP:
Print(hWnd);
break;
3. В конец файла добавим описание функции Print().
Void Print(HWND hWnd)
{
DOCINFO docinfo;
PRINTDLG pd;
ZeroMemory(&pd, sizeof(pd));
pd.lStructSize = sizeof(PRINTDLG);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
if (PrintDlg(&pd))
{
ZeroMemory(&docinfo, sizeof(docinfo));
docinfo.cbSize = sizeof(docinfo);
if (StartDoc(pd.hDC, &docinfo) > 0)
{
StartPage(pd.hDC);
ShadyDiagram(pd.hDC, 1600, 1300, 1000, 50, g_data, g_pBrushes, g_iNumOfPies);
EndPage(pd.hDC);
EndDoc(pd.hDC);
}
}
if(pd.hDevMode)
GlobalFree(pd.hDevMode);
if(pd.hDevNames)
GlobalFree(pd.hDevNames);
if (pd.hDC)
DeleteDC(pd.hDC);
return;
}
По щелчку в окне левой кнопкой мыши появляется диалоговое окно печати, в котором можно выбрать принтер и нажать «Печать». Результат печати показан на рисунке 2.2.3.
Функция Print() работает следующим образом:
Обнуляется память, занимаемая структурой PRINTDLG:
ZeroMemory(&pd, sizeof(pd));
Затем некоторым полям присваиваются значения:
pd.lStructSize = sizeof(PRINTDLG);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
Важным является установка флага PD_RETURNDC.
Следующим шагом вызывается функция PrintDlg() и, если она возвращает истинный результат, что соответствует нажатию кнопки «Печать» (а не «Отмена»), выполняется тело условного оператора if:
if (PrintDlg(&pd))
При печати заполняется единственное требуемое поле структуры DOCINFO. В целом структура содержит информацию для очереди печати, например, количество страниц, которое программа собирается напечатать, название документа. Подробнее можно узнать, обратившись к справке по данной структуре.
Далее вызывается функция начала печати нового документа:
if (StartDoc(pd.hDC, &docinfo) > 0)
И если не произошло ошибок (результат положительный), начинается печатать первая и единственная страница:
StartPage(pd.hDC);
Все графические построения на принтере осуществляет такая же функция, что и на экране:
ShadyDiagram(pd.hDC, 1600, 1300, 1000, 50, g_data, g_pBrushes, g_iNumOfPies);
Следует обратить внимание на увеличенный в пять раз масштаб (значения координат). Дело в том, что по умолчанию применяются единицы измерения конкретного устройства. Для монитора – это пиксели, для принтера – точки. И если 600 пикселей на экране часто занимают половину монитора, то на принтере это чуть больше 2,5 см (при разрешении 600 точек на дюйм). При печати удобно применять системы координат в мм или дюймах, что задается функцией SetMapMode().
После вывода изображения, сообщаем принтеру о завершении печати страницы и документа в целом:
EndPage(pd.hDC);
EndDoc(pd.hDC);
Последним шагом освобождаем используемые ресурсы (которых может быть не быть, если пользователь отменил печать):
if(pd.hDevMode)
GlobalFree(pd.hDevMode);
if(pd.hDevNames)
GlobalFree(pd.hDevNames);
if (pd.hDC)
DeleteDC(pd.hDC);
Результат работы программы показан на рисунке 2.2.3.
Рисунок 2.2.3 – Распечатка на бумаге диаграммы демонстрационной программой
Контрольные вопросы
- Какую функцию выполняет драйвер видеокарты при выполнении в программе команды построения графического примитива, например, эллипса?
- Что такое контекст устройства? Какое его назначение?
- Можно ли просто изменить байты информации в видеопамяти и сразу увидеть результат на экране монитора в рамках ОС Windows?
- Последние версии ОС Windows часто используют DirectX с целью вывода информации на экран, например, в браузере, а не универсальные функции GDI? С чем это связано?
- Что такое перо, что такое кисть в терминах GDI?
- Как в приведенном примере построения круговой диаграммы реализуется эффект «тени»?
- Где по умолчанию находится центр координат при выводе информации на экран? Какое направление осей координат? В каких единицах измеряются координаты?
- Зачем при выборе цвета кисти заливки замкнутой фигуры необходимо сохранять идентификатор предыдущей используемой кисти?
- Существуют ли отличия в программном выводе информации на экран монитора или на принтер? Почему?
- Как получить контекст принтера для печати на нем?