Глава 1.
Знакомьтесь — библиотека классов MFC
Чего не понимают, тем не владеют.
Иоганн Вольфганг Гете
Для того чтобы выполнять какую-либо работу на профессиональном уровне, недостаточно поверхностного знакомства с тем инструментарием, которым собираешься пользоваться. Необходимо совершенно четко и ясно представлять себе внутреннюю структуру и логику работы используемого средства. В полной мере это относится и к широко применяемой в настоящее время для построения (создания) программных продуктов библиотеке классов MFC (Microsoft Foundation Class). K MFC следует подходить именно как к инструменту, который, принимая на себя большую часть черновой работы, требует от нас не просто знакомства с ним, но и глубокого изучения. Только знание всех нюансов построения и возможностей этой библиотеки позволит нам быстро и легко создавать программы любой степени сложности. В противном случае никогда не будет уверенности, что созданное приложение при любых условиях работает правильно — слишком много скрыто от программиста. Функции вызываются не только из недр Windows, но и из недр библиотеки. Ни о каком последовательном вызове функций речь не идет. Данные готовятся неизвестно где и неизвестно кем и когда обрабатываются. Так что какая уж тут уверенность.
Что же представляет собой библиотека MFC? Это набор классов, охватывающих большую часть функциональных возможностей операционных систем Microsoft Windows, а также предоставляющих разработчику значительное количество не только очень мощных дополнительных классов, но и целые механизмы, которые, не нарушая идеологию операционной системы, существенно ее расширяют и ... упрощают.
Перед создателями библиотеки стояла задача создания объектно-ориентированного интерфейса для Windows, удовлетворяющего следующим основным целям проектирования программных продуктов:
Надо сказать, что поставленная задача была решена на очень высоком уровне. Созданная библиотека классов охватывает все компоненты Windows — окна, блоки диалога, контексты устройств, общие объекты GDI (битовые образы и кисти), элементы управления и многие другие стандартные элементы. Суть программирования под Windows — обработка событий — предоставлена программистам в удобном и привычном виде. Классы библиотеки полностью вобрали в себя многочисленные операторы switch, которые так загромождают программы, написанные на языке С. Наряду с этим вы можете совершенно свободно смешивать вызовы библиотеки классов с прямыми вызовами Win32 API. Кроме того, за сравнительно небольшой промежуток времени корпорацией Microsoft было разработано несколько версий библиотеки MFC, которые становились все мощнее и удобнее. Вместе с Visual C++ 6.0 поставляется версия 4.23 библиотеки, и есть все основания считать, что ее развитие будет продолжено. К тому же поддержка библиотеки MFC компиляторами и средствами разработки ПО, созданными другими компаниями, позволяет сконцентрироваться именно на ней. Но давайте рассмотрим все по порядку. Начнем с макросов, глобальных функций и переменных, которые, хотя и не входят непосредственно в библиотеку, очень тесно с ней связаны и значительно облегчают программирование, а затем перейдем к общему обзору библиотеки.
Макросы, глобальные функции и переменные
Знакомство с библиотекой будет неполным и не даст существенных преимуществ при работе с ней, если мы не рассмотрим включенные в нее макросы, глобальные функции и переменные.
Все макросы, глобальные функции и переменные можно разбить на несколько категорий: основные, работа с базами данных, работа в Internet, OLE и элементы управления OLE. Поскольку в данной книге мы не будем подробно рассматривать работу с базами данных, в сети и с использованием OLE (этим вопросам будут посвящены следующие книги), то ограничимся изучением только основных макросов, глобальных функций и переменных. Начнем, естественно, с начала. Практически все глобальные функции начинаются с префикса "Afx". Исключение составляют большинство функций для работы с базами данных и функции, обеспечивающие обмен данными. Для всех без исключения глобальных переменных применяется префикс "Afx", а все макросы записываются заглавными буквами.
Большинство типов данных полностью соответствуют представленным в SDK, однако есть типы, специфичные только для MFC.
BOOL
Булевское значение
BSTR
32-битный указатель на символ
BYTE
8-битное целое без знака
COLORREF
32-битное значение, используемое для задания цвета
DWORD
32-битное целое без знака или адрес
LONG
32-битное целое со знаком
LPARAM
32-битное значение, посылаемое в качестве параметра в оконную процедуру или функцию обратного вызова
LPCSTR
32-битный указатель на константную строку символов
LPSTR
32-битный указатель на строку символов
LPCTSTR
32-битный указатель на константную строку символов, которая переносима в Unicode и DBCS
LPTSTR
32-битный указатель на строку символов, которая переносима в Unicode и DBCS
LPVOID
32-битный указатель на неопределенный тип данных
LRESULT
32-битное значение, возвращаемое из оконной процедуры или функции обратного вызова
UINT
32-битное целое без знака для Win32 и 16-битное — для Windows 3.x
WNDPROC
32-битный указатель на оконную процедуру
WORD
16-битное целое без знака
WPARAM
Значение, посылаемое в качестве параметра в оконную процедуру или функцию обратного вызова; 32-битное для Win32 и 16-битное для Windows 3.x
POSITION
32-битное целое без знака, используемое для обозначения позиции элемента в коллекции
LPCRECT
32-битный указатель на немодифицируемую структуру RECT
HINSTANCE
32-битное целое без знака для идентификации дескриптора экземпляра приложения
Примечание
Функция обратного вызова или CallBack-функция вызывается операционной системой по указателю, переданному ей ранее приложением. Это весьма удобный и часто используемый механизм подключения к стандартным функциям специфических обработчиков.
Имеются также некоторые другие типы данных, которые будут описаны в соответствующих местах.
Получение информации о приложении
Когда вы пишете приложение с использованием классов MFC, вы создаете единственный объект, производный от CWinApp. Для получения из любого места программы информации об этом объекте и связанных с ним параметрах разработчики предоставили следующие функции.
CWinApp* AfxGetApp()
Возвращает указатель на единственный объект CWinApp приложения.
LPCNSRT AfxGetAppName()
Возвращает строку, заканчивающуюся нулем, содержащую имя приложения.
HINSTANCE AfxGetlnstanceHandle()
Возвращает дескриптор текущего приложения для исполняемого файла (.ЕХЕ); в случае, если функция вызвана из динамически подключаемой библиотеки, связанной с USRDLL-версией MFC, то возвращается HINSTANCE этой библиотеки DLL.
CWnd* AfxGetMainWhd()
Возвращает значение переменной rn_pMainWnd объекта приложения, если оно не является сервером OLE, или указатель на активное главное окно приложения — в противном случае.
HINSTANCE AfxGetResourceHandle()
Возвращает дескриптор загруженных по умолчанию ресурсов приложения.
void AfxSetResourceHandle(HINSTANCE hlnstResource)
Устанавливает дескриптор экземпляра приложения или модуля .ЕХЕ или .DLL, из которого будут загружены ресурсы приложения.
LPCTSTR AFXAPI AfxRegisterWndClass(
UINT nClassStyle,
HCURSOR hCursor =0,
HBRUSH hbrBackground = 0,
HICON hlcon = 0)
Возвращает строку, заканчивающуюся нулем, которая содержит имя зарегистрированного класса. В качестве параметров в эту функцию передаются: nClassStyle — стиль или комбинация стилей класса окна Windows, которые будут рассмотрены при описании регистрации класса окна, hCursor— дескриптор ресурса курсора, hbrBackground— дескриптор ресурса кисти, определяющей цвет фона, hlcon — дескриптор ресурса-пиктограммы.
BOOL AFXAPI AfxRegisterClass(WNDCLASS* IpWndClass)
Возвращает значение TRUE при успешной регистрации класса окна и FALSE при ошибке. В качестве параметра в функцию передается указатель на структуру WNDCLASS, которая должна содержать информацию, необходимую для успешной регистрации. При использовании этой функции для регистрации класса окна в модуле DLL следует иметь в виду, что имя зарегистрированного класса автоматически удаляется из системы при выгрузке библиотеки из памяти.
HINSTANCE AFXAPI AfxLoadLibrary(LPCTSTR IpszModuleName)
Загружает в память модуль (.DLL или .ЕХЕ файл), имя которого передается в качестве параметра IpszModuleName. Если загрузка прошла успешно, то возвращается дескриптор модуля и увеличивается счетчик ссылок для этой библиотеки; в противном случае возвращается NULL.
BOOL AFXAPI AfxFreeLibrary(HINSTANCE hlnstLib)
Уменьшает счетчик ссылок для загруженного модуля DLL, определяемого параметром hlnstLib. Когда счетчик ссылок становится равным нулю, модуль выгружается из адресного пространства текущего процесса.
Модель объекта времени выполнения (run-time object)
Как будет показано дальше, все классы, производные от CObject и CruntimeClass, приобретают определенные возможности по обслуживанию объектов, включающие динамическую информацию о классе (run-time class information), сериализацию и динамическое создание объекта.
Здесь описаны макросы, которые облегчают использование этих возможностей.
DECIARE_DYNAMIC(className)
Разрешает доступ к динамической информации об объекте класса (должен использоваться при объявлении класса); className— действительное С++-имя класса без кавычек.
IMPLEMENT_DYNAMIC(className, baseClassName)
Генерирует код, необходимый для получения динамической информации об объекте класса, производного от CObject (должен использоваться в реализации класса).
DECLARE_DYNCREATE(className)
Разрешает динамическое создание объекта, производного от CObject, и получение доступа к динамической информации об этом объекте (должен использоваться при объявлении класса).
IMPLEMENT_DYNCREATE(className, baseClassName)
Разрешает динамическое создание объекта (должен использоваться в реализации класса) и доступ к динамической информации о классе. Приложение использует эту возможность для динамического создания нового объекта, например, когда требуется прочитать объект из файла на диске во время процесса сериализации.
При использовании этих макросов вы получаете возможность использовать макрос RUNTIME_GLASS и функцию CObject::IsKindOf для определения класса вашего объекта во время выполнения программы.
DECLARE_SERIAL(ClassName)
Генерирует С++-код, необходимый для сериализации объекта класса, производного от CObject (должен использоваться при объявлении класса) и доступа к динамической информации о классе. Определение этого макроса включает в себя все функциональные возможности, предоставляемые обоими макросами DECLARE_DYNAMIC и DECLARE_DYNCREATE.
IMPLEMENT_SERXAL(className, baseClassName, wSchema)
Генерирует С++-код, необходимый для сериализации объекта класса, производного от CObject (должен использоваться в реализации класса) и доступа к динамической информации о классе. Параметр wSchema— переменная типа UINT, определяющая "номер версии" приложения, которой может быть закодирован архив, для возможности применения процесса десериализации. Этот номер не должен быть равен -1.
RUNTIME_CLASS (dassName)
Для объектов, производных от CObject и объявленных с использованием макросов DECLARE_DYNAMIC, DECLARE_DYNCREATE или DECLARE_SERIAL, возвращает указатель на структуру CRuntimeClass, которая соответствует имени класса className.
Диагностический сервис, предоставляемый библиотекой MFC, значительно упрощает отладку приложений. Он включает в себя макросы и глобальные функции, которые позволяют отслеживать распределение памяти, содержимое дампа объекта и осуществлять печать отладочной информации во время выполнения.
ASSERT(booleanExpression)
Прерывает выполнение программы (в отладочной версии библиотеки), если вычисляемое выражение booleanExpression равно FALSE, и печатает сообщение об ошибке в следующей форме:
assertion failed in file <имя_файла> in line <номер_стрски>,
где <номер строки> определяет строку, в которой произошла ошибка.
ASSERT_KINDOF(className, pObject)
Проверяет, является ли pObject объектом класса className, где className — имя класса, производного от класса Cobject, этот макрос работает только, если в области объявления класса используется макрос DECLARE_DYNAMIC или DECLARE_SERIAL.
ASSERT_VALID(pObject)
Используется для оценки доступности внутреннего состояния pObject (сначала проверка pObject на NULL, затем вызов его метода AssertValid). Если хотя бы одна проверка приводит к возникновению ошибки, то выводится сообщение, аналогичное ASSERT.
TRACE (exp)
Позволяет вывести на экран форматированную строку, определяемую выражением ехр, аналогично функции print!для С-программ в консольных программах, например, TRACE("Проверка вывода строк %s и чисел %а", "Отладка программы", 5).
TRACED - TRACE3
Упрощенные версии макроса TRACE, позволяющие вывести форматированную строку с числом аргументов от 0 до 3.
Рассмотренные макросы работают только в отладочной версии библиотеки.
VERIFY(booleanExpression)
Действует аналогично макросу ASSERT, но для рабочей версии библиотеки.
Основные глобальные переменные
CDumpContext afxDump
Предопределенная переменная, которая позволяет послать информацию в окно отладчика.
BOOL afxTraceEnable
Используется для разрешения или запрещения работы макроса TRACE.
Форматирование строк и окна сообщений
Эта группа содержит три глобальные функции: две для работы со строками и одну для вывода произвольного сообщения в блок диалога.
void AfxFormatStringl(
CStrings rString,
UINT nIDS,
LPCTSTR Ipsz)
Загружает строку из ресурсов, определяемую идентификатором nIDS, в объект rString с одновременной заменой шаблона "%1" на строку, указанную в Ipsz. Например, если в ресурсах определена строка "Для профессионального программирования 32-разрядных приложений — читайте книгу %1", a Ipsz указывает на строку "<Программирование на Visual C++ 6.0>", то после выполнения этой функции rString будет содержать строку "Для профессионального программирования 32-разрядных приложений — читайте книгу программирование на Visual C++ 6.0>".
void AfxFormatString2(
CString& rString,
UINT nIDS, LPCTSTR Ipszl,
LPCTSTR Ipsz2)
Аналогична функции AfxFormatStringl, но используется два шаблона ("%1" и "%2") и две строки для подстановки, соответственно.
Последняя функция этой группы имеет две формы: со строкой текста и с идентификатором ресурса:
int AfxMessageBox(
LPCTSTR IpszText,
UINT nType = MB_OK,
UINT nIDHelp =0)
И
int AfxMessageBox (
UINT nIDPrompt,
UINT nType = MB_OK,
UINT nIDHelp = (UINT) -1)
Выводит текст в окно сообщений. Первая форма этой функции выводит в окно сообщений строку IpszText и использует идентификатор nIDHelp для перехода к соответствующей теме справки при нажатии клавиши <F1>. Вторая форма функции использует для определения текста идентификатор строки ресурса, задаваемый параметром nIDPrompt. Если в качестве nIDHelp используется значение по умолчанию, равное -1, то nIDPrompt задает тему справки.
Обе функции возвращают одно из следующих значений:
Параметр пТуре определяет тип окна сообщений и может принимать одно из следующих значений:
MB_ABORTRETRYIGNORE — содержит три кнопки Abort, Retry и Ignore (Стоп, Повтор и Пропустить)
MBJDK — содержит только кнопку ОК
MB_OKCANCEL — содержит две кнопки ОК и Cancel (OK и Отмена)
MB_RETRYCANCEL — содержит две кнопки Retry и Cancel (Повтор и Отмена)
MB_YESNO — содержит две кнопки Yes и No (Да и Нет)
MB_YESNOCANCEL — содержит три кнопки Yes, No и Cancel (Да, Нет и Отмена)
Кроме того, можно использовать следующие константы для определения степени модальности окна сообщений:
- MB_APPLMODAL — модальность на уровне пользователя
- MB_SYSTEMMODAL — модальность на уровне системы
- MB_TASKMODAL — модальность на уровне процесса
Примечание
В скобках приведены названия для русскоязычной версии системы Windows.
Помимо кнопок, в окне сообщений можно разместить одну из предопределенных (системных) пиктограмм (рис. 1.1 — 1.4).
Рис. 1.1. MB_ICONEXCLAMATION— восклицательный знак
Рис. 1.2. MB_ICONINFORMATION— пиктограммы в виде символа "i"
Рис. 1.3. MB_ICONQUESTION— знак вопроса
Рис. 1.4. MB_ICONSTOP — пиктограмма "стоп"
При использовании в блоке сообщений более одной кнопки можно также установить кнопку по умолчанию:
На рис. 1.5 представлена иерархия основных категорий классов библиотеки MFC. Как и положено, каждый новый производный класс обладает как свойствами, унаследованными им от родительских классов, так и приобретает новые, характерные только для данного класса. На вершине иерархии находится единственный базовый класс CObject. В зависимости от отношения к этому классу все остальные классы библиотеки MFC можно условно разбить на две группы: классы, производные от Cobject, и классы, не зависящие от него.
Рис. 1.5. Иерархия классов MFC по категориям
О структуре и возможностях библиотеки классов MFC мы будем говорить на протяжении всей книги. Здесь же я приведу описание только вершины представленной иерархии — класса CObject, который незримо входит практически во все остальные классы, с которыми мы будем работать.
CObject— вершина иерархии классов
Большинство классов в библиотеке MFC являются производными от единственного класса CObject — корня иерархической структуры. Методы и элементы данных этого класса представляют наиболее общие свойства производных от него классов MFC. Основное назначение данного класса заключается в предоставлении всем своим производным классам следующих возможностей:
Единственной переменной класса CObject является статическая переменная classObject типа CRuntimeClass, которая -хранит информацию об ассоциированном с классом Cobject объекте во время выполнения программы.
Класс CObject не поддерживает множественного наследования, т. е. создаваемые вами классы могут иметь только один CObject в качестве базового, и он должен быть "самым левым" в иерархии. Однако такой производный класс позволяет иметь "справа от себя" в качестве базовых несколько структур и/или классов, не являющихся производными от него. Например,
class A : public Cobject class В class С
{ { {
}; }; } ;
class D : public А, В, С
{
};
Как уже отмечалось, каждый класс, производный от Cobject, ассоциируется со структурой CRuntimeClass, которую можно использовать для получения информации об объекте или его базовом классе во время выполнения программы.
Для получения динамической информации использование этой структуры должно сопровождаться обязательным включением одного из макросов DECLARE_DYNAMIC, DECLARE_DYNCREATE, DECLARE_SERIAL в области объявления и соответствующих им макросов IMPLEMENT_DYNAMIC, IMPLEMENT_DYNCREATE, IMPLEMENT_SERIAL в области реализации.
Наряду с рассмотренными функциями, для получения структуры класса времени выполнения широко используется макрос RUNTIME_CLASS, который позволяет получить эту структуру по имени класса C++.
В заключение обзора класса CObject — корня иерархической структуры библиотеки MFC — рассмотрим четыре уровня функциональных возможностей, которые он нам предоставляет:
При создании классов, производных от CObject, уровень их функциональных возможностей задается выбором определенных макросов объявления и реализации.
Динамическую информацию о классе CObject поддерживает посредством своего метода IsKindOf, который позволяет определить, относится ли объект к определенному классу или, может быть, является производным от него. Эта функция позволяет осуществить безопасное приведение типа в производном классе. Ее аргументом является объект класса CRuntimeClass, который можно получить, используя макрос RUNTIME_CLASS с именем класса. Метод IsKindOf не поддерживает множественного наследования или виртуальных классов, хотя при необходимости можно использовать множественное наследование для классов, производных от классов библиотеки MFC.
CObject также поддерживает динамическое создание, т. е. создание объекта определенного класса во время выполнения программы. Объект создается методом CreateObject структуры CRuntimeClass, как показано в примере:
CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CMyClass);
CObject* pObject = pRuntimeClass->CreateObject();
ASSERT(pObject->IsKindOf(RUNTIME_CLASS(CMyClass)));
Надеюсь, теперь у вас сложилось общее впечатление о возможностях и мощи библиотеки классов MFC и можно перейти к рассмотрению вопросов построения приложения на ее базе. Как и любое другое средство, данная библиотека требует выполнения некоторых (очень немногих) условий. Заодно познакомимся с мастером AppWizard, который выполнит за вас большую часть подготовительной работы.