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

ЖАНРЫ

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

Jenter Алекс

Шрифт:

Небезызвестный вам Борис Бердичевский (см. выпуск №3) делится своим решением часто возникающей проблемы с сериализацией.

Приветствую!

Случилось так, что мне пришлось потратить целый рабочий день на поиск одного решения в области сериализации; готов поделиться.

Как я уже отмечал недавно, при сериализации данных нужно решать проблему совместимости старых структур сохраняемых данных, т.е. если вы добавили новое поле в какой-то класс, подлежащий сериализации, будьте добры, обеспечьте чтение этого класса, ранее сохраненного без этого поля.

Способ, который я приводил (ReadClass/WriteClass), вполне хорош. Но закавыка-то в том, что в предыдущей версии я по какой-то причине (просто по неопытности) сохранял без WriteClass! А имплементация сериализации была описана как IMPLEMENT_SERIAL(MyClass, CObject, 0)

Итак, подобным образом сериализованный класс надо было успешно прочитать. Понятное дело, ReadClass на такую сериализацию вызывает CArchiveException

с кодом CArchiveException::badIndex (=5, для справки)

Казалось бы, лови CArchiveException и обрабатывай себе, но не тут-то было! Вроде незначительная проблема: указатель архива продвигается, и невозможно уже прочитать данные из-за смещения. Никакого средства для возврата указателя на место для CArchive не существует! (Я бы мог попросить уважаемых читателей порыться, но заранее заявляю: бессмысленно! Говорите, можно манипулировать с функцией Seek принадлежащего

CArchive классу CFile? – пробовал, не работает.)

По счастью, нашлась недокументированная возможность. Выяснилось, и это очень важный нюанс, который не разъяснен в MSDN: У функции CReadClass есть 3 параметра, 2 последних имеют умолчание NULL:

CRuntimeClass* ReadClass(const CRuntimeClass* pClassRefRequested = NULL, UINT* pSchema = NULL, DWORD* obTag = NULL);

throw CArchiveException;

throw CNotSupportedException;

Первый параметр – RuntimeClass для проверки на соответствие загружаемого класса.

Второй параметр – поинтер на UINT – версию сериализуемого класса.

Третий параметр, и это самое интересное (кстати, в MSDN записано, что, он предназначен для внутреннего использования в функции ReadClass и обычно задается как NULL) – если не задан нулем, возвращает в младших 2-х байтах значение, сериализованное из архива, а CArchiveException при этом не вырабатывается! Версия при этом не заполняется.

Отсюда решение, которое проиллюстрировано во фрагменте кода:

#define BASE_DATA_VERSION 0x100

IMPLEMENT_SERIAL(MyClass, CObject, VERSIONABLE_SCHEMA | BASE_DATA_VERSION)

void MyClass::Serialize(CArchive& ar) {

 UINT Version=NULL;

 CObject::Serialize(ar);

 DWORD Tag;

 if(ar.IsLoading) {

TRY {

ar.ReadClass(RUNTIME_CLASS(MyClass), &Version, &Tag);

if (Version == BASE_DATA_VERSION)

ar >> dwValue; // описано в классе DWORD dwValue;

else {

WORD HighW;

ar >> HighW;

dwValue = MAKELONG(Tag, HighW);

}

ar.Read( title, sizeof(title)); // описано в классе

// char title[LEN_TITLE];

ar>> val1;

ar>> val2; // нормальная сериализация

Борис Бердичевский

Здравствуйте Алекс!

Только что подписался на вашу рассылку, и прочитал все выпуски из архива. Мне показался интересным вопрос из выпуска N2 про возможность структурного сохранения данных в MFC.

На мой взгляд опубликованные ответы были не очень точны, т.к. в MFC все-таки имеются некоторые способы такого хранения, хотя конечно не совсем до конца доделанные.

Прежде всего существует класс COleDocument, который позволяет хранить данные в compound file – в которых как раз и хранятся иерархические данные.

У этого класса есть, к сожалению не документированный, член m_lpRootStg который представляет из себя корневой storage документа. К сожалению в MFC нет стандартных средств работы со storage, но никто не мешает пользоваться его собственным интерфейсом.

Зато есть класс COleStreamFile который инкапсулируют в себе IStream являясь в то же время потомком CFile, что делает возможным его использования для CArchive и соответственно serialize.

И наконец последнее замечание. К сожалению, модель MFC такова, что при использовании своих IStorage объектов, их НЕОБХОДИМО записывать в функции serialize, иначе они могут потеряться при команде Save As.

Вот и все что я хотел напистаь. Может немного сумбурно и поздновато, но может кому-нибудь пригодиться.

С уважением,

Nick Pisanov

Спасибо Нику за ответ и полезную информацию. Я просто хочу заметить, что все-таки вместо "недоделанных" способов чаще предпочитаю использовать свои, хоть с потом и кровью созданные, но доделанные, удобные, не основывающиеся на недокументированных возможностях, досконально известные и работающие на все 100%. 

Но это вопрос философский, конечно… Иногда

действительно это приводит к изобретению велосипеда. Программирование – это все-таки больше искусство, чем наука ;) Каждый творит по-своему.

Пришло дополнение к прошлому выпуску:

Hello!

Маленькое замечание. После Shell_NotifyIcon( NIM_ADD, &nid); надо еще добавить ::DestroyIcon(nid.hIcon);

Я как-то делал анимацию иконки в трэе (типа как TheBat крылышками там машет) и долго не мог понять, почему после нескольких часов работы прога вешает всю систему, а никакой утечки памяти нет.

Андрей, Норильск

Огромное спасибо, Андрей! Действительно, если делать такие анимации в tray, то своевременное уничтожение иконки становится критичным. 

И еще на тему прошлого выпуска:

В 11 выпуске был вопрос, касаемо часов и реестра. Дело не в тебе, а в авторе сообщения. Дело в том, что windoza ставит в этот ключ что-то только если user что-то поменял в разделе "язык и стандарты". Если же там всё по умолчанию, то в реестре у тебя будет по этому пути только параметр Locale, указывающий код страны, правила записи даты, времени и т.п. которой используются для вывода системной даты.

Пригожев Александр (alexproger)

Что ж, видимо так оно и есть. Я дописал нужные параметры и все заработало. Но только после того, как я изменил региональный стандарт с русского на английский(США). Те два параметра, в которых вы записывали свое имя – на самом деле это метки "до полудня" и "после полудня", по умолчанию равные "AM" и "PM". В русском стандарте эти метки не используются.

ПРОШУ ВНИМАНИЯ:

Это последний выпуск рассылки в этом сезоне. К сожалению, я не знаю никого, кому мог бы доверить вести рассылку на время моего отпуска (и кто бы горел желанием это делать), поэтому рассылка также уходит в отпуск до конца лета . 

Большая просьба не ждать от меня ответа на свои письма раньше сентября – я буду отсутствовать физически. Прошу прощения у тех, на чьи письма и вопросы не успел ответить.

Надеюсь, что рассылка для вас является интересной и познавательной. Также уверен, что после выхода из отпуска она станет еще полезнее, так как я собираюсь воплотить несколько новый идей. А еще осенью должен открыться официальный сайт рассылки. Так что

до встречи в новом сезоне! Оставайтесь с нами!
Красноярск, 2000.

Программирование на Visual C++

Выпуск №13 от 7 сентября 2000 г.

Здравствуйте, дорогие подписчики!

Чрезвычайно рад приветсвовать вас после чуть более чем месячного отдыха. Как вы уже наверное сами догадались, рассылка вышла из отпуска и снова будет периодически радовать вас всяческой полезной информацией о мощнейшем современном инструменте разработки, а именно Microsoft Visual C++, ну а также о всем, что с ним связано. Начинается новый сезон выпусков, и начинается он с весьма счастливого номера – тринадцатого ;) Впрочем, я никогда не был особенно суеверным, так что пропускать это замечательное число никак не намерен.

Я взял на себя смелость немного изменить оформление рассылки – спасибо ребятам с subscribe.ru, они наконец-то догадались, что далеко не все пребывают в диком восторге от их цвета для фона страницы HTML-варианта, который мне почему-то всегда напоминал цвет известного сельскохозяйственного удобрения. Теперь это можно изменить, что я, особо не мешкая, и сделал. Впрочем, как говорится, на вкус и цвет товарища нет, так что будем условно считать возможную дискуссию на эту тему несостоявшейся.

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

Есть, к сожалению, и не очень приятные новости. Сейчас у меня будет оставаться не так много времени на рассылку, как раньше. Выпуски будут выходить так же регулярно, но, возможно, самую малость пореже. И еще: я не буду делать отдельную текстовую версию, придется положиться на конвертер сервера. Прошу прощения у подписчиков текстового варианта. Понимаю, что звучит банально (я такие утверждения встречал почти в каждой рассылке, а теперь дошел до того, что пишу это сам) но HTML-вариант ДЕЙСТВИТЕЛЬНО гораздо лучше выглядит и занимает ненамного больше времени для загрузки.

MFC

Начать всегда труднее всего, так что, облегчая себе задачу, я решил не начинать все заново и строить "новый мир" на обломках "старого", а просто продолжить с того места, на котором остановился. Итак, если вы помните, в предыдущем (двенадцатом) выпуске, я вам рассказывал о том, как с помощью MFC организовать набор закладок, без которых, замечу, просто немыслим удобный и структурированный интерфейс для диалога с большим количеством различных опций (вновьподписавшимся рекомендую обратиться к архиву рассылки, ссылка на него есть в конце этого выпуска). Кстати, в некоторых книгах закладки называют "вкладками", и некоторые считают, что так правильнее, хотя суть от этого, конечно же, не меняется.

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