Программирование на Visual C++. Архив рассылки
Шрифт:
Как видим, большинство объектов имеют в корне иерархии класс GdiPlusBase. Вам не понадобится создавать экземпляры этого класса, так как он содержит только средства управления памятью (для него перегружены операторы new/new[] и delete/delete[], которые используют функции GDI+ GdipAlloc и GdipFree). Все классы, инкапсулирующие работу с ресурсами GDI+, порождены от GdiPlusBase. Это не значит, что их экземпляры нельзя создавать на стеке – напротив, так даже удобнее контролировать время их жизни. Зато такая архитектура позволит, например, передавать указатель на созданный объект GDI+ в модуль, написанный с использованием
ПРИМЕЧАНИЕ
Не путайте управление памятью под экземпляры классов-оберток C++, которое осуществляется перегруженными операторами new/delete, и управление собственно ресурсами GDI+, которое скрыто от разработчиков в недрах соответствующих функций, например, GdipCreateSolidFill.
Ключевым же классом в GDI+ является Graphics (программисты на J++ вздрогнули). Именно он содержит почти две сотни методов, отвечающих за рисование, отсечение и параметры устройства вывода. Напрашивается явная аналогия с контекстом устройства (Device Context) прежнего GDI, и эти понятия действительно тесно связаны. Из четырех конструкторов Graphics два создают его из HDC. Главное отличие заключается в изменении программной модели: теперь вы не работаете с хендлом, а вызываете методы класса. Хотя программистам на MFC эта концепция уже хорошо знакома.
Дальнейшее наследование (например, класс TextureBrush порожден от Brush) скорее отражает цели разработчиков (скрытие деталей реализации и повторное использование оберточного кода), чем инфраструктуру библиотеки, так как в inline-методах "родственных" классов просто содержатся вызовы различных функций GdiPlus.dll. Можно сказать, что Microsoft в очередной раз спроецировала обычный "плоский" API языка C на объектно-ориентированную библиотеку C++.
Оставшаяся часть классов не имеет общего родителя и предназначена для упрощения работы со структурами данных GDI+.
Перед тем как начать использовать классы и функции GDI+, необходимо инициализировать эту библиотеку. Для этого где-нибудь в начале своей программы нужно поместить вызов функции GdiplusStartup:
Поля структуры GdiplusStartupInput управляют различными аспектами инициализации: в частности, можно задать функцию, которая будет вызываться при возникновении ошибок, или перехватывать все обращения к функциям GDI+. Эти детали мы рассматривать не будем. К счастью, конструктор по умолчанию структуры GdiplusStartupInput выполняет инициализацию, достаточную в большинстве случаев. При этом в качестве выходного параметра output можно задать NULL.
"Магическое значение", на которое указывает выходной параметр token, необходимо сохранить.
Для завершения работы с библиотекой вызовите функцию GdiplusShutdown:
Здесь в качестве параметра и необходимо передать то самое число, которое возвратила GdiplusStartup в параметре token.
ПРИМЕЧАНИЕ
Вы можете вызвать GdiplusStartup и GdiplusShutdown из разных потоков, но необходимо убедиться, что вне этой пары функций никакого
Настало время применить все эти сведения на практике. Для этого создадим в MS Visual C++ базовое WINAPI-приложение, которое послужит полигоном для дальнейших экспериментов. Ниже для этого приведена пошаговая процедура.
Итак, создаем новый проект Win32 Application. Выбираем опцию A typical "Hello, World!" application и нажимаем "finish". Получившееся приложение необходимо подготовить для использования GDI+. Для этого в файле stdafx.h после строки с комментарием:
добавляем следующие строчки:
и в конце файла stdafx.cpp добавляем строку
Кроме того, в файле stdafx.h необходимо удалить или закомментировать строку
Иначе компилятор выдаст кучу ошибок об отсутствии символов MIDL_INTERFACE, PROPID, IStream и т.д.
Если полученное в результате приложение успешно собралось, значит, мы все сделали правильно. Пойдем дальше.
Найдем в сгенерированном основном .cpp файле нашего проекта функцию WinMain и добавим в начале ее код инициализации:
а в конце, перед оператором return, добавим код очистки:
Готово. Наконец-то мы можем что-нибудь нарисовать. Найдите в теле функции WndProc обработчик сообщения WM_PAINT и замените следующим кодом:
Теперь где-нибудь перед функцией WndProc создадим функцию OnPaint с кодом рисования: