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

Выпуск №3 от 23/06/2000

Здравствуйте!

Да, это должно было произойти и это произошло! Рассылка получила официальный статус "обычной некоммерческой рассылки" (причем гораздо быстрее, чем я ожидал), с чем я себя и всех вас и поздравляю! 

Хочу извиниться перед подписчиками 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. Идея рассылки и её тематика очень понравились, даже добавлять или изменять ничего не хочется, как по заказу. И даже уже вопрос созрел. При первом знакомстве с MFC помню была одна проблема. Никак не получалось сменить пиктограмку курсора во время выполнения программы. Т.е. последовательность стандартных действий LoadCursor и SetCursor не срабатывала, хотя при создании окна этих действий хватало. В связи с этим вопрос: Какие ещё действия надо выполнить для смены пиктограмки курсора во время работы приложения. Сейчас, к сожалению, интересы лежат не в области C++ и MFC. Поэтому на разрешение вопроса своими силами просто нет времени.

(softmax)

A. Спасибо за добрые слова о рассылке. По вопросу – проблема здесь в том, что система автоматически при каждом движении мыши восстанавливает тот курсор, который был указан при регистрации класса окна. Вообще я знаю три способа изменить курсор в MFC-приложении, причем два из них имеют некоторые ограничения: один используется, в самом деле, при создании окна, а второй работает только с одним курсором – стандартными песочными часами. Думаю, что стоит описать все три способа, для того, чтобы вы могли выбрать наиболее для вас подходящий. Итак:

Способ №1 (универсальный). Нужно перекрыть функцию OnSetCursor() класса CWnd, родителя вашего окна (вида). В ней необходимо сообщение обработать самому, устанавливая нужный курсор. Для тех, кто не знает, сообщение WM_SETCURSOR посылается окну тогда, когда курсор мыши двигается внутри окна, причем  мышь приложением  не захвачена (с помощью функции SetCapture()). Вот пример из MSDN:

BOOL CMyView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) {

 if (m_ChangeCursor) {

  // устанавливаем стандартный курсор вида "I"

  ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_IBEAM));

  return TRUE;

 }

 return CView::OnSetCursor(pWnd, nHitTest, message);

}

Конечно, можно установить и ваш собственный курсор, только вместо LoadStandardCursor() нужно будет воспользоваться LoadCursor() или LoadOEMCursor(). С помощью параметра nHitTest можно определить область, в которой сейчас находится курсор. Вообще, этот способ лучше применять только тогда, когда вам в самом деле нужно динамически менять один курсор на другой (причем отличный от песочных часов), потому этот способ самый нерациональный (прикиньте-ка. сколько раз будет выполняться этот обработчик). Лучше все нужные курсоры загрузить заранее, а из функции– обработчика Load..Cursor() не вызывать. Хотя, в принципе, я для примера сделал такой обработчик – никакой разницы в скорости не заметил…но это уже зависит от конкретного компьютера, наверное. И потом, наверное не стали бы в MSDN это советовать, если бы не знали, что делали ;) 

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

Способ №2 (песочные часы). Этот способ самый простой. Вызывайте функцию BeginWaitCursor() перед началом операции и EndWaitCursor() после ее завершения. Единственный нюанс здесь в том, что если эти два вызова должны находиться в разных функциях-обработчиках, то вам все же придется перекрыть OnSetCursor(), причем это выглядит примерно так:

BOOL CMyView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) {

 if (m_ChangeCursor) {

  RestoreWaitCursor(); // восстанавливаем курсор-пес.часы

  return TRUE;

 }

 return CView::OnSetCursor(pWnd, nHitTest, message);

}

В этом случае перед вызовом BeginWaitCursor()  m_ChangeCursor нужно приравнять к TRUE, а после EndWaitCursor() – к FALSE.

Способ №3 (класс окна). Этот метод применяется, когда вам для какого-то окна нужно установить конкретный курсор, причем желательно на все время существования окна. Перекрываете PreCreateWindow() и регистрируете свой класс окна, изменяя поле lpszClass параметра cs типа CREATESTRUCT:

BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs) {

 cs.lpszClass = AfxRegisterWndClass(

  CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, //стили окна

  AfxGetApp()->LoadCursor(IDC_MYCURSOR),// курсор

  (HBRUSH)(COLOR_WINDOW + 1));          // цвет фона окна

 return CView::PreCreateWindow(cs)

}

В  качестве  первого параметра для AfxRegisterWndClass() можно указать "cs.style", чтобы установить стиль окна по умолчанию.


Ну вот и все на сегодня. Удачного вам программирования.

(©Алекс Jenter mailto:jenter@mail.ru) (Красноярск, 2000.)







Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх