Сообщений 2    Оценка 0        Оценить  
Система Orphus

Класс для простой инициализации GDI+

Автор: Виталий Брусенцев
The RSDN Group
Опубликовано: 31.10.2002
Исправлено: 13.03.2005
Версия текста: 1.0

Исходный код класса и пример – 1,6 Кб

Как известно (см. статьи в RSDN Magazine №1 и на сайте RSDN), графическая подсистема GDI+ реализована в виде библиотеки динамической компоновки GdiPlus.DLL. Все приводимые в MSDN примеры ориентированы на статическую компоновку с библиотекой импорта Gdiplus.Lib. При использовании GDI+ возник довольно интересный вопрос: а что, если система, в которой запущено приложение, не поддерживает GDI+? Как в таком случае можно избежать системного сообщения об ошибке и продолжить работу с использованием GDI? Ведь, в отличие от большинства обычных библиотек импорта, "начинка" GDI+ спрятана за структурой классов-оберток…

Здесь может помочь такая сравнительно малоизвестная возможность Visual C++, как отложенная загрузка (Delayed Loading) DLL. При использовании этой опции компоновщик генерирует специальные заглушки для всех импортируемых функций. Приложение стартует немного быстрее, а реальная загрузка библиотеки откладывается (отсюда и название) до первого вызова любой импортируемой функции. Подробно почитать об этой технике можно в декабрьском выпуске MSJ за 1998 год сразу в двух колонках: "Under The Hood" Мэтта Питрека и "QnA Win32" Джефри Рихтера.

Здесь же только упомяну, что для использования отложенной загрузки в проект необходимо включать библиотеку DELAYIMP.LIB.

Скрыть детали использования DLL и инициализации GDI+ можно, используя примерно такой класс:

// InitGdiPlus.h
namespace RSDN
{
  class InitGdiPlus  
  {
  public:
    InitGdiPlus();
    virtual ~InitGdiPlus();
    bool Good(){ return present; } // Была ли инициализация удачной?
  private:
    bool present;
    ULONG_PTR token;
 };
}

При рассмотрении реализации данного класса учтем следующее:

Во-первых, при попытке использовать любую функцию GdiPlus.DLL в системе, где эта библиотека не установлена, возникнет структурное исключение (SE). Класс должен обрабатывать такое исключение и сообщать о неудаче. В данном случае мы добавили в описание класса метод Good(), сигнализирующий об успехе или провале инициализации GDI+.

Во-вторых, по крайней мере в Visual Studio 6.0 SP4/SP5 существует ошибка, приводящая к аварийному завершению компоновщика при линковке Delay-loading модулей и модулей с отладочной информацией (см., например, сообщение в форуме RSDN: http://www.rsdn.ru/Forum/Message.aspx?mid=101066). Поэтому для отладочной версии нашего класса отложенная загрузка выполняться не будет (все равно, на при отладке на Вашей системе GDI+ установлена).

В-третьих, желательно максимально упростить использование класса и не выносить наружу детали реализации (в частности опции компоновщика имеет смысл задать в виде #pragma comment).

ПРИМЕЧАНИЕ

К сожалению, компилятор Visual C++ 7.0 воспринимает директиву линкера /delayload как ошибочную и выдает соответствующее предупреждение. Пользователям VC7 придется задавать эту опцию самостоятельно.

Реализация класса RSDN::InitGdiPlus

Таким образом, этот класс самостоятельно занимается инициализацией/очисткой GDI+, а также обрабатывает структурное исключение, возникающее при отсутствии библиотеки Gdiplus.dll. Для инициализации библиотеки достаточно объявить экземпляр такого класса.

Для более детального анализа возникшего исключения в конструкторе нашего класса можно применить функцию DelayLoadDllExceptionFilter, описанную в указанной статье Рихтера.

Приведем небольшой пример использования данного класса. Напишем маленькую утилиту для сохранения содержимого экрана в файл формата PNG.

// Компиляция:
// cl grab.cpp InitGdiPlus.cpp user32.lib kernel32.lib gdi32.lib
// для пользователей VC7 добавьте ключ /link /delayload:gdiplus.dll

#define STRICT
#include <windows.h>
#include <GdiPlus.h>
#include "InitGdiPlus.h"

//Внимание: в будущих версиях GDI+ GUID кодека может измениться
//В таком случае, для определения GUID обратитесь к статье в RSDN Magazine №1
static const GUID png = 
{ 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e } };

int CALLBACK WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, 
                     IN LPSTR lpCmdLine, IN int nShowCmd )
{
  HDC hdc = ::GetDC(NULL);
  RECT rc={0, 0, GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN)};
  HDC hdcMem = CreateCompatibleDC(hdc);
  HBITMAP hBitmap = CreateCompatibleBitmap(hdc, (int)rc.right, (int)rc.bottom);
  HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap);
  BitBlt(hdcMem, 0, 0, (int)rc.right, (int)rc.bottom, hdc, 0, 0, SRCCOPY);
  hBitmap =(HBITMAP) SelectObject(hdcMem, hOldBmp);
  DeleteDC(hdcMem);
  RSDN::InitGdiPlus init;
  if(init.Good())
  {
    Gdiplus::Bitmap bitmap(hBitmap, NULL);
    bitmap.Save(L"c:\\screen.png", &png);
  }
  else
  {
    MessageBox(0, "Ошибка инициализации GDI+", 0, MB_OK|MB_ICONHAND);
  }
  DeleteObject(hBitmap);
  return 0;
}

Только не забывайте при использовании класса InitGdiPlus, что время его жизни должно быть больше времени жизни создаваемых в Вашей программе объектов GDI+, иначе их деструкторы выполнятся после вызова GdiplusShutdown.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 2    Оценка 0        Оценить