Да, это должно было
произойти и это произошло! Рассылка
получила официальный статус "обычной
некоммерческой рассылки" (причем
гораздо быстрее, чем я ожидал), с чем я
себя и всех вас и поздравляю!
Хочу извиниться перед
подписчиками HTML-версии: во втором выпуске
случилось некоторое искажение исходного
кода (я упустил из виду, что при
автоматической генерации HTML сервер Гор.
Кота звездочки (*) интерпретирует как
указание сделать шрифт жирным), в
результате чего были потеряны указатели.
Вот как этот код должен был выглядеть на
самом деле:
CRunTimeClass *pClass = RUNTIME_CLASS(СMyObject);
// получаем ук-ль на структуру CRunTimeClass
CObject *pNewObject = pClass->CreateObject();
// создаем новый объект нужного класса
ASSERT(pNewObject->IsKindOf(RUNTIME_CLASS(CMyObject));
// проверяем класс объекта
Меня удивило,
что большинство из вас подписывается
именно на HTML - я думал, наши люди как
никакие другие считают каждый килобайт.
Но, видимо, времена меняются - в лучшую
сторону. Дай бог! Так что по вышеописанным
причинам я решил поднапрячься и
сработать собственный HTML-вариант. То, что
получилось, сейчас перед вами, уважаемые
HTML-подписчики. Тех, кто выписывает
текстовый вариант, уговаривать
подписаться на HTML я не буду, потому что
прекрасно их понимаю ;) Оба варианта я
буду делать лично, никакой
автоматической генерации. Enjoy.
Мне пришло
интересное письмо на тему предыдущего
выпуска. Хочу предложить его
вашему вниманию:
Приветствую!
Я только что обнаружил эту рассылку,
подписался и 2 первых выпуска прочитал в
архиве. Очень надеюсь, что смогу оказать
посильную помощь автору и читателям
рассылки, так как около 30 лет занимаюсь
программированием и последние 3-4 года -
Visual C++. На работе сейчас я программирую
именно на этом [языке -
AJ], Visual Studio 6.0
В отношении вопроса Броника. Конечно,
система сериализации для ActiveX имеется.
Для этого рекомендуется использовать
класс
COLEControl (порожденный
из CWnd
->CCMDTarget->CObject).
Вот пример из MSDN (у меня есть этот хелп и
на работе и дома)
void CMyCtrl::Serialize(CArchive& ar)
{
DWORD dwVersion =
SerializeVersion(ar, MAKELONG(_wVerMinor, _wVerMajor));
SerializeExtent(ar);
SerializeStockProps(ar);
if (ar.IsLoading())
{
// загрузить свойства
}
else
{
// сохранить свойства
}
}
Пару замечаний
относительно сериализации. Вот пример,
как можно поддерживать версию при
сериализации. Это важная вещь, поскольку
в классы регулярно заносятся новые
данные, и чтение-сериализация должны
поддерживать старые версии
распространенных среди пользователей
Вашей программы файлов.
IMPLEMENT_SERIAL( CMyClass, CObject, VERSIONABLE_SCHEMA | 2 )
// 2- Номер текущей (новой) версии. 1 - номер старой версии
void CMyClass::Serialize(CArchive& ar)
{
if (ar.IsLoading())
{
UINT Version;
ar.ReadClass( RUNTIME_CLASS(CMyClass), &Version);
switch(Version)
{
case 2:
// чтение по-новому
break;
case 1:
// чтение по-старому
break;
}
}
if (ar.IsStoring())
{
ar.WriteClass( RUNTIME_CLASS(CMyClass));
// запись по-новому
}
}
Понятное дело,
при продвижении версии номер в IMPLEMENT_SERIAL возрастает, и
добавляется новый
случай case только при чтении
соответствующих версий.
Еще одно важное замечание. При
использовании механизма сериализации мы
платим некоторую цену: не допускается
никаких абстрактных классов - забудьте,
что это существует!
- Boris
Berdichevski
Что ж, огромное
спасибо Борису за комментарии и
дополнения. Я надеюсь, он и в будущем
будет нам посильно помогать. Что касается
вопроса Броника - думаю, он все-таки
спрашивал не о том, как сделать
сериализацию для ActiveX(хотя
это тоже очень интересный момент), а как
организовать структурированное хранение
данных в файле, наподобие того, что
присутствует в ActiveX. Ответа на этот вопрос,
за исключением предложенного мной в
предыдущем выпуске, пока нет.
Просьба:
когда пишете мне, пожалуйста
оговаривайте ваше отношение к публикации
вашего e-mail адреса. Я оставляю за собой
право решать, какие из ваших писем
появятся в рассылке. По умолчанию адрес
публиковаться не будет. Если вы
хотите связаться с человеком, письмо
которого вы прочитали в рассылке, но
чей адрес не был указан, пишите
мне с
пометкой в subject'e для кого это письмо.
Q.Идея рассылки и еe:
тематика очень понравились, даже
добавлять или изменять ничего не хочется,
как по заказу. И даже уже вопрос созрел.
При первом знакомстве с MFC помню была одна
проблема. Никак не получалось сменить
пиктограмку курсора во время выполнения
программы. Т.е. последовательность
стандартных действий LoadCursor и SetCursor не срабатывала, хотя при
создании окна этих действий хватало. В
связи с этим вопрос: Какие ещe: действия
надо выполнить для смены пиктограмки
курсора
во время работы приложения. Сейчас, к
сожалению, интересы лежат не в области С++
и MFC. Поэтому на разрешение вопроса своими
силами просто нет времени.
-
softmax
A.Спасибо
за добрые слова о рассылке. По вопросу -
проблема здесь в том, что система
автоматически при каждом движении мыши
восстанавливает тот курсор, который был
указан при регистрации класса окна.
Вообще я знаю три способа изменить курсор
в MFC-приложении, причем два из них имеют
некоторые ограничения: один используется,
в самом деле, при создании окна, а второй
работает только с одним курсором -
стандартными песочными часами. Думаю, что
стоит описать все три способа, для того,
чтобы вы могли выбрать наиболее для вас
подходящий. Итак:
Способ
1 (универсальный). Нужно перекрыть
функцию OnSetCursor() класса CWnd,
родителя вашего окна (вида). В ней
необходимо сообщение обработать самому,
устанавливая нужный курсор. Для тех, кто
не знает, сообщение WM_SETCURSOR посылается окну тогда, когда
курсор мыши двигается внутри окна, причем
мышь приложением не захвачена (с
помощью функции SetCapture()). Вот пример из MSDN:
Конечно,
можно установить и ваш собственный
курсор, только вместо LoadStandardCursor()
нужно будет воспользоваться LoadCursor() или LoadOEMCursor(). С помощью параметра nHitTest можно
определить область, в которой сейчас
находится курсор. Вообще, этот способ
лучше применять только тогда, когда вам в
самом деле нужно динамически менять один
курсор на другой (причем отличный от
песочных часов), потому этот способ самый
нерациональный (прикиньте-ка. сколько раз
будет выполняться этот обработчик). Лучше
все нужные курсоры загрузить заранее, а
из функции- обработчика Load..Cursor() не
вызывать. Хотя, в принципе, я для примера
сделал такой обработчик - никакой разницы
в скорости не заметил...но это уже зависит
от конкретного компьютера, наверное. И
потом, наверное не стали бы в MSDN это
советовать, если бы не знали, что делали ;)
Ну а тем, кому
лишь надо видом курсора показать
пользователю, что компьютер сейчас занят
какой-то операцией, идеально подходит
Способ
2 (песочные часы). Этот способ
самый простой. Вызывайте функцию BeginWaitCursor()
перед началом операции и EndWaitCursor()
после ее завершения. Единственный нюанс
здесь в том, что если эти два вызова
должны находиться в разных функциях-обработчиках,
то вам все же придется перекрыть OnSetCursor(),
причем это выглядит примерно так:
В этом случае
перед вызовом BeginWaitCursor() m_ChangeCursor
нужно приравнять к TRUE, а
после EndWaitCursor() - к FALSE.
Способ
3 (класс окна). Этот метод
применяется, когда вам для какого-то окна
нужно установить конкретный курсор,
причем желательно на все время
существования окна. Перекрываете PreCreateWindow() и регистрируете свой класс окна,
изменяя поле lpszClass параметра cs типа CREATESTRUCT: