Функция SetMetaFileBitsBetter отличается от SetMetaFileBits тем, что делает "хозяином" глобального блока, не ваше приложение, а GDI. Таким образом этот объект может использоваться буфером обмена или средствами OLE, так как он не уничтожается при завершении работы вашего приложения. Однако при этом уже вы сами обязаны проследить за тем, что бы в итоге освободить все занятые ресурсы.
Операция SetEnhMetaFileBits создает метафайл из блока данных, заданного его размером и указателем. Функция SetWinMetaFileBits создает метафайл Win32 из данных, подготовленных для метафайла Windows 3.x.
Возможна и обратная операция — преобразовать метафайл в блок глобальной памяти. Это делается с помощью функции:
HGLOBAL GetMetaFileBits (hMF);
UINT GetEnhMetaFileBits (hEnhMF, cbBuffer, lpBuffer); 1
UINT GetWinMetaFileBits (hEnhMF, cbBuffer, lpBuffer, int nMapMode, hdcRef); 1
После вызова функции GetMetaFileBits нельзя использовать hMF, он считается уничтоженным, но зато вы можете использовать возвращенный хендл блока глобальной памяти. Так вы можете узнать размер метафайла, сохранить его на диске и т.д.
Функция GetEnhMetaFileBits в отличие от GetMetaFileBits не уничтожает метафайла, так что вы сами должны будете позаботиться об его уничтожении. Функция GetWinMetaFileBits кроме того записывает полученные данные в старом формате.
В том случае, когда надо метафайл, использующий память, превратить в метафайл, использующий диск, надо воспользоваться функцией:
HMETAFILE CopyMetaFile (hMF, lpszFileName);
HENHMETAFILE CopyEnhMEtaFile (hEnhMF, lpszFileName); 1
которая скопирует данные метафайла из памяти в дисковый файл с заданным именем. После копирования можно закрыть исходный метафайл с помощью функции DeleteMetaFile (DeleteEnhMetaFile1).
Для воспроизведения метафайла предназначена функция
BOOL PlayMetaFile (hDC, hMF);
BOOL PlayEnhMetaFile (hDC, hEnhMF, lpRect); 1
которая проигрывает указанный параметром hMF метафайл на заданном контексте устройства hDC.
В некоторых случаях, когда воспроизведение метафайла занимает много времени, или если надо отдельные записи метафайла корректировать перед воспроизведением, удобно воспользоваться другой функцией для воспроизведения метафайла:
BOOL EnumMetaFile (hDC, hMF, lpfnMFEnumProc, lParam);
BOOL EnumEnhMetaFile (hDC, hEnhMF, lpfnEnhMFEnumProc, lParam, lpRect); 1
Эта функция перебирает все записи метафайла hMF (hEnhMF) и для каждой из них вызывает функцию, заданную указателем lpfnMFEnumProc (lpfnEnhMFEnumProc). Параметр hDC указывает контекст устройства, на котором должен воспроизводиться метафайл, а lParam произвольные данные, которые вы решите передать в функцию lpfnMFEnumProc (lpfnEnhMFEnumProc). В случае WindowsAPI параметр lpfnMFEnumProc должен быть не указателем на саму функцию, а указателем, возвращенным функцией MakeProcInstance; при этом после перебора всех записей метафайла (то есть после возврата из EnumMetaFile) вы должны уничтожить созданный указатель с помощью функции FreeProcInstance; в случае Win32 API надо передавать непосредственно указатель на требуемую функцию.
Функция, которая вызывается для обработки каждой записи метафайла должна иметь следующий вид:
int CALLBACK _export MFEnumProc (
HDC hDC, LPHANDLETABLE lpHTable, LPMETARECORD lpMR, int cHandles, LONG lParam){...}
Или, в случае Win32 API:
int EnhMFEnumProc (// 1
HDC hDC, HANDLETABLE FAR* lpHTable, ENHMETARECORD *lpMR, int cHandles, LONG lParam){...}
Когда Windows вызывает эту процедуру, параметр hDC указывает хендл контекста устройства, на котором воспроизводиться метафайл. Параметр lpHTable является указателем на таблицу хендлов объектов GDI, созданных при воспроизведении метафайла. Причем параметр cHandles указывает число объектов в этой таблице. Параметр lpMR является указателем на структуру, описывающую ту запись метафайла, которая должна воспроизводиться, а параметр lParam содержит те данные, которые вы передали через одноименный параметр функции EnumMetaFile.
Функцию MFEnumProc вы должны написать сами. В простейшем случае она может состоять из единственного вызова функции:
void PlayMetaFileRecord (hDC, lpHTable, lpMR, cHandles);
void PlayEnhMetaFileRecord (hDC, lpHTable, lpMR, cHandles); 1
Которая осуществит воспроизведение данной записи метафайла. Однако до, после или вместо этого вы можете проанализировать запись метафайла и выполнить нужные действия. Вы можете даже изменять запись, причем эти изменения сохраняются в метафайле.
Итак, что содержится в записи?
typedef struct tagMETARECORD {
DWORD rdSize;
UINT rdFunction;
UINT rdParm[ 1 ];
} METARECORD;
typedef struct tagENHMETARECORD {// 1
DWORD iType;
DWORD nSize;
DWORD dParm[ 1 ];
} ENHMETARECORD;
Поле rdSize указывает полный размер записи метафайла, включая заголовок, в словах (то есть реально его надо умножать на 2, что бы получить размер в байтах).
Поле rdFunction задает номер выполняемой функции GDI. Эти номера вы можете посмотреть в windows.h, они начинаются с префикса META_ (например META_RECTANGLE). Младший байт этого слова содержит номер функции GDI, а старший — размер передаваемых этой функции аргументов, выраженный в словах.
Массив rdParm является массивом данных, передаваемых функции GDI. Данные перечислены в обратном порядке, по сравнению с тем, как они размещены в описании функции. Если при этом функция содержит указатель на что–либо (например TextOut содержит указатель на текст), то этот объект целиком включается в запись на месте соответствующего аргумента.
В случае Win32 поля несколько иные: iType задает «тип записи», то есть номер функции, которые имеют префикс EMR_, вместо META_ (например EMR_SETMAPMODE), поле nSize задает размер непосредственно в байтах, а dParm — параметры записи.
Вы можете легко проанализировать запись и выполнить требуемые действия.
[1] Логический дюйм часто равен физическому. При выводе на экран разрешающая способность может быть чересчур низкой, по сравнению с разрешающей способностью принтера. При этом становится целесообразным несколько увеличивать реальное изображение, что бы сохранить приемлемое восприятие изображения. Так, например, шрифт размером 8 пунктов на принтере читается совершенно свободно, но при отображении на экране в реальном масштабе буквы часто становятся плохо различимыми. Логический дюйм это либо нормальный дюйм — для устройств с высокой разрешающей способностью, либо больше — для устройств с низкой разрешающей способностью.
[2] По крайней мере так считает Microsoft. Вообще говоря, существует несколько различных полиграфических систем с применением разных единиц отсчета, в которых размер точки может несколько варьироваться.
[3] Исключение — WindowsNT. В этой системе для каждого запущенного приложения загружается своя копия системных модулей, включая GDI32.EXE; Этот механизм не настолько громоздок, как кажется, за счет специальных механизмов защиты страниц «копирование при записи». Достоинство — высокая защищенность, так как приложение может навредить только себе, даже если оно разрушит или не освободит системные объекты. В этом случае при завершении работы приложения все ресурсы, созданные им, автоматически освобождаются. Однако очень надеяться на эту особенность не стоит — если ресурсы не освобождать вовремя, то размер файла подкачки страниц может существенно увеличиться, в связи с чем возникнет вопрос о наличии свободного дискового пространства.
[4] По–видимому, некоторые подобные рекомендации сделаны для обеспечения возможной совместимости с другими системами в будущем. Так, например, при работе в X–Windows (в основном UNIX–машины), при удалении контекста удаляются все объекты, выбранные в него. Если оставаться в рамках Windowsили Win32 API, то выбранные в контекст устройства объекты GDI вовсе не обязательно заменять на стандартные перед освобождением контекста.
[5] По крайней мере современные версии OS/2 часто применяют битмапы в ином формате — попытка их проанализировать, используя подобное описание приводит к ошибке, так что использованное название «битмап OS/2» выглядит по меньшей мере сильно устаревшим. Ключом в определении версии битмапа является размер его заголовка. Так, например, размер заголовка битмапа OS/2 (версия 1.2 — формат, поддерживаемый также и MicrosoftWindows) равен 12 байтам, размер заголовка Windowsбитмапа равен 40 байтам (не считая так называемых 4ой и 5ой версий битмапов); более современный формат битмапов OS/2 (версия 2.0) равен 64 байтам.
[6] Иногда возникает непроизвольная ошибка при попытке создать палитру (как объект GDI): для создания палитры необходимо в функцию CreatePalette передать массив структур типа PALETTENTRY, который имеет схожий с RGBQUAD формат. Естественное желание — просто использовать палитру битмапа в качестве массива структур PALETEENTRY. Неприятность связана с тем, что компоненты красного, синего и зеленого цветов в этих структурах перечислены в разном порядке; таким образом необходимо все–таки создавать собственный массив структур PALETENTRY для создания палитры.
[7] В случае WindowsNTтребуется только чтобы биты для каждой компоненты образовывали непрерывную группу, а число бит для задания каждой компоненты и порядок задания этих групп не фиксированы.
[8] Формат 5–6–5 является альтернативным, для его задания обязательно задание масок цветов; в то время как для задания формата 5–5–5 маски можно опустить — он считается стандартным.
[9] Очень может быть, что это зависит от видеоадаптера и драйверов — на тестовой машине был установлен DiamondStealth64 PCIс чипом S3 Vision968. Стандартные драйвера для Windows 3.xвообще не поддерживают режимов 16 и 32 бит/пиксель.
[10] Или RGBTRIPLE, если битмап соответствует формату OS/2
[11] Если вы будете его использовать, то вы должны включить файл драйвера DIB.DRV в установочный комплект вашего приложения — в стандартной системе этот драйвер не содержится; обычно он включен в redistribution kit, сопровождающий компиляторы.
[12] Разве что кроме форматов OS/2, использующих для задания палитры записи RGBTRIPLE по 3 байта каждая; тогда необходимо следить, что бы размер палитры обеспечивал выравнивание начала изображения на границу, кратную 4 байтам.
[13] На практике воспроизведение одного метафайла на другом выполняется успешно. Однако, коль скоро Microsoftсоветует так не делать, то лучше и не пытаться — вполне может оказаться так, что последующие версии Windowsперестанут выполнять эту операцию.