void CMetavw1Doc::Serialize(CArchive& ar) {
if (ar.IsStoring()) {}
else {
cemf.Load(m_szPathName);
}
}
Заметьте, что в обоих фрагментах я использовал переменную m_szPathName как аргумент при вызове функций GetEnhMetaFile и Load. Поначалу я думал, что можно получить полное имя файла из параметра типа CArchive функции Serialize. Ведь CArchive содержит переменную-член m_pDocument, которая указывает на сериализуемый в данный момент объект типа CDocument. Отлично, у CDocument есть очень удобная переменная-член, которая выглядела как раз как то, что мне было нужно: m_strPathName. К сожалению, m_pDocument->strPathName инициализируется нулем при открытии файла. Так что я решил получить имя файла и путь к нему перекрыв фукцию OnOpenDocument. Путь напрямую передавался в OnOpenDocument, так что я просто сделал копию внутри класса CMetavw1Doc в той самой переменной, которая передавалась в качестве параметра функциям GetEnhMetaFile и Load.
BOOL CMetavw1Doc::OnOpenDocument(LPCTSTR lpszPathName) {
m_szPathName = lpszPathName;
if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE;
return TRUE;
}
Итак, что я получил практически ничего не делая? На этот вопрос легко ответить. Все, что перечислено в следующем списке (плюс многое другое, что я еще просто не успел оценить, я уверен) было предоставлено MFC:
• Стандартное диалоговое окно открытия файла
• Список недавно открытых файлов в меню Файл
• Возможность перетаскивать файлы из Проводника в мое приложение (они даже открываются!)
• Невообразимое ощущение легкости.
Прием №1: Класс расширенного метафайла: CEMF
После заполнения моего класса представления (METAVVW.CPP) кодом, необходимым для успешной отрисовки расширенного метафайла, мне стало ясно что я возвращаюсь к своим старым привычкам неорганизованного кодирования на C. Так что я решил убрать весь этот код из класса представления и создать класс, который будет заниматься загрузкой и воспроизведением метафайлов.
Для целей моего маленького приложения, Load и Draw были самыми важными функциями этого класса. Полностью в духе C++, я также написал несколько дополнительных функций для доступа к различным атрибутам метафайла, таким как дескриптор (handle), строка описания, и указатель на заголовок. Следующий код (взятый из CEMF.H) дает хорошее представление о том, что я сделал из этого класса. Заметьте, что я унаследовал класс от CObject, а не от CDC или CMetaFileDC. CDC включает в себя функции PlayMetaFile и AddMetaFileComment, и в ретроспективе, возможно, было бы более удобно унаследовать класс от CDC. Наследование от CMetaFileDC казалось неправильным, потому что я не создавал метафайлы, а просто просматривал уже существующие. Тем не менее, полностью функциональный класс метафайла мог бы быть унаследован и от CMetaFileDC. Да, есть много способов содрать с кота шкуру (прошу прошения у любителей кошек!)
class CEMF : public CObject {
// Операции
public:
CEMF();
~CEMF();
BOOL Load(const char *szFileName);
BOOL Draw(CDC* pDC, RECT* pRect);
LPENHMETAHEADER GetEMFHeader() {
return ((m_pEMFHdr) ? m_pEMFHdr : NULL);
}
LPTSTR GetEMFDescString() {
return ((m_pDescStr) ? m_pDescStr : NULL);
}
HENHMETAFILE GetEMFHandle() {
return ((m_hemf) ? m_hemf : NULL);
}
protected:
BOOL GetEMFCoolStuff();
BOOL LoadPalette();
// Данные
protected:
CString m_szPathName;
HENHMETAFILE m_hemf;
LPENHMETAHEADER m_pEMFHdr;
LPTSTR m_pDescStr;
LPPALETTEENTRY m_pPal;
UINT m_palNumEntries;
LPLOGPALETTE m_pLogPal;
LOGPALETTE m_LogPal;
HPALETTE m_hPal;
};
Функция Load подозрительно смотрит на начало файла, как и мой предыдущий код в функции Serialize. Но теперь нет объекта типа CArchive со всеми его преимуществами. Нет проблем. Использование объекта типа CFile позволяет прочитать сигнатуру. Функции GetEMFCoolStuff и LoadPalette взяты из моей программы-примера EMFDCODE в MSDN. Они получают копии заголовка метафайла, строки описания, и палитры внедренной в метафайл. Конечно, они теперь находятся в классе CEMF.
BOOL CEMF::Load(const char *szFileName) {
UINT uiSig;
// Сохранить имя файла.
m_szPathName = szFileName;
// Проверить сигнатуру
CFile cfEMF;
cfEMF.Open(m_szPathName, CFile::modeRead | CFile::shareDenyWrite);
cfEMF.Read(&uiSig, sizeof(UINT));
cfEMF.Close();
// Если это EMF, получить его дескриптор.
if (uiSig == EMR_HEADER) {
m_hemf = GetEnhMetaFile(m_szPathName);
GetEMFCoolStuff();
LoadPalette();
} else m_hemf = NULL;
// Возвращаем результат.
return ((m_hemf) ? TRUE : FALSE);
}
Функция Draw вызывается из функции OnDraw класса представления. Особо интересного там ничего не происходит. Если есть палитра, на что указывает не равное NULL значение переменной-члена m_hPal, палитра выбирается в контекст устройства (DC). Я был сильно озадачен, когда узнал что CDC::SelectPalette требует указатель на объект типа CPalette. Но я был так же заинтригован, когда вдруг обнаружил функцию CPalette::FromHandle. Я мог легко преобразовать дескриптор палитры в объект типа CPalette. Далее это было уже просто делом воспроизведения метафайла с помощью CDC::PlayMetaFile.
Читать дальше