ПРОГРАММИРОВАНИЕ НА VISUAL C++
|
shaman |
Дата: Воскресенье, 19.09.2010. Сообщение # 1
|
Заблокированный
Сообщений: 887
Нет на сайте
|

Выпуск 1.
/*____________________________________________________________________
Программирование на Visual C++
выпуск No. 1
____________________________________________________________________
*/
Добрый день всем!
Итак, свершилось : вы видите перед собой первый выпуск рассылки.
Я понимаю, что ведение такой рассылки - это огромная ответственность,
принимая во внимание огромное количество сайтов по этой теме (кстати,
именно вопиющее отсутствие рассылки о Visual C++ на горкоте и
подвигнуло меня на ее создание). Это мой первый опыт такого рода, так
что пока я прошу вас не судить слишком строго. Я же со своей стороны
постараюсь сделать все от меня зависящее, чтобы рассылка была
интересной, занимательной и полезной.
Прежде всего, я хотел бы уточнить тематику наших бесед. А именно то,
что вы здесь не найдете учебника по программированию типа
"шаг-за-шагом". По этому принципу построено очень много разных книг,
сайтов в Рунете и других источников. На мой взгляд, такие учебники
учат только тому, что в них заложено. Они предлагают решение какой-то
задачи, но если вам нужно решить задачу немного отличную от описанной,
то начинаются проблемы. Ведь очень часто авторы для упрощения
изложения выбирают самые тривиальные решения, не подходящие для
большинства реальных задач. И приходится выкручиваться своими силами -
штудируя help, делая методом "тыка". И, хочу заметить, гораздо чаще
такие вот искания оказываются полезнее тупого выполнения шагов
учебника, потому что в процессе поиска выдумаетеирешаетесами.
С другой стороны, не поймите меня неверно - я вовсе не отрицаю право
таких учебников на существование (думаю, что на какой-то стадии они
могут быть даже очень полезны), я лишь подчеркиваю их ограниченную
применимость.
Именно поэтому в рассылке не будет учебников типа "пишем свою записную
книжку".
Хочу подчеркнуть, что все вышеизложенное является моим личным мнением
и вы вправе быть с ним не согласны. По правде говоря, я на это
рассчитываю Пишите мне - и мы это обсудим.
Итак, что же будет освещено в рассылке?
Во-первых, некоторые теоретические сведения - знание теории никогда и
никому не вредило. Причем основной упор будет делаться вовсе не на С++
как язык программирования (хотя это я тоже не исключаю), а на такие
вещи, как MFC и WinAPI.
Во-вторых, разные полезные приемы программирования и хитрости, советы
и трюки.
Еще, объяснение некоторых английских терминов по программированию.
Потом, обзор некоторых книг по данной теме, перевод интересных статей
из интернета и, конечно же, ваши письма, вопросы, мнения.
Но этим я не хотел бы жестко ограничиваться. Пишите, что интересно Вам
лично?
//////////////////////////////////////////////////////////////////////
Я долго думал, с чего лучше всего будет начать. Ведь уровень у каждого
из вас совершенно разный. И решил, что для начала это должно быть
что-то "легкое", понятное всем, но и не совсем бесполезное в то же
время.
Поэтому следующий материал к программированию как таковому не
относится, он лишь позволяет сделать его более комфортным.
Пробовали ли Вы хоть раз менять стандартную синтаксическую подсветку в
Visual C++ IDE? Если да, то знаете, что, к сожалению, в настройках
доступно только 16 возможных цветов. Какая жалость! Ведь можно было бы
сделать очень приятную цветовую схему, ярко выражающую вашу
индивидуальность... ну или просто более приятную для глаз.
Неужели ничего нельзя поделать? Оказывается, можно! Для исполнения
этого желания нам придется воспользоваться стандартным виртуальным
"ломом" для Windows. Нет, я не имею ввиду дизассемблирование - избави
бог! В качестве лома в данном случае будет выступать просто
редактор реестра (regedit.exe).
Запустите RegEdit и откройте ветвь
HKEY_CURRENT_USER\
Software\Microsoft\DevStudio\6.0\Format\Source Window
В окне просмотра появится список значений. Каждому синтаксическому
элементу соответствует определенный набор шестнадцатеричных чисел.
Первые три из них - это цвет самого элемента в виде RGB. Потом одно
число пропускается (там, как правило, 0) и три следующие числа - это
цвет фона. Т.е. формат такой: RGB-0-RGB-0 (остальные числа лучше
не трогать).
Вот и все - теперь для любого элемента программы ( комментария,
строки, ключевого слова или др.) вы можете назначить любой из 16 млн.
доступных цветов!
//// ВОПРОС-ОТВЕТ ////////////////////////////////////////////////////
Q. Как изменить стили окон, создаваемых MFC AppWizard'ом по умолчанию?
A. Чтобы изменить стиль по умолчанию какого-нибудь окна, нужно
перекрыть виртуальную функцию PreCreateWindow() класса этого окна. Эта
функция позволяет приложению получить доступ к процессу созданию окна,
который по умолчанию происходит в недрах MFC с помощью класса
CDocTemplate. Библиотека вызывает PreCreateWindow() перед созданием
окна. Этой функции передается параметр - указатель на структуру
CREATESTRUCT. Путем изменения членов этой структуры вы можете влиять
на стиль создаваемого окна.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Q. Как сделать так, чтобы положение элементов управления менялось,
когда размер окна изменяется, т.е.,например, чтобы они выравнивались
по правому или нижнему краю?
А. Увы, это не так элементарно делается, как, скажем, в С++ Builder.
Но и здесь есть свои плюсы - вы получаете больший контроль. Пусть,
скажем, у Вас есть кнопка, которую Вам нужно выровнять по правому
краю, и соответствующая переменная m_Btn типа СButton в классе вашего
окна или вида. Тогда в функции обработки сообщения WM_SIZE- OnSize()..
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CFormView::OnSize(nType, cx, cy);
.
.
// ... добавьте вот это:
if (::IsWindow(m_Btn.m_hWnd)) // условие на корректность
m_Btn.MoveWindow ( cx - BtnWidth - 10, BtnY,
cx - 10, BtnY + BtnHeight,
0 ); // двигаем кнопку
// конец кода для добавления.
.
.
}
Здесь вместо BtnY вставьте желаемую Y-координату кнопки, BtnWidth и
BtnHeight - соответственно целевые ширина и высота кнопки.
Параметр cx, передаваемый в функцию - это новая ширина окна. Данный
код изменяет положение кнопки, чтобы она оставалась ширины Btn_Width и
отстояла от правого края окна на 10 единиц. Функция MoveWindow()
меняет размер и положение кнопки. Если вы не знаете BtnY|Width|Height,
то их можно определить с помощью функции m_btn.GetClientRect(), ведь
любой элемент управления - это, в принципе, тоже окно.
Выравнивание по нижнему краю производится аналогично, просто по смыслу
меняются параметры MoveWindow().
//////////////////////////////////////////////////////////////////////
Ну вот, на сегодня пока все. Жду ваших писем с замечаниями,
предложениями и пожеланиями. До скорого!
(С)2000 by Алекс Jenter mailto:[email protected].
http://subscribe.ru/
E-mail:[email protected] byCorbina
РейтингуетсяSpyLog
|
|
|
|
|
shaman |
Дата: Воскресенье, 19.09.2010. Сообщение # 2
|
Заблокированный
Сообщений: 887
Нет на сайте
|
Выпуск 2.
/*____________________________________________________________________
Программирование на Visual C++
выпуск No. 2
____________________________________________________________________
*/
Приветствую вас, уважаемые подписчики!
Очень рад, что число вас растет, несмотря на то, что рассылка еще не
вышла из категории "Рассылки для каждого" и не была официально
анонсирована. Если новоподписавшимся интересно узнать, о чем мы в
рассылке беседуем, они могут посмотреть в архиве первый выпуск.
Сегодня мы немного поговорим об устройстве MFC, а также рассмотрим
один интересный вопрос.
/// НЕМНОГО ТЕОРИИ ///////////////////////////////////////////////////
Как известно, основой всех основ в MFC является класс CObject.
Основным назначением этого класса является предоставление некоторых
базовых возможностей всем своим наследникам, а именно доступ к
информации о классе во время выполнения и поддержка сериализации, т.е.
сохраняемости объектов.
Однако уровень предоставляемых возможностей варьируется в зависимости
от вашего выбора; он зависит от включения определенных макросов
объявления и реализации при создании классов - наследников CObject.
Без сомнения, вы с этими макросами уже сталкивались, например в коде,
который генерируют Wizard'ы. Пришла пора разобраться с ними более
детально.
Итак, на характер вашего класса, производного от CObject, вы можете
влиять с помощью нескольких макросов. Существуют определенные пары
макросов - один включается в объявление класса (имеет префикс
DECLARE_), а соответствующий ему - в реализацию (префикс IMPLEMENT_).
Первая пара макросов - это DECLARE_DYNAMIC|IMPLEMENT_DYNAMIC. С
помощью включения этих макросов в код вашего класса вы можете включить
одну из базовых функций CObject - способность узнавать класс объекта
прямо во время выполнения программы. Для этого вы можете пользоваться
функцией IsKindOf() в связке с макросом RUNTIME_CLASS, который
возвращает указатель на структуру CRuntimeClass ( где хранится вся
информация о классе: имя, размер, версия, информация о базовом классе,
указатель на конструктор объекта и т.д.)
Следующая пара - DECLARE_DYNCREATE|IMPLEMENT_DYNCREATE аналогична
первой, но к возможности получать информацию о классе добавляется еще
и возможность создавать объекты этого класса во время выполнения.
Объект создается функцией CreateObject структуры CRuntimeClass. Вот
пример:
CRunTimeClasspClass = RUNTIME_CLASS(СMyObject);
// получаем ук-ль на структуру CRunTimeClass
CObjectpNewObject= pClass->CreateObject();
// создаем новый объект нужного класса
ASSERT(pNewObject->IsKindOf(RUNTIME_CLASS(CMyObject));
// проверяем класс объекта
И, наконец, мы подошли к последней паре макросов DECLARE_SERIAL|
IMPLEMENT_SERIAL. Преобразование в последовательную форму и обратно -
сериализация - дает программисту возможность сохранения и восста-
новления объектов. Для того, чтобы воспользоваться этой возможностью,
в классе-наследнике нужно перекрыть виртуальную функцию Serialize().
Из нее обязательно нужно сначала вызвать родительскую версию. Одна и
та же функция используется как для сохранения, так и для
восстановления объекта. Какую операцию нужно произвести, она
определяет из своего единственного параметра ar типа CArchive . Вот
пример:
void CMyObject::Serialize( CArchive ar )
{
CObject::Serialize( ar ); // вызываем версию базового класса
if( ar.IsStoring() ) // если сохраняем,
{
ar << something; // то сохранить что-то
}
else // а иначе
{
ar >> something; // восстановить
}
}
Заметьте, что DECLARE_SERIAL|IMPLEMENT_SERIAL помимо сериализации
включают и те возможности, которые дают две первые пары - это
естественно, ведь если вы восстанавливаете объект, то вам понадобится
возможность создать его во время выполнения программы. Например,
приложению нужно сохранять и восстанавливать некоторый набор объектов
различного типа. А для вызова соответствующего конструктора при
восстановлении объекта нужно знать его тип. Механизм сериализации
сохраняет информацию об объекте вместе с теми данными, что вы
записываете явно в функции Serialize().
/// ВОПРОС - ОТВЕТ ///////////////////////////////////////////////////
Следующий вопрос поступил от одного из подписчиков:
Q При программировании элементов ActiveX, в этой технологии есть
возможность структурного хранения данных на диске, т.е. создание в
файле так называемых хранилищ и потоков (использование интерфейсов
IStream и IStorage), проще говоря - представление файла данных в виде
иерархической системы внутренних каталогов и файлов, которые там (в
данном файле данных) имеют свои строковые имена. Есть ли в MFC
возможность структурного хранения, скажем, используя объект класса
CArchive в переопределяемой функции Serialize(CArchive) класса,
производного от CDocument, ну и так далее? Конечно, этого можно
добиться, создав свои собственные наработки (а как хороша эта идея, я
имею в виду использование потоков и хранилищ), но все таки хочется
знать, есть ли такие возможности в MFC, чтобы не тратить зря время.
- Броник ([email protected])
A Насколько мне известно, поддержки такой иерархической системы в
MFC нет. Во всяком случае, я ее не обнаружил. CArchive и Serialize для
этой цели явно не предназначены: в них важную роль играет
последовательность записи, т.е. в каком порядке вы что-то записали, в
таком нужно это и прочитать. Так что, скорее всего, придется писать
свой класс для этой цели. Конечно, это не слишком обнадеживает, но
зато этот класс можно будет использовать во всех дальнейших
программах, где потребуется такая форма хранения данных. Или - как
вариант - можно сделать просто класс-обертку для интерфейсов IStorage
и IStream. Конечно, в этом случае придется подключать библиотеку COM
(которую в MFC-приложениях, в принципе, никто не запрещает
использовать). Впрочем, если кто знает что-нибудь о существовании
такого механизма в MFC - пожалуйста, поделитесь с нами.
//////////////////////////////////////////////////////////////////////
Всего наилучшего и чтоб программы ваши не знали ошибок.
.
|
|
|
|
|
shaman |
Дата: Воскресенье, 19.09.2010. Сообщение # 3
|
Заблокированный
Сообщений: 887
Нет на сайте
|
Выпуск 3.
/ *
--------------------------------------------------------------------------------
Программирование на Visual C++
выпуск № 3
--------------------------------------------------------------------------------
* /
Здравствуйте!
Да, это должно было произойти и это произошло! Рассылка получила официальный статус "обычной некоммерческой рассылки" (причем гораздо быстрее, чем я ожидал), с чем я себя и всех вас и поздравляю!
Хочу извиниться перед подписчиками 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", чтобы установить стиль окна по умолчанию.
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Ну вот и все на сегодня. Удачного вам программирования.
|
|
|
|
|
shaman |
Дата: Воскресенье, 19.09.2010. Сообщение # 4
|
Заблокированный
Сообщений: 887
Нет на сайте
|
Выпуск 4.
/ *
--------------------------------------------------------------------------------
Программирование на Visual C++
выпуск No. 4
--------------------------------------------------------------------------------
* /
Добрый день, уважаемые подписчики!
Мне приходит очень много ваших писем с вопросами, советами, комментариями, предложениями и т.д. Как повелось, самые интересные из них я публикую в рубриках "Обратная связь" и "Вопрос-ответ". Но, как вы наверное сами понимаете, я просто физически не в состоянии отвечать на такое количество вопросов, касающихся программирования. Я отвечаю быстро, когда сразу знаю ответ, но ведь чаще приходится самому сидеть и разбираться, а на это уходит много времени. От этого в первую очередь страдают другие рубрики рассылки - я не успеваю подготовить хороший материал для вас (кстати, я как раз хочу сделать несколько новых рубрик... но пусть лучше это будет сюрприз). Моя задача как автора рассылки состоит в том, чтобы готовить и публиковать интересную информацию по обозначенной тематике. Я же в последнее время больше сам занимаюсь программированием. Для меня, это, конечно, полезно но вот как насчет вас? На многие вопросы я уже ответил, хотя они не появятся в рассылке, т.к. довольно узко направлены и вряд ли интересны для "широкой общественности". Ответы на другие или слишком обширны и тянут на тему отдельного выпуска, или попросту элементарны.
Решив, что так дальше дело все-таки не пойдет, я придумал новую схему. Мне пришло несколько писем с предложениями помощи от программистов, выписывающих рассылку (огромное спасибо им!), так что теперь лишь на некоторые (самые интересные вопросы я буду отвечать лично (в рубрике "Вопрос-ответ"), а другие просто опубликую отдельно. В дальнейшем эти вопросы (вкратце) уже вместе с ответами на них появятся в рубрике "Вопрос-ответ" (мне, кстати, предлагали расширить эту рубрику - вот хороший повод). Я надеюсь, наше с вами мнение совпадет и вы тоже посчитаете, что так будет лучше. Еще я очень полагаюсь на ваше сотрудничество - если знаете ответ на вопрос, не поленитесь и напишите! Человек будет вам благодарен, да и не только он, а все читатели, которые узнают что-то новое. А я лично преобладающую свою роль программиста-консультанта сменю на роль ведущего рассылки.
С другой стороны, я ни в коем случае не хочу рассылку превращать в тривиальную дискуссионную группу. Будет несколько новых рубрик! Поэтому количество вопросов, рассматриваемых в выпуске, будет ограничиваться (в среднем) двумя-тремя вопросами в рубрике "Вопрос-ответ" плюс два-три вопроса, ожидающие ответа.
Кстати, могу дать очень хороший совет для тех, кто отчаялся найти решение своей проблемы: попробуйте, действительно, задать вопрос в дискуссионной группе, например на news.microsoft.com, в microsoft.public.ru.vc или microsoft.public.russian.programming, а если знаете английский, лучше в одну из многочисленных microsoft.public.vc.* Там отвечают действительно быстро, сам не раз прибегал к этому! И не забудьте порекомендовать подписаться на рассылку 
Очень многие спрашивают, где можно прочитать предыдущие выпуски рассылки. Отвечу лучше здесь: смотрите на http://subscribe.ru/archive/comp.prog.visualc/
Да, и еще. Выпуски, скорее всего, станут выходить чаще, до 3-4 раз в неделю, иначе размер каждого заметно увеличится. А я сам знаю, как неприятно забирать 30-40 Кб письма, когда постоянно рвется связь (да простят меня читатели с хорошей связью;) В каждом выпуске помимо обсуждений и вопросов я постараюсь публиковать что-то интересное. Have fun.
/ / / / ОБРАТНАЯ СВЯЗЬ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
По поводу вопроса смены курсора в MFC-приложении (выпуск No.3) пришло несколько дополнений и замечаний (некоторые несущественные моменты я опустил, вместо них поставил знак "[...]", свои комментарии в теле письма я также привожу в квадратных скобках):
Подобно третьему случаю, но не только в PreCreateWindow()
В тот момент, когда хотим поменять курсор (для всего приложения) проделываем следующие действия:
::SetClassLong( theApp.m_pMainWnd->m_hWnd,
GCL_HCURSOR,
::LoadCursor(theApp.m_hInstance,
MAKEINTRESOURCE(IDC_MY_CURSOR)));
Немного длинная строчка кода :)) [...]
Как обычно встречается такая проблема: theApp, хоть и объявлена глобально (объявляется если проект создается стандартным Wizard-ом), не видна в других файлах проекта. Чтобы обойти это просто в файле объявления класса вашего приложения (производного от CWinApp) сразу после объявления класса добавьте следующую строчку:
extern CMyApp theApp;
где CMyApp - имя класса вашего приложения.
- Andrew Gromyko
Замечу, что theApp так объявлять необязательно, так как многие, и я в том числе, пользуются вместо этого стандартными функциями MFC AfxGetApp() и AfxGetMainWnd(), которые возвращают указатель на объект-приложение и главное окно соответственно.
Вот еще одно письмо на эту тему:
Проблема смены курсора меня настигла еще на раннем этапе моего программистского становления. Поэтому хочу предложить тот вариант, который является по-моему наиболее адекватно подходящим с точки зрения скорости и идеологии Win32 API. Способ три конечно хорош и его можно расширить на все время существования окна. Вот как происходит вызов в классе окна:
::SetClassLong (m_hWnd,GCL_HCURSOR,
(long)AfxGetApp()->LoadCursor (IDC_MY_CURSOR));
При этом курсор меняется перманентно, т.е. например при вызове SetCursor() вид курсора меняется при начале движения мыши. А так меняется просто класс окна. Работает быстро и надежно. [...]
- Alex ([email protected])
Спасибо Андрею и Алексу за это дополнение, теперь мы с вами знаем четыре способа изменить курсор;). Должен признаться, в предыдущем выпуске я допустил оплошность, ляпнув что первый параметр у AfxRegisterWndClass() можно заменить на cs.style, причем, как назло, понял свою ошибку как раз в тот момент, когда отправлял рассылку "в эфир". Мне не преминули на эту ошибку указать, и я очень рад этому. Пожалуйста, будьте бдительны!!! Я тоже человек! Подробности читайте ниже...
Спасибо Вам за создание действительно нужной рассылки. [Мерси за комплимент - AJ]
Я не так давно начал использовать MFC, и информация, ответы на вопросы мне очень пригодятся. Интересно, что уже в первом прочитанном мной выпуске обсуждался вопрос о курсоре, решение которого я буквально только что искал сам. Поэтому я решил написать этот отзыв в виде нескольких замечаний. На всякий случай оговорю, что все ниже написанное не более чем мое humble opinion :-))
1) Мне не кажется, что описанный Вами универсальный способ изменения курсора нерационален. Не этот обработчик, так CWnd::OnSetCursor() все равно вызывается при каждом движении мыши. Поэтому разнице в скорости практически неоткуда взяться. Хотя, LoadCursor() действительно лучше вызвать один раз (когда этот курсор впервые устанавливается), сохранить дескриптор нового курсора, который и передавать системной функции SetCursor() в обработчике. Мне кажется, это будет по сути то же самое, что делается при обработке события WM_SETCURSOR по умолчанию.
2) О "песочных часах". Если операция, на время которой высвечиваются часики, относительно невелика по времени, и при ее выполнении приложение может не реагировать на другие события, проще всего выделить блок обработки и определить в этом блоке локальный объект CWaitCursor. Все необходимые операции по установке и удалению курсора будут сделаны конструктором и деструктором этого объекта. Именно так, например, в MFC реализованы часики при открытии и сохранении документа. Если же во время операции система реагирует на другие события, то удобнее применять Begin/Restore/EndWaitCursor() [Итак, способов уже пять! - AJ]
3) Вы показываете пример с переопределением PreCreateWindow(), в котором регистрируется новый класс окна, и в конце пишете: "В качестве первого параметра для AfxRegisterWndClass() можно указать "cs.style", чтобы установить стиль окна по умолчанию." По-моему, это некорректно. Ведь cs.style есть комбинация стилей для окна, а не для класса окна, что требуется при регистрации класса. [Виновен, Ваша честь! - AJ] Мне пришлось столкнуться с этой проблемой, когда я хотел для своего класса CMyView добавить стиль CS_OWNDC, не меняя ничего больше. Дело в том, что при самом первом вызове CMyView::PreCreateWindow() класс окна просмотра еще не существует, так как он регистрируется в CView::PreCreateWindow(). Поэтому пришлось вызывать родительскую функцию дважды. Вот мое решение, которое, быть может, будет кому-то полезно:
//h-файл
class CMyView :public CView
{
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
static LPCTSTR lpszViewClassName;
}
//cpp-файл
LPCTSTR CMyView::lpszViewClassName = NULL;
BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs)
{
if (lpszViewClassName == NULL)
{
CView::PreCreateWindow(cs);
WNDCLASS wc;
//пытаемся получить информацию о классе
//если не удается, выходим с ошибкой
if (!::GetClassInfo(AfxGetInstanceHandle(),
cs.lpszClass, &wc))
return FALSE;
// теперь изменяем в wc все, что нужно
// например, стиль класса
wc.style |= CS_OWNDC;
// регистрируем класс
lpszViewClassName = AfxRegisterWndClass(
wc.style, wc.hCursor,
wc.hbrBackground, wc.hIcon);
}
// изменяем класс окна на созданный нами:
cs.lpszClass = lpszViewClassName;
return CView::PreCreateWindow(cs);
}
[Я немножко подправил код и добавил комментарии. Надеюсь, автор на меня не обидится - AJ]
Есть у меня и вопросы, ответы на которые, возможно, будут интересны не только мне.
Q1) В приложении есть операция, которая требует, скажем, больше пяти минут времени, причем по некоторым причинам дальнейшее выполнение не может быть продолжено до завершения этой операции. Хотелось бы, чтобы при этом окно приложения нормально обновлялось, могло быть свернуто-развернуто и т.п. Я нашел некоторое решение, но оно требует создания второго цикла обработки сообщений и потому мне не очень нравится, хотелось бы сделать более естественно.
Q2) Можно ли переопределенный обработчик событий сделать подставляемым (inline)?
- Куканов Алексей ([email protected])
Очень хорошее, обстоятельное письмо. Хотя оно и содержит вопросы, я все же решил поместить его в "обратную связь", т.к. по большей части относится к теме, ну а разбивать письмо на две части - это было бы варварство. Если кто-нибудь знает ответы на заданные вопросы - очень прошу поделиться! Вопросы действительно интересные.
Если у вас есть еще какие-либо комментарии на тему смены курсора мыши, пишите лучше сейчас. После следующего выпуска тема будет считаться закрытой.
/ / / / ВОПРОС - ОТВЕТ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Вопросы прислал Ivan Pouzyrevsky. И хотя у некоторых из вас они могут вызвать улыбку, я решил опубликовать их в рассылке вместо личного ответа, т.к. все-таки довольно большое число начинающих программистов на VC далеко не сразу понимает, как работать с диалогами в концепции MFC.
Q. Создаю кнопку About. В MFC Class Wizard создаю функцию: IDS_ABOUT->BN_CLICKED. А какой код на открытие окна About?
A. Чтобы вызвать модальный (т.е. не разрешающий переключаться куда-то еще в приложении, пока пользователь не закрыл его) диалог, следует воспользоваться ф-цией DoModal():
CAboutDlg aboutDlg;
aboutDlg.DoModal();
AppWizard генерирует такой код сам на команду ID_APP_ABOUT. Так что проще всего, если вы при создании приложения попросили AppWizard создать окно About, назначить Вашей кнопке идентификатор ID_APP_ABOUT. Тогда больше ничего делать не надо.
А вообще, таким образом можно вызвать не только диалоговое окно About, но и любое другое. Кому не совсем ясно, как обеспечить обмен данными с диалогом, присылайте заявки. В случае наличия интереса это станет одной из следующих тем выпуска.
Господа опытные программисты, прошу не тратить силы на ворчание - я не могу угодить всем! Для вас тоже будет кое-что интересное.
Q. У меня в программе при написании слова выполняется функция. Например:
if(UpperValue==CALCULATOR)
{
system("calc.exe");
m_TestEdit="";
UpdateData(FALSE);
}
Но тут вопрос: как пользователю без изменения кода добавлять в базу данных слова?
Например диал. окно
Слово=
Файл Запуска=
И как указывать путь к файлу, а то при system("D:\UnrealTournament\System\unrealtournament.exe") пишет, что файл не найден?
A. Попробуйте сделать два массива (или списка) CString - один для слов, другой для файлов. Добавляйте в массивы данные по мере ввода их пользователем. При запуске вызывайте нужный файл. Это можно более эффективно сделать с использованием ассоциативного списка (СMap), но сейчас, пожалуй, лучше не забивайте этим голову.
В С++ в строках символ "\" воспринимается как управляющий, чтобы представлять такие вещи, как "\n" - Enter, "\b"- звонок, "\0" - "косой ноль" и др. В частности, управляющая последовательность "\\" сама представляет символ "\", поэтому в строке его надо удвоить, а т.к. используются длинные имена, то лучше еще заключить строку в дополнительные кавычки с помощью управляющей посл-ти \" (хотя у меня работало и без них):
system("\"D:\\UnrealTournament\\System\\unrealtournament.exe\"");
Да, надо сказать, уважаемый Ivan, что мне дико нравится файл, который Вы запускаете 
/ / / / В ПОИСКАХ ИСТИНЫ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Ну вот, как я и обещал - новая рубрика. Ваши вопросы, на которые я надеюсь от вас же получить ответы. Ну, поехали:
Q. При вылизывании проекта под MS Visual C++ 5.0 была произведена замена операции заполнения служебных переменных данными из файла на диске на операцию заполнения из строковых ресурсов проекта (файл .rc). Используется ф-ция LoadString() MFC класса CString с использованием в качестве аргумента ф-ции числового идентификатора ресурса (передается не IDS_XXXX, а его числовое значение). Файл "resource.h" в необходимые файлы включен. Под VC++ 6.0 - картина аналогичная :(. Компиляция проекта при этом происходит без ошибок и предупреждений. При выполнении проекта в Debug-версии на этапе выполнения указанной выше ф-ции возникает "Debug Assertion Failed" в файле afxwin1.inl на строке 22. Этот блок из себя представляет следующее:
_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()
{
ASSERT(afxCurrentResourceHandle != NULL);//строка 22
return afxCurrentResourceHandle;
}
При нажатии на клавишу "Пропустить" программа идет дальше и вываливается на следующей операции загрузки строки с теми же симптомами и так до тех пор, пока не будут загружены все строки. После этого выполнение программы продолжается в нормальном режиме и все остальное работает как надо (строки все-таки загружаются!). В Release-версии программа пролетает это место без спотыканий.
Ясно, что можно не обращать на это внимание и сделать условную компиляцию, но дело в принципе! Не могу разобраться в чем тут закавыка.
- Евгений
По-моему, вопрос несложный, и имей я лишнее время - разобрался бы сам. Что-то тут с инициализацией, попытка использования раньше времени (как мне кажется)... Но я рассчитываю на тех, кто, прочитав это, воскликнет "ну это ж элементарно!" и сразу начнет писать ответ;)
Те, кто задал вопрос, но пока его не увидел в рассылке и не получил личного ответа - не отчаивайтесь, ждите новых выпусков.
/ / / / АНОНС / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Читайте в следующих выпусках рассылки:
Что дядя Билли нам готовит, или Visual Studio Next Generation
WinAPI: не запутайтесь в типах
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Ну вот, видите какой большой получился выпуск, хотя в нем, фактически, не было ничего кроме ваших писем. Думаю, это служит доказательством целесообразности нового режима выхода рассылки. Не бойтесь, что рассылка станет просто большой конференцией - я постараюсь этого не допустить! Все хорошо в меру. Все ваши замечания и предложения с благодарностью принимаются.
До новых встреч. Всего хорошего!
|
|
|
|
|
shaman |
Дата: Воскресенье, 19.09.2010. Сообщение # 5
|
Заблокированный
Сообщений: 887
Нет на сайте
|
Выпуск 5
/ *
--------------------------------------------------------------------------------
Программирование на Visual C++
выпуск No. 5
--------------------------------------------------------------------------------
* /
Приветствую!
Итак, рассылка снова с вами, уважаемые подписчики, и вы видите сейчас уже пятый выпуск. Сегодня мы поговорим о типах данных и рассмотрим ваши вопросы и ответы.
/ / / / WINAPI / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
WinAPI - это одна из обещанных мной новых рубрик. Как следует из ее названия, в ней мы будем рассматривать вопросы, посвященные Windows API.
WinAPI: НЕ ЗАПУТАЙТЕСЬ В ТИПАХ
Вы когда-нибудь попадали на страницу Win32 Simple Data Types в Help? В переводе с английского simple означает "простой", т.е. Microsoft хочет сказать, что это "простые типы". В С++ простыми типами были int, double и другие. При программировании для Windows эти типы никуда не деваются, но появляется очень много новых. Они, конечно, не входят в стандарт C++ и не являются его ключевыми словами (ведь на С++ программируют не только под Windows), но любой Windows-программист должен эти типы хорошо знать. И краткого описания типа, приведенного на вышеуказанной странице MSDN, часто бывает недостаточно, так что иногда приходится лезть в исходники и смотреть, что же из себя представляет тип на самом деле. Я вам предлагаю обзор самых основных и важных типов.
Типы Win32 гораздо легче понять, если знать некоторые соглашения. Например, названия типов, по своей природе являющихся указателями, начинается с префикса P или LP. Кстати, LP означает Long Pointer (дальний указатель) и остался в наследие от Windows 3.1, когда указатели еще делились на ближние (содержащие только смещение в сегменте) и дальние (содержащие как сегмент, так и смещение). Префикс H означает HANDLE - это типы, используемые для описания различных объектов, а префикс U - что тип беззнаковый.
С типами INT, UINT, LONG, ULONG, WORD, DWORD, VOID, SHORT, USHORT, CHAR, FLOAT. BYTE, BOOL(BOOLEAN), у вас не должно быть никаких проблем, и было бы глупо их тут расписывать. Эти типы дублируют встроенные типы С++, и единственное, на что здесь нужно обращать пристальное внимание - это размер типа. Эти типы рекомендуется использовать вместо встроенных в С++ для улучшения переносимости приложения, т.к. в разных системах встроенные типы имеют различные размеры.
Очень интересен тип WINAPI. По-хорошему это все-таки не тип. Если вы посмотрите в файл windef.h, то увидите следующую строку: "#define WINAPI __stdcall". __stdcall - это ключевое слово языка С++, оно, в частности, влияет на механизм передачи параметров функции. Суть механизма, определяемого __stdcall состоит в том, что 1) аргументы передаются справа налево; 2) аргументы из стека выбирает вызываемая функция; 3) аргументы передаются по значению (by value), а не по ссылке (by reference), т.е. функции передаются копии переменных; 4) определяет соглашение по декорированию имени функции, т.е. включению в имя дополнительной информации, используемой компоновщиком; 5) регистр символов не изменяется.
То есть оказалось, что WINAPI - это не вовсе тип, а указание о том, что функция использует соглашение __stdcall. Кстати, имейте в виду, что описатель PASCAL и __pascal - это то же самое, что и WINAPI. Но этот описатель является устаревшим, оставлен лишь для совместимости, и Microsoft рекомендует повсеместно использовать вместо него WINAPI.
Использование соглашения __сdecl вместо __stdcall иногда оправданно, но приводит к увеличению размера исполняемого модуля из-за того, что имя функции декорируется в этих соглашениях по-разному.
...продолжение следует...
/ / / / ВОПРОС - ОТВЕТ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Я очень рад, что мой расчет оказался верным и нашлись знающие люди, готовые ответить на заданные в предыдущем выпуске вопросы. Огромное им спасибо!
Q. В приложении есть операция, которая требует, скажем, больше пяти минут времени, причем по некоторым причинам дальнейшее выполнение не может быть продолжено до завершения этой операции. Хотелось бы, чтобы при этом окно приложения нормально обновлялось, могло быть свернуто- развернуто и т.п.
- Куканов Алексей ([email protected])
A1. А в чем проблема? Внутри, допустим цикла иногда добавляется цикл:
for( ; GetMessage(lpMsg, hWnd, 0, 0); DispatchMessage(lpmsg) );
И для красоты на все "запрещенные" действия ставим флаг (который взводим/гасим по необходимости). Вот и весь велосипед.
- Сергей Бойко
Мне вот только не совсем понятно, что значит "иногда добавляется"... Время от времени добавляется, что ли? 
A2. Я решил написать ответ на вопрос Куканова Алексея, о корректной прорисовке окна во время какого-то процесса. С MFC это решается элементарно. Пусть есть функция LRESULT Calculation (LPVOID pParam); Не обращайте внимание на параметры объясню позже. Так вот вместо того чтобы в теле какого-то обработчика запускать эту функцию
CMyDlg::OnButtonClick ()
{
Calculation ();
}
и ждать когда она закончит лучше сделать так
CMyDlg::OnButtonClick ()
{
AfxBeginThread (Calculation,
(LPVOID)m_hWnd,THREAD_PRIORITY_LOWEST);
}
По сути MFC запускает параллельную нить, которая никак не влияет на перерисовку всего остального. Обычно можно в качестве pParam передать HWND окна. Потому как узнать когда закончится процесс можно только при помощи сообщений. Например в теле Calculation
::SendMessage ((HWND)pParam,WM_STOP,0,0);
А кто хочет узнать побольше читайте MSDN - "Worker threads".
- Alex ([email protected])
Кстати, Alex пишет нам уже второй раз, хочу поблагодарить его за активность.
В принципе такой же, но более обстоятельный ответ на этот вопрос пришел чуть позже:
A3. Самый оптимальный по-моему способ: Это запустить worker thread - второй поток (если пока только один :)) ) апликации. В качестве параметра передать туда структуру с необходимыми данными, а можно и ничего не передавать. Если все данные хранятся в наследнике CWinApp (дальше - CMyApp) , то получить доступ к объекту апликации можно с помощью функции AfxGetApp(). Единственное замечание по передаче данных из одного потока в другой заключается в том, что надо доступаться ТОЛЬКО к мемберам класса - нельзя вызывать функции класса из другого потока (вернее, можно, если они не изменяют данных класса или не обращаются к оконным функциям класса (относиться к наследникам CWnd)). В итоге имеем схему:
1. Создается worker thread (поток одной функции, при ее завершении завершается и поток). В качестве параметра функции
AfxBeginThread передается указатель на необходимые данные.
2. В основном потоке создается собственное сообщение, сигнализирующее о завершении потока. Оно будет брошено рабочим потоком перед своим завершением с помощью PostMessage (при работе с потоками я предпочитаю PostMessage для обмена такого рода сообщениями, ведь SendMessage ждет завершения работы обработчика события, что часто просто не нужно).
3. Запущенный поток выполняет всю черновую работу, в то время как основной поток апликации занимается важными делами, а именно - ничего не делает, знай крутит себе цикл обработки сообщений и не жужжит.
4. По завершении работы, worker thread посылает основному потоку мессагу, мол я закончил, выкладывает результаты так, чтобы основной знал где они (как это сделать - миллионы способов :)), в частности, передать в завершающем сообщении указатель на данные результата. )
Примерный код таков.
UINT WorkerThreadFunction(WPARAM, LPARAM lpData)
{
// тутачки работаем с lpData и выполняем
// всю необходимую работу
// результат запихиваем в память,
// а адрес на нее - в lpResult
AfxGetMainWnd()->PostMessage(ID_WORKER_THREAD_END, 0, lpResult);
// возвращаем код успеха (а вообще это на ваш вкус)
return 0;
}
void CMyApp::OnStartExecution()
{
// заполняем lpData нужными данными, и вызываем ..
CWinThread *pThread = AfxBeginThread(WorkerThreadFunction, lpData);
if ( !pThread )
{
// Не смогли запустить поток.
// Правда обычно этот код не выполняется :)).
// Я до сих пор не знаю ситуации, когда поток
// может не запуститься, кроме low memory.
AfxMessageBox(_T("Can't start thread."));
}
}
LRESULT CMyApp::OnWorkerThreadEnd(WPARAM wParam, LPARAM lpResult)
{
// тутачки обрабатываем завершение расчетов.
}
Замечания:
1. Объявлять обработчик сообщения ID_WORKER_THREAD_END надо через ON_MESSAGE макрос
2. Потоков запускать можно сколько душе угодно (если хватает памяти :)) ).
3. Повторюсь: важно понимать, что доступ к данным может быть одновременным из разных потоков. Поэтому необходима синхронизация. чтобы не получилось ситуации, что один поток пишет, а второй читает в одно время - как результат, можно разрушить логическую целостность данных. Поэтому в приведенных примерах лучше сбрасывать данные в отдельный блок памяти и передавать указатель на него. И функция рабочего потока, и обработчик сообщения завершения должны освобождать передаваемую им память.
- Oleg Tselobyonok, Applied systems, Ltd.
A4. Самым естественным способом решения задачи N1 является создание дополнительного потока в процессе, который собственно и будет выполнять ту самую длительную операцию. С другой стороны - основной поток должен ожидать завершения операции. Для этого в Win32 существует функция
WaitForSingleObject, одним из параметров которой задается описатель ожидаемого потока. Но в этом случае ждущий поток не может обрабатывать
сообщения своего окна, так как ему система перестает выделять процессорное время. Здесь можно придумать много разных способов: во-первых, совсем можно обойтись и без функции WaitForSingleObject, создав глобальную переменную, которая при запуске потока инициализируется в false, а по его завершении - в true (или наоборот); можно, кроме того, используя функцию WaitForSingleObject, задавать ей вместо INFINITE лимит времени, по истечении которого будет возобновлено исполнение потока - вся байда проводится в цикле, при каждой итерации которого производится обработка сообщений окна;
- Епрст
Ну вот, на вопрос получены очень хорошие ответы. А о многозадачности мы еще обязательно поговорим в одном из выпусков. Главное понять - это не так сложно , как кажется! 
____________________________
Q. Можно ли переопределенный обработчик событий сделать подставляемым (inline)? (автор тот же)
На этот вопрос пришел только один ответ, но, на мой взгляд, исчерпывающий, причем тоже от Олега, который умудрился ответить на все заданные в предыдущем выпуске вопросы:
A. Естественно, нельзя. Дело в том, что инлайновые функции не имеют собственного указателя - они похожи на макросы в этом смысле. А диспетчеру обработчиков (если так можно выразиться) надо давать адрес обработчика.
____________________________
Q. Вопрос про ресурсные строки: " ...при нажатии на клавишу "Пропустить" программа идет дальше и вываливается на следующей операции загрузки строки с теми же симптомами и так до тех пор, пока не будут загружены все строки. После этого выполнение программы продолжается в нормальном режиме и все остальное работает как надо (строки все-таки загружаются!). В Release-версии программа пролетает это место без спотыканий."
- Евгений
(Полностью вопрос Евгения см. в предыдущем выпуске, который можно найти по адресу http://subscribe.ru/archive/comp.prog.visualc )
A1. Скорее всего, приложение преобразовано из обычного в MFC и преобразовано некорректно. Нельзя использовать ресурсные функции CString в не-MFC приложении, потому что они требуют специальной инициализации. Другие функции будут работать, а эти нет. Инициализация всех внутренних переменных происходит при инициализации приложения в вызовах AfxWinInit() и прочих служебных функций в AfxWinMain(). Можно посоветовать или сгенерировать приложение заново или использовать код вида:
CString s;
::LoadString(g_hInstance, 12345, s.GetBuffer(256), 256);
s.ReleaseBuffer();
- Дмитрий Дулепов, MCSE
A2. По вопросу о CString::LoadString: Скорее всего, эта функция вызывается до статической инициализации библиотеки MFC. Например, это может произойти в конструкторе объекта, объявленного статическим. Все тот же пресловутый theApp.... :-) Микрософт рекомендует переносить все действия по инициализации в InitInstance, собственно для чего эта ф-ция и предназначена.
- Sergey Emantayev
A3. А по поводу вопроса в рубрике "В поисках истины" - похоже, что зачитка данных идет в конструкторе CMyApp, а не в InitInstance. Предположение насчет некорректной инициализации похоже правильно.
- Oleg Tselobyonok, Applied systems, Ltd.
Огромное всем, кто не поленился написать ответ, особенно Олегу - он у нас сегодня рекордсмен!
Ну вот, кажется на все вопросы получены ответы. Полная идиллия... только вот уже поступили новые вопросы... 
/ / / / В ПОИСКАХ ИСТИНЫ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
Q. Просто кульно, что ты взялся за эту рассылку, а то, как ты и говорил, в инете нет рассылок по MSVC++. Сам я за ним сижу уже два года, и всё более углубляясь внутрь, возникают всё новые вопросы Но я ещё маленький Хочу задать тебе вопрос: как делать окна нестандартной формы? Например, круг (как у диска Компьютерры - там окно обычное, но с помощью прозрачности виден только круг, так?)
- eFi
Если никто не ответит на этот вопрос, я отвечу сам в следующем выпуске. Но от этого может пострадать тема выпуска - я все не успеваю, а по этому вопросу мне надо будет еще кое-что уточнить - поэтому кто знает или делал такие окна, напишите!
Должен констатировать, что проблема курсоров не закрыта - пришел еще один вопрос:
Q. Хочу поблагодарить за прекрасную рассылку, надеюсь почерпнуть много интересной и полезной информации. Хочу задать несколько вопросов:
1.Что касается курсоров - как все-таки загружать 256-цветный курсор в приложении? Т.е. проблема в том что в редакторе ресурсов можно сделать либо только черно-белый курсов, либо еще и цветной, но при этом LoadCursor загружет только ч.б. Скорее всего ларчик просто открывается, но все же?
2. Еще вопрос, скорее всего тоже очень популярный. Версия Debug работает без проблем, а при запуске версии Release появляется сообщение о недопустимой операции. Хотелось бы знать в чем проблема и пути ее решения.
- George V. Samodumov
А вот по второму вопросу хочу порекомендовать посмотреть ответ на вопрос Евгения - чуть выше. Как правило, отказ работать debug-версии значит, что все-таки что-то у вас не так, и для вас же будет лучше выяснить, в чем именно проблема. Это может быть связано, например, с инициализацией, с памятью и еще с уймой других вещей.
Q. Спасибо за рассылку - наконец-то свершилось! А то по VB их уже несколько, а по VC++ не было до недавнего времени ни одной. У меня есть вопрос по обработке события WM_KEYUP. Играя с диалогом, обнаружил, что он сам никак не реагирует на нажатия клавы. Как решение, использовал следующий способ: для каждого типа контрола делал свой класс, который реагирует на WM_KEYUP, и в обработчике этого события пересылал сообщение окну диалога. Например, для кнопок создал класс СМуButton, наследуемый от СButton, и в функции CMyButton::OnKeyDown() пересылаю сообщение родительскому окну как GetParent()->SendMessage(...). То же самое для других типов контролов по аналогии.
Но такой способ отдаёт некоторой горбатостью, может быть существует какое-то более элегантное решение?
|
|
|
|
|