/ *


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