Чтение онлайн

ЖАНРЫ

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

А не правда ли, было бы неплохо просто вызвать что-то вроде Load(m_szPathName) чтобы загрузить файл? Ага! Эта будет наш первый хитрый прием! Я решил написать класс, который будет иметь дело непосредственно с метафайлами, загружать их и воспроизводить (об этом подробнее см. дальше). Использование этого класса уменьшило необходимый код загрузки до следующего:

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.

BOOL CEMF::Draw(CDC *pdc, RECT *pRect) {

 ASSERT(m_hemf);

 BOOL fRet = FALSE;

 CRect crect;

 CPalette cpalOld = NULL;

 if (m_hemf) {

if (m_hPal) {

CPalette cpal;

if ((cpalOld = pdc->SelectPalette(cpal.FromHandle(m_hPal), FALSE))) pdc->RealizePalette;

}

fRet = pdc->PlayMetaFile(m_hemf, pRect);

Поделиться с друзьями: