Интегрированная среда разработчика объединяет одной оболочкой Microsoft Visual C++ версии 4.0, Microsoft Visual Test, Microsoft Fortran PowerStation, Microsoft Development Library и Microsoft Visual SourceSafeДля программистов, работающих в среде Visual C++, наиболее интересна возможность интегрирования с базой данных Microsoft Development Library. После того как вы установите в компьютере Microsoft Development Library, вы сможете осуществлять поиск интересующей вас информации не только в справочной базе Visual C++, но и в базе данных Microsoft Development Library. При этом вам не придется переключаться на другое приложение, достаточно выбрать справочную систему в панели Info Viewer.
Глобальная сеть | Наш адрес |
---|---|
CompuServe | >internet:frolov@glas.apc.org |
GlasNet | frolov@glas.apc.org |
Internet | frolov@glas.apc.org |
Relcom | frolov@glas.apc.org |
UUCP | uunet!cdp!glas!frolov |
Издательский отдел АО "ДИАЛОГ-МИФИ". Индекс 115409, город Москва, улица Москворечье, дом 31, корпус 2.Приносим свои извинения за то, что не можем ответить на каждое письмо. Мы также не занимаемся рассылкой дискет и исходных текстов к нашим книгам. По этому вопросу обращайтесь непосредственно в издательство “Диалог-МИФИ”.
cout << “Значение переменной iInt = ”;
cout << iInt;
cout << “\n”;
cout << “Значение переменной cChar = ” << cChar << “\n”;
cout << “Строка szString = ” << szString << “\n”;
Оператор >> и символ inp предназначены для ввода данных. Они позволяют пользователю ввести с клавиатуры значение какой-либо переменной. Ниже мы привели пример, в котором для ввода целочисленного значения используется inp и оператор >>:
int iNum;
cout << "Введите целочисленное значение:";
cin >> iNum;
Чтобы воспользоваться возможностями потокового ввода/вывода, необходимо включить в программу файл iostream.h.
Забегая вперед, скажем, что символы inp и outp, которые иногда называют потоками, представляют собой объекты специального класса, предназначенного для ввода и вывода информации. Операторы << и >> переопределены в этом классе и выполняют новые функции. О переопределении операторов вы можете прочитать в разделе “Перегрузка операторов”.
// Включаемый файл для потокового ввода/вывода
#include <stdio.h>
int main(void) {
// Объявляем две константы
const int max_nuber = 256;
// Выводим текстовую строку на экран
printf("Const Number is %d \n", max_nuber);
return 0;
}
Ключевое слово const можно указывать при объявлении постоянных указателей, которые не могут менять своего значения. Заметим, что объект (переменная), определяемый постоянным указателем, может быть изменен:
int iNumber;
int *const ptrNumber = &iNumber;
// Включаемый файл для потокового ввода/вывода
#include <iostream.h>
void main(void) {
// Определяем переменную iVar
int iVar = 10;
// Определяем ссылку iReferenceVar на переменную iVar
int& iReferenceVar = iVar;
// Отображаем значение переменной и ссылки
cout << "iVar = " << iVar << ";
iReferenceVar = " << iReferenceVar << '\n';
// Изменяем значение переменной iVar пользуясь ссылкой
iReferenceVar = 20;
// Отображаем значение переменной и ссылки
cout << "iVar = " << iVar << ";
iReferenceVar = " << iReferenceVar << '\n';
}
Вы можете использовать ссылки для передачи параметров функциям. При этом фактически вы передаете функции указатель на объект, представленный ссылкой. Внутри функции вы можете работать с ссылкой как с самим объектом, а не как с указателем.
Функция может не только принимать ссылки в качестве своих параметров, она также может возвращать ссылку. Такую функцию можно привести в левой части оператора присваивания.
new type-name [initializer];
new (type-name) [initializer];
В качестве аргумента type-name надо указать имя типа создаваемого объекта. Дополнительный аргумент initializer позволяет присвоить созданному объекту начальное значение. Вот простой пример вызова оператора new:
char *litera;
int *pi;
litera = new char;
pi = new int(3,1415);
В этом примере оператор new используется для создания двух объектов – одного типа char, а другого типа int. Указатели на эти объекты записываются в переменные litera и pi. Заметим, что объект типа int сразу инициализируется значением 3,1415.
Чтобы освободить память, полученную оператором new, надо вызвать оператор delete. Вы должны передать оператору delete указатель pointer, ранее полученный оператором new:
delete pointer;
Оператор new позволяет создавать объекты не только простых типов, он может использоваться для динамического создания массивов. Следующий фрагмент кода создает массив из ста элементов типа long. Указатель на первый элемент массива записывается в переменную pData:
long *pData = new long[100];
Чтобы удалить массив, созданный оператором new, надо воспользоваться другой формой вызова оператора delete:
delete [] pointer;
Прямоугольные скобки указывают на то, что надо удалить массив элементов.
int Sqware(int a, int b);
int Sqware(int a);
Как видите, эти функции имеют одинаковые имена, но разные параметры. Первая функция, предназначенная для вычисления площади прямоугольника имеет два параметра, задающие длины его сторон. Вторая функция позволяет вычислить площадь квадрата и содержит только один параметр, определяющий длину стороны квадрата. Вот определения этих функций:
int Sqware(int a, int b) {
return (a * b);
}
int Sqware(int a) {
return (a * a);
}
Вы можете вызывать обе функции Sqware из своей программы. Компилятор определит по количеству и типу параметров, какую конкретно функцию надо выполнить:
void main() {
int value;
value = Sqware(10, 20);
print(“Площадь прямоугольника равна %d”, value);
value = Sqware(10);
print(“Площадь квадрата равна %d”, value);
}
int Summa(int first, int second, int third=0, int fourth=0) {
return(first + second + third + fourth);
}
Функцию Summa можно использовать для сложения четырех, трех или двух чисел. Если складываются два числа, то третий и четвертый параметр можно опустить:
void main() {
int value1 = 10, value2 = 20, value3 = 30, value4 = 40;
int result;
// Вызываем функцию с четырьмя параметрами
result = Summa(value1, value2, value3, value4);
print(“Сумма четырех чисел равна %d”, result);
// Вызываем функцию с тремя параметрами
result = Summa(value1, value2, value3);
print(“Сумма трех чисел равна %d”, result);
// Вызываем функцию с двумя параметрами,
// последний параметр задается по умолчанию
result = Summa(value1, value2);
print(“Сумма первых двух чисел равна %d”, result);
}
inline unsigned int Invert(unsigned int number) {
return (~number);
}
class [<tag>]
{
<member-list>
} [<declarators>];
Когда вы определяете класс, то сначала указывается ключевое слово class, а затем в качестве аргумента <tag> имя самого класса. Это имя должно быть уникальным среди имен других классов, определенных в вашей программе.
Затем в фигурных скобках следует список элементов класса <member-list>. В качестве элементов класса могут фигурировать данные (переменные), битовые поля, функции, вложенные классы, а также некоторые другие объекты. Вы можете включить качестве элемента класса указатель на другие объекты этого класса.
Классы образуют собственное пространство имен. Имена элементов одного класса могут совпадать с именами элементов другого класса и даже с именами других переменных и функций определенных в программе.
Функции входящие в класс, называются функциями-элементами, или следуя терминологии объектно-ориентированного подхода, методами. Далее мы будем называть такие функции методами. Внутри класса вы можете свободно обращаться ко всем его элементам функциям и данным, без указания имени класса или имени объекта этого класса.
После закрывающей фигурной скобки в аргументе <declarators> можно объявить один или несколько объектов данного класса. Объекты класса можно объявить и позже, точно так же как объявляются переменные простых типов:
[class] tag declarators;
Ключевое слово class перед именем класса можно опустить.
Для динамического создания и удаления объектов классов можно пользоваться операторами new и delete. Эти операторы позволяют легко строить списки классов, деревья и другие сложные структуры.
class Sample {
int iX;
void Load();
public:
void SetStr();
void GetStr();
char sDataText[80];
private:
char sNameText[80];
int iIndex;
public:
void ConvertStr();
int iLevel;
};
В классе описаны элементы данных iX, sDataText, sNameText, iIndex, iLevel и методы Load, SetStr, GetStr, ConvertStr.
Элементы данных и методы SetStr, GetStr, sDataText, ConvertStr, iLevel объявлены public. К ним можно обращаться как из методов класса Sample, так и из программы. Остальные элементы класса объявлены как private. Доступ к ним открыт только для методов самого класса, а также дружественных функций и дружественных методов других классов. Дружественные функции и дружественные классы описаны в следующем разделе.
class line {
public:
void SetLength(int newLength) { length = newLength; }
int GetLength() { return length; }
private:
int length;
};
Если исходный код методов не такой короткий, то при определении класса указывается только объявление метода, а его определение размещается отдельно. Встраиваемые методы также можно определить вне класса. Когда вы определяете метод отдельно от класса, то имени метода должно предшествовать имя класса и оператор разрешения области видимости :: .
class convert {
public:
void GetString() { scanf(sText, "%s"); }
void ShowString() { puts(sText); }
int ConvertString();
void DummyString();
private:
char sText[80];
};
void convert::ConvertString(void) {
int i;
for (i = 0; sText[i] != ‘\0’; i++ ) {
sText[i] = tolower(sText[i]);
}
return i;
}
inline void convert::DummyString(void) {
int i = 0;
while (sText[i++]) sText[i] = 0;
}
Чтобы вызвать метод, надо сначала указать имя объекта класса, для которого будет вызван метод, а затем через точку имя метода. Вместо имени объекта можно использовать указатель на объект. В этом случае вместо символа точки надо использовать оператор –>. Если метод вызывается из другого метода этого же класса, то имя объекта и оператор выбора элемента указывать не надо.
Следующий пример демонстрирует вызов методов класса convert, исходный текст которого приведен выше:
void main() {
convert ObjectA;
ObjectA.GetString();
ObjectA.ConvertString();
ObjectA.ShowString();
convert *pObjectB = new convert;
pObjectB->GetString();
pObjectB->ConvertString();
pObjectB->ShowString();
}
Методы класса могут быть перегружены. В одном и том же классе можно определить несколько методов с одинаковыми именами, но различным набором параметров.
class BookList {
// Конструкторы класса
void BookList();
void BookList(int);
// Остальные члены класса
};
// Первый конструктор класса
BookList::BookList(void) { }
// Второй конструктор класса
BookList::BookList(int iList) { }
Когда вы создаете объекты класса, вы можете указать параметры для конструктора. Ниже создаются два объекта класса BookList – FirstBook и SecondBook:
BookList FirstBook;
BookList SecondBook(100);
При создании первого объекта параметры конструктора не указываются, поэтому используется первый конструктор класса. А вот при создании второго объекта мы указали числовой параметр, поэтому в этом случае используется второй конструктор.
Имя деструктора также соответствует имени класса, но перед ним должен стоять символ тильда. Деструктор вызывается автоматически, когда объект уничтожается. Например, если определить объект данного класса внутри блока, то при выходе из блока для объект будет вызвана функция деструктор. Функция деструктор не имеет параметров, поэтому она не может быть перегружена, а значит у данного класса может быть только один деструктор.
Ниже представлен класс Object, для которого определен деструктор ~Object:
class Object {
void ~Object();
// Остальные члены класса
};
Object::~Object(void) { }
void main(void);
// Класс ClassMen включает элемент данных и два метода для
// обращения к нему
class ClassMen {
public:
void SetWeight(int newWeight);
int GetWeight() const;
private:
int weight;
};
// Метод GetWeight позволяет определить значение элемента
// weight. Этот метод объявлен как const и не может
// модифицировать объекты класса ClassMen
int ClassMen::GetWeight() const {
return weight ;
}
// Метод SetWeight позволяет изменить значение weight.
// Такой метод нельзя объявлять как const
void ClassMen::SetWeight(int newWeight) {
weight = newWeight;
}
// Главная функция программы
void main(void) {
// Создаем объект класса ClassMen
ClassMen alex;
// Устанавливаем значение элемента weight объекта alex
alex.SetWeight(75);
// Отображаем значение элемента weight объекта alex
cout << alex.GetWeight() << "\n";
}
class Circle {
public:
static void GetPi()
{ return fPi; }
private:
static float fPi;
};
float Circle::fPi = 3.1415;
Вы можете вызвать метод GetPi следующим образом:
float fNumber;
fNumber = Circle::GetPi();
Обратите внимание, что объект класса Circle не создается.
class CWindow {
public:
int xLeftTop, xRightBottom;
int yLeftTop, yRightBottom;
static char title[80];
void SetTitle(char*);
};
char Cwindow::title[80] = "заголовок окна";
Каждый объект класса Cwindow будет иметь уникальные координаты, определяемые элементами данных xLeftTop, xRightBottom, yLeftTop, yRightBottom и одинаковый заголовок, хранимый элементом данных title.
Общие элементы данных находятся в области действия своего класса. Методы класса могут обращаться к общим элементам точно так же, как к остальным данным из класса:
void SetTitle(char* sSource) {
strcpy(title, sSource);
}
Чтобы получить доступ к общим элементам из программы, надо объявить их как public. Для обращения к такой переменной перед ее именем надо указать имя класса и оператор ::.
printf(Cwindow::title);
//==========================================================
// Класс point
class point {
public:
// Функция Clear объявляется дружественной классу point
friend void point::Clear(point*);
// Интерфейс класса…
private:
int m_x;
int m_y;
};
//==========================================================
// Функция Clear
void Clear(point* ptrPoint) {
// Обращаемся к элементам класса, объявленным как private
ptrPoint->m_x = 0;
ptrPoint->m_y = 0;
return;
}
//==========================================================
// Главная функция
void main() {
point pointTestPoint;
// Вызываем дружественную функцию
Clear(&pointTestPoint);
}
С помощью ключевого слова friend вы можете объявить некоторые методы одного класса дружественными для другого класса. Такие методы могут обращаться ко всем элементам класса, даже объявленным как private и protect, несмотря на то, что сами они входят в другой класс.
В следующем примере мы определяем два класса – line и point. В классе point определяем метод Set и объявляем его в классе line как дружественный. Дружественный метод Set может обращаться ко всем элементам класса line:
// Предварительное объявление класса line
class line;
//==========================================================
// Класс point
class point {
public:
// Метод Set класса point
void Set(line*);
// …
};
//==========================================================
// Класс line
class line {
public:
// Метод Set класса point объявляется дружественной
// классу point
friend void point::Set(line*);
private:
int begin_x, begin_y;
int end_x, end_y;
};
//==========================================================
// Функция Clear
void point::Set(line* ptrLine) {
// Обращаемся к элементам класса line, объявленным как
// private
ptrLine->begin_x = 0;
ptrLine->begin_y = 0;
// …
return;
}
//==========================================================
// Главная функция
void main() {
point pointTestPoint;
line lineTestPoint;
// Вызываем дружественный метод
pointTestPoint.Set(&lineTestPoint);
}
//==========================================================
// Класс point
class point {
// …
};
//==========================================================
// Класс line
class line {
public:
// Класс point объявляется дружественным классу line
friend class point;
};
class [<tag>[:<base>]]
{
<member-list>
} [<declarators>];
Перед названием базового класса может быть указан спецификатор доступа public, private или protect. Назначение этих спецификаторов мы рассмотрим в разделе “Разграничение доступа к элементам базового класса”. Сейчас же мы скажем только, что если вы не укажите спецификатор доступа, то по умолчанию будет подразумеваться спецификатор private.
Ниже мы определили базовый класс Base, содержащий несколько элементов, а затем наследовали от него два новых класса DerivedFirst и DerivedSecond. В каждом из порожденных классов мы определили различные дополнительные методы и элементы данных.
// Класс Base
class Base {
// Элементы класса Base
};
// Класс DerivedFirst, наследованный от базового класса Base
class DerivedFirst : Base {
// Элементы класса DerivedFirst
};
// Класс DerivedSecond, наследованный от базового класса Base
class DerivedSecond : Base {
// Элементы класса DerivedSecond
};
Классы DerivedFirst и DerivedSecond сами могут выступать в качестве базовых классов.
Вы можете определять в пороженном классе элементы, имена которых совпадают с именами элементов базовых классов. Если вы выполнили такое переопределение, вы можете обратиться к элементу базового класса, если укажете его полное имя. Полное имя должно состоять из имени класса, к которому относится элемент, оператора :: и имени самого элемента.
В качестве примера приведем базовый класс Base и производный от него класс Derived. В обоих классах определен элемент данных iNumber. Чтобы получить доступ из порожденного класса к элементу iNumber базового класса указывается его полное имя Base::iNumber.
// Класс Base
class Base {
public:
int iNumber;
// Другие элементы класса
};
// Класс Derived, наследованный от базового класса Base
class Derived : Base {
public:
// Это объявление скрывает элемент iNumber базового класса
int iNumber;
int GetNumber(void) { return iNumber + Base::iNumber; }
};
Указатель на объект базового класса можно присвоить указатель на объект класса порожденного от него. Эта возможность будет широко использоваться в библиотеке классов MFC.
class [<tag>[:[<base-list>]]
{
<member-list>
} [<declarators>];
Порядок, в котором вы перечислите базовые классы влияет только на последовательность в которой вызываются конструкторы и деструкторы базовых классов. Конструкторы базовых классов вызываются в том порядке, в котором они перечислены (слева на право). Деструкторы базовых классов вызываются в обратном порядке.
Один и тот же класс нельзя указывать два или более раза в качестве базового класса (за исключением тех случаев, когда он является непрямым базовым классом).
В следующем примере определены два базовых класса BaseFirst и BaseSecond. От них наследован один новый класс Derived. Результирующий класс Derived объединяет элементы обоих базовых классов и добавляет к ним собственные элементы.
// Класс BaseFirst
class BaseFirst {
// Элементы класса BaseFirst
};
// Класс BaseSecond
class BaseSecond {
// Элементы класса BaseSecond
};
// Класс Derived, наследованный от базового класса Base
class Derived : BaseFirst, BaseSecond {
// Элементы класса Derived
};
Так как библиотека классов MFC не использует множественное наследование, мы не станем останавливаться на нем более подробно. При необходимости вы можете получить дополнительную информацию из справочников или учебников по языку Си++ (см. список литературы).
Спецификатор доступа базового класса | |||
---|---|---|---|
Спецификатор доступа элемента базового класса | public | protected | private |
public | Доступны как public | Доступны как protected | Доступны как private |
protected | Доступны как protected | Доступны как protected | Доступны как private |
private | Недоступны | Недоступны | Недоступны |
#include <iostream.h>
// Базовый класс Figure
class Figure {
public:
// Виртуальный метод
virtual void PrintName(void) {cout << Figure PrintName << ‘\n’}
// Невиртуальный метод
void PrintDimention(void) {cout << Figure PrintDimention << ‘\n’}
};
// Порожденный класс Rectangle
class Rectangle : public Figure {
// Переопределяем виртуальный метод базового класса
virtual void PrintName(void) {cout << Rectangle PrintName << ‘\n’}
// Переопределяем невиртуальный метод базового класса
void PrintDimention(void) {cout << Rectangle PrintDimention << ‘\n’}
};
// Главная функция
void main(void) {
// Определяем объект порожденного класса
Rectangle rectObject;
// Определяем указатель на объект порожденного класса
// и инициализируем его
*Rectangle ptrRectObject = &rectObject;
// Определяем указатель на объект базового класса Figure
// и записываем в него адрес объекта порожденного класса.
*Figure ptrFigObject = &rectObject;
// Вызываем методы класса Rectangle, используя имя объекта
rectObject.PrintName;
rectObject.PrintDimention;
cout << '\n';
// Вызываем методы класса базового класса Figure
rectObject.Figure::PrintName;
rectObject.Figure::PrintDimention;
cout << '\n';
// Вызываем методы класса Rectangle, используя указатель на
// объекты класса Rectangle
ptrRectObject->PrintName;
ptrRectObject->PrintDimention;
cout << '\n';
// Вызываем методы класса Rectangle, используя указатель на
// объекты класса Figure
ptrFigObject->PrintName;
ptrFigObject->PrintDimention;
}
Если вы запустите приведенную выше программу, она выведет на экран следующую информацию:
Rectangle PrintName
Rectangle PrintDimention
Figure PrintName
Figure PrintDimention
Rectangle PrintName
Rectangle PrintDimention
Figure PrintName
Figure PrintDimention
// Абстрактный класс Abstract
class Abstract {
public:
// Чисто виртуальный метод, не имеет определения
virtual int PureFunc(void) = 0;
void SetValue(int i) {iValue = i;}
int iValue;
};
// Класс Fact
class Fact : public Abstract {
int PureFunc(void) {return iValue * iValue;}
};
//====================================================
// Класс ClassData
class ClassData {
int iPrivateValue;
public:
int iPublicValue;
};
//====================================================
// Структура StructData
struct StructData {
int iPublicValue;
private:
int iPrivateValue;
};
Еще одно различие между структурами и классами проявляется в разграничении доступа к элементам базового класса (см. раздел “Разграничение доступа к элементам базового класса”). Если вы наследуете новый класс от базового класса и не указываете спецификатор доступа, по умолчанию используется спецификатор private. Когда же вы наследуете от базового класса структуру, по умолчанию используется спецификатор public.
template <template-argument-list> declaration;
Аргумент template-argument-list представляет собой список условных имен для определения типов, по которым будут различаться различные реализации классов или функций данного шаблона.
! = < > += –=
!= , –> –>* & |
( ) [ ] new delete >> <<=
^= &= |= << >>= ==
~ *= /= %= % ^
+ - * / ++ --
<= >= && ||
Переопределение операторов вызывает перегрузку операторов. Как в случае перегруженных функций и методов, перегруженные операторы вызываются в зависимости от количества и типа их параметров. О перегруженных функциях вы можете прочитать в разделе “Перегрузка имен функций”.
Для переопределения операторов предназначено ключевое слово operator. Вы должны определить функцию или метод, имя которого состоит из ключевого слова operator и самого оператора. Параметры этой функции должны соответствовать параметрам переопределяемого оператора.
Вы можете переопределить оператор для объектов класса, используя соответствующий метод класса или дружественную функцию класса. Когда вы переопределяете оператор с помощью метода класса, то в качестве неявного параметра метод принимает ключевое слово this, являющееся указателем на данный объект класса. Поэтому если переопределяется бинарный оператор, то переопределяющий его метод класса должен принимать только один параметр, а если переопределяется унарный оператор – метод класса вообще не должен иметь параметров.
Если оператор переопределяется при помощи дружественной функции, то он должен принимать два параметра для бинарных операторов и один параметр для унарных операторов.
Существует ряд ограничений, которые вы должны учитывать при переопределении операторов:
• Нельзя изменять количество параметров оператора. Например, нельзя переопределить унарную операцию как бинарную и наоборот
• Нельзя изменять старшинство операторов
• Нельзя определять новые операторы
• Нельзя переопределять операторы принимающие в качестве параметров стандартные типы данных языка, такие как int или char
• Переопределенные операторы не могут иметь параметров, используемых по умолчанию
• Нельзя переопределять следующие операторы: (.), (.*), (::), (?:), а также символы, обрабатываемые препроцессором (символы комментария и т. д.).
В нашей первой книге, посвященной языку программирования Си++ и библиотеке классов MFC, мы не будем переопределять операторы. Однако мы будем вызывать операторы, уже переопределенные в классах MFC. Если вы желаете получить больше информации о методике переопределения операторов, обращайтесь к литературе, посвященной языку Си++.
#include <iostream.h>
int main() {
long num = 7;
long res = 0;
// Введите число num
cout << "Input number: ";
cin >> num;
// Блок try, из которого можно вызвать исключение
try {
if ((num == 7) || (num == 0))
// Если переменная num содержит значение 0 или 7,
// тогда вызываем исключение типа float
throw num;
// Значения num равные 0 или 7 вызовут ошибку
// деления на нуль в следующем выражении
res = 100 / (num * (num – 7));
// Отображаем на экране результат вычисления
cout << "Result = " << res << endl;
}
// Обработчик исключения типа float
catch(long num) {
// Отображаем на экране значение переменной num
cout << "Exception, num = " << num << endl;
}
cout << "Stop program" << endl;
return 0;
}
В определении обработчика сообщения можно не указывать имя переменной и ограничиться только названием типа. Так, обработчик в предыдущем примере может выглядеть следующим образом:
catch(long) {
// Отображаем на экране значение переменной num
cout << "Exception, num = " << num << endl;
}
catch(…) {
…
}
Исключения в языке Си++ могут быть различного типа, в том числе они могут быть объектами классов. Вы можете определить несколько обработчиков исключений различного типа. В этом случае исключение будет обрабатывать обработчик соответствующего типа.
#include <eh.h>
#include <iostream.h>
#include <process.h>
void FastExit(void);
int main() {
// Устанавливаем функцию term_func
set_terminate(FastExit);
try {
// …
// Вызываем исключение типа int
throw (int) 323;
// …
}
// Определяем обработчик типа char. Обработчик исключений
// типа int и универсальный обработчик не определены
catch(char) {
cout << "Exception " << endl;
}
return 0;
}
// Определение функции FastExit
void FastExit() {
cout << "Exception handler not found" << endl;
exit(-1);
}
Среда Visual C++ версии 4.0 позволяет запретить или разрешить обработку исключений языка Си++. Для управления исключениями выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, в которой определяются все режимы работы. Выберите страницу C/C++. Затем из списка Category выберите строку C++ Language. Чтобы включить обработку исключительных ситуаций установите переключатель Enable exception handling.
Среда разработки | Версия MFC | Разрядность |
---|---|---|
Microsoft C/C++ версии 7.0 | 1.0 | 16 |
MSVC 1.0 | 2.0 | 16 |
MSVC 1.1 | 2.1 | 32 |
MSVC 1.5 | 2.5 | 16 |
MSVC 2.0 | 2.51 | 16 |
MSVC 2.1 | 2.52 | 16 |
MSVC 2.2 | 2.52b | 16 |
MSVC 4.0 | 2.5c | 16 |
MSVC 2.0 | 3.0 | 32 |
MSVC 2.1 | 3.1 | 32 |
MSVC 2.2 | 3.2 | 32 |
MSVC 4.0 | 4.0 | 32 |
MSVC 4.1 | 4.1 | 32 |
// Microsoft Foundation Classes версии 4.00
#define _MFC_VER 0x0400
Мы будем рассматривать библиотеку MFC версий 3.0, 4.0 и 4.1, однако приведенная информация верна и для других версий MFC. В тех случаях, когда эти версии имеют существенные отличия мы будем на это специально указывать.
Класс | Орган управления |
---|---|
CAnimateCtrl | Используется для отображения видеоинформации |
CBitmapButton | Кнопка с рисунком |
CButton | Кнопка |
CComboBox | Список с окном редактирования |
CEdit | Поле редактирования |
CHeaderCtrl | Заголовок для таблицы |
CHotKeyCtrl | Предназначен для ввода комбинации клавиш акселераторов |
CListBox | Список |
CListCrtl | Может использоваться для отображения списка пиктограмм |
CProgressCtrl | Линейный индикатор |
CPropertySheet | Блокнот. Может состоять из нескольких страниц |
CRichEditCtrl | Окно редактирования, в котором можно редактировать форматированный текст |
CScrollBar | Полоса просмотра |
CSliderCtrl | Движок |
CSpinButtonCtrl | Обычно используется для увеличения или уменьшения значения какого-нибудь параметра |
CStatic | Статический орган управления |
CTabCtrl | Набор “закладок” |
CToolBarCtrl | Панель управления |
CToolTipCtrl | Маленькое окно содержащее строку текста |
CTreeCtrl | Орган управления, который позволяет просматривать иерархические структуры данных |
Класс | Описание |
---|---|
CArchiveException | Исключение, вызванное ошибкой при использовании объекта класса CArchive. Класс CArchive применяется для сохранения и загрузки документа из файла на диске |
CDaoException | Ошибка при работе с базами данных (при использовании классов DAO) |
CDBException | Ошибка при работе с базами данных (при использовании ODBC) |
CFileException | Ошибка, связанная с файловой системой |
CMemoryException | Недостаточно оперативной памяти |
CNotSupportedException | Попытка выполнить неопределенную операцию |
COleDispatchException, COleException | Ошибка OLE |
CResourceException | Не найден ресурс |
CUserException | Ошибка приложения, вызванная действиями пользователя |
Класс | Массив содержит |
---|---|
CByteArray | Байты |
CDWordArray | Двойные слова |
CObArray | Указателей на объекты класса CObject |
CPtrArray | Указателей типа void |
CStringArray | Объекты класса CString |
CUIntArray | Элементы класса unsigned integer или UINT |
CWordArray | Слова |
Класс | Список содержит элементы |
---|---|
CObList | Указатели на объекты класса CObject |
CPtrList | Указатели типа void |
CStringList | Объекты класса CString |
Класс | Ключевое поле | Поле, связанное с ключевым |
---|---|---|
CMapPtrToPtr | Указатель типа void | Указатель типа void |
CMapPtrToWord | Указатель типа void | Слово |
CMapStringToOb | Объекты класса CString | Указатели на объекты класса CObject |
CMapStringToPtr | Объекты класса CString | Указатель типа void |
CMapStringToString | Объекты класса CString | Объекты класса CString |
CMapWordToOb | Слово | Указатели на объекты класса CObject |
CMapWordToPtr | Слово | Указатель типа void |
Класс | Описание |
---|---|
CClientDC | Контекст отображения, связанный с внутренней областью окна (client area). Для получения контекста конструктор класса вызывает функцию программного интерфейса GetDC, а деструктор – функцию ReleaseDC |
CMetaFileDC | Класс предназначен для работы с метафайлами |
CPaintDC | Конструктор класса CPaintDC для получения контекста отображения вызывает метод CWnd::BeginPaint, деструктор метод CWnd::EndPaint. Объекты данного класса могут использовать только при обработке сообщения WM_PAINT. Это сообщение обычно обрабатывает метод OnPaint |
CWindowDC | Контекст отображения, связанный со всем окном. Для получения контекста конструктор класса вызывает функцию программного интерфейса GetWindowDC, а деструктор – функцию ReleaseDC |
Класс | Описание |
---|---|
CBitmap | Растровое изображение bitmap |
CBrush | Кисть |
CFont | Шрифт |
CPalette | Палитра цветов |
CPen | Перо |
CRgn | Область внутри окна |
Класс | Описание |
---|---|
CPoint | Объекты класса описывают точку |
CRect | Объекты класса описывают прямоугольник |
CSize | Объекты класса определяют размер прямоугольника |
CString | Объекты класса представляют собой текстовые строки переменной длинны |
CTime | Объекты класса служат для хранения даты и времени. Большое количество методов класса позволяют выполнять над объектами класса различные преобразования |
CTimeSpan | Объекты класса определяют период времени |
// Включаемый файл для MFC
#include <afxwin.h>
//=====================================================
// Класс CMFHelloApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFHelloApp
//=====================================================
class CMFHelloApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFHelloApp
CMFHelloApp MFHelloApp;
//=====================================================
// Метод InitInstance класса CMFHelloApp
// Переопределяем виртуальный метод InitInstance
// класса CWinApp. Он вызывается каждый раз при запуске
// приложения
//=====================================================
BOOL CMFHelloApp::InitInstance() {
AfxMessageBox("Hello, MFC!");
return FALSE;
}
Единственный файл с исходным текстом приложения создан и его надо включить в проект. Выберите из меню Insert строку Files into Project. На экране появится диалоговая панель Insert Files into Project. Выберите файл MFHello.cpp и нажмите кнопку Add. Диалоговая панель закроется. Просмотрите еще раз папку с файлами проекта. Теперь в ней расположен файл MFHello.cpp (рис. 2.10).
error LNK2001: unresolved external symbol __endthreadex
error LNK2001: unresolved external symbol __beginthreadex
Когда для создания нового приложения используется MFC AppWizard, библиотека MFC подключается автоматически. Вам не надо вручную вносить изменения в диалоговой панели Project Settings. Более подробно об использовании MFC AppWizard вы можете прочитать в следующих разделах книги.
// Включаемый файл для MFC
#include <afxwin.h>
В этом файле определены классы, методы, константы и другие структуры для библиотеки классов MFC. Кроме того включаемый файл afxwin.h автоматически подключает другой включаемый файл windows.h уже известный вам по предыдущим томам серии библиотеки системного программиста. Поэтому даже если из приложения будут вызываться функции стандартного программного интерфейса Windows файл windows.h включать не надо.
В предыдущих томах серии “Библиотека системного программиста”, посвященных программированию в среде операционных систем Windows и Windows 95, мы рассказывали о функции WinMain , которая является главной функцией приложения. Эта функция вызывается, когда пользователь или операционная система запускает приложение. Все приложения, которые мы рассматривали до сих пор содержали функцию WinMain:
// Самое простое приложение Windows
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Отображаем на экране небольшую панель с сообщением
MessageBox(NULL,"Hello, world", "Text Message", MB_OK);
// Завершаем приложение
return 0;
}
Взгляните на исходные тексты приложения. Вы нигде не обнаружите хорошо знакомой функции WinMain. Не видно также переменных, представляющих параметры этой функции.
Оказывается, в приложениях, основанных на классах MFC, функция WinMain скрыта от программиста в определении класса CWinApp. В каждом приложении определяется главный класс приложения, наследуемый от базового класса CWinApp. Обычно этот класс называют CProjectApp, где в качестве Project указывают имя проекта. Приложение должно иметь только один объект главного класса приложения, наследованного от класса CWinApp.
Класс CWinApp выполняет все действия, которые обычно выполняла функция WinMain – инициализирует приложение, обрабатывает сообщения и завершает приложение. Для этого класс CWinApp включает виртуальные методы InitApplication, InitInstance, Run и ExitInstance.
Чтобы выполнить инициализацию приложения, функция WinMain вызывает методы InitApplication и InitInstance для объекта главного класса приложения. Метод InitApplication выполняет инициализацию на уровне приложения. Вы можете переопределить этот метод в своем приложении. Метод InitInstance выполняет инициализацию каждой запущенной копии приложения. Обычно метод InitInstance создает главное окно приложения. Вы обязательно должны переопределить этот метод в своем приложении. Остальные методы, например Run, можно не переопределять.
Затем функция WinMain начинает обрабатывать цикл сообщений. Для этого вызывается метод Run. Вы можете переопределить этот метод, чтобы реализовать собственный цикл обработки сообщений.
Когда приложение заканчивает работу и цикл обработки сообщений завершается, вызывается метод ExitInstance. Вы можете переопределить этот метод, чтобы выполнить какие-либо действия перед завершением приложения.
В нашем случае главный класс приложения, который называется CMFHelloApp, определяется следующим образом:
//=====================================================
// Класс CMFHelloApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFHelloApp
//=====================================================
class CMFHelloApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
Мы наследуем класс CMFHelloApp от базового класса CWinApp. При этом базовый класс указан как public. Это означает, что в программе доступны все элементы базового класса CWinApp, объявленные как public. Мы можем вызывать методы класса CWinApp для объектов класса CMFHelloApp и обращаться к элементам данных класса CWinApp.
В определении класса CMFHelloApp мы объявляем виртуальный метод InitInstance. Он будет нами переопределен. Изначально метод InitInstance определен в классе CWinApp. Метод InitInstance отвечает за инициализацию приложения. Он вызывается каждый раз, когда пользователь запускает приложение. Если пользователь запустит приложение несколько раз, то метод InitInstance будет вызываться каждый раз.
Метод InitInstance обязательно должен быть переопределен в вашем главном классе приложения. Остальные виртуальные методы можно оставить без изменения.
Сразу после объявления главного класса приложения мы создаем объект этого класса – MFHelloApp. Вы должны определить объект главного класса приложения как глобальный. В этом случае он будет создан сразу при запуске приложения и сможет управлять всей работой приложения. После создания глобальных объектов вызывается функция WinMain, определенная в классе CWinApp. Она выполняет свои обычные функции – регистрирует классы окон, создает окно, и т. д.
Нельзя создавать два и более объектов класса, наследованного от базового класса CWinApp. Каждое приложение должно иметь один и только один объект главного класса приложения:
// Создаем объект приложение класса CMFHelloApp
CMFStartApp MFHelloApp;
Метод InitInstance главного класса приложения CMFHelloApp служит для инициализации. Он вызывается автоматически каждый раз, когда запускается очередная копия приложения. Мы используем метод InitInstance чтобы вывести на экран компьютера диалоговую панель с сообщением “Hello, MFC!”. Для этого вызываем функцию AfxMessageBox:
//=====================================================
// Метод InitInstance класса CMFHelloApp
// Переопределяем виртуальный метод InitInstance
// класса CWinApp. Он вызывается каждый раз при запуске
// приложения
//=====================================================
BOOL CMFHelloApp::InitInstance() {
AfxMessageBox("Hello, MFC!");
return FALSE;
}
Функция AfxMessageBox определена в библиотеке классов MFC. Вместо функции AfxMessageBox вы можете воспользоваться хорошо известной вам функцией MessageBox программного интерфейса Windows.
Когда вы вызываете из приложения, созданного с использованием классов MFC, функции программного интерфейса Windows, необходимо указывать перед именем функции символы ::. Так, вызов функции MessageBox мог бы выглядеть следующим образом:
::MessageBox(NULL,"Hello, world!", "Message", MB_OK);
В конце метода InitInstance мы вызываем оператор return и возвращаем значение FALSE. Приложение сразу завершается. Если метод InitInstance вернет значение TRUE, приложение продолжит работу и приступит к обработке очереди сообщений. Более подробно о методе InitInstance и свойствах базового класса CWinApp вы узнаете позже, когда мы будем рассматривать средства автоматизированной разработки приложений.
Тип метода | Отображать |
---|---|
All | Все методы |
Virtual | Виртуальные методы |
Static | Статические методы |
Non–Static | Все методы, кроме статических |
Non–Virtual | Все методы, кроме виртуальных |
Non–Static Non-Virtual | Все методы, кроме статических и виртуальных |
None | Не отображать методы класса |
Тип элементов данных | Отображать |
---|---|
All | Все элементы данных |
Static | Статические элементы данных |
Non–Static | Все данные, кроме статических |
None | Не отображать элементы данных |
// Включаемый файл для MFC
#include <afxwin.h>
//=====================================================
// Класс CMFStartApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFStartApp
//=====================================================
class CMFStartApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFStartApp
CMFStartApp MFStartApp;
//=====================================================
// Класс CMFStartWindow
// Наследуем от базового класса CFrameWnd класс
// CMFStartWindow. Он будет представлять главное
// окно нашего приложения
//=====================================================
class CMFStartWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFStartWindow
CMFStartWindow();
};
//=====================================================
// Метод InitInstance класса CMFStartApp
// Переопределяем виртуальный метод InitInstance
// класса CWinApp. Он вызывается каждый раз при запуске
// приложения
//=====================================================
BOOL CMFStartApp::InitInstance() {
// Создаем объект класса CMFStartWindow
m_pMainWnd = new CMFStartWindow();
// Отображаем окно на экране. Параметр m_nCmdShow
// определяет режим в котором оно будет отображаться
m_pMainWnd–>ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd–>UpdateWindow();
return TRUE;
}
//=====================================================
// Конструктор класса CMFStartWindow
//=====================================================
CMFStartWindow::CMFStartWindow() {
// Создаем окно приложения, соответствующее
// данному объекту класса CMFStartWindow
Create(NULL, "Hello MFC");
}
Просмотрите папку с файлами проекта. Теперь в ней расположен файл MFStart.cpp. Затем откройте страницу ClassView в окне Project Workspace (рис. 2.20). В ней отображаются два класса CMFStartApp и CMFStartWindow. В класс CMFStartApp входит метод InitInstance, а в класс CMFStartWindow конструктор CMFStartWindow. Кроме того, определена глобальная переменная MFStartApp.
//=====================================================
// Класс CMFStartApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFStartApp
//=====================================================
class CMFStartApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
Как видите главный класс приложения MFStart определяется точно так же как у приложения MFHello. Класс CMFStartApp наследуется от базового класса CWinApp. При этом базовый класс указан как public. Мы можем вызывать методы класса CWinApp для объектов класса CMFStartApp и обращаться к элементам данных класса CWinApp. В определении класса CMFStartApp объявлен виртуальный метод InitInstance. Он будет переопределен нами несколько позже, после объявления класса окна приложения.
После объявления главного класса приложения мы создаем объект этого класса – MFStartApp:
// Создаем объект приложение класса CMFStartApp
CMFStartApp MFStartApp;
Второй класс, определенный в нашем приложении, наследуется от базового класса CFrameWnd как public и будет представлять главное окно приложения. В классе главного окна мы определили только конструктор, который будет описан ниже:
//=====================================================
// Класс CMFStartWindow
// Наследуем от базового класса CFrameWnd класс
// CMFStartWindow. Он будет представлять главное
// окно нашего приложения
//=====================================================
class CMFStartWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFStartWindow
CMFStartWindow();
};
Метод InitInstance главного класса приложения CMFStartApp служит для инициализации. Он вызывается автоматически каждый раз, когда запускается очередная копия приложения.
Мы используем метод InitInstance, чтобы отобразить на экране окно приложения. Для этого мы создаем объект класса CMFStartWindow и записываем указатель на этот объект в элемент данных m_pMainWnd класса CWinThread (класс CWinThread является базовым для класса CWinApp). Таким образом, объект приложения и объект окна приложения связываются вместе.
Для создания объекта класса CMFStartWindow мы используем оператор new. Он создает новый объект указанного класса, отводит память и возвращает указатель на него. При создании нового объекта оператором new для него автоматически вызывается конструктор, описанный ниже.
Окно появится на экране только после того, как будет вызван метод ShowWindow. В качестве параметра методу ShowWindow передается параметр m_nCmdShow. Переменная m_nCmdShow является элементом класса CWinApp. Его назначение соответствует параметру nCmdShow функции WinMain, то есть определяет, как должно отображаться главное окно приложения сразу после его запуска.
После того как окно появилось на экране, мы передаем ему сообщение WM_PAINT, вызывая метод UpdateWindow. По этому сообщению приложение должно обновить содержимое окна. В нашем первом приложении мы ничего не будем отображать в окне, поэтому данный метод можно не вызывать.
В конце метода InitInstance мы вызываем оператор return и возвращаем значение TRUE, означающее, что инициализация приложения завершилась успешно и можно приступать к обработке очереди сообщений.
Если метод InitInstance вернет значение FALSE, приложение немедленно завершится. Мы использовали эту возможность в приложении MFHello, описанном выше.
//=====================================================
// Метод InitInstance класса CMFStartApp
// Переопределяем виртуальный метод InitInstance
// класса CWinApp. Он вызывается каждый раз при запуске
// приложения
//=====================================================
BOOL CMFStartApp::InitInstance() {
// Создаем объект класса CMFStartWindow
m_pMainWnd = new CMFStartWindow();
// Отображаем окно на экране. Параметр m_nCmdShow
// определяет режим в котором оно будет отображаться
m_pMainWnd–>ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd–>UpdateWindow();
return TRUE;
}
Чтобы создать окно, мы создаем объект класса CMFStartWindow. Такой объект не является собственно окном, которое пользователь видит на экране компьютера, а представляет собой внутреннее представление окна. Для создания окна предназначается метод Create, определенный в классе CFrameWnd. Он создает окно и связывает его с объектом Си++, в нашем случае с объектом класса CMFStartWindow:
//=====================================================
// Конструктор класса CMFStartWindow
//=====================================================
CMFStartWindow::CMFStartWindow() {
// Создаем окно приложения, соответствующее
// данному объекту класса CMFStartWindow
Create(NULL, "Hello MFC");
}
Для упрощения мы поместили описание классов, определения их методов и определения глобальных переменных в одном файле. На практике описания различных классов размещают в отдельных включаемых файлах. А определения методов записывают в программные файлы, имеющие расширение cpp.
Например, мы могли бы поместить описание классов CMFStartApp и CMFStartWindow в файлы MFStartApp.h и MFStartWindow.h. Метод InitInstance класса CMFStartApp и определение глобальной переменной MFStartApp можно поместить в файл MFStartApp.cpp, а определение конструктора класса CMFStartWindow – в файл MFStartWindow.cpp.
Так как в методе InitInstance класса CMFStartApp мы создаем новый объект класса CMFStartWindow, то мы должны включить в файл MFStartApp.cpp не только файл MFStartApp.h но еще и файл MFStartWindow.h. В проект MFStart мы должны записать оба программных файла MFStartApp.cpp и MFStartWindow.cpp. Листинги, представленные ниже содержат проект MFStart, разделенный на несколько файлов. Для того, чтобы сократить размер файлов, мы убрали из них комментарии.
Файл MFStartApp.h содержит описание главного класса приложения CMFStartApp. Этот файл представлен в листинге 2.3.
#include <afxwin.h>
class CMFStartApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
Виртуальный метод InitInstance переопределен нами в файле MFStartApp.cpp. В этом же файле создается объект класса CMFStartApp, представляющий само приложение. Файл MFStartApp.cpp показан в листинге 2.4.
#include <afxwin.h>
#include "MFStartApp.h"
#include "MFStartWindow.h"
CMFStartApp MFStartApp;
BOOL CMFStartApp::InitInstance() {
m_pMainWnd = new CMFStartWindow();
m_pMainWnd–>ShowWindow(m_nCmdShow);
m_pMainWnd–>UpdateWindow();
return TRUE;
}
Класс окна приложения CMFStartWindow определяется в файле MFStartWindow.h, представленном листингом 2.5. Мы наследуем класс CMFStartWindow от базового класса CFrameWnd.
#include <afxwin.h>
class CMFStartWindow : public CFrameWnd {
public:
CMFStartWindow();
};
И наконец, последний файл MFStartWindow.cpp модифицированного проекта MFStart показан в листинге 2.6. В этом файле определяется конструктор класса CMFStartWindow.
#include <afxwin.h>
#include "MFStartWindow.h"
CMFStartWindow::CMFStartWindow() {
Create(NULL, "Hello MFC");
}
MSG message;
while(GetMessage(&message, 0, 0, 0)) {
DispatchMessage(&message);
}
Для более сложных приложений цикл обработки сообщений содержал вызовы других функций, например TranslateMessage, TranslateAccelerator. Они обеспечивали предварительную обработку сообщений.
Каждое окно приложения имеет собственную функцию окна. В процессе обработки сообщения операционная система вызывает функцию окна и передает ей структуру, описывающую очередное сообщение.
Функция обработки, сообщения опознает, какое именно сообщение поступило для обработки и выполняет соответствующие действия. Сообщения распознаются по его коду. Обычно функция окна содержит оператор switch, который служит для определения кода сообщений. Вот пример типичной функции окна:
long FAR PASCAL _export WndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam) {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
DrawText(hdc, "Hello, Windows!", –1, &rect, DT_SINGLELINE | DT_CENTER);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
В сложных приложениях функция окна может обрабатывать большое количество разнообразных сообщений и оператор switch может стать очень длинным. Конечно, работать с блоком программного кода, который не помещается на одном экране, очень неудобно.
Чтобы “избавиться” от длинного оператора switch, применялись всевозможные ухищрения. Например, в приложении создавался массив, каждый элемент которого содержал код сообщения и указатель на функцию для его обработки. В функции окна помещался только небольшой программный код, который при получении сообщения просматривал массив, искал для него соответствующий элемент массива и вызывал определенную в нем функцию.
Если вы используете библиотеку классов MFC, то за обработку сообщений отвечают классы. Любой класс, наследованный от базового класса CCmdTarget, может обрабатывать сообщения. Чтобы класс смог обрабатывать сообщения, необходимо, чтобы он имел таблицу сообщений класса. В этой таблице для каждого сообщения указан метод класса, предназначенный для его обработки.
Сообщения, которые могут обрабатываться приложением, построенным на основе библиотеки классов MFC, подразделяются в зависимости от процедуры их обработки на три большие группы. Вот эти группы сообщений:
• оконные сообщения;
• сообщения от органов управления;
• команды.
Макрокоманда | Устанавливает методы для обработки сообщений |
---|---|
ON_WM_<name> | Стандартных сообщений операционной системы Windows |
ON_REGISTERED_MESSAGE | Зарегистрированные сообщения операционной системы Windows |
ON_MESSAGE | Сообщений, определенных пользователем |
ON_COMMAND, ON_COMMAND_RANGE | Командных сообщений |
ON_UPDATE_COMMAND_UI, ON_UPDATE_COMMAND_UI_RANGE | Сообщений, предназначенных для обновления пользовательского интерфейса |
ON_<name>, ON_CONTROL_RANGE | Сообщений от органов управления |
Сообщение | Макрокоманда | Метод обработчик |
---|---|---|
WM_CHAR | ON_WM_CHAR() | afx_msg void OnChar(UINT, UINT, UINT); |
WM_CREATE | ON_WM_CREATE() | afx_msg int OnCreate(LPCREATESTRUCT); |
WM_HSCROLL | ON_WM_HSCROLL() | afx_msg void OnHScroll(UINT, UINT, CWnd*); |
WM_KEYDOWN | ON_WM_KEYDOWN() | afx_msg void OnKeyDown(UINT, UINT, UINT); |
WM_KEYUP | ON_WM_KEYUP() | afx_msg void OnKeyUp(UINT, UINT, UINT); |
WM_LBUTTONDOWN | ON_WM_LBUTTONDOWN() | afx_msg void OnLButtonDown(UINT, CPoint); |
WM_LBUTTONUP | ON_WM_LBUTTONUP() | afx_msg void OnLButtonUp(UINT, CPoint); |
WM_PAINT | ON_WM_PAINT() | afx_msg void OnPaint(); |
WM_SIZE | ON_WM_SIZE() | afx_msg void OnSize(UINT, int, int); |
WM_TIMER | ON_WM_TIMER() | afx_msg void OnTimer(UINT); |
WM_VSCROLL | ON_WM_VSCROLL() | afx_msg void OnVScroll(UINT, UINT, CWnd*); |
#define afx_msg
Макрокоманды ON_WM_<name> не имеют параметров. Однако методы, которые вызываются для обработки соответствующих сообщений, имеют параметры, количество и назначение которых зависит от обрабатываемого сообщения.
Когда вы определяете обработчик стандартного сообщения Windows в своем классе, он будет использоваться вместо обработчика определенного в классе CWnd (или другом базовом классе). В любом случае вы можете вызвать метод обработчик базового класса из своего метода обработчика.
ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn)
ON_MESSAGE(message, memberFxn)
ON_COMMAND(id, memberFxn)
В общем случае командные сообщения не имеют обработчиков, используемых по умолчанию. Существует только небольшая группа стандартных командных сообщений, имеющих методы обработчики, вызываемые по умолчанию. Эти сообщения соответствуют стандартным строкам меню приложения. Так, например, если вы (или MFC AppWizard) присвоите строке Open меню File идентификатор ID_FILE_OPEN, то для его обработки будет вызван метод OnFileOpen, определенный в классе CWinApp. Список стандартных командных сообщений и их описание представлены в главах “Однооконный интерфейс” и “Многооконный интерфейс”.
Принято, что методы обработчики командных сообщений (как и методы обработчики всех других сообщений) должны быть определены с ключевым словом afx_msg.
ON_COMMAND_RANGE(id1, id2, memberFxn)
ON_UPDATE_COMMAND_UI(id, memberFxn)
Методы, предназначенные для обработки данного класса сообщений, должны быть определены с ключевым словом afx_msg и иметь один параметр – указатель на объект класса CCmdUI . Для удобства, имена методов, предназначенных для обновления пользовательского интерфейса, начинаются с OnUpdate:
afx_msg void OnUpdate<имя_обработчика>(CCmdUI* pCmdUI);
В качестве параметра pCmdUI методу передается указатель на объект класса CCmdUI. В нем содержится информация о объекте пользовательского интерфейса, который надо обновить – строке меню или кнопке панели управления. Класс CCmdUI также включает методы, позволяющие изменить внешний вид представленного им объекта пользовательского интерфейса.
Сообщения, предназначенные для обновления пользовательского интерфейса передаются, когда пользователь открывает меню приложения и во время цикла ожидания приложения, когда очередь сообщений приложения становится пуста.
При этом посылается несколько сообщений, по одному для каждой строки меню. С помощью макрокоманд ON_UPDATE_COMMAND_UI можно определить методы-обработчики, ответственные за обновление внешнего вида каждой строки меню и соответствующей ей кнопке на панели управления. Эти методы могут изменять состояние строки меню – отображать ее серым цветом, запрещать ее выбор, отображать около нее символ v и т. д.
Если вы не определите метод для обновления данной строки меню или кнопки панели управления, выполняется обработка по умолчанию. Выполняется поиск обработчика соответствующего командного сообщения и если он не обнаружен, выбор строки меню запрещается.
Дополнительную информацию об обновлении пользовательского интерфейса приложения вы можете получить в разделе “Однооконный интерфейс”.
ON_UPDATE_COMMAND_UI_RANGE(id1, id2, memberFxn)
ON_BN_CLICKED(id, memberFxn)
ON_CONTROL_RANGE(wNotifyCode, id1, id2, memberFxn)
// Включаемый файл для MFC
#include <afxwin.h>
#include "MFMenuRes.h"
//=====================================================
// Класс CMFMenuApp – главный класс приложения
//=====================================================
class CMFMenuApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFMenuApp
CMFMenuApp MFMenuApp;
//=====================================================
// Класс CMFMenuWindow – представляет главное окно
//=====================================================
class CMFMenuWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFMenuWindow
CMFMenuWindow();
// Объявляем методы для обработки команд меню
afx_msg void MenuCommand();
afx_msg void ExitApp();
// Макрокоманда необходима, так как класс
// CMFMenuWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
//=====================================================
// Метод MenuCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CMFMenuWindow::MenuCommand() {
MessageBeep(0);
}
//=====================================================
// Метод ExitApp
//=====================================================
void CMFMenuWindow::ExitApp() {
DestroyWindow();
}
//=====================================================
// Таблица сообщений класса CMFMenuWindow
//=====================================================
BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)
ON_COMMAND(ID_TEST_BEEP, MenuCommand)
ON_COMMAND(ID_TEST_EXIT, ExitApp)
END_MESSAGE_MAP()
//=====================================================
// Метод InitInstance класса CMFMenuApp
//=====================================================
BOOL CMFMenuApp::InitInstance() {
// Создаем объект класса CMFMenuWindow
m_pMainWnd = new CMFMenuWindow();
// Отображаем окно на экране
m_pMainWnd–>ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd–>UpdateWindow();
return TRUE;
}
//=====================================================
// Конструктор класса CMFMenuWindow
//=====================================================
CMFMenuWindow::CMFMenuWindow() {
// Создаем окно приложения, соответствующее
// данному объекту класса CMFMenuWindow
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
}
Переключатель | Описание |
---|---|
Break | Этот переключатель может принимать три различных значения – None, Column и Bar. Он позволяет задать структуру меню. По умолчанию выбрано значение None оно соответствует нормальному виду меню без деления на колонки. Если выбрать значение Column, тогда пункт меню будет размещен в новом столбце. Значение Bar соответствует Column, за исключением меню верхнего уровня. Если указать Bar для меню верхнего уровня, то новая колонка будет отделена от старой вертикальной линией |
Checked | Если установить переключатель, то строка меню будет выделена символом √. Потом, обращаясь к специальным методам, вы сможете удалять или отображать этот символ |
Grayed | Если включить переключатель Grayed, тогда пункт меню будет отображаться серым цветом и будет недоступен для выбора пользователем. Такая возможность удобна, если вам надо сделать недоступным какие-либо возможности приложения. Впоследствии приложение может сделать пункт меню доступным. Для этого надо вызвать соответствующий метод |
Help | Если установить переключатель Help, тогда для него будет установлено выравнивание по правому краю. Обычно этот переключатель устанавливают для меню верхнего уровня, которое управляет справочной системной приложения |
Inactive | Если включен переключатель Grayed, тогда переключатель недоступен. В противном случае вы можете установить переключатель Inactive. В этом случае пункт меню будет неактивен |
Pop-up | Вы можете создавать многоуровневые меню. Если вы включите переключатель Pop-up, то данный пункт меню будет являться меню верхнего уровня, которое можно открыть. По умолчанию, все пункты главного меню имеют установленный переключатель Pop-up. Так как меню верхнего уровня служит только для объединения других пунктов меню, то оно не имеет идентификатора |
Separator | Если переключатель установлен, тогда в меню вставляется разделитель. Для разделителя все остальные поля и переключатели диалоговой панели не используются |
//{{NO_DEPENDENCIES}}
// Включаемый файл, созданный Microsoft Developer Studio
// Используется в файле ресурсов MFMenu.rc
//
#define IDR_MENU 101
#define ID_TEST_BEEP 40001
#define ID_TEST_EXIT 40002
// Следующие значения идентификаторов используются по
// умолчанию для новых объектов
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
В листинге 2.9 мы привели файл ресурсов MFMenuRes.rc приложения. Этот файл был подготовлен редактором ресурсов Visual C++. Одна из первых строк файла содержит директиву #include которой подключается файл MFMenuRes.h, содержащий описание идентификаторов ресурсов (листинг 2.8).
Среди прочих служебных строк, необходимых редактору ресурсов и компилятору Visual C++, вы можете обнаружить описание меню приложения IDR_MENU. Для первого приложения, использующего ресурсы мы привели файл ресурсов полностью. Впоследствии мы ограничимся словесным описанием ресурсов и будем приводить только выдержки из файла ресурсов.
// Файл описания ресурсов приложения, созданный
// Microsoft Developer Studio
#include "MFMenuRes.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Включаем файл afxres.h, содержащий определения стандартных
// идентификаторов
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Руссификацированные ресурсы
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
////////////////////////////////////////////////////////////
// Меню
//
IDR_MENU MENU DISCARDABLE
BEGIN
POPUP "Test"
BEGIN
MENUITEM "Beep", ID_TEST_BEEP
MENUITEM SEPARATOR
MENUITEM "Exit", ID_TEST_EXIT
END
END
#ifdef APSTUDIO_INVOKED
////////////////////////////////////////////////////////
// Ресурсы TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"MFMenuRes.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Руссификацированные ресурсы
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
#endif
//=====================================================
// Класс CMFMenuWindow – представляет главное окно
//=====================================================
class CMFMenuWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFMenuWindow
CMFMenuWindow();
// Объявляем методы для обработки команд меню
afx_msg void MenuCommand();
afx_msg void ExitApp();
// Макрокоманда необходима, так как класс
// CMFMenuWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
Однако это еще не все. Необходимо также определить таблицу сообщений. Таблица начинается макрокомандой BEGIN_MESSAGE_MAP и заканчивается макрокомандой END_MESSAGE_MAP. Между этими макрокомандами расположены строки таблицы сообщений, определяющие сообщения, подлежащие обработке данным классом и методы, которые выполняют такую обработку.
Приложение может содержать несколько классов, обладающих собственными таблицами сообщений. В следующем разделе мы приведем пример такого приложения. Чтобы однозначно определить класс, к которому относится таблица сообщений, имя этого класса записывается в первый параметр макрокоманды BEGIN_MESSAGE_MAP.
Приложение MFMenu обрабатывает только две команды от меню приложения. Первая команда имеет идентификатор ID_TEST_BEEP и передается, когда пользователь выбирает из меню Test строку Beep. Для обработки этой команды вызывается метод MenuCommand. Вторая команда с идентификатором ID_TEST_EXIT передается приложению, когда пользователь выбирает из меню Test строку Exit. Обработка этого сообщения выполняется методом ExitApp.
//=====================================================
// Таблица сообщений класса CMFMenuWindow
//=====================================================
BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)
ON_COMMAND(ID_TEST_BEEP, MenuCommand)
ON_COMMAND(ID_TEST_EXIT, ExitApp)
END_MESSAGE_MAP()
Конечно, приложению MFMenu может поступать гораздо больше сообщений и команд, чем указано в таблице сообщений класса CMFMenuWindow. Необработанные сообщения передаются для обработки базовому классу CMFMenuWindow – классу CFrameWnd . Класс, который будет обрабатывать сообщения, не указанные в таблице сообщений, указывается во втором параметре макрокоманды BEGIN_MESSAGE_MAP.
// Включаемый файл для MFC
#include <afxwin.h>
#include "resource.h"
//=====================================================
// Класс CMFMessageApp – главный класс приложения
//=====================================================
class CMFMessageApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
afx_msg void AppMessageCommand();
// Макрокоманда необходима, так как класс
// CMFMessageWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
// Создаем объект приложение класса CMFMessageApp
CMFMessageApp MFMessageApp;
//=====================================================
// Класс CMFMessageWindow – представляет главное окно
//=====================================================
class CMFMessageWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFMessageWindow
CMFMessageWindow();
// Объявляем методы для обработки команд меню
afx_msg void FrameMessageCommand();
afx_msg void ExitApp();
// Макрокоманда необходима, так как класс
// CMFMessageWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
//=====================================================
// Метод MessageCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CMFMessageWindow::FrameMessageCommand() {
::MessageBox(NULL, "Command received in CMFMessageWindow Message Map", "Message", MB_OK);
}
//=====================================================
// Метод MessageCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CMFMessageApp::AppMessageCommand() {
::MessageBox(NULL, "Command received in CMFMessageApp Message Map", "Message", MB_OK);
}
//=====================================================
// Таблица сообщений класса CMFMessageWindow
//=====================================================
BEGIN_MESSAGE_MAP(CMFMessageWindow, CFrameWnd)
ON_COMMAND(ID_TEST_INFRAMECLASS, FrameMessageCommand)
ON_COMMAND(ID_TEST_INBOTHCLASS, FrameMessageCommand)
END_MESSAGE_MAP()
//=====================================================
// Таблица сообщений класса CMFMessageApp
//=====================================================
BEGIN_MESSAGE_MAP(CMFMessageApp, CWinApp)
ON_COMMAND(ID_TEST_INAPPCLASS, AppMessageCommand)
ON_COMMAND(ID_TEST_INBOTHCLASS, AppMessageCommand)
END_MESSAGE_MAP()
//=====================================================
// Метод InitInstance класса CMFMessageApp
//=====================================================
BOOL CMFMessageApp::InitInstance() {
// Создаем объект класса CMFMessageWindow
m_pMainWnd = new CMFMessageWindow();
// Отображаем окно на экране
m_pMainWnd–>ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd–>UpdateWindow();
return TRUE;
}
//=====================================================
// Конструктор класса CMFMessageWindow
//=====================================================
CMFMessageWindow::CMFMessageWindow() {
// Создаем окно приложения, соответствующее
// данному объекту класса CMFMessageWindow
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
}
Используя редактор ресурсов, создайте файл ресурсов и включите в него меню Test, содержащее четыре строки, имеющие идентификаторы, описанные в следующей таблице. Присвойте меню идентификатор IDR_MENU. Затем включите файл ресурсов в проект.
Строка меню Test | Идентификатор |
---|---|
In Frame Class | ID_TEST_INFRAMECLASS |
In App Class | ID_TEST_INAPPCLASS |
In Both Class | ID_TEST_INBOTHCLASS |
Exit | ID_APP_EXIT |
//////////////////////////////////////////////////////////////
// Меню
//
IDR_MENU MENU DISCARDABLE
BEGIN
POPUP "Test"
BEGIN
MENUITEM "In Frame Class", ID_TEST_INFRAMECLASS
MENUITEM "In App Class", ID_TEST_INAPPCLASS
MENUITEM "In Both Class", ID_TEST_INBOTHCLASS
MENUITEM "Exit", ID_APP_EXIT
END
END
Идентификаторы, необходимые для файла ресурсов, записываются в файл resource.h, показанный в листинге 2.12. Этот файл создается автоматически редактором ресурсов. Все что вы должны сделать – это включить его в исходный текст приложения – файл MFMessage.cpp.
//{{NO_DEPENDENCIES}}
// Включаемый файл, созданный Microsoft Developer Studio
// Используется в файле ресурсов MFMessage.rc
//
#define IDR_MENU 101
#define ID_TEST_BEEP 40001
#define ID_TEST_EXIT 40002
#define ID_TEST_INAPPCLASS 40003
#define ID_TEST_INFRAMECLASS 40004
#define ID_TEST_INBOTHCLASS 40006
// Следующие значения идентификаторов используются по
// умолчанию для новых объектов
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS 1
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40007
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Укажите в проекте MFMessage, что приложение использует библиотеку классов MFC и постройте проект. Запустите полученный выполнимый файл. На экране появится главное окно приложение, имеющее меню Test (рис. 2.26).
#define CModalDialog CDialog
Как создать и отобразить на экране диалоговую панель? В первую очередь необходимо добавить в файл ресурсов приложения шаблон новой диалоговой панели и при помощи редактора ресурсов изменить его по своему усмотрению.
Следующим этапом создается класс для управления диалоговой панелью. Этот класс наследуется непосредственно от базового класса CDialog.
Каждая диалоговая панель обычно содержит несколько органов управления. Работая с диалоговой панелью, пользователь взаимодействует с этими органами управления – нажимает кнопки, вводит текст, выбирает элементы списков. В результате генерируются соответствующие сообщения, которые должны быть обработаны классом диалоговой панели.
Так как класс диалоговой панели обрабатывает сообщения, то он содержит таблицу сообщений и соответствующие методы обработчики сообщений.
Чтобы создать модальную диалоговую панель, сначала необходимо создать объект определенного вами класса диалоговой панели, а затем вызвать метод DoModal, определенный в классе CDialog.
Процедура создания немодальной диалоговой панели несколько другая. Для этого используется метод Create класса CDialog. Мы рассмотрим создание немодальных диалоговых панелей позже.
// Включаемый файл для MFC
#include <afxwin.h>
#include "resource.h"
//=====================================================
// Класс CMFDialogApp – главный класс приложения
//=====================================================
class CMFDialogApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFDialogApp
CMFDialogApp MFDialogApp;
//=====================================================
// Класс CMyDialog – класс диалоговой панели
//=====================================================
class CMyDialog : public CDialog {
public:
CMyDialog();
CString m_Text;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// Обработчики сообщений от кнопок диалоговой панели
afx_msg void OnDefault();
virtual void OnCancel();
virtual void OnOK();
// Макрокоманда необходима, так как класс
// CMyDialog обрабатывает сообщения от органов
// управления диалоговой панели
DECLARE_MESSAGE_MAP()
};
// Конструктор клаасса CMyDialog
CMyDialog::CMyDialog() : CDialog(CMyDialog::IDD) {
// Инициализируем переменную m_Text
m_Text = "";
}
//=====================================================
// Метод DoDataExchange класса CMyDialog
//=====================================================
void CMyDialog::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT, m_Text);
}
//=====================================================
// Таблица сообщений класса CMyDialog
//=====================================================
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_BN_CLICKED(IDC_DEFAULT, OnDefault)
END_MESSAGE_MAP()
//=====================================================
// Метод OnDefault класса CMyDialog
//=====================================================
void CMyDialog::OnDefault() {
// TODO:
m_Text = "Start Text";
UpdateData(FALSE);
MessageBeep(0);
}
//=====================================================
// Метод OnCancel класса CMyDialog
//=====================================================
void CMyDialog::OnCancel() {
// Подаем звуковой сигнал
MessageBeep(0);
// Вызываем метод OnCancel базового класса
CDialog::OnCancel();
}
//=====================================================
// Метод OnOK класса CMyDialog
//=====================================================
void CMyDialog::OnOK() {
// Вызываем метод OnOK базового класса
CDialog::OnOK();
// Подаем звуковой сигнал
MessageBeep(0);
}
//=====================================================
// Метод InitInstance класса CMFDialogApp
//=====================================================
BOOL CMFDialogApp::InitInstance() {
// Создаем объект класса CMyDialog
CMyDialog dlgTest;
m_pMainWnd = &dlgTest;
// Отображаем на экране модельную диалоговую панель
dlgTest.DoModal();
// Отображаем на экране значение переменной m_Text,
// ввходящей в класс CMyDialog
AfxMessageBox(dlgTest.m_Text);
return FALSE;
}
//////////////////////////////////////////////////////////////
// Диалоговая панель
//
DIALOGPANEL DIALOG DISCARDABLE 0, 0, 186, 46
STYLE DS_MODALFRAME|DS_CENTER|WS_POPUP|WS_CAPTION|WS_SYSMENU
CAPTION "My Dialog"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,129,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14
PUSHBUTTON "Default",IDC_DEFAULT,70,7,50,14
EDITTEXT IDC_EDIT,7,24,113,14,ES_AUTOHSCROLL
LTEXT "Line Editor",IDC_STATIC,9,10,34,8
END
Идентификаторы, задействованные в файле ресурсов приложения по умолчанию, определяются во включаемом файле resource.h. Мы привели этот файл в листинге 2.15. Вы можете изменить название включаемого файла, выбрав из меню View строку Resource Includes.
//{{NO_DEPENDENCIES}}
// Включаемый файл, созданный Microsoft Developer Studio
// Используется в файле ресурсов MFDlgRes.rc
//
#define IDR_MENU 101
#define IDC_DEFAULT 1000
#define IDC_EDIT 1001
#define ID_TEST_DIALOG 40001
#define ID_TEST_EXIT 40002
// Следующие значения идентификаторов используются по
// умолчанию для новых объектов
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Обратите внимание, что включаемый файл resource.h содержит не только определения идентификаторов, но также дополнительную служебную информацию. Она расположена после директивы #ifdef APSTUDIO_INVOKED и представляет собой ряд макроопределений. Данные макроопределения используются редактором ресурсов при создании новых идентификаторов.
Откройте страницу ClassView в окне Project Workspace. Обратите внимание, что если вы переместите окно Project Workspace к границе главного окна Visual C++, его заголовок пропадет и окно станет похоже на обычную панель управления. Если горизонтальный размер окна Project Workspace уменьшится, тогда исчезнут названия в закладках страниц и останутся только маленькие пиктограммы (рис. 2.31).
В ней отображаются два класса CMFDialogApp и CMyDialog. В главный класс приложения CMFDialogApp входит метод InitInstance. В класс CMyDialog входит конструктор CMyDialog, метод DoDataExchange , предназначенный для обмена данными между органами управления диалоговой панели и привязанных к ним переменных, а также методы OnOK , OnCancel и OnDefault. Последние три метода вызываются когда пользователь нажимает на кнопки OK, Cancel и Default, расположенные в диалоговой панели. Кроме того, определена глобальная переменная MFDialogApp.
//=====================================================
// Метод InitInstance класса CMFDialogApp
//=====================================================
BOOL CMFDialogApp::InitInstance() {
// Создаем объект класса CMyDialog
CMyDialog dlgTest;
m_pMainWnd = &dlgTest;
// Отображаем на экране модельную диалоговую панель
dlgTest.DoModal();
// Отображаем на экране значение переменной m_Text,
// входящей в класс CMyDialog
AfxMessageBox(dlgTest.m_Text);
return FALSE;
}
Сначала создается объект dlgTest класса CMyDialog, который будет представлять диалоговую панель. Когда объект dlgTest создан, диалоговая панель еще не появляется на экране, для этого надо воспользоваться методом DoModal, определенным в классе CDialog.
Следующим оператором мы присваиваем адрес объекта диалоговой панели элементу данных m_pMainWnd , входящему в класс CWinApp. Элемент данных m_pMainWnd определяет главное окно приложения. В нашем случае главное окно как таковое отсутствует и вместо него выступает диалоговая панель.
Чтобы отобразить диалоговую панель на экране, мы вызываем для объекта dlgTest метод DoModal . На этом выполнение метода InitInstance приостанавливается, пока пользователь не закроет диалоговую панель.
Когда диалоговая панель закрыта, мы отображаем на экране состояние переменной dlgTest.m_Text, которая соответствует полю ввода Edit диалоговой панели. Последний оператор метода return возвращает значение FALSE и приложение завершается.
//=====================================================
// Класс CMyDialog – класс диалоговой панели
//=====================================================
class CMyDialog : public CDialog {
public:
CMyDialog();
CString m_Text;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// Обработчики сообщений от кнопок диалоговой панели
afx_msg void OnDefault();
virtual void OnCancel();
virtual void OnOK();
DECLARE_MESSAGE_MAP()
};
Обратите внимание на то, как мы определяем конструктор класса CMyDialog. После названия конструктора стоит символ двоеточие и название конструктора класса CDialog. При этом в качестве параметра, конструктору CDialog передается идентификатор диалоговой панели "DIALOGPANEL":
// Конструктор класса CMyDialog
CMyDialog::CMyDialog() : CDialog("DIALOGPANEL") {
// Инициализируем переменную m_Text
m_Text = "";
}
Основное назначение конструктора CMyDialog – вызвать конструктор класса CDialog, указав ему в качестве параметра идентификатор диалоговой панели "DIALOGPANEL". Именно конструктор класса CDialog выполняет создание диалоговой панели.
В конструкторе также инициализируется переменная m_Text, входящая в класс CMyDialog. В нее записывается пустая строка.
void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value);
Метод DoDataExchange позволяет выполнять проверку данных, которые пользователь вводит в диалоговой панели. Для этого предназначен ряд функций DDV_. Эти функции позволяют гарантировать, что данные, введенные пользователем в диалоговой панели, соответствуют определенным условиям.
Если вы используете функцию DDV_ для проверки ввода в данном органе управления, вы должны вызвать ее сразу после вызова функции DDX_ для этого же органа управления.
Если функция DDV_ обнаруживает ошибку пользователя при вводе информации в органе управления диалоговой панели, она отображает сообщение и передает фокус ввода соответствующему органу управления.
В отличие от функций DDX_, функции DDV_, в зависимости от их предназначения, имеют различное количество параметров. Первый параметр, как и в случае функций DDX_, содержит указатель на объект класса CDataExchange . Остальные параметры имеют различное назначение в зависимости от функции. Так, например, прототип функции, которая проверяет максимальную длину введенной текстовой строки, выглядит следующим образом:
void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars);
Второй параметр содержит ссылку на элемент данных класса диалоговой панели, в который записана текстовая строка из поля редактирования панели. Третий параметр определяет максимально допустимую длину этой строки.
Другие функции DDV_ имеют больше параметров. Так, функция DDV_MinMaxInt, проверяющая, что введенное значение находится в определенных границах, имеет 4 параметра:
void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal);
Первый параметр, как всегда, содержит указатель на объект класса CDataExchange, второй параметр определяет проверяемую величину, а третий и четвертый параметры устанавливают минимальное и максимальное значения для проверяемой величины.
Приложение не должно напрямую вызывать метод DoDataExchange. Он вызывается через метод UpdateData, определенный в классе CWnd (рис. 2.34). Если вам надо выполнить обмен данными, между органами управления и элементами данных класса диалоговой панели, используйте метод UpdateData . Приведем прототип метода UpdateData:
BOOL UpdateData(BOOL bSaveAndValidate = TRUE);
//=====================================================
// Метод DoDataExchange класса CMyDialog
//=====================================================
void CMyDialog::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT, m_Text);
}
Методу DoDataExchange передается указатель pDX на объект класса CDataExchange. Этот объект создается, когда вы инициируете процесс обмена данными, вызывая метод UpdateData . Элементы данных класса CDataExchange определяют процедуру обмена данными, в том числе определяют, в каком направлении будет происходить этот обмен. Обратите внимание, что указатель pDX передается функциям DDX_ и DDV_.
В библиотеку MFC входит большое количество функций DDX_ и DDV_. Чтобы облегчить задачу написания метода DoDataExchange для класса вашей диалоговой панели, используйте ClassWizard. Он позволяет привязать к органам диалоговой панели элементы данных класса. При этом метод DoDataExchange создается ClassWizard автоматически. ClassWizard сам выбирает, какие функции DDX_ и DDV_ надо использовать для данного органа управления и связанного с ним элемента данных класса диалоговой панели. Подробно об использовании ClassWizard для разработки диалоговых панелей вы можете прочитать в главе “Приложение с главной диалоговой панелью”.
Класс диалоговой панели должен обрабатывать сообщения от своих органов управления, поэтому он должен иметь таблицу сообщений. В заголовке таблицы сообщений указывается имя класса CMyDialog и имя базового класса CDialog:
//=====================================================
// Таблица сообщений класса CMyDialog
//=====================================================
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_BN_CLICKED(IDC_DEFAULT, OnDefault)
END_MESSAGE_MAP()
Таблица сообщений содержит только одну строку, в которой обрабатывается сообщение с кодом извещения ON_BN_CLICKED от кнопки “Default”. Когда пользователь нажимает кнопку, вырабатывается данное сообщение и вызывается его обработчик – метод OnDefault, определенный в классе CMyDialog:
//=====================================================
// Метод OnDefault класса CMyDialog
//=====================================================
void CMyDialog::OnDefault() {
// TODO:
m_Text = "Start Text";
UpdateData(FALSE);
MessageBeep(0);
}
Две другие кнопки диалоговой панели "DIALOGPANEL", OK и Cancel не представлены в таблице сообщений, но тем не менее в приложении определены методы OnOK и OnCancel, которые вызываются при нажатии на них. Оказывается для диалоговых панелей определены две стандартные кнопки OK и Cancel, которым присвоены специальные идентификаторы IDOK и IDCANCEL.
Базовый класс CDialog, также как и класс CMyDialog, содержит таблицу сообщений. Если вы установили библиотеку MFC вместе с исходными текстами, вы можете изучить реализацию класса CDialog в файле Dlgcore.cpp. Сам класс CDialog определен во включаемом файле Afxwin.h. Ниже представлена выдержка из таблицы сообщений, определенной в файле Dlgcore.cpp:
BEGIN_MESSAGE_MAP(CDialog, CWnd)
//{{AFX_MSG_MAP(CDialog)
ON_WM_CTLCOLOR()
ON_COMMAND(IDOK, OnOK)
ON_COMMAND(IDCANCEL, OnCancel)
ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp)
ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
ON_MESSAGE(WM_SETFONT, HandleSetFont)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Среди прочих сообщений, в этой таблице определены командные сообщения с идентификаторами IDOK и IDCANCEL. Для обработки этих командных сообщений определены виртуальные методы OnOK и OnCancel. Данные методы также определены в MFC (файл Dlgcore.cpp). Поэтому когда диалоговая панель содержит кнопки с идентификаторами IDOK и IDCANCEL, как правило нет необходимости создавать для них обработчики.
Так как в таблице сообщений класса CMyDialog отсутствуют макрокоманды для обработки сообщений от кнопок OK и Cancel, они передаются для обработки базовому классу CDialog. Здесь они обрабатываются виртуальными методами OnOK и OnCancel.
Метод OnOK, определенный в классе CDialog, копирует данные из полей диалоговой панели в связанные с ними переменные. Для этого вызывается метод UpdateData с параметром TRUE. Затем выполняется вызов метода EndDialog, который закрывает диалоговую панель и возвращает значение IDOK. После того как диалоговая панель закрыта, метод DoModal возвращает значение IDOK и продолжает работать метод InitInstance.
void CDialog::OnOK() {
if (!UpdateData(TRUE)) {
// В процессе обмена данными произошла ошибка
TRACE0("UpdateData failed during dialog termination.\n");
return;
}
EndDialog(IDOK);
}
Метод OnCancel, определенный в классе CDialog, еще проще, чем OnOK. Он только закрывает диалоговую панель и возвращает значение IDCANCEL. Копирование данных из полей диалоговой панели не происходит, так как пользователь отменил изменения, нажав кнопку Cancel.
void CDialog::OnCancel() {
EndDialog(IDCANCEL);
}
Так как методы OnOK и OnCancel определены в классе CDialog как виртуальные, вы можете переназначить их в своем классе CMyDialog. В этом случае управление получат переопределенные вами методы, а не методы класса CDialog. Методы базового класса CDialog можно вызвать, явно указав класс.
//=====================================================
// Метод OnCancel класса CMyDialog
//=====================================================
void CMyDialog::OnCancel() {
// Подаем звуковой сигнал
MessageBeep(0);
// Вызываем метод OnCancel базового класса
CDialog::OnCancel();
}
Переопределенный нами метод OnCancel вызывает функцию программного интерфейса MessageBeep, которая подает звуковой сигнал, а затем вызываем метод OnCancel базового класса CDialog. Метод OnCancel базового класса CDialog вызывается в конце, так как он закрывает саму диалоговую панель.
Аналогично методу OnCancel мы переопределили метод OnOK.
//=====================================================
// Метод OnOK класса CMyDialog
//=====================================================
void CMyDialog::OnOK() {
// Вызываем метод OnOK базового класса
CDialog::OnOK();
// Подаем звуковой сигнал
MessageBeep(0);
}
Конечно, наша первая программа использует далеко не все возможности класса CDialog. Другие методы этого класса будут рассмотрены нами позже в главе посвященной автоматизированным средствам проектирования приложений.
BOOL Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);
BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = NULL);
Параметр lpszTemplateName должен содержать указатель на строку, закрытую нулем, содержащую имя ресурса диалоговой панели. Если вы используете метод Create, имеющий второй прототип, вместо имени шаблона, надо указать в параметре nIDTemplate его идентификатор.
Параметр pParentWnd задает родительское окно диалоговой панели. Если в качестве pParentWnd указать NULL, то в качестве родительского окна выступает главное окно приложения.
Метод Create возвращает управление сразу после того, как диалоговая панель отобразилась на экране. Он возвращает ненулевое значение, если создание диалоговой панели завершилось успешно и нуль в противном случае.
Чтобы закрыть немодальную диалоговую панель, можно воспользоваться методом DestroyWindow (метод DestroyWindow определен в классе CWnd и вы можете вызвать его для объектов класса диалоговой панели).
Теперь вам остается удалить объект, управляющий диалоговой панелью. Для этого в классе диалоговой панели можно переопределить виртуальный метод PostNcDestroy (этот метод первоначально определен в базовом классе CWnd). В нем вы можете вызвать оператор delete, передав ему в качестве параметра указатель на данный объект this .
CObject();
Второй конструктор класса CObject называется конструктором копирования, потому что в качестве параметра objectSrc он принимает ссылку на другой объект этого же класса. Конструктор копирования создает новый объект и выполняет копирование всех элементов объекта, указанного в качестве параметра.
Для класса CObject конструктор копирования описан как private и не имеет определения. Если вы попытаетесь передать по значению объект класса, наследованного от CObject, и не определите конструктор копирования в своем классе, вы получите ошибку на этапе компиляции.
В случае, если бы конструктор копирования класса CObject не был описан, компилятор создал бы его автоматически. Таким образом описание конструктора копирования как private, принуждает вас явно определить конструктор копирования для порожденных классов (конечно если конструктор копирования необходим):
private:
CObject(const CObject& objectSrc);
Для динамического создания и удаления объектов класса CObject вы можете воспользоваться операторами new и delete. Эти операторы переопределены в классе CObject, однако назначение и основные принципы их работы остались прежними.
private:
void operator=(const CObject& src);
Таким образом для классов, наследованных от CObject запрещается выполнение операции копирования по умолчанию. Если такая операция необходима, вы должны явно определить ее в своем классе. Если вы не определите в своем классе оператор присваивания, но попытаетесь им воспользоваться, компилятор выдаст сообщение об ошибке.
virtual void AssertValid() const;
Если вы наследуете класс от базового класса CObject и желаете использовать возможности метода AssertValid, вы должны переопределить его. Переопределенный метод AssertValid должен вызывать метод AssertValid базового класса, чтобы проверить целостность соответствующей части объекта. Затем необходимо выполнить проверку элементов порожденного класса. Для этого используйте макрокоманду ASSERT:
ASSERT(booleanExpression)
Макрокоманда ASSERT проверяет свой параметр booleanExpression. Если параметр макрокоманды имеет нулевое значение (FALSE), она отображает диагностическое сообщение и прерывает работу приложения. Если параметр booleanExpression не равен нулю (TRUE) работа приложения продолжается и макрокоманда не выполняет никаких действий.
Макрокоманда ASSERT выполняет проверку только в отладочной версии приложения. В окончательной версии приложения, построенной без отладочной информации, макрокоманда ASSERT не работает.
Если проверку параметра макрокоманды необходимо выполнять и в окончательной версии приложения, вы можете использовать вместо макрокоманды ASSERT макрокоманду VERIFY. Но при обнаружении ошибки работа приложения не будет прервана.
Вот пример переопределения метода AssertValid для класса CFigure, наследованного от базового класса CObject:
// Класс CFigure наследуется от базового класса CObject
class CFigure : public CObject {
// Переопределяем виртуальный метод базового класса
int m_area = 0;
// Остальные элементы класса…
}
// Переопределяем виртуальный метод AssertValid класса CObject
void CFigure::AssertValid() const {
// Сначала проверяем целостность элементов базового класса
CObject::AssertValid();
// Проверяем элемент m_area.
// Он должен быть больше или равен нулю
ASSERT(m_area >= 0);
}
virtual void Dump(CDumpContext& dc) const;
Метод Dump имеет единственный параметр dc, определяющий контекст отображения для вывода дампа объекта. Часто в качестве параметра dc используется предопределенный объект afxDump. Он позволяет передавать информацию в окно отладчика Visual C++. Объект afxDump определен только для отладочной версии приложения.
Вы можете переопределить метод Dump для своего класса. Переопределенный метод должен сначала вызывать метод Dump базового класса, а затем выводить значения элементов самого класса. Для вывода значений элементов объекта класса в контекст dc можно использовать операторы <<, переопределенные для класса CDumpContext.
Если класс определен с макрокомандами IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL, то метод Dump класса CObject будет отображать также имя самого класса.
Для класса CFigure, описанного выше, метод Dump можно определить следующим образом:
void CFigure::Dump(CDumpContext &dc) const {
// Вызываем метод Dump базового класса
CObject::Dump(dc);
// Выводим в контекст dc значение элемента m_area
// класса CFigure
dc << "Площадь = " << m_area;
}
BOOL IsSerializable() const;
virtual void Serialize(CArchive& ar);
throw(CMemoryException);
throw(CArchiveException);
throw(CFileException);
Ниже прототипа метода Serialize мы указали исключения, которые могут быть им вызваны. Более подробно об исключениях вы можете прочитать в разделе “Исключения – класс CException” данной главы.
virtual CRuntimeClass* GetRuntimeClass() const;
Для каждого класса, наследованного от CObject поддерживается своя структура CRuntimeClass. Если вы желаете использовать метод GetRuntimeClass в своем классе, наследованном от CObject, вы должны поместить в реализации класса макрокоманду IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL.
Структура CRuntimeClass содержит различную информацию о классе. Ниже перечислены несколько основные полей этой структуры.
Поле структуры CRuntimeClass | Описание |
---|---|
const char* m_pszClassName | Указатель на строку, закрытую двоичным нулем, в которой расположено имя класса |
int m_nObjectSize | Размер объектов класса |
WORD m_wSchema | Номер схемы (schema number) класса. Используется при автоматическом сохранении и восстановлении объектов класса в файле. Если объекты класса не могут быть сохранены и восстановлены (в объявлении класса отсутствует макрокоманда IMPLEMENT_SERIAL), m_wSchema содержит значение –1 |
void (*m_pfnConstruct) (void* p) | Указатель на конструктор класса, используемый по умолчанию. Этот конструктор не имеет параметров |
CRuntimeClass* m_pBaseClass | Указатель на структуру CRuntimeClass, содержащую аналогичную информацию о базовом классе |
CObject* CreateObject();
BOOL IsKindOf(const CRuntimeClass* pClass) const;
Метод возвращает ненулевое значение, если объект, для которого он вызван, принадлежит классу заданному параметром pClass или классу наследованному от него. В противном случае метод возвращает нулевое значение.
CPoint();
Вы можете создать объект класса CPoint и сразу присвоить ему значения. Если известны отдельные координаты точки, воспользуйтесь конструктором с двумя параметрами:
CPoint(int initX, int initY);
Первый параметр initX определяет х-координату точки, а параметр initY – y-координату точки. Если надо создать объект CPoint и записать в него координаты из переменной типа POINT или другого объекта класса CPoint, используйте другой конструктор:
CPoint(POINT initPt);
Можно создать объект CPoint и записать в него данные из объекта класса CSize или структуры SIZE:
CPoint(SIZE initSize);
Если у вас есть переменная типа DWORD, в младшем слове которой записана x-координата точки, а в старшем слове y-координата, то можно создать объект класса CPoint и записать в него данные из этой переменной:
CPoint(DWORD dwPoint);
Объекты класса CPoint можно сравнивать друг с другом, пользуясь обычными операторами равенства == (равно) и != (не равно). Результатом действия этих операторов является значение типа BOOL. Если условие определенное операторами равенства выполняется, возвращается ненулевое значение. В противном случае результат равен нулю.
CString();
Для класса CString определен конструктор копирования. Он позволяет создать строку на основе другой, ранее определенной строки. Этот и все остальные конструкторы могут вызвать исключение CMemoryException, если для создания строки недостаточно оперативной памяти.
CString(const CString& stringSrc) throw(CMemoryException);
Конструктор, представленный ниже, позволяет создать строку из nRepeat символов ch. Параметр nRepeat можно не указывать. В этом случае строка будет содержать единственный символ ch.
CString(TCHAR ch, int nRepeat = 1) throw(CMemoryException);
Вы можете создать новую строку и скопировать в нее nLength символов из массива lpch. Строка, расположенная в массиве lpch, может не иметь завершающего двоичного нуля. Для этого используйте следующий указатель:
CString(LPCTSTR lpch, int nLength) throw(CMemoryException);
Новую строку можно создать и скопировать в нее данные из массива символов, закрытого двоичным нулем. В этом случае количество символов, которые будут записаны в новую строку не указывается. Копируются все символы до двоичного нуля.
Для создания строки вы можете воспользоваться одним из трех конструкторов, представленных ниже. Как видите, конструкторы отличаются только типом параметра:
CString(const unsigned char* psz) throw(CMemoryException);
CString(LPCWSTR lpsz) throw(CMemoryException);
CString(LPCSTR lpsz) throw(CMemoryException);
template <class TYPE, class ARG_TYPE>
class CArray : public CObject
Параметр шаблона TYPE определяет тип элементов массива. В качестве TYPE можно указывать не только простые типы, например int, char, но также типы, являющиеся классами. Второй параметр шаблона ARG_TYPE определяет тип параметра массива, который используется для доступа к его элементам.
Мы будем использовать шаблон CArray для организации массива в приложении Single (см. раздел “Простейший графический редактор” главы “Однооконный интерфейс”).
template <class TYPE, class ARG_TYPE>
class CList : public CObject
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
class CMap : public CObject
Параметр шаблона KEY определяет тип ключевого поля словаря. Параметр ARG_KEY является типом данных, используемых в качестве аргумента KEY. Параметр VALUE соответствует типу элементов, хранящихся в словаре, а параметр ARG_VALUE используется в качестве аргумента VALUE.
CTime();
Объекты, созданные этим конструктором не инициализированы. Перед тем как ими пользоваться, запишите в них дату и время:
CTime(const CTime& timeSrc);
Параметр timeSrc определяет уже существующий объект класса CTime. Новый объект класса инициализируется значением даты и времени, взятым из существующего объекта.
Если в программе уже существует переменная типа time_t, в которой хранится значение, то можно создать новый объект класса CTime, основываясь на этой переменной:
CTime(time_t time);
Параметр time определяет переменную типа time_t, значение которой будет записано в создаваемый объект.
Если существует несколько переменных, в которых отдельно хранятся год, месяц, день, часы, минуты и секунды, то можно создать объект класса CTime, сразу же записав в него значения из этих переменных:
CTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = –1);
Первые 6 параметров метода определяют время и дату. Параметр nYear должен содержать год, nMonth – месяц, nDay – день, nHour – час, nMin – минуты, nSec – секунды. NYear может принимать значения от 1900 до 2038, nMonth – от 1 до 12 и nDay от 1 до 31.
Параметр nDST управляет режимом перехода на летнее время. Если nDST содержит нуль, то время представляется в стандартном виде, режим перехода на летнее время не используется. Чтобы включить этот режим, параметр nDST должен содержать значение, больше нуля. Параметр nDST является необязательным. Если его не указать, считается, что он равен нулю. В этом случае конструктор самостоятельно определяет режим перехода на летнее время.
Следующий конструктор позволяет создать объект класса CTime и записать в него дату и время, определенные в формате, принятом в операционной системе MS-DOS:
CTime(WORD wDosDate, WORD wDosTime, int nDST = –1);
Параметры wDosDate и wDosTime должны содержать, соответственно, дату и время в формате MS-DOS. Параметр nDST управляет режимом перехода на летнее время. Мы уже рассматривали его выше.
Кроме класса CTime, существует, как минимум, еще две структуры, в которых может храниться значения даты и времени. Первая такая структура называется SYSTEMTIME. В ней хранятся текущие значения даты и времени. Структура SYSTEMTIME определена следующим образом:
typedef struct _SYSTEMTIME {
WORD wYear; // год
WORD wMonth; // месяц
WORD wDayOfWeek; // день недели
WORD wDay; // календарная дата
WORD wHour; // часы
WORD wMinute; // минуты
WORD wSecond; // секунды
WORD wMilliseconds; // миллисекунды
} SYSTEMTIME;
Если необходимо создать объект класса CTime, сразу записав в него текущие значения времени и даты, используйте конструктор, представленный ниже:
CTime(const SYSTEMTIME& sysTime, int nDST = –1);
Параметр sysTime является указателем на структуру типа SYSTEMTIME. Необязательный параметр nDST управляет режимом отсчета даты и описан нами выше.
Вторая структура, в которой хранятся значения даты и времени, называется FILETIME. Она служит для хранения 64-битового числа, представляющего дату и время как количество 100 наносекундных интервалов времени, прошедших с первого января 1601 года.
typedef struct _FILETIME {
DWORD dwLowDateTime; // младшие 32 бита
DWORD dwHighDateTime; // старшие 32 бита
} FILETIME, *PFILETIME, *LPFILETIME;
Конструктор имеет следующий прототип:
CTime(const FILETIME& fileTime, int nDST = –1);
virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);
В качестве параметра lpszFileName надо указать имя открываемого файла. Можно указывать только имя файла или полное имя файла, включающее полный путь к нему.
Второй параметр nOpenFlags определяет действие, выполняемое методом Open с файлом, а также атрибуты файла. Ниже представлен список возможных значений параметра nOpenFlags:
Возможные значения nOpenFlags | Описание |
---|---|
CFile::modeCreate | Создается новый файл. Если указанный файл существует, то его содержимое стирается и длина устанавливается равной нулю |
CFile::modeNoTruncate | Этот флаг предназначен для использования совместно с флагом CFile::modeCreate. Если создается уже существующий файл, то его содержимое не будет удалено |
CFile::modeRead | Файл открывается только для чтения |
CFile::modeReadWrite | Файл открывается для чтения и записи |
CFile::modeWrite | Файл открывается только для записи |
CFile::modeNoInherit | Указывает, что файл не должен наследоваться порожденным процессом |
CFile::shareCompat | Открывает файл в режиме совместимости. Любой другой процесс может открыть этот файл несколько раз. Операция вызывает ошибку, если файл уже открыт другим процессом в любом другом режиме кроме режима совместимости |
CFile::shareDenyNone | Не запрещается доступ к открываемому файлу ни на чтение, ни на запись. Вызывает ошибку, если файл уже открыт в режиме совместимости любым другим процессом |
CFile::shareDenyRead | После того как файл открыт, другим процессам запрещается его чтение. Вызывает ошибку, если уже открыт в режиме совместимости или для чтения другим процессом |
CFile::shareDenyWrite | После того как файл открыт, другим процессам запрещается запись в него. Вызывает ошибку, если уже открыт в режиме совместимости или для записи другим процессом |
CFile::shareExclusive | После того как файл открыт, другим процессам запрещается запись и чтение из этого файла. Вызывает ошибку, если файл уже открыт для чтения или для записи любым процессом |
CFile::typeText | Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в текстовом режиме. Текстовый режим обеспечивает преобразование комбинации символа возврата каретки и символа перевода строки. |
CFile::typeBinary | Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в двоичном режиме |
virtual void Close() throw(CFileException);
Метод закрывает файл. Если вы создали объект класса CFile и открыли файл, а затем объект удаляется, то связанный с ним файл закрывается автоматически с помощью деструктора.
virtual UINT Read(void* lpBuf, UINT nCount) throw(CFileException);
Данные, прочитанные из файла, записываются в буфер lpBuf. Параметр nCount определяет количество байт, которое надо считать из файла. Фактически из файла может быть считано меньше байт, чем запрошено параметром nCount. Это происходит, если во время чтения достигнут конец файла. Метод Read возвращает количество байт, прочитанных из файла:
DWORD ReadHuge(void* lpBuffer, DWORD dwCount) throw(CFileException);
Назначение параметров метода ReadHuge аналогично назначению параметров метода Read. Спецификация ReadHuge считается устаревшей и оставлена только для обратной совместимости с 16-разряднымми операционными системами.
Для записи в файл предназначены методы Write и WriteHuge. Метод WriteHuge не накладывает ограничения на количество одновременно записываемых байт. В 16-разрядных версиях операционной системы Windows метод Write позволяет записать не больше 65535 байт. Это ограничение снято в операционных системах Windows NT и Windows 95.
virtual void Write(const void* lpBuf, UINT nCount) throw(CFileException);
Метод Write записывает в открытый файл nCount байт из буфера lpBuf. В случае возникновения ошибки записи, например, переполнения диска, метод Write вызывает исключение.
Не смотря на то, что спецификация WriteHuge считается устаревшей, вы можете использовать ее для создания 16-разрядных приложений в среде Visual C++ версии 1.5х.
virtual void Flush() throw(CFileException);
static void PASCAL Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName) throw(CFileException);
Метод Rename изменяет имя файла, определенного параметром lpszOldName на lpszNewName. Метод нельзя использовать для переименования каталогов. В случае возникновения ошибки метод вызывает исключение.
Для удаления файлов предназначена команда DEL операционной системы MS-DOS. Класс CFile включает статический метод Remove, позволяющий удалить указанный файл:
static void PASCAL Remove(LPCTSTR lpszFileName) throw(CFileException);
Параметр lpszFileName должен содержать путь удаляемого файла. Метод Remove не позволяет удалять каталоги. Если удалить файл невозможно, например, из-за неправильно указанного имени файла, то метод вызывает исключение.
Чтобы определить дату и время создания файла, его длину и атрибуты, предназначен статический метод GetStatus. Существует две разновидности метода – первый определен как виртуальный, а второй – как статический методы.
Виртуальная версия метода GetStatus определяет состояние открытого файла, связанного с данным объектом класса CFile. Вызывайте этот метод только тогда, когда объект класса CFile создан и файл открыт:
BOOL GetStatus(CFileStatus& rStatus) const;
Статическая версия метода GetStatus позволяет определить характеристики файла, не связанного с объектом класса CFile. Чтобы воспользоваться этим методом, не обязательно предварительно открывать файл.
static BOOL PASCAL GetStatus(LPCTSTR lpszFileName, CFileStatus& rStatus);
Параметр lpszFileName должен содержать путь к файлу. Путь может быть полным или не полным – относительно текущего каталога.
Параметр rStatus должен содержать указатель на структуру типа CFileStatus, в которую заносится информация о файле.
Структура типа CFileStatus имеет элементы, описанные в следующей таблице:
Поле структуры CFileStatus | Описание |
---|---|
CTime m_ctime | Дата и время создания файла. Описание класса CTime представлено нами в главе “Дата и время” |
CTime m_mtime | Дата и время последней модификации файла |
CTime m_atime | Дата и время, когда последний раз выполнялось чтение из файла |
LONG m_size | Размер файла в байтах |
BYTE m_attribute | Атрибуты файла |
char m_szFullName[_MAX_PATH] | Полное имя файла в стандарте операционной системы Windows. Виртуальная версия метода не заполняет это поле |
enum Attribute {
normal = 0x00,
readOnly = 0x01,
hidden = 0x02,
system = 0x04,
volume = 0x08,
directory = 0x10,
archive = 0x20
};
Атрибут | Описание |
---|---|
normal | Нормальный файл |
readOnly | Файл, который можно открыть только для чтения |
hidden | Скрытый файл |
system | Системный файл |
volume | Метка тома |
directory | Каталог |
archive | Архивный |
virtual void LockRange(DWORD dwPos, DWORD dwCount) throw(CFileException);
Параметр dwPos указывает на начало фрагмента данных внутри файла, который надо заблокировать. Параметр dwCount определяет количество байт для блокировки. В одном файле можно установить несколько блокировок на различные, не перекрывающиеся фрагменты данных.
Чтобы снять установленные блокировки, надо воспользоваться методом UnlockRange. Если в одном файле установлено несколько блокировок, то каждая из них должна сниматься отдельным вызовом метода UnlockRange:
virtual void UnlockRange(DWORD dwPos, DWORD dwCount) throw(CFileException);
Как и для метода LockRange, параметры dwPos и dwCount должны указывать начало фрагмента и его размер. Размер фрагмента должен соответствовать размеру фрагмента, указанному при вызове метода LockRange.
virtual LONG Seek(LONG lOff, UINT nFrom) throw(CFileException);
Параметр lOff определяет число байт, на которое надо переместить указатель текущей позиции файла. Параметр lOff может быть положительной или отрицательной величиной. Если lOff больше нуля, то указатель смещается в сторону конца файла, если lOff меньше нуля, то указатель смещается в начало файла.
Параметр nFrom определяет, относительно чего задано это смещение. В качестве nFrom можно указать три различные константы, также определенные в классе CFile:
Константа | Параметр lOff задает смещение относительно |
---|---|
CFile::begin | Начала файла |
CFile::current | Текущей позиции файла |
CFile::end | Конца файла |
void SeekToBegin() throw(CFileException);
Фактически вызов метода SeekToBegin эквивалентен вызову метода Seek с параметром lOff, содержащим нуль и параметром nFrom, содержащим константу CFile::begin.
Метод SeekToEnd имеет почти такой же прототип как метод SeekToBegin, но перемещает указатель в конец файла:
DWORD SeekToEnd() throw(CFileException);
Метод SeekToEnd возвращает длину файла в байтах. Если вам надо определить длину открытого файла, совсем не обязательно перемещать его указатель. Можно воспользоваться методом GetLength. Этот метод также возвращает длину открытого файла в байтах:
virtual DWORD GetLength() const throw(CFileException);
Метод SetLength позволяет изменить длину открытого файла:
virtual void SetLength(DWORD dwNewLen) throw(CFileException);
Параметр dwNewLen задает новую длину файла в байтах. Метод SetLength может увеличить или уменьшить размер файла. Если новая длина файла меньше чем его текущая длина, то последние байты файла теряются. Если вы увеличиваете размер файла, то значение последних байт неопределенно.
Вы можете определить текущую позицию указателя файла с помощью метода GetPosition. Возвращаемое методом GetPosition 32-разрядное значение определяет смещение указателя от начала файла:
virtual DWORD GetPosition() const throw(CFileException);
virtual CString GetFilePath() const;
Если требуется определить только имя и расширение открытого файла, можно воспользоваться методом GetFileName. Он возвращает объект класса CString, в котором находится имя файла:
virtual CString GetFileName() const;
В случае, когда надо узнать только имя открытого файла без расширения, воспользуйтесь методом GetFileTitle:
virtual CString GetFileTitle() const;
Последний метод класса CFile позволяет установить путь файла. Этот метод не создает, не копирует и не изменяет имени файла, он только заполняет соответствующий элемент данных в объекте класса CFile:
virtual void SetFilePath(LPCTSTR lpszNewName);
Единственный параметр метода lpszNewName должен содержать указатель на строку символов, содержащую путь файла. Эта строка должна оканчиваться двоичным нулем.
CMemFile(UINT nGrowBytes = 1024);
Этот конструктор создает в оперативной памяти пустой файл. После создания файл автоматически открывается. Вы не должны специально вызывать метод Open.
Когда вы начинаете запись в такой файл, автоматически выделяется блок памяти. Для получения памяти методы класса CMemFile вызывают стандартные функции malloc, realloc и free. Если выделенного блока памяти недостаточно, его размер увеличивается. Увеличение блока памяти файла происходит частями по nGrowBytes байт. После удаления объекта класса CMemFile используемая им память автоматически возвращается системе.
Второй конструктор класса CMemFile имеет более сложный прототип. Этот конструктор используется в тех случаях, когда вы сами выделяете память для файла:
CMemFile(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes = 0);
Параметр lpBuffer указывает на буфер, который будет использоваться для файла. Размер буфера определяется параметром nBufferSize.
Необязательный параметр nGrowBytes используется более комплексно, чем в первом конструкторе класса. Если nGrowBytes содержит нуль, то созданный файл будет содержать данные из буфера lpBuffer. Длина такого файла будет равна nBufferSize.
Класс CMemFile позволяет получить указатель на область памяти, используемую файлом. Через этот указатель можно непосредственно работать с содержимым файла, не ограничивая себя методами класса CFile.
Для получения указателя на буфер файла вы можете воспользоваться методом Detach:
BYTE * Detach();
Перед эти полезно определить длину файла и соответственно, размер буфера памяти, вызвав метод GetLength.
Метод Detach закрывает данный файл и возвращает указатель на используемый им блок памяти. Если вам требуется опять открыть файл и связать с ним блок оперативной памяти, вызовите метод Attach:
void Attach(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes = 0);
Параметры метода Attach соответствуют параметрам второго конструктора класса CMemFile, рассмотренному выше. Параметр lpBuffer указывает на буфер размера nBufferSize, который будет связан с файлом.
Если необязательный параметр nGrowBytes равен нулю, то созданный файл будет содержать данные из буфера lpBuffer. Если nGrowBytes больше нуля, то содержимое буфера lpBuffer игнорируется. Если вы запишете в такой файл больше данных, чем помещается в отведенном вами буфере, его размер автоматически увеличивается на nGrowBytes байт. Для управления буфером файла класс CMemFile вызывает стандартные функции malloc, calloc и free. Поэтому, чтобы не нарушать механизм управления памяти, буфер lpBuffer должен быть создан функциями malloc или calloc.
BYTE * Alloc(DWORD nBytes);
Когда размер файла изменяется, может возникнуть необходимость изменения размера блока памяти, используемого файлом. Для этого методы класса CMemFile могут вызывать метод Realloc:
BYTE * Realloc(BYTE* lpMem, DWORD nBytes);
В качестве параметра методу Realloc передается указатель lpMem на блок памяти и число nBytes, определяющее новый размер блока памяти файла. Метод Realloc возвращает указатель на новый блок памяти. Его адрес может измениться. Если операционная система не может изменить размер блока памяти, метод Realloc возвращает значение NULL.
После использования блока памяти, его необходимо освободить и вернуть операционной системе. Для этого предназначен метод Free:
void Free(BYTE * lpMem);
В качестве параметра lpMem задается адрес блока памяти файла, который надо освободить.
Виртуальные методы класса CFile Read и Write, переназначенные в классе CMemFile, вызывают метод Memcpy. Метод Memcpy предназначен для обмена данными. Вы можете переопределить этот метод в своем классе:
BYTE * Memcpy(BYTE* lpMemTarget, BYTE* lpMemSource, UINT nBytes);
Переменная lpMemSource указывает на область памяти размера nBytes байт, которая должна быть записанная по адресу lpMemTarget. Метод Memcpy возвращает значение соответствующее параметру lpMemTarget.
Если происходит изменение длины файла, вызывается метод GrowFile. В качестве параметра dwNewLen указывается новый размер файла. Вы можете переназначить этот метод в своем классе:
void GrowFile(DWORD dwNewLen);
CStdioFile();
Этот конструктор только создает объект класса, но не открывает никаких файлов. Чтобы открыть файл, надо вызвать метод Open базового класса CFile.
Второй конструктор класса CStdioFile можно вызывать, если файл уже открыт и вам надо создать новый объект класса и связать с ним открытый файл:
CStdioFile(FILE* pOpenStream);
Этот конструктор можно использовать, если файл был открыт стандартной функцией fopen.
Параметр pOpenStream должен содержать указатель на файл, полученный вызовом стандартной функции fopen.
Третий, последний конструктор можно использовать, если надо создать объект класса CStdioFile, открыть новый файл и связать его с только что созданным объектом:
CStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags) throw(CFileException);
Если указанный файл не может быть открыт, вызывается исключение CFileException.
Параметр lpszFileName должен содержать указатель на строку с именем файла. Можно указать полный путь файла, а не только его имя. Параметр nOpenFlags определяет режим, в котором будет открыт файл. Возможные значения этого параметра были описаны ранее (см. Метод Open класса CFile).
Для чтения и записи в текстовый файл класс CStdioFile включает два новых метода ReadString и WriteString. Метод ReadString позволяет прочитать из файла строку символов, а метод WriteString – записать.
Метод ReadString имеет две формы. Первая используется для чтения строк из файла в буфер памяти, а вторая для чтения строк и записи их в объект класса CString.
Вот описание первой формы метода ReadString:
virtual LPTSTR ReadString(LPTSTR lpsz, UINT nMax) throw(CFileException);
Из открытого файла считывается текстовая строка и записывается в буфер lpsz. Считается, что строка файла оканчивается символами перевода строки и возврата каретки. В конец строки, записанной в буфер lpsz, заносится символ двоичного нуля (\0).
Максимальное количество считываемых символов определяется параметром nMax. Если в строке файла больше, чем nMax – 1 байт, то остальные символы не будут прочитаны. Метод ReadString возвращает указатель на прочитанную строку или NULL, если достигнут конец файла.
Вторая форма метода ReadString не намного отличается от первой. Вместо двух параметров lpsz и nMax указывается один параметр rString, указывающий на объект класса CString, в который будет записана строка, прочитанная из файла:
BOOL ReadString(CString& rString) throw(CFileException);
Еще одно отличие второй формы метода ReadString заключается в типе возвращаемого им значения. Если достигнут конец файла, то вторая форма метода ReadString возвращает константу FALSE.
Для записи в файл текстовой строки предназначен метод WriteString:
virtual void WriteString(LPCTSTR lpsz) throw(CFileException);
В качестве параметра lpsz этого метода надо указать адрес буфера с текстовой строкой, закрытой символом \0. Символ \0 не записывается в файл. Если в текстовой строке lpsz есть символы перевода строки, они записываются как пара символов возврата каретки и перевода строки.
Метод WriteString может вызвать исключение, если во время записи в файл произойдет ошибка, например переполнение диска.
BOOL GetVolumeInformation(LPCTSTR lpRootPathName, LPTSTR lpVolumeNameBuffer, DWORD nVolumeNameSize, LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags, LPTSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize);
Первый параметр lpRootPathName перед вызовом функции должен указывать на строку, содержащую путь корневого каталога тома, информацию о котором надо получить. Если вы присвоите lpRootPathName значение NULL, по умолчанию будет выбран корневой каталог текущего тома.
Параметр lpVolumeNameBuffer должен содержать указатель на буфер, в который будет записано имя тома. Длина буфера определяется параметром nVolumeNameSize. Если вас не интересует имя тома, вы можете указать для параметра lpVolumeNameBuffer значение NULL. В этом случае значение nVolumeNameSize не используется.
Параметр lpVolumeSerialNumber должен содержать адрес переменной типа DWORD, в которую будет записан серийный номер тома. Если вас не интересует серийный номер, вы можете указать для параметра значение NULL.
Параметр lpMaximumComponentLength должен содержать адрес переменной, в которую будет записана максимальная длина файла, для данной файловой системы. Для операционной системы Windows 95 эта переменная будет содержать значение 255, несмотря на то, что тип файловой системы остался FAT.
Параметр lpFileSystemFlags указывает на переменную, которая может быть одной или комбинацией нескольких констант, перечисленных в следующей таблице.
Константа | Описание |
---|---|
FS_CASE_IS_PRESERVED | При записи файлов на диск сохраняется написание (заглавные и прописные символы) имени файла |
FS_CASE_SENSITIVE | Файловая система различает заглавные и прописные символы в именах файлов |
FS_UNICODE_STORED_ON_DISK | Файловая система обеспечивает кодировку имен файлов Unicode |
FS_FILE_COMPRESSION | Обеспечивается возможность сжатия отдельных файлов, расположенных на томе. Эта константа не может быть использована вместе с другими |
FS_VOL_IS_COMPRESSED | Данный том является компрессованным. Такой том может быть создан системами типа DoubleSpace. Эта константа не может быть использована вместе с другими |
// Включаемый файл для MFC
#include <afxwin.h>
//=====================================================
// Главный класс приложения CMFStartApp
//=====================================================
class CMFStartApp : public CWinApp {
public:
virtual BOOL InitInstance();
void FileSystemInfo();
};
// Создаем объект приложение класса CMFStartApp
CMFStartApp MFStartApp;
//=====================================================
// Переопределяем виртуальный метод InitInstance
// класса CWinApp
//=====================================================
BOOL CMFStartApp::InitInstance() {
// Определяем характеристики файловой системы
FileSystemInfo();
// Завершаем приложение
return FALSE;
}
//=====================================================
// Метод FileSystemInfo главного класса приложения
// Определяет характеристики текущего тома и записывает
// их в файл
//=====================================================
void CMFStartApp::FileSystemInfo() {
// Метка тома
CString VolumeNameBuffer;
// Максимальная длина метки тома
DWORD nVolumeNameSize = 100;
// Серийный номер тома
DWORD VolumeSerialNumber;
// Максимальная длина имени файла
DWORD MaximumComponentLength;
// Характеристики файловой системы
DWORD FileSystemFlags;
// Тип файловой системы
CString FileSystemNameBuffer;
// Максимальная длина строки типа файловой системы
DWORD nFileSystemNameSize = 100;
// Получаем данные о файловой системе и текущем устройстве
GetVolumeInformation(NULL, VolumeNameBuffer.GetBuffer(nVolumeNameSize), nVolumeNameSize, &VolumeSerialNumber, &MaximumComponentLength, &FileSystemFlags, FileSystemNameBuffer.GetBuffer(nFileSystemNameSize), nFileSystemNameSize);
// Снимаем блокировку буферов
VolumeNameBuffer.ReleaseBuffer();
FileSystemNameBuffer.ReleaseBuffer();
// Обрабатываем ошибочные ситуации, которые могут
// возникнуть при работе с файлами
try {
// Создаем файл fsystem.dat и открываем его для записи
CStdioFile file("fsystem.dat", CFile::modeCreate | CFile::modeWrite | CFile::typeText);
// Временная строка, используемая для записи в файл
CString strTmpOut;
// Увеличиваем размер буфера до 512 байт
strTmpOut.GetBuffer(512);
// Записываем в файл метку тома
strTmpOut.Format("Метка тома: %s \n", VolumeNameBuffer);
file.WriteString(strTmpOut);
// Записываем в файл серийный номер
strTmpOut.Format("Серийный номер: %X \n", VolumeSerialNumber);
file.WriteString(strTmpOut);
// Записываем в файл тип файловой системы
strTmpOut.Format("Тип файловой системы: %s \n", FileSystemNameBuffer);
file.WriteString(strTmpOut);
// Записываем в файл максимальную длину имени файла
strTmpOut.Format("Максимальная длина имени файла: %d \n", MaximumComponentLength);
file.WriteString(strTmpOut);
// Записываем в файл свойства файловой системы
strTmpOut = "Свойства файловой системы \n";
if (FileSystemFlags & FS_CASE_IS_PRESERVED) strTmpOut += " FS_CASE_IS_PRESERVED\n";
if (FileSystemFlags & FS_CASE_SENSITIVE) strTmpOut += " FS_CASE_SENSITIVE\n";
if (FileSystemFlags & FS_UNICODE_STORED_ON_DISK) strTmpOut += " FS_UNICODE_STORED_ON_DISK\n";
if (FileSystemFlags & FS_PERSISTENT_ACLS) strTmpOut += " FS_PERSISTENT_ACLS\n";
if (FileSystemFlags & FS_FILE_COMPRESSION) strTmpOut += " FS_FILE_COMPRESSION\n";
if (FileSystemFlags & FS_VOL_IS_COMPRESSED) strTmpOut += " FS_VOL_IS_COMPRESSED\n";
file.WriteString(strTmpOut);
// Закрываем файл
file.Close();
// Отображаем сообщение об успешном завершении приложения
MessageBox(NULL, "File fsystem.dat", "Message", MB_OK);
}
// Обработчик исключения. Вызывается при ошибках
// работы с файлами
catch(...) {
// Отображаем сообщение о возникшей ошибке
MessageBox(NULL, "File I/O Error", "Error", MB_OK);
}
return;
}
Файл fsystem.dat, созданный приложением, можно просмотреть в любом текстовом редакторе, например Notepad или WordPad. В листинге 3.2 приведен пример файла, полученного при помощи приложения TestFile на нашем компьютере, на котором установлена операционная система Windows 95.
Метка тома: LIBRARY
Серийный номер: 1D794E8D
Тип файловой системы: FAT
Максимальная длина имени файла: 255
Свойства файловой системы
FS_CASE_IS_PRESERVED
FS_UNICODE_STORED_ON_DISK
CMemoryException ←|←CException
CFileException ←|
CArchiveException ←|
CNotSupportedException←|
CResourceException ←|
CDaoException ←|
CDBException ←|
COleException ←|
COleDispatchException ←|
CUserException ←|
Как правило, эти исключения вызываются методами классов MFC, когда возникают какие-либо ошибочные ситуации. Так, например, если вы попытаетесь открыть несуществующий файл, воспользовавшись для этого методом Open из класса CFile, то будет вызвано исключение CFileException.
Если вы желаете обрабатывать исключения, которые вызываются методами классов MFC, вы должны определить обработчики для этих исключений. Каждый такой обработчик должен представлять собой блок catch, в качестве аргумента которого надо использовать указатель на объект класса CException или указатель на объект класса, наследованного от класса CException:
try {
// Здесь может находится код, который вызывает исключение
}
// Обработчик для исключения типа CMemoryException
catch(CMemoryException* ptrException) {
// Обработка исключения …
// В конце удаляем объект исключения
ptrException–>Delete;
}
Еще раз подчеркнем, что обработчик исключений MFC должен принимать указатель на объект класса CException (или класса, наследованного от CException). Этот объект создается при возникновении исключительных ситуаций внутри методов MFC. После того как этот объект окажется не нужен, ваш обработчик должен его удалить. Для этого предназначен метод Delete, определенный в классе CException. Не используйте для удаления объектов класса CException и объектов классов, наследованных от него, обыкновенный оператор delete.
Обработчик исключения может выполнять различные действия в зависимости от того какое исключение и в каком контексте было вызвано. Для этого вы можете использовать методы и данные из объекта, переданного в обработчик исключения.
Методы классов MFC могут вызывать различные исключения. В следующей таблице кратко перечислены причины, по которым вызываются исключения разных типов:
Класс | Исключение вызывается |
---|---|
CMemoryException | При распределении оперативной памяти |
CFileException | При работе с файлами |
CArchiveException | Во время записи или восстановления объектов (Archive/Serialization) |
CNotSupportedException | При обращении к неизвестному методу, который не поддерживается данным классом |
CResourceException | Ошибка при работе с ресурсами Windows |
CDaoException | Ошибка при работе с базами данных, через средства DAO |
CDBException | Ошибка при работе с базами данных, через средства ODBC |
COleException | Ошибка при работе OLE |
COleDispatchException | Ошибка при работе OLE |
CUserException | При обработке этого исключения на экране отображается сообщение, а затем вызывается исключение CException |
virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError, PUINT pnHelpContext = NULL);
Когда вы вызываете в обработчике исключения метод GetErrorMessage, он записывает в буфер lpszError сообщение об ошибке, вызвовшей исключение. Размер буфера надо указать в параметре nMaxError. В конце сообщения всегда записывается символ двоичного нуля. Если сообщение не помещается в буфер lpszError (оно больше чем nMaxError – 1 байт), тогда в буфер записываются только nMaxError – 1 символов сообщения. В последний байт записывается двоичный нуль.
Необязательный параметр pnHelpContext может содержать указатель на переменную типа UINT, в которую будет записан идентификатор контекстной подсказки (help context ID).
Метод GetErrorMessage возвращает ненулевое значение, если сообщение об ошибке доступно, и нуль в противном случае.
Вы можете вызывать метод ReportError из обработчика исключений:
virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);
Метод ReportError отображает в диалоговой панели на экране сообщение об ошибке, вызвавшей данное исключение. Параметр nType определяет внешний вид диалоговой панели сообщения. В качестве параметра nType можно указать любую комбинацию стилей панелей сообщения, таких как MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_ICONEXCLAMATION, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONSTOP. Если вы не укажите этот параметр, тогда подразумевается стиль MB_OK, то есть панель сообщения с одной кнопкой OK.
Иногда исключение может не иметь текстового описания. Вы можете указать методу ReportError, чтобы он отображал в этом случае определенное сообщение. Текст этого сообщения надо сохранить в строковом ресурсе, а соответствующий идентификатор передать методу ReportError в качестве второго параметра nMessageID. Если вы не укажите этот параметр, тогда отображается сообщение “No error message is available”.
Метод ReportError возвращает значение типа AfxMessageBox. Оно определяет, какая кнопка была нажата в диалоговой панели с сообщением.
Методы GetErrorMessage и ReportError используют данные из ресурсов, созданных AppWizard. Поэтому они могут работать неправильно, если приложение создано без использования AppWizard.
void AfxThrowMemoryException();
Эта функция не пытается получить у операционной системы дополнительную память, а использует память полученную ранее. В противном случае, возможно, вы не смогли бы создать даже объекта CMemoryException, так как свободная память уже кончилась.
Приведем небольшой пример использования этой функции. Допустим, вы пытаетесь получить область оперативной памяти для хранения данных с помощью функции GlobalAlloc. Если операционная система не может выделить область памяти такого размера, она возвращает NULL и вы можете вызвать функцию AfxThrowMemoryException:
if (GlobalAlloc(GMEM_FIXED, 1000000) == NULL) AfxThrowMemoryException();
Константа | Причина ошибки |
---|---|
CFileException::none | Без ошибки |
CFileException::generic | Неопределенная ошибка |
CFileException::fileNotFound | Файл не найден |
CFileException::badPath | Задан несуществующий путь |
CFileException::tooManyOpenFiles | Открыто слишком много файлов |
CFileException::accessDenied | Доступ к файлу закрыт |
CFileException::invalidFile | Использование неправильного идентификатора (дескриптора) файла |
CFileException::removeCurrentDir | Попытка удалить текущий каталог |
CFileException::directoryFull | Переполнение структуры каталогов. Невозможно создать новый каталог |
CFileException::badSeek | Ошибка во время перемещения указателя файлов |
CFileException::hardIO | Ошибка аппаратного обеспечения компьютера |
CFileException::sharingViolation | Программа SHARE.EXE не загружена или общая область заблокирована (locked) |
CFileException::lockViolation | Попытка заблокировать область файла, которая уже была заблокирована ранее |
CFileException::diskFull | Нет свободного пространства на диске |
CFileException::endOfFile | Достигнут конец файла |
#include "stdafx.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
try {
CFile file("This file is absent", CFile::modeRead);
// Здесь могут быть операторы, вызывающие другие
// исключения
}
// Обработчик для исключения типа CMemoryException
catch(CMemoryException* ptrException) {
MessageBox(NULL,"Memory Exception", "Exception", MB_OK | MB_ICONSTOP);
ptrException–>Delete();
}
// Обработчик для исключения типа CFileException
catch(CFileException* ptrException) {
if (ptrException–>m_cause == CFileException::fileNotFound) MessageBox(NULL,"File Not Found", "Exception", MB_OK | MB_ICONSTOP);
else if (ptrException–>m_cause == CFileException::diskFull) MessageBox(NULL,"The disk is full", "Exception", MB_OK | MB_ICONSTOP);
else MessageBox(NULL, "File Exception", "Exception", MB_OK | MB_ICONSTOP);
ptrException–>Delete();
}
// Обработчик для исключений класса CException и
// классов наследованных от него
catch (CException* ptrException) {
MessageBox(NULL,"Exception", "Exception", MB_OK | MB_ICONSTOP);
ptrException–>Delete();
}
// Все остальные исключения обрабатываются здесь
catch(...) {
MessageBox(NULL,"Another Exception", "Exception", MB_OK | MB_ICONSTOP);
}
return 0;
}
В блоке try мы пытаемся открыть для чтения файл с именем This file is absent. Длинные имена файлов, содержащие символы пробелов, разрешены в операционных системах Windows 95 и Windows NT. Если файла This file is absent нет на диске, тогда создается объект класса CFileException и вызывается исключение.
Обработчик исключений, связанных с ошибками при работе с файловой системой, проверяет, вызвано ли оно тем, что приложение пытается открыть несуществующий файл. Если это так, на экране отображается сообщение File Not Found.
После обработки исключения, управление передается первому оператору за последним блоком catch. В нашем примере это оператор return. Он завершает работу приложения.
Вы можете сами создать объект класса CFileException и вызвать исключение. Для этого рекомендуется использовать функцию AfxThrowFileException:
void AfxThrowFileException(int cause, LONG lOsError = –1);
Параметр cause должен определять причину исключения. В качестве этого параметра можно задавать возможные значения для элемента данных m_cause из класса CFileException (см. таблицу выше). Необязательный параметр lOsError может содержать код ошибки, определенной операционной системой.
Константа | Причина ошибки |
---|---|
CArchiveException::none | Без ошибки |
CArchiveException::generic | Неопределенная ошибка |
CArchiveException::readOnly | Попытка записи в архивный объект, открытый для чтения |
CArchiveException::endOfFile | Обнаружен конец файла при чтении объекта |
CArchiveException::writeOnly | Попытка читать из архивного объекта, открытого для записи |
CArchiveException::badIndex | Неправильный формат файла |
CArchiveException::badClass | Попытка прочитать объект в объект неправильного типа |
CArchiveException::badSchema | Попытка чтения объекта с несоответствующей версией класса |
void AfxThrowArchiveException(int cause);
Параметр cause должен определять причину вызова исключения. Возможный список значений этого параметра представлен в таблице выше (см. элемент данных m_cause класса CArchiveException).
CNotSupportedException();
Однако если вы сами желаете вызвать из своего кода исключение этого типа, то вместо того, чтобы создавать объект класса CNotSupportedException вручную и передавать его оператору throw, воспользуйтесь функцией AfxThrowNotSupportedException:
void AfxThrowNotSupportedException();
void AfxThrowResourceException();
void AfxThrowUserException();
DECLARE_SERIAL(class_name)
Макрокоманду IMPLEMENT_SERIAL надо указать перед определением класса в файле исходного текста приложения, имеющего расширение CPP. Прототип макрокоманды IMPLEMENT_SERIAL представлен ниже:
IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
Параметр class_name определяет имя вашего класса, base_class_name – имя базового класса из которого непосредственно наследуется ваш класс. Последний параметр wSchema – это число типа UINT, определяющее версию программы. Если вы разработаете новую версию своего приложения и измените набор данных, которые необходимо записать в файл, измените параметр wSchema.
В классе должны быть определены специальные методы для записи и восстановления состояния объектов этого класса. Обычно эти методы сохраняют и восстанавливают элементы данных из класса. Таким образом, объекты класса сами отвечают за то, как они сохраняют и восстанавливают свое состояние.
Методы, сохраняющие и восстанавливающие состояние класса, взаимодействуют с объектом класса CArchive, который осуществляет непосредственную запись и чтение информации из файла на диске.
Класс CObject содержит виртуальный метод Serialize , отвечающий за запись и чтение объектов классов, наследованных от класса CObject:
virtual void Serialize(CArchive& ar) throw(CMemoryException) throw(CArchiveException) throw(CFileException);
В качестве параметра ar, методу передается указатель на объект класса CArchive , используемый для записи и восстановления его состояния из файла. Чтобы узнать, какую операцию должен выполнить метод Serialize, воспользуйтесь методами CArchive::IsLoading или CArchive::IsStoring.
Новая реализация метода Serialize должна первым делом вызвать метод Serialize базового класса. Это гарантирует, что при сохранении и восстановлении объекта будут обработаны все элементы всех базовых классов.
Метод Serialize вызывается объектами класса CArchive когда приложение читает или записывает этот объект, вызывая методы CArchive::ReadObject или CArchive::WriteObject. Сразу отметим, что с методами CArchive::ReadObject и CArchive::WriteObject непосредственно связаны операторы записи << и чтения >>.
Перед тем как создать объект класса CArchive, необходимо создать объект класса CFile. Связывая с объектом CFile файл на диске, имейте в виду, что если вы желаете записать объект файл, то файл надо открыть на запись, а если надо считать файл с диска и загрузить из него данные в объект, открыть файл надо для чтения.
Конструктор класса CArchive имеет следующий вид:
CArchive(CFile* pFile, UINT nMode, int nBufSize = 512, void* lpBuf = NULL) throw(CMemoryException, CArchiveException, CFileException);
Параметр pFile должен содержать указатель на объект класса CFile, из которого будут считываются или записываться данные. Перед вызовом конструктора файл, связанные с объектом pFile должен быть уже открыт.
Параметр nMode определяет, будут данные записываться в файл или считываться из него. Параметр nMode может принимать одно из трех значений CArchive::load, CArchive::store или CArchive::bNoFlushOnDelete, описанных в следующей таблице.
Константа | Описание |
---|---|
CArchive::load | Данные считываются из файла на диске. Впоследствии они будут записаны в восстанавливаемый объект. В этом случае файл, определенный параметром pFile, должен быть открыт для чтения. |
CArchive::store | Данные будут записываются в файл на диске. Файл, определенный параметром pFile должен быть открыт для записи. |
CArchive::bNoFlushOnDelete | Когда вызывается деструктор класса CArchive, он автоматически вызывает метод Flush для файла pFile. Если вы укажите этот флаг, то метод Flush вызван не будет. Чтобы предотвратить потерю данных, вы должны будете перед вызовом деструктора закрыть данный файл. |
BOOL IsStoring() const;
Метод IsLoading является полной противоположностью метода IsStoring. Он возвращает ненулевое значение, если данный объект предназначен для чтения из файла и нуль в противном случае. Вы можете использовать любой метод IsLoading или IsStoring.
Основное предназначение объекта класса CArchive заключается в том, что объекты вашего класса могут посредством него записать свое состояние в файл, а затем при необходимости восстановить его. Для этого в классе CArchive определены операторы записи в файл << и чтения из файла >>. Вы также можете использовать методы WriteString, Write, ReadString и Read. Опишем эти операторы и методы более подробно.
void Write(const void* lpBuf, UINT nMax) throw(CFileException);
Он позволяет записать в файл определенное количество байт из указанного буфера памяти.
Параметр lpBuf является указателем на сохраняемую область данных, а параметр nMax определяет количество байт из этой области, которое надо записать в файл.
Если требуется сохранить строку символов, закрытую нулем, то гораздо удобнее вместо метода Write использовать метод WriteString. Метод WriteString записывает в архивный файл строку lpsz:
void WriteString(LPCTSTR lpsz) throw(CFileException);
UINT Read(void* lpBuf, UINT nMax) throw(CFileException);
Параметр lpBuf определяет буфер памяти, в который будут записаны считанные из файла данные. Параметр nMax указывает максимальное количество байт, которое можно считать из архивного файла и записать в буфер lpBuf. Метод Read возвращает количество прочитанных байт.
Если требуется прочитать из архивного файла строку, записанную в него методом WriteString, воспользуйтесь методом ReadString. В состав класса CArchive входят два метода ReadString, которые предназначены для записи прочитанной из файла строки в объект класса CString или в строку Си.
Первый метод имеет более простой прототип. Единственный параметр rString определяет объект класса CString, в который будет записана полученная информация. Если метод ReadString успешно завершит чтение, он возвращает значение TRUE, в противном случае – значение FALSE:
BOOL ReadString(CString& rString);
Если вам надо записать прочитанную из архивного файла строку в массив символов, воспользуйтесь другим прототипом метода ReadString:
LPTSTR ReadString(LPTSTR lpsz, UINT nMax) throw(CArchiveException);
Параметр lpsz должен указывать на буфер памяти, в который будет записана информация. Параметр nMax определяет максимальное количество символов, которое метод может прочитать из файла. Размер буфера lpsz должен быть как минимум на единицу больше, чем значение nMax (в конец строки lpsz будет записан символ \0).
Когда метод Serialize завершит работу по восстановлению или записи объекта из архивного файла, вы должны закрыть используемый для этого объект класса CArchive. Для этого необходимо вызвать метод Close:
void Close() throw(CArchiveException, CFileException);
После вызова этого метода закройте файл, связанный с объектом CArchive, вызвав метод CFile::Close, и удалите сам объект класса CFile.
Приложение Pview 95 позволяет просмотреть список процессов, загруженных в памяти и остановить любой процесс.
Тип приложения | Описание |
---|---|
MFC AppWizard (exe) | Приложение, создаваемое с использованием библиотеки классов MFC. С помощью AppWizard вы можете автоматически создать основные классы необходимые для приложения |
MFC AppWizard (dll) | Библиотека динамической компоновки – DLL, создаваемая с помощью библиотеки классов MFC. AppWizard позволяет автоматически создать все основные файлы, необходимые для DLL |
OLE ControlWizard | Органы управления OLE, созданные с использованием библиотеки классов MFC. Компилятор автоматически создает базовый набор файлов для проекта этого типа |
Application | Приложение, созданное на основе библиотеки классов MFC или с использованием только вызовов функций программного интерфейса Windows |
Dynamic-Link Library | Библиотека динамической компоновки, созданная с использованием только вызовов функций программного интерфейса Windows |
Console Application | Приложение, разработанное с использованием функций консольного ввода/вывода. Этот тип приложений можно использовать для создания небольших программ, работающих в пакетном режиме |
Static Library | Библиотека функций |
Makefile | Предоставляет дополнительные возможности для использования MAKE-файла |
Custom AppWizard | Позволяет создать собственный “волшебник” Custom AppWizard, который можно будет использовать для разработки шаблонов приложений с заданными вами свойствами |
Имя поля | Описание |
---|---|
Class name | Имя класса приложения, выбранное вами из списка. Вы можете изменить его по вашему усмотрению или оставить как есть |
Base class | Имя базового класса MFC, из которого наследуется класс выбранный из списка Class name. Для ряда классов базовый класс можно изменить. Более подробно об этом мы расскажем позже |
Header file, Implementation file | Эти два поля определяют названия включаемого файла, в котором описан класс, и файла, содержащего исходный код методов класса. Вы можете изменить их по своему усмотрению |
Имя файла | Описание |
---|---|
Dialog.h | Основной включаемый файл приложения. В нем описан главный класс приложения CDialogApp, а также подключены другие включаемые файлы, необходимые для библиотеки MFC |
Dialog.cpp | Основной файл приложения. В нем определены методы главного класса приложения и глобальные объекты |
Dialog.rc | Файл ресурсов. В этом файле описаны все ресурсы приложения. Сами ресурсы могут быть записаны в каталоге RES, расположенном в главном каталоге проекта |
Resource.h | Файл содержит определения идентификаторов ресурсов приложения, например идентификаторы строк меню |
res\Dialog.ico | Пиктограмма приложения |
res\Dialog.rc2 | В этом файле определены ресурсы, которые нельзя редактировать с помощью редактора ресурсов среды Visual C++ |
DialogDlg.h | Файл, в котором определен класс главной диалоговой панели приложения |
DialogDlg.cpp | В этом файле определены методы класса главной диалоговой панели |
ReadMe.txt | Текстовый файл, содержащий описание проекта. В нем кратко рассмотрен каждый файл, входящий в проект, перечислены классы приложения, а также представлена некоторая другая дополнительная информация |
Dialog.clw | Файл содержит информацию, необходимую для правильной работы ClassWizard |
StdAfx.h, StdAfx.cpp | Использование этих файлов позволяет ускорить процесс повторного построения проекта. Более подробное описание файлов представлено ниже |
Префикс | Представляет идентификаторы |
---|---|
HID_ | Контекстной подсказки для команд |
HIDD_ | Контекстной подсказки для диалоговых панелей |
ID_ | Строк меню и кнопок панелей управления |
IDB_ | Растровых изображений bitmap |
IDC_ | Курсоров |
IDC_ | Органов управления диалоговых панелей |
IDD_ | Шаблонов диалоговых панелей |
IDI_ | Пиктограмм |
IDP_ | Строковых ресурсов, используемые в диалоговых панелях message box для отображения приглашения |
IDR_ | Приложение может иметь несколько ресурсов различного типа с одинаковыми идентификаторами. Для таких идентификаторов используют префикс IDR_ |
IDS_ | Строковых ресурсов |
IDOK, IDCANCEL | Стандартные идентификаторы для кнопок OK и Cancel диалоговых панелей |
//{{AFX_
…
//}}AFX_
Такие комментарии образуют блок кода программы, который управляется только средствами MFC AppWizard и ClassWizard. Пользователь не должен вручную вносить изменения в этом блоке. Для этого необходимо использовать средства ClassWizard.
Чтобы подчеркнуть особое положение программного кода, заключенного в комментарии //{{AFX_, он отображается серым цветом. Это еще раз напоминает пользователю, о том, что он не должен вручную вносить изменения в этот код. В следующей таблице представлено краткое описание некоторых блоков //{{AFX_.
Блок | Включает |
---|---|
//{{AFX_DATA //}}AFX_DATA | Объявление элементов данных класса. Используется в описании классов диалоговых панелей |
//{{AFX_DATA_INIT //}}AFX_DATA_INIT | Инициализация элементов данных класса. Используется в файле реализации классов диалоговых панелей |
//{{AFX_DATA_MAP //}}AFX_DATA_MAP | Макрокоманды DDX, предназначенные для связывания элементов данных класса и органов управления диалоговых панелей. Используется в файле реализации классов диалоговых панелей |
//{{AFX_MSG //}}AFX_MSG | Описание методов, которые предназначены для обработки сообщений. Этот блок используется при описании класса |
//{{AFX_MSG_MAP //}}AFX_MSG_MAP | Макрокоманды таблицы сообщений класса. Используются совместно с AFX_MSG |
//{{AFX_VIRTUAL //}}AFX_VIRTUAL | Описание переопределенных виртуальных методов класса. Блок AFX_VIRTUAL используется при описании класса |
Комментарий | После комментария размещаются |
---|---|
// Constructors | Конструкторы класса и методы, используемые для инициализации объектов класса. Как правило, элементы класса размещенные в этой секции определены с ключевым словом public |
// Attributes | Элементы данных класса, и методы для доступа к ним (свойства класса). Как правило, элементы класса размещенные в этой секции определены с ключевым словом public |
// Operations | Виртуальные и невиртуальные методы, используемые для выполнения операций над объектами класса. Как правило элементы класса размещенные в этой секции определены с ключевым словом public |
// Overridables | Здесь расположены виртуальные методы, которые вы можете переопределить в порожденных классах. Как правило, элементы класса размещенные в этой секции определены с ключевым словом protected. В большинстве случаев, названия виртуальных методов класса начинается с символов On |
// Implementation | Методы и элементы данных, относящиеся к внутреннему устройству класса – реализации класса. Как правило, элементы класса размещенные в этой секции определены с ключевым словом protected или private |
// Dialog Data | Элементы данных, класса диалоговой панели, связанные с органами управления |
//////////////////////////////////////////////////////////////
// Шаблоны диалоговых панелей приложения
IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 217, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About Dialog"
FONT 8, "MS Sans Serif"
BEGIN
ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20
LTEXT "Dialog Version 1.0",
IDC_STATIC,40,10,119,8,SS_NOPREFIX
LTEXT "Copyright © 1996", IDC_STATIC, 40, 25, 119, 8
DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
END
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 185, 92
STYLE DS_MODALFRAME|WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "Dialog"
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,128,23,50,14
DEFPUSHBUTTON "OK",IDOK,128,7,50,14
LTEXT "TODO: Place dialog controls here.",
IDC_STATIC,5,34,113,8
END
//////////////////////////////////////////////////////////////
// Пиктограмма
IDR_MAINFRAME ICON DISCARDABLE "res\\Dialog.ico"
Пиктограмма IDR_MAINFRAME содержит два цветных изображения с разрешением 32×32 и 16×16 пикселов (рис. 4.7). Вы можете изменить эти пиктограммы по своему усмотрению.
//////////////////////////////////////////////////////////////
// Таблица строк
STRINGTABLE DISCARDABLE
BEGIN
IDS_ABOUTBOX "&About Dialog…"
END
// Dialog.h : Главный включаемый файл для приложения Dialog
//
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // включаемый файл содержащий
// идентификаторы ресурсов приложения
//////////////////////////////////////////////////////////////
// Класс CDialogApp:
// Методы класса CDialogApp определены в файле Dialog.cpp
//
class CDialogApp : public CWinApp {
public:
CDialogApp();
// Overrides
// В следующем блоке ClassWizard помещает описания
// переопределенных виртуальных методов класса
//{{AFX_VIRTUAL(CDialogApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CDialogApp)
// В этом блоке ClassWizard размещает описания методов
// класса. Не редактируйте содержимое этого блока вручную
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Для класса CDialogApp описан конструктор CDialogApp, не имеющий параметров. Этот конструктор будет использоваться в момент запуска приложения для создания объекта класса CDialogApp.
Кроме конструктора, в классе CDialogApp, переопределяется виртуальный метод InitInstance базового класса CWinApp. Как видите, метод InitInstance находится, после комментария // Overrides, который обозначает секцию переопределения виртуальных методов.
В принципе вы можете удалить комментарий // Overrides, это ни как не повлияет на работу приложения. Все комментарии типа // Overrides и // Implementation вставляются MFC AppWizard для программиста, чтобы ему было легче определить назначение метода или элемента данных класса.
MFC AppWizard поместил объявление метода InitInstance внутри блока комментариев AFX_VIRTUAL. Первая и последняя строка, обозначающая этот блок, не влияют на работу программы, так как они являются комментариями (расположены после символов комментария //). Блок AFX_VIRTUAL нужен ClassWizard, чтобы выделить методы класса, которые им управляются. Вы не должны вручную вносить изменения в этот блок и другие блоки AFX_.
Более подробно о блоках AFX_ и других комментариях, вставляемых MFC AppWizard и ClassWizard, мы рассказывали в разделе “Имена, используемые MFC” данной главы книги.
Основной файл приложения имеет имя, совпадающее с именем проекта – Dialog.cpp. Текст этого файла приведен нами в листинге 4.2. Файл содержит реализацию методов главного класса приложения CDialogApp.
// Dialog.cpp : Определяет главный класс приложения
//
// Включаемые файлы
#include "stdafx.h"
#include "Dialog.h"
#include "DialogDlg.h"
// Для отладочной версии приложения включается дополнительные
// определения
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////
// CDialogApp
BEGIN_MESSAGE_MAP(CDialogApp, CWinApp)
//{{AFX_MSG_MAP(CDialogApp)
// ClassWizard размещает в данном блоке макрокоманды для
// обработки сообщений. Не изменяйте содержимое этого блока
// вручную
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////
// Конструктор класса CDialogApp
CDialogApp::CDialogApp() {
// TODO: здесь вы можете добавить собственный код
}
//////////////////////////////////////////////////////////////
// Создаем один объект класса CDialogApp. Это будет главный
// объект приложения
CDialogApp theApp;
//////////////////////////////////////////////////////////////
// Инициализация приложения
BOOL CDialogApp::InitInstance() {
// Стандартная инициализация приложения. Вы можете сократить
// размер выполняемого модуля приложения, если удалите
// инициализацию, которая вам не нужна
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
CDialogDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK) {
// TODO: Здесь вы можете разместить код приложения,
// который вызывается, если пользователь нажмет кнопку OK
// в диалоговой панели приложения
} else if (nResponse == IDCANCEL) {
// TODO: Здесь вы можете разместить код приложения,
// который вызывается, если пользователь нажмет кнопку
// Cancel в диалоговой панели приложения
}
// Так как диалоговая панель закрыта, возвращаем значение
// FALSE чтобы завершить приложение
return FALSE;
}
В начале файла Dialog.cpp подключены три файла stdafx.h, Dialog.h и DialogDlg.h. Файл stdafx.h будет описан нами ниже. Сейчас отметим, что он содержит определения, необходимые для библиотеки классов MFC.
Файл Dialog.h содержит описание главного класса приложения CDialogApp. Файл DialogDlg.h включает описание класса диалоговой панели приложения. Именно эта панель будет представлять пользовательский интерфейс нашего приложения.
Далее директива #ifdef проверяет, был ли определен символ _DEBUG. Вы не найдете определение _DEBUG ни в одном исходном файле проекта. Этот символ определяется самой средой VIsual C++, если вы создаете отладочную версию приложения.
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
Для отладочной версии приложения определяется символ DEBUG_NEW и переопределяется статическая переменная THIS_FILE. Если такой символ уже был определен, он предварительно отменяется директивой #undef, а затем определяется снова.
THIS_FILE определяется как символьная строка, в которую записывается имя исходного файла данного модуля, определенное специальным символом __FILE__ (то, есть Dialog.cpp).
Исходные тексты приложений, подготовленных с использованием средств автоматизированного проектирования MFC AppWizard и ClassWizard, активно используют символы, определенные в среде VIsual C++. Чтобы просмотреть их список, выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, содержащая несколько страниц. Выберите страницу C/C ++ (рис. 4.9).
В поле Settings For приложение Dialog представлено двумя строками. Одна выбирает параметры проекта для отладочной версии приложения, а вторая для законченной, не отладочной, версии.
В поле Preprocessor definitions отображается список символов, определенных для данного проекта. Вы можете добавить в этот список новые символы, или убрать символы, которые уже определены.
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CDialogApp
BEGIN_MESSAGE_MAP(CDialogApp, CWinApp)
//{{AFX_MSG_MAP(CDialogApp)
// ClassWizard размещает в данном блоке макрокоманды для
// обработки сообщений. Не изменяйте содержимое этого блока
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
Обратите внимание, что внутри таблицы сообщений расположены две макрокоманды AFX_MSG, помещенные за знаками комментария. Сразу после создания приложения между ними нет ни одной макрокоманды. Когда вы будете создавать обработчики сообщений при помощи ClassWizard, он будет располагать новые обработчики между этими комментариями. Не рекомендуется вручную вносить изменения в код, расположенный в блоке AFX_MSG . Используйте для этого средства ClassWizard.
Если вам требуется добавить новый обработчик в таблицу сообщений, без использования ClassWizard, расположите его после блока AFX_MSG и до макрокоманды END_MESSAGE_MAP.
Приложение Dialog содержит еще одну таблицу сообщений, принадлежащую классу диалоговой панели приложения. Мы рассмотрим эту таблицу позже.
Непосредственно после таблицы сообщений главного класса приложения расположено определение конструктора CDialogApp. Этот конструктор вызывается в момент создания объекта приложения. Конструктор, созданный MFC AppWizard, пустой и не выполняет никаких дополнительных действий. Для инициализации приложения используются методы InitInstance и InitApplication.
CDialogApp theApp;
Объект класса CWinApp обязательно входит во все приложения, созданные с использованием MFC AppWizard, вне зависимости от пользовательского интерфейса этого приложения. Приложения с однооконным, многооконным интерфейсом и с интерфейсом, основанном на диалоговой панели, имеют единственный объект класса CWinApp.
BOOL CDialogApp::InitInstance() {
// …
}
Если вы указали, что ваше приложение должно иметь трехмерный графический интерфейс, то метод InitInstance вызывает метод Enable3dControls или Enable3dControlsStatic , определенные в классе CWinApp. Эти методы разрешают использование трехмерных органов управления. Какой из этих методов будет использоваться определяется на этапе работы препроцессора в зависимости от того, определен или нет символ _AFXDLL.
// Использовать для приложения трехмерный интерфейс
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
Символ _AFXDLL определяется средой Visual C++, если вы используете библиотеку классов MFC как библиотеку DLL. Если же код MFC подключается к приложению как статическая библиотека, этот символ не определен.
Вы можете удалить вызов методов Enable3dControls, если уже после создания проекта решите, что приложение должно иметь простой “плоский” интерфейс. И наоборот, вы можете добавить вызов этого метода, если на этапе разработки приложения забыли указать на необходимость использования трехмерного интерфейса.
Затем метод InitInstance создает диалоговую панель, которая и будет выполнять роль пользовательского интерфейса приложения. Для этого сначала создается объект dlg класса CDialogDlg, который управляет диалоговой панелью. Затем адрес этого объекта присваивается элементу данных m_pMainWnd главного класса приложения.
CDialogDlg dlg;
m_pMainWnd = &dlg;
Только после этого вызывается метод DoModal для объекта dlg класса CDialogDlg. Он создает модальное диалоговое окно и отображает его на экране. Диалоговая панель, которую создает MFC AppWizard, показана нами на рисунке 2.2. Она имеет всего две кнопки OK и Cancel. Когда пользователь нажимает на одну из этих кнопок, метод DoModal возвращает идентификатор этой кнопки. По умолчанию кнопка OK имеет идентификатор IDOK, а кнопка Cancel – IDCANCEL.
int nResponse = dlg.DoModal();
В исходный текст метода InitInstance включается два оператора if и else if, которые определяют, какая кнопка была нажата. Вы можете поместить после этих операторов ваш собственный код. Он будет вызываться при нажатии на соответствующую кнопку в диалоговой панели.
if (nResponse == IDOK) {
// Поместите здесь код, который будет выполняться
// когда пользователь нажмет кнопку OK
} else if(nResponse == IDCANCEL) {
// Поместите здесь код, который будет выполняться
// когда пользователь нажмет кнопку Cancel
}
Все! Теперь диалоговое окно закрыто и вам надо завершить приложение. Для этого достаточно, чтобы метод InitInstance вернул значение FALSE.
return FALSE;
//////////////////////////////////////////////////////////////
// Класс CDialogDlg главной диалоговой панели приложения
class CDialogDlg : public CDialog {
// Construction
public:
// Стандартный конструктор
CDialogDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CDialogDlg)
enum { IDD = IDD_DIALOG_DIALOG };
// В этом блоке ClassWizard размещает новые элементы данных
// класса
//}}AFX_DATA
// В следующем блоке ClassWizard выполняет переопределение
// виртуальных методов
//{{AFX_VIRTUAL(CDialogDlg)
protected:
// Поддержка DDX/DDV
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// В следующем блоке перечислены методы обработчики
// сообщений класса CDialogDlg
//{{AFX_MSG(CDialogDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Методы класса диалогового окна определены в файле DialogDlg.cpp. Исходный текст этого файла представлен в листинге 4.4. В нем содержатся определение конструктора и методов класса CDialogDlg.
#include "stdafx.h"
#include "Dialog.h"
#include "DialogDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////
// Описание класса CAboutDlg, который используется для
// управления диалоговой панелью About. Методы этого класса
// определены ниже
class CAboutDlg : public CDialog {
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// В следующем блоке ClassWizard размещает переопределение
// виртуальных методов
//{{AFX_VIRTUAL(CAboutDlg)
protected:
// Поддержка DDX/DDV
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
// Конструктор класса CAboutDlg
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) {
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
// Метод DoDataExchange класса CAboutDlg
void CAboutDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
// Таблица сообщений класса CAboutDlg
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// Класс CAboutDlg не обрабатывает никаких сообщений
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////
// Ниже определены различные методы класса CDialogDlg
// Конструктор класса CDialogDlg
CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/) : CDialog(CDialogDlg::IDD, pParent) {
//{{AFX_DATA_INIT(CDialogDlg)
// В этом блоке ClassWizard размещает инициализацию
// элементов данных класса
//}}AFX_DATA_INIT
// Вызов LoadIcon не требует последующего вызова
// DestroyIcon, если вы используете программный интерфейс
// Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
// Метод DoDataExchange класса CDialogDlg
void CDialogDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDialogDlg)
// Здесь ClassWizard размещает вызовы методов DDX и DDV
//}}AFX_DATA_MAP
}
// Таблица сообщений класса CDialogDlg
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CDialogDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// Метод OnInitDialog класса CDialogDlg
BOOL CDialogDlg::OnInitDialog() {
CDialog::OnInitDialog();
// Добавление строки "About…" к системному меню приложения
// Проверяем, что идентификатор IDM_ABOUTBOX относится к
// системным командам
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty()) {
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
// Выбираем пиктограмму для диалоговой панели. Если главное
// окно приложения не является диалоговой панелью, этот код
// не нужен
SetIcon(m_hIcon,TRUE); // выбираем пиктограмму большого
// размера
SetIcon(m_hIcon,FALSE); // выбираем пиктограмму маленького
// размера
// TODO: Здесь вы можете выполнить дополнительную
// инициализацию
return TRUE;
}
// Метод OnSysCommand класса CDialogDlg
void CDialogDlg::OnSysCommand(UINT nID, LPARAM lParam) {
if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
CAboutDlg dlgAbout;
dlgAbout.DoModal();
} else {
CDialog::OnSysCommand(nID, lParam);
}
}
// Если вы добавили кнопку минимизации к диалоговой панели,
// следующий код нужен, чтобы отобразить пиктограмму
// Метод OnPaint класса CDialogDlg
void CDialogDlg::OnPaint() {
if (IsIconic()) {
CPaintDC dc(this); // контекст устройства
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Выравниваем по центру пиктограмму
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() – cxIcon + 1) / 2;
int y = (rect.Height() – cyIcon + 1) / 2;
// Отображаем пиктограмму
dc.DrawIcon(x, y, m_hIcon);
} else {
CDialog::OnPaint();
}
}
// Данный метод вызывается для определения формы курсора,
// отображаемого, когда пользователь переносит
// минимизированное окно
// Метод OnQueryDragIcon класса CDialogDlg
HCURSOR CDialogDlg::OnQueryDragIcon() {
return (HCURSOR) m_hIcon;
}
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CDialogDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
При помощи ClassWizard вы можете обнаружить, что макрокоманды выполняют обработку сообщений WM_SYSCOMMAND, WM_PAINT, WM_QUERYDRAGICON, вызывая для этого методы OnSysCommand, OnPaint и OnQueryDragIcon.
//////////////////////////////////////////////////////////////
// Конструктор класса CDialogDlg
CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/) : CDialog(CDialogDlg::IDD, pParent) {
//{{AFX_DATA_INIT(CDialogDlg)
// В этом блоке ClassWizard размещает инициализацию
// элементов данных класса
//}}AFX_DATA_INIT
// Вызов LoadIcon не требует последующего вызова
// DestroyIcon, если вы используете программный интерфейс
// Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
В теле конструктора расположен блок AFX_DATA_INIT . В него ClassWizard будет добавлять код инициализации элементов данных класса CDialogDlg. Конструктор также инициализирует m_hIcon, записывая в него идентификатор пиктограммы IDR_MAINFRAME.
Функция AfxGetApp возвращает указатель на объект главного класса приложения. Такой объект для данного приложения всегда один. В нашем случае AfxGetApp определяет указатель на объект theApp. Вот прототип этой функции:
CWinApp* AfxGetApp();
void CDialogDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDialogDlg)
// Здесь ClassWizard размещает вызовы методов DDX и DDV
//}}AFX_DATA_MAP
}
BOOL CDialogDlg::OnInitDialog() {
CDialog::OnInitDialog();
// Добавление строки "About…" к системному меню приложения
// Проверяем, что идентификатор IDM_ABOUTBOX относится к
// системным командам
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty()) {
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
// Выбираем пиктограмму для диалоговой панели. Если главное
// окно приложения не является диалоговой панелью этот код
// не нужен
SetIcon(m_hIcon,TRUE); // Выбираем пиктограмму большого
// размера
SetIcon(m_hIcon,FALSE); // Выбираем пиктограмму маленького
// размера
// TODO: Здесь вы можете выполнить дополнительную
// инициализацию
return TRUE;
}
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
Параметр nID содержит идентификатор строки системного меню, вызвавшего сообщение. Младшие четыре бита параметра nID используются операционной системой и могут принимать любые значения. Параметр nID (без учета четырех младших бит) может принимать одно из следующих значений.
Параметр nID | Описание |
---|---|
SC_CLOSE | Закрывает объект CWnd |
SC_HOTKEY | Активизирует объект CWnd, связанный с комбинацией клавиш, определенной приложением. Младшее слово параметра lParam содержит идентификатор активизируемого окна |
SC_HSCROLL | Свертка по горизонтали |
SC_KEYMENU | Выбор из меню при помощи комбинации клавиш |
SC_MAXIMIZE, SC_ZOOM | Максимизировать объект CWnd |
SC_MINIMIZE, SC_ICON | Минимизировать объект CWnd |
SC_MOUSEMENU | Выбор из меню при помощи мыши |
SC_MOVE | Перемещение окна CWnd |
SC_NEXTWINDOW | Переключение на следующее окно |
SC_PREVWINDOW | Переключение на предыдущее окно |
SC_RESTORE | Восстановление нормального расположения и размера окна |
SC_SCREENSAVE | Запустить приложение, предохраняющее экран монитора, указанное в секции [boot] файла SYSTEM.INI |
SC_SIZE | Изменить размер окна CWnd |
SC_TASKLIST | Запустить или активизировать приложение Task Manager |
SC_VSCROLL | Свертка по вертикали |
void CDialogDlg::OnSysCommand(UINT nID, LPARAM lParam) {
// Пользователь выбрал строку About системного меню
if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
// Все другие сообщение передаем для обработки методу
// OnSysCommand базового класса CDialog
else {
CDialog::OnSysCommand(nID, lParam);
}
}
Реализация метода OnSysCommand, созданная MFC AppWizard для класса CDialogDlg, определяет причину вызова. Если метод OnSysCommand вызван потому что пользователь выбрал из системного меню строку About, создается объект класса CAboutDlg. Класс CAboutDlg представляет собой класс для управления диалоговой панелью About. Затем вызывается метод DoModal, который и отображает диалоговую панель About на экране.
Если метод OnSysCommand вызван по любой другой причине, тогда вызывается метод OnSysCommand базового класса CDialog, который выполняет обработку этого сообщения по умолчанию.
Описание класса CAboutDlg, а также определение его методов, содержится в файле DialogDlg.cpp (листинг 4.4). Мы не будем подробно описывать класс CAboutDlg, так как он фактически представляет собой упрощенный вариант класса CDialogDlg.
void CDialogDlg::OnPaint() {
// Определяем размер диалоговой панели
if (IsIconic()) {
// Если диалоговая панель минимизирована, отображаем
// пиктограмму
CPaintDC dc(this); // получаем контекст устройства
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
// Определяем размеры внутренней области окна
CRect rect;
GetClientRect(&rect);
// Выравниваем пиктограмму по центру
int x = (rect.Width() – cxIcon + 1) / 2;
int y = (rect.Height() – cyIcon + 1) / 2;
// Отображаем пиктограмму
dc.DrawIcon(x, y, m_hIcon);
} else {
// Выполняем обработку по умолчанию
CDialog::OnPaint();
}
}
После вызова метода OnPaint он проверяет состояние диалоговой панели. Для этого вызывается метод IsIconic, определенный в классе CWnd. Если окно, или в нашем случае диалоговая панель, связанная с объектом, для которого вызывается метод IsIconic, минимизировано, возвращается ненулевое значение, в противном случае – нуль.
BOOL IsIconic() const;
В случае если диалоговая панель имеет нормальный размер, управление передается методу OnPaint базового класса CDialog для выполнения обработки по умолчанию.
Если вызов OnPaint произошел в следствии минимизации диалоговой панели, тогда определяются размеры внутренней области минимизированного окна и размеры пиктограммы. Затем пиктограмма отображается в центре минимизированного окна.
HCURSOR CDialogDlg::OnQueryDragIcon() {
// Возвращаем идентификатор пиктограммы
return (HCURSOR) m_hIcon;
}
#include "stdafx.h"
Фактически файл StdAfx.cpp содержит только директиву #include, предназначенную для подключения файла StdAfx.h. Включаемый файл StdAfx.h, представлен нами в листинге 4.6.
Включаемый файл StdAfx.h предназначен для включения стандартных системных включаемых файлов afxwin.h, afxext.h и afxcmn.h. Если в вашем проекте определены редко изменяемые включаемые файлы, которые используются во многих модулях приложения, вы можете также подключить их в этом файле.
// Исключить редко используемые директивы из файлов windows.h
// и afxv_w32.h
#define VC_EXTRALEAN
// Файл afxwin.h необходим при использовании MFC
#include <afxwin.h>
// Файл afxwin.h определяет некоторые расширения MFC
#include <afxext.h>
#ifndef _AFX_NO_AFXCMN_SUPPORT
// Файл afxcmn.h используется для органов управления
// операционной системы Windows 95
#include <afxcmn.h>
#endif // _AFX_NO_AFXCMN_SUPPORT
Объекты порожденные от класса CCmdTarget могут обрабатывать сообщения Windows и команды, поступающие от меню, кнопок, акселераторов. Класс CCmdTarget и другие наследованные от него классы имеют таблицу сообщений (Message map) – набор макрокоманд, позволяющий сопоставить сообщениям Windows и командам методы класса.Чтобы создать класс, нажмите кнопку Add Class из любой страницы главной диалоговой панели ClassWizard. Откроется временное меню, содержащее три строки: New, From a file, From an OLE TypeLib. Для создания нового класса выберите из этого меню строку New. Если вы уже имеете исходные тексты класса и их просто требуется подключить к проекту, выберите из меню строку From a file. Последняя строка меню From an OLE TypeLib используется для подключения классов из библиотеки OLE. Когда вы создаете новый класс, на экране появляется диалоговая панель Create New Class. В поле Name введите имя создаваемого класса. Рекомендуется начинать названия классов с символа “C”. Для создаваемого класса организуются два файла реализации класса, имеющие расширения CPP и H. В них будут помещаться объявления класса, а также определения его методов и данных. Имя файлов реализации отображается в левой части группы File. По умолчанию файлы реализации имеют имена, соответствующие имени класса. Однако их можно изменить, воспользовавшись кнопкой Change из группы File. Теперь выберите из списка Base Class имя базового класса. Список Base Class достаточно велик. В нем содержатся не только основополагающие классы типа CCmdTarget, CDialog, CDocument, CFrameWnd, CView, CWinThread, CWnd. Список базовых классов включает классы большинства органов управления, например CAnimateCtrl, CButton, CColorDialog, CComboBox, CDragListBox, CEdit, CEditView, CFileDialog, CFontDialog, CHeaderCtrl, CHotKeyCtrl, CListBox, CListCtrl, CListView, CProgressCtrl, CStatic и многие многие другие. Доступны также базовые классы, предназначенные для работы с базами данных: CDaoRecordSet, CDaoRecordView, CRecordset, CRecordView, классы обеспечивающие технологию OLE: COleDocument, COleLinkingDoc, COleServerDoc. Так, например, вы можете создать новый класс CNewClass, наследованный от базового класса окна просмотра CEditView. Определение класса помещается во включаемый файл NewClass.h (листинг 4.7).
// Класс окна просмотра CNewClass
class CNewClass : public CEditView {
protected:
CNewClass();
DECLARE_DYNCREATE(CNewClass)
// Attributes
public:
// Operations
public:
// Overrides
//{{AFX_VIRTUAL(CNewClass)
protected:
virtual void OnDraw(CDC* pDC);
//}}AFX_VIRTUAL
// Implementation
protected:
virtual ~CNewClass();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Методы, предназначенные для обработки сообщений
protected:
//{{AFX_MSG(CNewClass)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Определение методов класса размещается в другом файле, имеющем расширение CPP (листинг 4.8).
#include "stdafx.h"
#include "Single.h"
#include "NewClass.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////
// Реализация класса CNewClass
IMPLEMENT_DYNCREATE(CNewClass, CEditView)
CNewClass::CNewClass() {}
CNewClass::~CNewClass() {}
BEGIN_MESSAGE_MAP(CNewClass, CEditView)
//{{AFX_MSG_MAP(CNewClass)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////
// Метод OnDraw класса CNewClass
void CNewClass::OnDraw(CDC* pDC) {
CDocument* pDoc = GetDocument();
// TODO: здесь можно расположить код, выполняющий вывод в
// окно
}
//////////////////////////////////////////////////////////////
// Диагностические методы класса CNewClass
#ifdef _DEBUG
void CNewClass::AssertValid() const {
CEditView::AssertValid();
}
void CNewClass::Dump(CDumpContext& dc) const {
CEditView::Dump(dc);
}
#endif //_DEBUG
Полученная заготовка класса полностью работоспособна. Ее можно дополнить по своему усмотрению новыми методами и данными. Эту работу можно выполнить вручную, но гораздо лучше и проще воспользоваться услугами предоставляемыми ClassWizard. За счет использования ClassWizard процедура создания собственного класса значительно ускоряется и уменьшается вероятность совершить ошибку во время объявления методов.
Список Message filter | Объект, сообщения от которого должны обрабатываться |
---|---|
Child Window | Дочернее окно |
Dialog | Диалоговое окно |
MDI Child Frame | Дочернее окно MDI |
Not a Window | Не окно |
Topmost Frame | Окно frame window – главное окно приложения |
Window | Окно |
Переключатель | Описание |
---|---|
None | Приложение не использует технологию OLE |
Container | Приложение сможет включать в свои документы, другие объекты и ссылки на них, то есть будет выступать в качестве клиента OLE |
Mini-server | Обеспечивается работа основных возможностей сервера OLE. Документы или объекты, подготовленные в приложении, можно будет включать в другие приложения. Однако приложение нельзя будет использовать автономно. Объекты, подготовленные в приложении, можно будет встраивать, однако нельзя будет встроить ссылку на объект, записанный в отдельном файле |
Full-server | Обеспечивается работа всех возможностей сервера OLE. Объекты, подготовленные в приложении, можно будет включать в другие приложения или встраивать ссылку на объект, записанный в отдельном файле. Приложение можно будет использовать автономно |
Both container and server | Приложение сможет работать и как сервер и как клиент OLE |
Переключатель | Описание |
---|---|
Thick frame | Главное окно имеет рамку, позволяющую изменить его размер |
Minimize box | Главное окно содержит кнопку для сворачивания его в пиктограмму |
Maximize box | Главное окно содержит кнопку для увеличения его размера до максимально возможного |
System menu | Главное окно приложения будет иметь системное меню |
Minimized | При запуске приложения его главное окно будет уменьшено до пиктограммы. Потом вы сможете открыть окно |
Maximized | При запуске приложения его главное окно принимает максимально возможный размер |
Переключатель | Описание |
---|---|
Thick frame | Все MDI окна приложения имеют рамку, позволяющую изменить их размер |
Minimize box | MDI окна содержат кнопку для сворачивания их в пиктограмму |
Maximize box | MDI окна содержат кнопку для увеличения их размера до максимально возможного |
Minimized | Открываемые окна MDI будут уменьшены до пиктограммы |
Maximized | Открываемые окна MDI будут иметь максимально возможный размер |
Имя файла | Описание |
---|---|
Single.h | В этом файле перечислены другие включаемые файлы и описан главный класс приложения CSingleApp |
Single.cpp | Основной файл приложения. В нем определены методы основного класса приложения CSingleApp |
MainFrm.h | Содержит описание класса frame, который называется CMainFrame. Класс CMainFrame наследуется от базового класса CFrameWnd определенного в библиотеке классов MFC |
MainFrm.cpp | Файл содержит определения методов класса CMainFrame |
SingleDoc.h | Содержит описание класса документов приложения – CSingleDoc |
SingleDoc.cpp | Включает определение методов класса CSingleDoc |
SingleView.h | Содержит описание класса окна просмотра приложения – CSingleView |
SingleView.cpp | Включает определение методов класса CSingleView |
Single.rc | Файл ресурсов. В этом файле описаны все ресурсы приложения. Сами ресурсы могут быть записаны в каталоге RES, расположенном в главном каталоге проекта |
Resource.h | Файл содержит определения идентификаторов ресурсов приложения, например, идентификаторы строк меню |
res\Single.ico | Пиктограмма приложения |
res\Single.rc2 | В этом файле определены ресурсы, которые нельзя редактировать с помощью редактора ресурсов среды Visual C++ |
res\Toolbar.bmp | Файл содержит изображение кнопок панели управления toolbar |
StdAfx.h, StdAfx.cpp | Использование этих файлов позволяет ускорить процесс повторного построения проекта. Более подробное описание файлов представлено ниже |
Single.clw | Файл содержит информацию, необходимую для правильной работы ClassWizard |
ReadMe.txt | Текстовый файл, содержащий описание проекта. В нем кратко рассмотрен каждый файл, входящий в проект, перечислены классы приложения, а также представлена другая дополнительная информация |
//////////////////////////////////////////////////////////////
// Меню
IDR_MAINFRAME MENU PRELOAD DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New\tCtrl+N", ID_FILE_NEW
MENUITEM "&Open…\tCtrl+O", ID_FILE_OPEN
MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE
MENUITEM "Save &As…", ID_FILE_SAVE_AS
MENUITEM SEPARATOR
MENUITEM "&Print…\tCtrl+P",ID_FILE_PRINT
MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW
MENUITEM "P&rint Setup…", ID_FILE_PRINT_SETUP
MENUITEM SEPARATOR
MENUITEM "Recent File", ID_FILE_MRU_FILE1,GRAYED
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_APP_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO
MENUITEM SEPARATOR
MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE
END
POPUP "&View"
BEGIN
MENUITEM "&Toolbar", ID_VIEW_TOOLBAR
MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR
END
POPUP "&Help"
BEGIN
MENUITEM "&About Single…", ID_APP_ABOUT
END
END
Большая часть строк меню IDR_MAINFRAME имеет стандартные идентификаторы, описанные в библиотеке MFC. Некоторые из команд, соответствующих этим идентификаторам полностью обрабатываются MFC. Список стандартных команд с их описанием представлен в разделе “Стандартные команды”.
//////////////////////////////////////////////////////////////
// Панель управления Toolbar
IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15
BEGIN
BUTTON ID_FILE_NEW
BUTTON ID_FILE_OPEN
BUTTON ID_FILE_SAVE
SEPARATOR
BUTTON ID_EDIT_CUT
BUTTON ID_EDIT_COPY
BUTTON ID_EDIT_PASTE
SEPARATOR
BUTTON ID_FILE_PRINT
BUTTON ID_APP_ABOUT
END
Обратите внимание, что идентификаторы кнопок панели управления соответствуют идентификаторам некоторых строк меню приложения. Поэтому эти кнопки дублируют соответствующие строки меню.
Образ кнопок панели управления расположен в файле Toolbar.bmp, записанном в подкаталоге res каталога проекта.
//////////////////////////////////////////////////////////////
// Изображение Bitmap, определяющее кнопки приложения
IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
//////////////////////////////////////////////////////////////
// Пиктограммы
IDR_MAINFRAME ICON DISCARDABLE "res\\Single.ico"
IDR_SINGLETYPE ICON DISCARDABLE "res\\SingleDoc.ico"
Пиктограмма IDR_MAINFRAME представляет минимизированное приложение (рис. 5.11). Эта же пиктограмма отображается в левом верхнем углу главного окна приложения.
//////////////////////////////////////////////////////////////
// Таблица текстовых строк
STRINGTABLE PRELOAD DISCARDABLE
BEGIN
IDR_MAINFRAME "Single\n\nSingle\n\n\nSingle.Document\nSingle Document"
END
Строка описания типа документа (IDR_MAINFRAME) состоит из семи фрагментов, разделенных символами перевода строки \n. Эти фрагменты строки отвечают за различные характеристики документа.
Формирование этой строки выполняется MFC AppWizard на основании информации, которую вы указали на страница Document Template Strings диалоговой панели Advanced Options (рис. 5.6).
Фрагмент | Поле панели Advanced Options | Описание |
---|---|---|
Первый | Main frame caption | Заголовок главного окна приложения. Используется для приложений с однооконным интерфейсом |
Второй | Doc type name (только для приложений с многооконным интерфейсом), для приложений с однооконным интерфейсом этот фрагмент изначально пуст | Имя файла, присваиваемое новому документу по умолчанию. Если этот фрагмент отсутствует, используется имя Untitled |
Третий | File new name (OLE short name) | Название типа документа. Если в приложении определено несколько типов документов (обычно используется для приложений с многооконным интерфейсом), то при создании нового документа отображается диалоговая панель, из которой вы должны выбрать название типа создаваемого документа |
Четвертый | Filter name | Название фильтра для имен файлов документов данного типа. Это название отображается в стандартных диалоговых панелях Open и Save As, в списке типов документов |
Пятый | File extension | Расширение файлов документов данного типа, используемое по умолчанию |
Шестой | File type ID | Идентификатор, под которым данный тип документов заносятся в регистрационную базу Windows 95. Вы можете просмотреть регистрационную базу Windows 95 при помощи приложения REGEDIT. |
Седьмой | File type name (OLE long name) | Тип файлов, используемых для хранения документов данного типа. Также используется в качестве длинного имени объекта OLE, если приложение использует OLE технологию |
STRINGTABLE PRELOAD DISCARDABLE
BEGIN
AFX_IDS_APP_TITLE "Single"
AFX_IDS_IDLEMESSAGE "Ready"
END
В следующем блоке текстовых строк определены несколько текстовых строк, имеющих стандартные идентификаторы. Эти строки используются для отображения различной информации в панели состояния.
STRINGTABLE DISCARDABLE
BEGIN
ID_INDICATOR_EXT "EXT"
ID_INDICATOR_CAPS "CAP"
ID_INDICATOR_NUM "NUM"
ID_INDICATOR_SCRL "SCRL"
ID_INDICATOR_OVR "OVR"
ID_INDICATOR_REC "REC"
END
И наконец, последний, самый большой блок текстовых строк содержит краткие описания каждой строки меню приложения. Идентификаторы этих строк соответствуют идентификаторам строк меню, которые они описывают.
Строки, описывающие меню, состоят из двух частей, разделенных символом перевода строки \n. Первая часть строки отображаются в панели состояния, когда пользователь выбирает строки меню. Вторая часть строки содержит краткую подсказку, которая отображается, если поместить указатель мыши на кнопки и подождать несколько секунд. Если вы не нуждаетесь в короткой подсказке для кнопок управляющей панели, то вторую часть строки можно не приводить.
STRINGTABLE DISCARDABLE
BEGIN
ID_FILE_NEW "Create a new document\nNew"
ID_FILE_OPEN "Open an existing document\nOpen"
ID_FILE_CLOSE "Close the active document\nClose"
ID_FILE_SAVE "Save the active document\nSave"
ID_FILE_SAVE_AS "Save the active document with a new name\nSave As"
ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup"
ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup"
ID_FILE_PRINT "Print the active document\nPrint"
ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview"
ID_APP_ABOUT " Display program information, version number and copyright\nAbout"
ID_APP_EXIT "Quit the application; prompts to save documents\nExit"
ID_FILE_MRU_FILE1 "Open this document"
ID_FILE_MRU_FILE2 "Open this document"
ID_FILE_MRU_FILE3 "Open this document"
ID_FILE_MRU_FILE4 "Open this document"
ID_FILE_MRU_FILE5 "Open this document"
ID_FILE_MRU_FILE6 "Open this document"
ID_FILE_MRU_FILE7 "Open this document"
ID_FILE_MRU_FILE8 "Open this document"
ID_FILE_MRU_FILE9 "Open this document"
ID_FILE_MRU_FILE10 "Open this document"
ID_FILE_MRU_FILE11 "Open this document"
ID_FILE_MRU_FILE12 "Open this document"
ID_FILE_MRU_FILE13 "Open this document"
ID_FILE_MRU_FILE14 "Open this document"
ID_FILE_MRU_FILE15 "Open this document"
ID_FILE_MRU_FILE16 "Open this document"
ID_NEXT_PANE "Switch to the next window pane\nNext Pane"
ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane"
ID_WINDOW_SPLIT "Split the active window into panes\nSplit"
ID_EDIT_CLEAR "Erase the selection\nErase"
ID_EDIT_CLEAR_ALL "Erase everything\nErase All"
ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy"
ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut"
ID_EDIT_FIND "Find the specified text\nFind"
ID_EDIT_PASTE "Insert Clipboard contents\nPaste"
ID_EDIT_REPEAT "Repeat the last action\nRepeat"
ID_EDIT_REPLACE "Replace specific text with different text\nReplace"
ID_EDIT_SELECT_ALL "Select the entire document\nSelect All"
ID_EDIT_UNDO "Undo the last action\nUndo"
ID_EDIT_REDO "Redo the previously undone action\nRedo"
ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar"
ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar"
END
//////////////////////////////////////////////////////////////
// Диалоговая панель
IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 217, 55
CAPTION "About Single"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20
LTEXT "Single Version 1.0",IDC_STATIC,40,10,119,8, SS_NOPREFIX
LTEXT "Copyright \251 1996",IDC_STATIC,40,25,119,8
DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
END
// Таблица акселераторов
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
BEGIN
"N", ID_FILE_NEW, VIRTKEY,CONTROL
"O", ID_FILE_OPEN, VIRTKEY,CONTROL
"S", ID_FILE_SAVE, VIRTKEY,CONTROL
"P", ID_FILE_PRINT, VIRTKEY,CONTROL
"Z", ID_EDIT_UNDO, VIRTKEY,CONTROL
"X", ID_EDIT_CUT, VIRTKEY,CONTROL
"C", ID_EDIT_COPY, VIRTKEY,CONTROL
"V", ID_EDIT_PASTE, VIRTKEY,CONTROL
VK_BACK, ID_EDIT_UNDO, VIRTKEY,ALT
VK_DELETE, ID_EDIT_CUT, VIRTKEY,SHIFT
VK_INSERT, ID_EDIT_COPY, VIRTKEY,CONTROL
VK_INSERT, ID_EDIT_PASTE, VIRTKEY,SHIFT
VK_F6, ID_NEXT_PANE, VIRTKEY
VK_F6, ID_PREV_PANE, VIRTKEY,SHIFT
END
Класс приложения | Базовый класс | Описание |
---|---|---|
CSingleApp | CWinApp | Главный класс приложения |
CMainFrame | CFrameWnd | Класс главного окна приложения |
CSingleDoc | CDocument | Класс документа приложения |
CSingleView | CView | Класс окна просмотра документа |
//////////////////////////////////////////////////////////////
// Класс CSingleApp:
class CSingleApp : public CWinApp {
public:
CSingleApp();
// Overrides
//{{AFX_VIRTUAL(CSingleApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CSingleApp)
afx_msg void OnAppAbout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
Таким образом, DECLARE_MESSAGE_MAP не является расширением языка Си++, а просто добавляет к вашему классу несколько новых элементов.
Так как в классе CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP, то он может обрабатывать сообщения и имеет таблицу сообщений. Таблица сообщений класса CSingleApp расположена в файле реализации Single.cpp.
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CSingleApp
BEGIN_MESSAGE_MAP(CSingleApp, CWinApp)
//{{AFX_MSG_MAP(CSingleApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
// Стандартные команды для работы с документами
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Стандартная команда выбора принтера
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
Кроме команды для обработки командного сообщения ID_APP_ABOUT, расположенного в блоке AFX_MSG_MAP, таблица сообщений содержит еще три макрокоманды, предназначенные для обработки командных сообщений с идентификаторами ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP.
Командные сообщения ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP поступают, когда пользователь выбирает из меню приложения строки с соответствующими идентификаторами. Для обработки этих командных сообщений вызываются методы класса CWinApp.
CSingleApp theApp;
//////////////////////////////////////////////////////////////
// Конструктор класса CSingleApp
CSingleApp::CSingleApp() {
// TODO: Здесь вы можете разместить свой код
}
//////////////////////////////////////////////////////////////
// Метод CSingleApp
BOOL CSingleApp::InitInstance() {
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
// Загружаем файл конфигурации
LoadStdProfileSettings();
// Создаем шаблон документа
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME, RUNTIME_CLASS(CSingleDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CSingleView));
// Регистрируем шаблон документа
AddDocTemplate(pDocTemplate);
// Выполняем стандартную обработку командной строки
// приложения
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Обрабатываем командную строку приложения
if (!ProcessShellCommand(cmdInfo)) return FALSE;
return TRUE;
}
После вызова метода Enable3dControls, описанного ранее, вызывается метод LoadStdProfileSettings. Этот метод загружает файл конфигурации приложения, имеющий расширение INI. В INI-файле записаны имена нескольких файлов, с которыми работало приложение. Эти имена файлов будут добавлены как отдельные строки в меню File приложения. Кроме того, в INI-файле может храниться и другая информация.
Метод LoadStdProfileSettings определен в классе CWinApp следующим образом:
void LoadStdProfileSettings(UINT nMaxMRU = _AFX_MRU_COUNT);
Необязательный параметр nMaxMRU определяет, сколько имен файлов документов будет запоминаться. Если указать в качестве параметра nMaxMRU нулевое значение, список файлов запоминаться не будет. По умолчанию параметру nMaxMRU присваивается значение _AFX_MRU_COUNT. Константа _AFX_MRU_COUNT определена в файле afxwin.h.
#define _AFX_MRU_COUNT 4
Напомним вам, что количество запоминаемых имен файлов можно указать в диалоговой панели MFC AppWizard – Step 4 of 6 (рис. 5.3), во время разработки приложения средствами MFC AppWizard.
Затем начинается создание шаблона документов. Сначала создается указатель pDocTemplate на соответствующий класс. Для однооконных приложений это класс CSingleDocTemplate, а для многооконных – CMultiDocTemplate. Создается новый объект класса и в переменную pDocTemplate записывается указатель на него. Для создания шаблона документа используется оператор new.
Конструктору класса CSingleDocTemplate передаются четыре параметра.
CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);
Первый параметр nIDResource определяет идентификатор ресурсов, используемых совместно с типом документов, управляемых шаблоном. К таким ресурсам относятся меню, пиктограмма, строковый ресурс, таблица акселераторов.
Остальные три параметра pDocClass, pFrameClass, pViewClass содержат указатели на объекты класса CRuntimeClass, полученные с помощью макрокоманд RUNTIME_CLASS из классов документа CSingleDoc , окна CMainFrame и окна просмотра CSingleView . Таким образом, шаблон документа объединяет всю информацию, относящуюся к данному типу документов.
Созданный шаблон документов заносится в список шаблонов с которыми работает приложение. Для этого указатель на созданный шаблон документа передается методу AddDocTemplate из класса WinApp. Указатель на шаблон документов передается через параметр pTemplate.
void AddDocTemplate(CDocTemplate* pTemplate);
Указатель pTemplate указывает на объекты класса CDocTemplate. Однако мы передаем через него указатели на объекты класса CSingleDocTemplate. Это допустимо, так как класс CDocTemplate является базовым классом для CSingleDocTemplate.
Если вы разрабатываете приложение, основанное на однооконном или многооконном интерфейсе, тогда объект главного класса приложения управляет одним или несколькими объектами класса шаблона документа. Они, в свою очередь, управляют созданием документов. Один шаблон используется для всех документов данного типа. Так как однооконные приложения, как правило, работают только с документами одного типа, они используют только один шаблон документов.
После создания шаблона документа, обрабатывается командная строка приложения. Для этого создается объект cmdInfo класса CCommandLineInfo.
Объект cmdInfo передается методу ParseCommandLine, определенному в классе CWinApp. Он заполняет объект cmdInfo, данными, взятыми из командной строки приложения. Подготовленный объект cmdInfo передается методу ProcessShellCommand класса CWinApp для обработки.
Если обработка завершилась успешно, метод ProcessShellCommand возвращает ненулевое значение. Если при обработке командной строки приложения возникли ошибки, метод ProcessShellCommand возвращает нуль.
После успешной инициализации приложения и обработки командной строки метод InitInstance возвращает значение TRUE. Начинается обработка цикла сообщений.
Кроме конструктора и метода InitInstance в главном классе приложения CSingleApp определен метод OnAppAbout. Он расположен в блоке AFX_MSG. Поэтому для работы с этим методом вы можете использовать ClassWizard.
// Метод OnAppAbout
void CSingleApp::OnAppAbout() {
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}
Описание класса CAboutDlg, а также определение его методов содержится в файле Single.cpp. Класс CAboutDlg приложения Single полностью соответствует классу CAboutDlg приложения Dialog, описанного в предыдущей главе. Мы не будем повторять описание класса CAboutDlg, вы можете самостоятельно найти его в листинге 4.4.
class CSingleDoc : public CDocument {
protected:
CSingleDoc();
DECLARE_DYNCREATE(CSingleDoc)
// Attributes
public:
// Operations
public:
// Overrides
//{{AFX_VIRTUAL(CSingleDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CSingleDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Методы, предназначенные для обработки сообщений
protected:
//{{AFX_MSG(CSingleDoc)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Просмотрите исходные тексты приложения. Вы не обнаружите кода, который бы явно создавал объекты этого класса. Объект класса CSingleDoc создается динамически шаблоном документа, во время работы приложения. Шаблон документа также динамически создает еще два объекта – класса окна и класса окна просмотра.
Для того чтобы объекты любого класса, наследованного от базового класса CObject, в том числе и CSingleDoc, можно было создавать динамически, необходимо выполнить следующее:
• в описании класса надо поместить макрокоманду DECLARE_DYNCREATE. В качестве параметра этой макрокоманды необходимо указать имя данного класса;
• определить конструктор класса, который не имеет параметров;
• разместить макрокоманду IMPLEMENT_DYNCREATE в файле реализации. Макрокоманда IMPLEMENT_DYNCREATE имеет два параметра. В первом указывается имя класса, а во втором имя его базового класса
// Макрокоманда необходима для динамического создания объектов
// CSingleDoc
IMPLEMENT_DYNCREATE(CSingleDoc, CDocument)
// Таблица сообщений класса CSingleDoc
BEGIN_MESSAGE_MAP(CSingleDoc, CDocument)
//{{AFX_MSG_MAP(CSingleDoc)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Сазу после создания проекта таблица сообщений класса CSingleDoc не содержит обработчиков сообщений. Когда вы продолжите разработку приложения, вы будете добавлять обработчики различных сообщений к классу CSingleDoc и другим классам приложения. Для добавления новых обработчикоов сообщений, а также для внесения других изменений в классы, следует использовать ClassWizard.
// Конструктор класса CSingleDoc
CSingleDoc::CSingleDoc() {
// TODO:
}
Вместе с конструктором класса CSingleDoc, создается деструктор ~CSingleDoc. Деструктор не содержит кода и представляет собой такую же заготовку как и конструктор.
// Деструктор класса CSingleDoc
CSingleDoc::~CSingleDoc() {}
CSingleDoc←CDocument←CCmdTarget←CObject
Метод OnNewDocument вызывается, когда надо создать новый документ для приложения. Если вы переопределяете метод OnNewDocument (в данном случае за вас это делает MFC AppWizard), то сначала необходимо вызвать метод OnNewDocument базового класса, и только затем можно выполнять инициализацию документа. Более подробно об использовании метода OnNewDocument мы расскажем в следующих главах, когда к шаблону прложения, созданному MFC AppWizard, мы будем добавлять собственный код.
BOOL CSingleDoc::OnNewDocument() {
if (!CDocument::OnNewDocument()) return FALSE;
// TODO: здесь можно выполнить инициализацию
// документа
return TRUE;
}
Если создание нового документа прошло успешно, метод OnNewDocument должен вернуть значение TRUE, а в противном случае FALSE. Когда вызывается метод OnNewDocument базового класса CDocument, следует выполнять проверку возвращаемого им значения. Если CDocument::OnNewDocument вернет FALSE, значит создание документа на уровне класса CDocument не прошло и следует прекратить дальнейшие действия.
Большой интерес представляет метод Serialize. Он вызывается в тех случаях, когда надо загрузить документ из файла на диске или наоборот, записать его в файл. Метод Serialize вызывается, когда пользователь выбирает из меню File строки Open или Save.
//////////////////////////////////////////////////////////////
// Сохранение и восстановление документа
void CSingleDoc::Serialize(CArchive& ar) {
if (ar.IsStoring()) {
// TODO: здесь выполняется запись документа в файл
} else {
// TODO: здесь выполняется чтение документа из файла
}
}
В качестве параметра ar методу Serialize передается объект класса CArchive, связанный с файлом в который надо записать или из которого надо прочитать документ.
Метод Serialize вызывается и для загрузки и для сохранения документа. Чтобы узнать, что надо делать с документом, используется метод IsStoring класса CArchive. Если он возвращает ненулевое значение, значит вы должны сохранить состояние документа в файле. В противном случае вы должны считать документ из файла. Боле подробно использование метода Serialize для сохранения и восстановления документов, описано в разделе “Запись и восстановление объектов” главы “Некоторые классы MFC”.
//////////////////////////////////////////////////////////////
// Диагностические методы класса CSingleDoc
#ifdef _DEBUG
void CSingleDoc::AssertValid() const {
CDocument::AssertValid();
}
void CSingleDoc::Dump(CDumpContext& dc) const {
CDocument::Dump(dc);
}
#endif //_DEBUG
class CSingleView : public CView {
protected:
CSingleView();
DECLARE_DYNCREATE(CSingleView)
// Attributes
public:
CSingleDoc* GetDocument();
// Operations
public:
// Overrides
//{{AFX_VIRTUAL(CSingleView)
public:
virtual void OnDraw(CDC* pDC);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CSingleView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Методы, предназначенные для обработки сообщений
protected:
//{{AFX_MSG(CSingleView)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Секция Overrides в описании класса CSingleView содержит описания переопределяемых виртуальных методов базового класса CView. Два метода описаны в ней как public – OnDraw и PreCreateWindow и три как protected – OnPreparePrinting, OnBeginPrinting, OnEndPrinting. Поэтому методы OnDraw и PreCreateWindow можно вызывать и из других классов приложения, а методы OnPreparePrinting, OnBeginPrinting, OnEndPrinting только из класса CSingleView.
// Объекты класса CSingleView могут создаваться динамически
IMPLEMENT_DYNCREATE(CSingleView, CView)
// Таблица сообщений класса CSingleView
BEGIN_MESSAGE_MAP(CSingleView, CView)
//{{AFX_MSG_MAP(CSingleView)
//}}AFX_MSG_MAP
// Стандартные команды предназначенные для печати документа
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
// Конструктор класса CSingleView
CSingleView::CSingleView() {
// TODO:
}
Вместе с конструктором класса CSingleView, MFC AppWizard определяет деструктор ~CSingleView. Сразу после создания проекта деструктор не выполняет никаких действий. В дальнейшем вы можете использовать его совместно с конструктором CSingleView.
// Деструктор класса CSingleView
CSingleView::~CSingleView() {}
// Окончательная версия приложения
#ifndef _DEBUG
inline CSingleDoc* CSingleView::GetDocument() { return (CSingleDoc*)m_pDocument; }
#endif
Отладочная версия GetDocument расположена в файле реализации класса окна просмотра SingleView.cpp. Откройте этот файл вручную, выбрав его название из страницы FileView окна Project Workspace.
// Отладочная версия приложения
#ifdef _DEBUG
CSingleDoc* CSingleView::GetDocument() {
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSingleDoc)));
return (CSingleDoc*)m_pDocument;
}
#endif //_DEBUG
Макрокоманда RUNTIME_CLASS возвращает указатель на структуру CRuntimeClass, содержащую информацию о классе CSingleDoc. Метод IsKindOf, определенный в классе CObject, проверяет, принадлежит ли объект, на который указывает m_pDocument, к классу CSingleDoc или классу наследованному от CSingleDoc. Если в приложении есть ошибка и m_pDocument не указывает на документ приложения, макрокоманда ASSERT отображает соответствующее сообщение и прерывает работу приложения.
Поле структуры CREATESTRUCT | Описание |
---|---|
lpCreateParams | Указатель на данные, используемые при создании окна |
hInstance | Идентификатор приложения |
hMenu | Идентификатор меню |
hwndParent | Идентификатор родительского окна. Содержит NULL, если окно не имеет родительского окна |
cy | Высота окна |
cx | Ширина окна |
y | Определяет y-координату верхнего левого угла окна. Для дочерних окон координаты задаются относительно родительского окна. Для родительского окна координаты указываются в экранной системе координат |
x | Определяет x-координату верхнего левого угла окна. Координаты задаются также как и для поля y |
style | Стиль класса |
lpszName | Указатель на строку, закрытую двоичным нулем, в которой находится имя окна |
lpszClass | Имя класса окна (смотри том 11 из серии “Библиотека системного программиста”) |
dwExStyle | Дополнительные стили окна |
BOOL CSingleView::PreCreateWindow(CREATESTRUCT& cs) {
// TODO: Здесь вы можете внести изменения в структуру cs
// Вызов метода PreCreateWindow базового класса CView
return CView::PreCreateWindow(cs);
}
BOOL IsPrinting() const;
Метод IsPrinting возвращает ненулевое значение, если объект контекста устройства, для которого он вызывается, является контекстом принтера. Если контекст представляет другое устройства отображения, например окно, тогда метод IsPrinting возвращает нуль.
MFC AppWizard переопределяет для вас метод OnDraw класса CView следующим образом:
void CSingleView::OnDraw(CDC* pDC) {
CSingleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: Здесь вы можете расположить код, для отображения
// данных в контексте устройства pDC
}
Первые две строки метода OnDraw служат для получения указателя pDoc на документ, связанный с данным окном просмотра. Предполагается, что используя указатель pDoc, вы получите данные из документа и отобразите их на экране.
Метод | Назначение |
---|---|
OnPreparePrinting | Вызывается перед печатью документа |
OnBeginPrinting | Используется, для получения шрифтов и других ресурсов GDI |
OnEndPrinting | Используется для освобождения ресурсов, полученных методом OnBeginPrinting |
//////////////////////////////////////////////////////////////
// Методы используемые для печати документов
BOOL CSingleView::OnPreparePrinting(CPrintInfo* pInfo) {
// Выполняется обработка по умолчанию
return DoPreparePrinting(pInfo);
}
void CSingleView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {
// TODO: здесь можно выполнить дополнительную инициализацию
// перед печатью документа
}
void CSingleView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {
// TODO: здесь можно выполнить действия после печати
// документа
}
//////////////////////////////////////////////////////////////
// Диагностические методы класса CSingleView
#ifdef _DEBUG
void CSingleView::AssertValid() const {
CView::AssertValid();
}
void CSingleView::Dump(CDumpContext& dc) const {
CView::Dump(dc);
}
#endif //_DEBUG
class CMainFrame : public CFrameWnd {
protected:
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
//{{AFX_VIRTUAL(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Панель управления и панель состояния
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
// Методы, предназначенные для обработки сообщений
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Шаблон документов приложения создает объекты класса CMainFrame динамически. Для этого в определении класса указана макрокоманда DECLARE_DYNCREATE, объявлен конструктор, не имеющий параметров, а в файле реализации добавлена макрокоманда IMPLEMENT_DYNCREATE.
// Объекты класса CMainFrame могут создаваться динамически
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
// Таблица сообщений класса CMainFrame
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Изначально в таблице сообщений расположена единственная макрокоманда ON_WM_CREATE. Эта макрокоманда устанавливает для обработки сообщения WM_CREATE метод OnCreate. Как вы знаете, сообщение WM_CREATE передается функции окна сразу после его создания, но до того, как окно появится на экране.
// Конструктор класса CMainFrame
CMainFrame::CMainFrame() {
// TODO:
}
// Деструктор класса CMainFrame
CMainFrame::~CMainFrame() {}
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
Параметр lpCreateStruct содержит указатель на объект CREATESTRUCT, содержащий характеристики создаваемого окна. Эта структура уже была нами описана ранее.
При нормальной работе OnCreate должен вернуть значение 0, чтобы продолжить создание окна. Если OnCreate возвращает –1, окно будет удалено (уничтожено).
MFC AppWizard переопределяет метод OnCreate в классе CMainFrame. Поэтому для обработки сообщения WM_CREATE будет вызван именно переопределенный метод OnCreate класса CMainFrame.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
// Вызываем метод OnCreate базового класса
if (CFrameWnd::OnCreate(lpCreateStruct) == –1) return –1;
// Создаем панель управления toolbar
if (!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
// Ошибка при создании панели управления toolbar
TRACE0("Failed to create toolbar\n");
return –1;
}
// Создаем панель состояния status bar
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) {
// Ошибка при создании панели состояния status bar
TRACE0("Failed to create status bar\n");
return –1;
}
// TODO: вы можете изменить характеристики панели
// управления, убрав некоторые флаги CBRS_
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// TODO: вы можете запретить перемещение панели управления,
// если удалите следующие три строки программы
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
Основное назначение метода OnCreate заключается в том, что он сначала вызывает метод OnCreate базового класса CFrameWnd , а затем создает и отображает внутри главного окна панель управления toolbar и панель состояния status bar.
Метод OnCreate базового класса CFrameWnd выполняет обработку сообщения WM_CREATE по умолчанию и возвращает нулевое значение если обработка прошла без ошибок.
В случае возникновения ошибок при обработке сообщения в базовом классе возвращается –1. Метод CMainFrame::OnCreate при этом также прерывает дальнейшую обработку и возвращает –1, вызывая удаление окна.
CStatusBar←|←CControlBar←CWnd←CmdTarget←CObject
CToolBar ←|
Полное описание этих классов заняло бы слишком много места, поэтому мы ограничимся рассказом о самых главных методах, необходимых для работы с панелями управления и состояния.
protected:
CToolBar m_wndToolBar;
Создание самой панели управления и отображение ее на экране выполняется во время обработки метода OnCreate класса CMainFrame. При этом методы Create и LoadToolBar вызываются в одной строке.
if (!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
// Ошибка при создании панели управления toolbar
TRACE0("Failed to create toolbar\n");
return –1;
}
В качестве родительского окна панели управления методу Create указано ключевое слово this. Таким образом, родительским окном является главное окно приложения, представленное объектом класса CMainFrame.
После создания панели управления сразу вызывается метод LoadToolBar, загружающий ресурс панели управления IDR_MAINFRAME. Если хотя бы один метод – Create или LoadToolBar, завершится с ошибкой, метод OnCreate класса CMainFrame возвращает –1.
protected:
CStatusBar m_wndStatusBar;
Создание панели состояния и отображение ее на экране выполняется во время обработки метода OnCreate класса CMainFrame сразу после создания панели управления.
Методы Create и SetIndicators, создающие панель, вызываются в одной строке.
// Создаем панель status bar
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) {
// Ошибка при создании панели состояния status bar
TRACE0("Failed to create status bar\n");
return –1;
}
В качестве родительского окна панели состояния методу Create указано ключевое слово this. Таким образом, родительским окном панели состояния, также как и панели управления, является главное окно приложения, представленное объектом класса CMainFrame.
Информация, которая должна отображаться в панели состояния, определяется идентификаторами, записанными в массиве. Каждый такой идентификатор представляет текстовую строку из таблицы ресурсов приложения.
В нашем приложении массив indicators, описывающий панель состояния, определен в файле MainFrm.cpp непосредственно после таблицы сообщений.
static UINT indicators[] = {
ID_SEPARATOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
Сразу после создания панели состояния вызывается метод SetIndicators. В первом параметре ему передается массив indicators, а во втором – количество элементов в этом массиве.
Метод SetIndicators отображает в панели состояния строки, идентификаторы которых представлены в массиве indicators. При этом первый элемент массива определяет крайнюю левую строку в панели состояния. По умолчанию строки в панели состояния выравниваются по правой границе. Вы можете из программы получить доступ к любому элементу панели состояния по его индексу или идентификатору.
После создания панели управления toolbar метод SetBarStyle класса CControlBar устанавливает различные стили этой панели. Стили определяются одной или несколькими константами CBRS_.
Константа | Описание |
---|---|
CBRS_ALIGN_TOP | Панель управления может закрепляться в верхней части окна |
CBRS_ALIGN_BOTTOM | Панель управления может закрепляться в нижней части окна |
CBRS_ALIGN_LEFT | Панель управления может закрепляться на левой стороне окна |
CBRS_ALIGN_RIGHT | Панель управления может закрепляться на правой стороне окна |
CBRS_ALIGN_ANY | Панель управления может закрепляться с любой стороны окна |
CBRS_FLOAT_MULTI | Несколько панелей управления могут объединяться вместе в одном мини-окне |
CBRS_TOOLTIPS | Когда пользователь устанавливает курсор на органы управления панели toolbar, их названия отображаются в маленькой прямоугольной рамке tool tips. Более подробно о tool tips вы можете прочитать в томе 22 серии “Библиотека системного программиста” |
CBRS_FLYBY | Текст в панели состояния изменяется, одновременно с отображением tool tips. В противном случае текст в панели состояния меняется только когда пользователь нажмет кнопку в панели управления |
CBRS_SIZE_DYNAMIC | Когда панель управления отображается в окне, пользователь может изменять ее размеры. При этом органы управления панели будут отображаться в нескольких строках. Этот стиль доступен только для Visual C++ версии 4.0 и выше |
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
Чтобы панель управления могла отображаться в собственном маленьком окне, не прикрепленном ни к одной стороне родительского окна, два раза вызываются метод EnableDocking – для панели управления и для главного окна приложения. Затем вызывается метод DockControlBar для главного окна приложения.
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {
// TODO: Здесь вы можете внести изменения в структуру cs
return CFrameWnd::PreCreateWindow(cs);
}
//////////////////////////////////////////////////////////////
// Диагностические методы класса CMainFrame
#ifdef _DEBUG
void CMainFrame::AssertValid() const {
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const {
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
На этом мы заканчиваем рассмотрение исходных текстов приложения с однооконным интерфейсом, созданных системой автоматизированного проектирования MFC AppWizard. В следующей главе мы расскажем как вы можете доработать полученное приложение, чтобы создать простейший графический редактор, с возможностью записи подготовленных в нем документов в файл.
Идентификатор командного сообщения | Описание |
---|---|
ID_FILE_NEW | Создать новый документ. Класс CWinApp содержит стандартный обработчик этого сообщения – метод OnFileNew. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single) |
ID_FILE_OPEN | Открыть документ, записанный на диске. Класс CWinApp содержит стандартный обработчик этого сообщения – метод OnFileOpen. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single) |
ID_FILE_CLOSE | Закрыть текущий документ. Класс CDocument содержит метод OnFileClose, предназначенный для обработки этого командного сообщения. Метод OnFileClose вызывает метод SaveModified, если документ приложения был изменен, а затем вызывает метод OnCloseDocument |
ID_FILE_SAVE | Сохранить текущий документ. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument |
ID_FILE_SAVE_AS | Сохранить текущий документ под новым именем. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument |
ID_FILE_SAVE_COPY_AS | Сохранить копию текущего документа под новым именем |
ID_FILE_PAGE_SETUP | Вызывает диалоговую панель выбора формата документа |
ID_FILE_PRINT_SETUP | Вызвать диалоговую панель для настройки принтера |
ID_FILE_PRINT | Выполнить печать текущего документа |
ID_FILE_PRINT_PREVIEW | Перейти в режим предварительного просмотра документа перед печатью |
ID_FILE_MRU_FILE1…FILE16 | Открыть один из наиболее часто используемых файлов приложения |
Идентификатор командного сообщения | Описание |
---|---|
ID_EDIT_CLEAR | Удалить выделенный объект |
ID_EDIT_CLEAR_ALL | Удалить содержимое документа |
ID_EDIT_COPY | Скопировать выделенный объект в универсальный буфер обмена clipboard |
ID_EDIT_CUT | Удалить выделенный объект и записать его в clipboard |
ID_EDIT_FIND | Отобразить на экране диалоговую панель для поиска заданного объекта в документе |
ID_EDIT_PASTE | Вставить в документ содержимое Clipboard |
ID_EDIT_REPEAT | Повторить последнюю операцию |
ID_EDIT_REPLACE | Отобразить диалоговую панель для поиска и замены текста |
ID_EDIT_SELECT_ALL | Выбрать (выделить) весь документ |
ID_EDIT_UNDO | Отменить последнюю операцию |
ID_EDIT_REDO | Выполнить последнюю отмененную операцию |
Идентификатор командного сообщения | Описание |
---|---|
ID_WINDOW_NEW | Открыть новое окно с текущим документом |
ID_WINDOW_ARRANGE | Выровнять пиктограммы в нижней части окна MDI |
ID_WINDOW_CASCADE | Выполнить каскадное размещение окон |
ID_WINDOW_TILE_HORZ | Расположить окна рядом по горизонтали |
ID_WINDOW_TILE_VERT | Расположить окна рядом по вертикали |
ID_WINDOW_SPLIT | Разделить окно на две части |
Идентификатор командного сообщения | Описание |
---|---|
ID_APP_EXIT | Завершить приложение. Данное командное сообщение обрабатывается методом OnAppExit класса CWinApp. Метод OnAppExit передает сообщение WM_CLOSE главному окну приложения |
ID_APP_ABOUT | Отобразить на экране краткую справку о программе – диалоговую панель About. Ни один из классов MFC не выполняет обработки этого сообщения по умолчанию, но MFC AppWizard автоматически создает необходимый для этого программный код |
Идентификатор командного сообщения | Описание |
---|---|
ID_HELP_INDEX | Отобразить список статей из справочной базы данных, записанной в HLP-файле |
ID_HELP_USING | Отобразить подсказку об использовании справочной системы |
ID_CONTEXT_HELP | Перейди в режим контекстной подсказки. Передается также при нажатии комбинации клавиш <Shift+F1> |
ID_HELP | Получить справочную информацию по данному контексту |
ID_DEFAULT_HELP | Получить справочную информацию определенную по умолчанию для данного контекста |
Идентификатор командного сообщения | Описание |
---|---|
ID_VIEW_TOOLBAR Отобразить или скрыть панель управления | toolbar |
ID_VIEW_STATUS_BAR | Отобразить или скрыть панель состояния status bar |
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point) {
// TODO: Здесь вы можете разместить код метода
CView::OnLButtonDown(nFlags, point);
}
Название этого метода ClassWizard выбирает автоматически на основе сообщения WM_LBUTTONDOWN. Для этого префикс WM_ в названии сообщения заменяется префиксом On и происходит замена некоторых прописных букв строчными.
Шаблон метода OnLButtonDown содержит вызов метода OnLButtonDown базового класса CView. Вы должны добавить свой код перед вызовом этого метода, сразу после коментария // TODO:.
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point) {
// TODO: Здесь вы можете разместить код метода
CClientDC dc(this);
dc.Ellipse(point.x-10, point.y-10, point.x+10,point.y+10);
CView::OnLButtonDown(nFlags, point);
}
Чтобы нарисовать в окне просмотра окружность, сначала необходимо получить контекст отображения. Для этого создается объект dc класса CClientDC. Конструктору передается указатель this, который указывает на объект класса CSingleView.
Затем вызывается метод Ellipse, который и отображает на экране небольшую окружность с центром, совпадающим с координатами указателя мыши.
Повторите только что проделанную процедуру для сообщения WM_RBUTTONDOWN. Создайте метод обработчик этого сообщения и добавьте в него команды отображения квадрата.
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point) {
// TODO: Здесь вы можете разместить код метода
CClientDC dc(this);
dc.Rectangle(point.x-10, point.y-10, point.x+10,point.y+10);
CView::OnRButtonDown(nFlags, point);
}
Постройте проект и запустите полученное приложение. Нажимайте правую и левую кнопку мыши. Вы увидите, что на экране появляются окружности и квадраты (рис. 5.13). Поэкспериментируйте с приложением. Вы заметите, что изображение на экране пропадает, если оно перекрывается другими окнами, а также в некоторых других случаях.
//////////////////////////////////////////////////////////////
// Класс определяет геометрическую фигуру
class CFigure {
public:
// Координаты центра фигуры
CPoint xyFigCenter;
// Тип фигуры: 'E' – оокружность, 'R' – кволрат
char cType;
};
Один объект класса CFigure представляет одну геометрическую фигуру. Так как документ нашего приложения может содержать несколько фигур, мы воспользуемся шаблоном CArray, чтобы определить массив объектов класса CFigure. Вы можете получить дополнительную информацию о шаблоне CArray в разделе “Коллекции” главы “Некоторые классы MFC”.
Определение этого массива, который получил название arrayFig, помещаем в класс документа CSingleDoc, в атрибутах класса.
//////////////////////////////////////////////////////////////
// Класс CSingleDoc
class CSingleDoc : public CDocument {
protected:
CSingleDoc();
DECLARE_DYNCREATE(CSingleDoc)
// Attributes
public:
CArray<CFigure, CFigure&> arrayFig;
Если вы используете шаблоны классов CArray, CMap или CList, вы должны включить в исходный текст приложения файл afxtempl.h. В данном файле содержатся определения этих шаблонов.
Так как мы работаем с объектами класса CArray в различных файлах, удобнее всего включить его в самом конце файла stdafx.h.
// Включаемый файл stdafx.h
// …
// Включаемый файл для шаблона CArray
#include <afxtempl.h>
Теперь у нас есть структура для хранения геометрических фигур, нарисованных в окне. Мы должны ее заполнить. Так как за взаимодействие с пользователем отвечает класс окна просмотра, мы изменяем определенные нами ранее методы OnLButtonDown и OnRButtonDown таким образом, чтобы одновременно с выводом на экран они сохраняли параметры новой фигуры в массиве arrayFig.
//////////////////////////////////////////////////////////////
// Метод OnLButtonDown класса CSingleView
// Обрабатывает сообщения левой кнопки мыши
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point) {
// Получаем указатель на документ (объект класса CSingleDoc)
CSingleDoc* pDoc = GetDocument();
// Проверяем указатель pDoc
ASSERT_VALID(pDoc);
// Отображаем на экране окружность
CClientDC dc(this);
dc.Ellipse(point.x-10, point.y-10, point.x+10, point.y+10);
// Сохраняем характеристики окружности
CFigure OneFigure;
OneFigure.xyFigCenter = point;
OneFigure.cType = 'E';
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Вызываем метод OnLButtonDown базового класса CView
CView::OnLButtonDown(nFlags, point);
}
//////////////////////////////////////////////////////////////
// Метод OnRButtonDown класса CSingleView
// Обрабатывает сообщения правой кнопки мыши
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point) {
// Получаем указатель на документ (объект класса CSingleDoc)
CSingleDoc* pDoc = GetDocument();
// Проверяем указатель pDoc
ASSERT_VALID(pDoc);
// Отображаем на экране квадрат
CClientDC dc(this);
dc.Rectangle(point.x-10, point.y-10, point.x+10, point.y+10);
// Сохраняем характеристики квадрата
CFigure OneFigure;
OneFigure.xyFigCenter = point;
OneFigure.cType = 'R';
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Вызываем метод OnRButtonDown базового класса CView
CView::OnRButtonDown(nFlags, point);
}
Теперь координаты и форма всех нарисованных фигур запоминаются в классе документа. Следующим шагом надо определить, как отображать эти фигуры на экране. Для этого следует внести изменения в метод OnDraw класса окна просмотра CSingleView.
//////////////////////////////////////////////////////////////
// Метод OnDraw класса окна просмотра
void CSingleView::OnDraw(CDC* pDC) {
CSingleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO:
int i;
for (i=0; i<pDoc->arrayFig.GetSize(); i++) {
if (pDoc->arrayFig[i].cType == 'E') pDC->Ellipse(pDoc->arrayFig[i].xyFigCenter.x-10, pDoc->arrayFig[i].xyFigCenter.y-10, pDoc->arrayFig[i].xyFigCenter.x+10, pDoc->arrayFig[i].xyFigCenter.y+10);
else if (pDoc->arrayFig[i].cType == 'R') pDC->Rectangle(pDoc->arrayFig[i].xyFigCenter.x-10, pDoc->arrayFig[i].xyFigCenter.y-10, pDoc->arrayFig[i].xyFigCenter.x+10, pDoc->arrayFig[i].xyFigCenter.y+10);
}
}
Постройте проект и запустите полученное приложение. Вы можете свободно изменять размеры окна приложения, перекрывать его окно окнами других приложений, минимизировать и восстанавливать размеры окна. Изображение документа, которое вы нарисуете, не пропадет.
Вы даже можете распечатать нарисованный документ на принтере. А ведь вы не написали для этого не единой строки кода. Перед печатью документа его можно проверить в режиме предварительного просмотра (рис. 5.14). Для этого выберите из меню File строку Print Preview.
//////////////////////////////////////////////////////////////
// Метод DeleteContents
void CSingleDoc::DeleteContents() {
// TODO:
// Очищаем документ, удаляя все элементы массива arrayFig.
// Метод RemoveAll определен в классе CArray
arrayFig.RemoveAll();
// Вызываем метод DeleteContents базового класса CDocument
CDocument::DeleteContents();
}
//////////////////////////////////////////////////////////////
// Метод Serialize класса CSingleDoc отвечает за сохранение и
// последующее восстановление документов приложения
void CSingleDoc::Serialize(CArchive& ar) {
if (ar.IsStoring()) {
// TODO: Здесь выполняется сохранение документа
} else {
// TODO: Здесь выполняется загрузка документа
}
}
Вы должны определить в методе Serialize, как он должен сохранять и восстанавливать документы приложения. Так как документ, с которым работает наше приложение представлен классом CSingleDoc, то все что должен делать метод Serialize – это сохранять все элементы массива arrayFig.
//////////////////////////////////////////////////////////////
// Метод Serialize класса CSingleDoc
void CSingleDoc::Serialize(CArchive& ar) {
int i; // временная переменная
int num; // количество фигур в документе
// Сохранение документа
if (ar.IsStoring()) {
// Определяем количество элементов массива arrayFig
num = arrayFig.GetSize();
// Записываем полученное число в файл
ar << num;
// Записываем в файл координаты и тип фигур
for (i=0; i<num; i++) {
// Сохраняем координаты центра фигуры
ar << arrayFig[i].xyFigCenter;
// Сохраняем тип фигуры
ar << arrayFig[i].cType;
}
}
// Загрузка документа
else {
// Считываем количество элементов, составляющих документ
ar >> num;
// Восстанавливаем документ
for (i=0; i<num; i++) {
CFigure OneFigure; // описание одной фигуры
// Считываем координаты центра фигуры
ar >> OneFigure.xyFigCenter;
// Считываем тип фигуры
ar >> OneFigure.cType;
// Добавляем описание очередной фигуры в документ
arrayFig.Add(OneFigure);
}
}
}
Метод Serialize имеет единственный параметр ar, представляющий ссылку на объект класса CArchive. Этот объект, называемый архивом, представляет файл документа, расположенный на диске. Кроме того, архив несет в себе информацию о том, что делать с документом – записать его в файл или загрузить из файла.
После вызова, метод Serialize определяет, какую операцию надо выполнить – сохранить документ в файле или загрузить его из файла. Для этого используется метод IsStoring, определенный в классе CArchive. Если метод IsStoring возвращает ненулевое значение для объекта ar, переданного методу Serialize, значит надо сохранить документ в файле.
Чтобы сохранить все элементы массива, мы определяем количество элементов в нем с помощью метода GetSize. Этот метод определен в шаблоне CArray и возвращает количество элементов массива.
Мы сохраняем количество элементов массива в файле, представленном архивом ar. Это значение поможет нам при восстановлении документа с файла на диске. Затем в цикле в файл записываются все элементы массива arrayFig.
Загрузка документа из файла выполняется в том же порядке. Сначала из файла документа, представленного архивом ar считывается значение, определяющее количество фигур в документе. Потом из файла считываются по очереди все элементы документа. При этом они сразу заносятся в массив arrayFig, представляющий документ. Для этого используется метод Add шаблона CArray.
После того, как вы внесете изменения в метод Serialize, постройте проект. Запустите полученное приложение. Теперь вы сможете записать документ в файл на диске, а затем загрузить его снова.
void SetModifiedFlag(BOOL bModified = TRUE);
Если документ изменен, установите флаг модификации, вызвав метод SetModifiedFlag с параметром bModified, равным TRUE или без параметра. В случае необходимости вы можете убрать установленный флаг. Для этого надо вызвать метод SetModifiedFlag с параметром bModified, равным FALSE.
Мы должны добавить вызов метода SetModifiedFlag в методах OnLButtonDown и OnRButtonDown, выполняющих модификацию документа. Вызов метода можно разместить в любом месте, например, сразу после добавления к массиву arrayFig, представляющему документ, нового элемента.
//////////////////////////////////////////////////////////////
// Метод OnLButtonDown класса CSingleView
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point) {
// …
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Устанавливаем флаг изменения документа
pDoc->SetModifiedFlag();
CView::OnLButtonDown(nFlags, point);
}
//////////////////////////////////////////////////////////////
// Метод OnRButtonDown класса CSingleView
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point) {
// …
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Устанавливаем флаг изменения документа
pDoc->SetModifiedFlag();
CView::OnRButtonDown(nFlags, point);
}
CEditView ←|←CCtrlView←|←CView←CWnd
CRichEditView←| |
CListView ←| |
CTreeView ←| |
|
CFormView←CScrollView←|
Опишем основные характеристики этих классов.
Класс | Описание |
---|---|
CView | Наиболее общий класс, обеспечивающий отображение документа и взаимодействие с пользователем |
CScrollView | Класс CScrollView наследован от базового класса CView. В этом классе добавлены полосы просмотра, позволяющие перемещать документ в окне |
CEditView | Класс наследован от класса CView. Класс CEditView предоставляет возможности простого текстового редактора |
CRichEditView | Класс наследован от класса CView. Класс предоставляет возможности текстового редактора. В отличие от CEditView, он позволяет работать с текстом в формате RTF |
CFormView | Класс обеспечивает форматированное отображение документа на основе диалоговой панели |
CListView | Класс обеспечивает отображение документа с использование спискового органа управления |
TreeView | Класс обеспечивает отображение документа с использование древовидного органа управления |
Строка меню Edit | Описание |
---|---|
Undo | Отменить последнюю операцию |
Cut | Удалить выделенный текст и записать его в clipboard |
Copy | Скопировать выделенный текст в clipboard |
Paste | Вставить в документ содержимое clipboard |