Microsoft Visual C++ и MFC. Программирование для Win95 и WinNT

         

Метод PreCreateWindow


Виртуальный метод PreCreateWindow определен в классе CWnd. Он вызывается непосредственно перед созданием окна, связанного с объектом класса. В качестве параметра cs этому методу передается структура CREATESTRUCT, определяющая характеристики создаваемого окна. Приложение может изменить данные, записанные в этой структуре, чтобы повлиять на внешний вид создаваемого окна.

Классы, наследованные от CWnd, в том числе CView и CFrameWnd, переопределяют этот метод, изменяя структуру cs. В следующей таблице описано назначение полей структуры CREATESTRUCT.

 

Поле структуры CREATESTRUCT

Описание

lpCreateParams

Указатель на данные, используемые при создании окна

hInstance

Идентификатор приложения



hMenu

Идентификатор меню

hwndParent

Идентификатор родительского окна. Содержит NULL, если окно не имеет родительского окна

cy

Высота окна

cx

Ширина окна

y

Определяет y-координату верхнего левого угла окна. Для дочерних окон координаты задаются относительно родительского окна. Для родительского окна координаты указываются в экранной системе координат

x

Определяет x-координату верхнего левого угла окна. Координаты задаются также как и для поля y

style

Стиль класса

lpszName

Указатель на строку, закрытую двоичным нулем, в которой находится имя окна

lpszClass

Имя класса окна (смотри том 11 из серии “Библиотека системного программиста”)

dwExStyle

Дополнительные стили окна

MFC AppWizard переопределяет для вас метод PreCreateWindow, но не вносит в структуру cs никаких изменений и вызывает метод PreCreateWindow базового класса CView.

BOOL CSingleView::PreCreateWindow(CREATESTRUCT& cs)

{

      // TODO: Здесь вы можете внести изменения в структуру cs

      // Вызов метода PreCreateWindow базового класса CView

      return CView::PreCreateWindow(cs);

}


MFC AppWizard переопределяет для класса CMainFrame виртуальный метод PreCreateWindow, но не вносит в структуру cs никаких изменений и вызывает метод PreCreateWindow базового класса CFrameWnd.

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

      // TODO: Здесь вы можете внести изменения в структуру cs

      return CFrameWnd::PreCreateWindow(cs);

}



Методы AssertValid и Dump


Класс CSingleDoc содержит переопределения еще двух виртуальных методов - AssertValid и Dump, входящих в базовый класс CObject. Описание методов AssertValid и Dump вы можете найти в разделе “Класс CObject - основной класс MFC” главы “Некоторые классы MFC”.

Обратите внимание, что описание этих методов и их определение расположено в блоке #ifdef _DEBUG. Поэтому эти методы используются только для отладочной версии приложения. Когда выполняется окончательное построение приложения, эти методы не переопределяются.

//////////////////////////////////////////////////////////////

// Диагностические методы класса CSingleDoc

#ifdef _DEBUG

void CSingleDoc::AssertValid() const

{

      CDocument::AssertValid();

}

void CSingleDoc::Dump(CDumpContext& dc) const

{

      CDocument::Dump(dc);

}

#endif //_DEBUG


Во время отладки приложения в состав класса CSingleView включаются переопределения виртуальных методов AssertValid и Dump. Эти методы определены в базовом классе CObject и используются при отладке приложения. Когда выполняется окончательное построение приложения, эти методы не переопределяются.

//////////////////////////////////////////////////////////////

// Диагностические методы класса CSingleView

#ifdef _DEBUG

void CSingleView::AssertValid() const

{

      CView::AssertValid();

}

void CSingleView::Dump(CDumpContext& dc) const

{

      CView::Dump(dc);

}

#endif //_DEBUG




Когда вы выполняете построение отладочной версии приложения, в состав класса CMainFrame включаются переопределения виртуальных методов AssertValid и Dump. Эти методы определены в базовом классе CObject и используются при отладке приложения.

Когда отладочный режим отключен, символ _DEBUG не определен и поэтому методы AssertValid и Dump класса CObject не переопределяются.

//////////////////////////////////////////////////////////////

// Диагностические методы класса CMainFrame

#ifdef _DEBUG

void CMainFrame::AssertValid() const

{

      CFrameWnd::AssertValid();

}

void CMainFrame::Dump(CDumpContext& dc) const

{

      CFrameWnd::Dump(dc);

}

#endif //_DEBUG

На этом мы заканчиваем рассмотрение исходных текстов приложения с однооконным интерфейсом, созданных системой автоматизированного проектирования MFC AppWizard. В следующей главе мы расскажем как вы можете доработать полученное приложение, чтобы создать простейший графический редактор, с возможностью записи подготовленных в нем документов в файл.



Методы, не изменяющие объекты класса


Если метод не изменяет объект, для которого он вызывается, такой метод можно объявить с ключевым словом const. Ключевое слово const указывается после закрывающей скобки списка аргументов метода. Вы должны указать, что метод не изменяет объект и в объявлении и в определении метода.

Методы, объявленные как const, не могут изменять элементы класса или вызывать другие методы, объявленные без ключевого слова const. Нарушение этих правил вызовет ошибку на этапе компиляции приложения.

В библиотеке классов MFC вы встретите много методов, объявленных как const. Их использование повышает надежность приложения, так как компилятор сможет обнаружить ошибки, связанные с непреднамеренным изменением элементов класса.

Ниже мы привели пример класса, для которого метод GetWeight определен как const. Если вы попытаетесь модифицировать элемент данных weight непосредственно из метода GetWeight, компилятор сообщит об ошибке.

#include <iostream.h>

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";

}



Методы OnNewDocument и Serialize


В классе CSingleDoc переопределены два виртуальных метода OnNewDocument и Serialize. Виртуальный метод OnNewDocument определен в классе CDocument, от которого непосредственно наследуется класс CSingleDoc. А вот виртуальный метод Serialize определен в классе CObject. Цепочка наследования классов в этом случае длиннее:

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”.



Методы OnPreparePrinting, OnBeginPrinting и OnEndPrinting


Виртуальные методы OnPreparePrinting, OnBeginPrinting и OnEndPrinting, определенные в классе CView, вызываются, если пользователь желает распечатать документ, отображенный в данном окне просмотра.

Метод

Назначение

OnPreparePrinting

Вызывается перед печатью документа

OnBeginPrinting

Используется, для получения шрифтов и других ресурсов GDI

OnEndPrinting

Используется для освобождения ресурсов, полученных методом OnBeginPrinting

В нашей первой книге, посвященной библиотеке классов MFC, мы не будем останавливаться на проблемах, связанных с печатью документов. Поэтому оставьте шаблоны методов OnPreparePrinting, OnBeginPrinting и OnEndPrinting, подготовленные MFC AppWizard без изменения.

//////////////////////////////////////////////////////////////

// Методы используемые для печати документов

BOOL CSingleView::OnPreparePrinting(CPrintInfo* pInfo)

{

      // Выполняется обработка по умолчанию

      return DoPreparePrinting(pInfo);

}

void CSingleView::OnBeginPrinting(CDC* /*pDC*/,

      CPrintInfo* /*pInfo*/)

{

      // TODO: здесь можно выполнить дополнительную инициализацию

      // перед печатью документа

}

void CSingleView::OnEndPrinting(CDC* /*pDC*/,

      CPrintInfo* /*pInfo*/)

{

      // TODO: здесь можно выполнить действия после печати

      // документа

}



Методы, входящие в класс


Если исходный текст метода очень короткий, то такой метод обычно определяется непосредственно внутри класса. Вы можете указать, что вместо вызова необходимо выполнять подстановку его тела. Для этого перед ее объявлением следует указать ключевое слово inline. Вот пример определения методов SetWeight и GetWeight непосредственно внутри класса:

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();

}

Методы класса могут быть перегружены. В одном и том же классе можно определить несколько методов с одинаковыми именами, но различным набором параметров.



Microsoft Visual C++


Существует несколько версий компилятора Microsoft Visual C++. Наибольшее распространение получили версии 1.51, 2.0 и 2.2. В начале 1996 года появилась новая версия Visual C++ 4.0.

Microsoft Visual C++ версии 1.51 содержит только 16-разрядный компилятор. Он позволяет разрабатывать программы для операционной системы MS-DOS и Windows 3.1. Эта версия компилятора не позволяет создавать приложения, предназначенные специально для 32-х разрядных операционных систем Windows NT и Windows 95.

Версия 2.0 содержит 32-разрядный компилятор. С его помощью можно создавать приложения для Windows NT и Windows 95. Сама среда Microsoft Visual C++ версии 2.0 может работать только в 32-разрядной операционной системе Windows NT или Windows 95. Как это ни печально, но Visual C++ версии 2.0 не позволит вам создать приложения для операционных систем MS-DOS и Windows 3.1. Для этого предлагается воспользоваться предыдущей версией компилятора.

На этом различия между Visual C++ версий 1.51 и 2.0 заканчиваются. В Visual C++ версии 1.51 используется 16-и разрядная библиотека классов MFC версии 2.5, а Visual C++ 2.0 поставляется с 32-разрядной библиотекой MFC версии 3.0.

При переходе к 32-разрядным приложениям фирма Microsoft отказалась поддерживать элементы управления VBX. Вместо них MFC 3.0 позволяет работать с элементами управления OLE. Новый стандарт на элементы управления получил название OCX (OLE Custom Controls).

Фирма Microsoft приложила большие усилия, чтобы организовать совместимость своего компилятора с различными архитектурами компьютеров. Специальные версии Visual C++ работают на компьютерах с MIPS архитектурой, на компьютерах с процессорами Alpha, Motorola PowerPC. Это позволяет легко переносить разработанные приложения с одной аппаратной платформы на другую.

Популярность библиотеки классов MFC настолько высока, что такие фирмы, как Watcom и Symantec, известные своими компиляторами Си и Си++, приобрели у Microsoft лицензии на эту библиотеку. Практически единственной широко известной в России фирмой, которая поставляет компиляторы Си++ с собственной библиотекой классов является Borland. Компиляторы Borland C++ включают библиотеку классов OWL (Object Windows Library). Эта библиотека совершенно не совместима с библиотекой MFC, поэтому программы, построенные с ее использованием, нельзя транслировать компилятором Visual C++. Компилятор Borland C++ версии 5.0 позволяет транслировать приложения, созданные с использованием библиотеки классов MFC.



В конце 1995 года появилась


В конце 1995 года появилась новая версия Microsoft Visual C++ - 4.0. Этот компилятор интегрирован в среду Microsoft Developer Studio, в состав которого могут входить другие программные  продукты Microsoft.



Интегрированная среда разработчика объединяет одной оболочкой 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.


Многозадачные приложения


Когда вы запускаете приложение в среде операционной системы Windows, начинает работать новый процесс. Если запустить это же приложение еще один раз, будет запущен новый процесс. Обычно такие процессы никак не связаны друг с другом. Когда приложение завершается, оканчивается соответствующий процесс.

Каждый процесс может состоять из нескольких нитей (thread) или задач. В рамках одного процесса его задачи выполняются параллельно. До сих пор мы рассматривали только приложения, имеющие единственную задачу. Однако, часто бывает полезно, чтобы внутри одного приложения одновременно выполнялись несколько задач. Например, одна задача может отвечать за взаимодействие с пользователем - принимать от него команды, данные, отображать информацию на экране. Вторая задача может выполнять дополнительную фоновую работу, например печать документа, обмен данными через модем, проводить вычисления.

Библиотека классов MFC позволяет создавать многозадачные приложения.

Приложения Windows 95 и Windows NT могут быть многозадачными. Такие приложения состоят из нескольких независимых задач. Дополнительные задачи могут использоваться, чтобы выполнять фоновую работу. Класс CWinApp представляет основную задачу приложения. Если вам надо создать дополнительные задачи, вы должны воспользоваться классом CWinThread.

Приложение Pview 95 позволяет просмотреть список процессов, загруженных в памяти и остановить любой процесс



Множественное наследование


Множественное наследование выполняется подобно единичному наследованию. В отличие от единичного наследования у порожденного класса может быть несколько базовых классов. На рисунке 1.2 представлен пример множественного наследования классов. Класс DerivedClaass имеет два базовых класса BaseClassOne и BaseClassSecond. Класс DerivedClaass и еще один класс BaseClass используются при множественном наследовании класса DerivedClaassSecond.

Рис. 1.2. Множественное наследование

Вместо имени единственного базового класса указывается список <base-list> имен базовых классов, разделенный запятыми. Непосредственно перед названиями базовых классов могут быть указаны спецификаторы доступа public, private и protect. Их назначение мы рассмотрим в разделе “Разграничение доступа к элементам базового класса”.

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 не использует множественное наследование, мы не станем останавливаться на нем более подробно. При необходимости вы можете получить дополнительную информацию из справочников или учебников по языку Си++ (см. список литературы).



Модель “документ - окно просмотра”


Библиотека классов MFC предлагает вам модель приложения, основанную на том, что приложение предоставляет пользователю средства для просмотра и изменения документов. С помощью меню или кнопок панели управления toolbar пользователь может создать новый документ или открыть документ, записанный в файле.

Пользователь может работать с документом, просматривая или редактируя его. Пользователь взаимодействует с документом через окно просмотра - View. Один документ одновременно может иметь несколько окон для его просмотра.

Измененный документ можно сохранить в файле на диске и продолжить с ним работу в следующий раз. Процесс сохранения документа и его загрузки в приложение называется записью и восстановлением объектов - serialize.

Процедура создания приложения, имеющего однооконный или многооконный интерфейс, несколько отличается от рассмотренной нами в разделе “Создание приложения с диалоговой панелью”. Однако первые этапы создания приложений, имеющих различный пользовательский интерфейс, полностью совпадают.



Модификация класса CMemFile


Вы можете наследовать от класса CMemFile собственные классы. При этом вы можете реализовать свой механизм выделения памяти для файла, чтения и записи данных. Для этого в состав CMemFile входят виртуальные методы Alloc, Free, Realloc, Memcpy и GrowFile.

Методы Alloc, Realloc и Free вызываются другими методами класса CMemFile чтобы выделить блок оперативной памяти для файла, изменить его размер и вернуть после использования операционной системе. Если вы решили сами управлять распределением памяти для файла, вы должны переназначить все эти методы.

Метод Alloc вызывается другими методами класса, когда необходимо получить блок оперативной памяти размера nBytes. Метод возвращает указатель на этот блок:

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);



Наследование


Пожалуй, самая важная возможность, предоставляемая программисту средствами языка Си++, заключается в механизме наследования. Вы можете наследовать от определенных ранее классов новые производные классы. Класс, от которого происходит наследование, называется базовым. Новый класс называется производным.

Производный класс включает в себя элементы базового класса и может дополнять их собственными элементами данных и методами. За счет наследования появляется возможность повторного использования кода программы.

Производный класс сам может служить базовым классом. Вы можете наследовать от него другие классы. Полученный в результате такого наследования класс будет включать в себя элементы всех его базовых классов.

От одного общего базового класса можно наследовать несколько новых производных классов. Производный класс сам может служить базовым классом для новых классов. Таким образом возможна древовидная структура наследования классов.

На рисунке 1.1 мы привели пример структуры наследования классов. От единственного базового класса BaseClass наследуются три класса DerivedClassOne, DerivedClassSecond и DerivedClassThird. Первые два из них сами выступают в качестве базовых классов.

Рис. 1.1. Наследование

Практически вся структура библиотеки классов MFC основывается на механизме наследования. Так, например, большинство классов MFC имеет базовый класс CObject, который обеспечивает наиболее общие свойства унаследованных от него классов.

Можно выделить две основных формы наследования. Когда производный класс наследуется от единственного базового класса - единичное наследование. Наследование, при котором производный класс наследуется сразу от нескольких базовых классов - это множественное наследование.



Некоторые классы MFC


В этом разделе мы опишем несколько классов, которые вы будете использовать при создании собственных приложений.  

В первую очередь, мы рассмотрим класс CObject, который является базовым для большей части классов библиотеки MFC. Затем мы изучим классы, предназначенные для хранения информации, классы, связанные с файловой системой компьютера, процедурой сохранения и восстановления объектов в файле на диске, классы реализующие механизм исключений.



Объекты графического интерфейса (класс CGdiObject)


Для отображения информации используются различные объекты графического интерфейса - GDI объекты. Для каждого из этих объектов библиотека MFC содержит описывающий его класс, наследованный от базового класса CGdiObject (рис. 2.7).

Рис. 2.7. Класс CGdiObject

Класс

Описание

CBitmap

Растровое изображение bitmap

CBrush

Кисть

CFont

Шрифт

CPalette

Палитра цветов

CPen

Перо

CRgn

Область внутри окна



Обмен данными


Виртуальный метод DoDataExchange, который мы переопределяем в классе диалоговой панели, первоначально определен в классе CWnd. Он служит для реализации механизмов автоматического обмена данными - Dialog Data Exchange (DDX) и автоматической проверки данных - Dialog Data Validation (DDV).

Механизм автоматического обмена данными позволяет привязать к органам управления диалоговой панели переменные или элементы данных класса диалоговой панели. Ряд специальных функций, определенных в библиотеке MFC, вызываются методом DoDataExchange и выполняют обмен данными между органами управления диалоговой панели и соответствующими элементами данных класса диалоговой панели. Такой обмен работает в обоих направлениях. Информация из органов управления диалоговой панели может записываться в элементы данных класса, или в обратном направлении - информация из элементов данных класса может отображаться в диалоговой панели.

Названия всех функций, обеспечивающих обмен данными, начинаются символами DDX_. Например определены функции DDX_Text, DDX_Radio, DDX_Check, DDX_Scroll и т. д. Практически каждый тип органов управления диалоговой панели имеет собственную функцию для выполнения процедуры обмена данными.

Все функции DDX_ имеют три параметра. Первый параметр содержит указатель на объект класса CDataExchange. Этот объект определяет параметры обмена, в том числе направление, в котором надо выполнить обмен данными. Второй параметр определяет идентификатор органа управления, с которым выполняется обмен данными. Третий параметр содержит ссылку на элемент данные класса диалоговой панели, связанный с данным органом управления.

Ниже представлен прототип функции DDX_, предназначенной для обмена информацией между полем редактирования диалоговой панели и элементом данных, входящим в класс диалоговой панели. Этот элемент имеет класс CString:

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);



Рис. 2.34. Вызов метода DoDataExchange



Необязательный параметр bSaveAndValidate, определяет, как будет происходить обмен данными. Если метод UpdateData вызывается с параметром FALSE, выполняется инициализация диалоговой панели. Информация из элементов данных класса отображается в органах управления диалоговой панели.

В случае, если метод UpdateData вызван с параметром TRUE, данные перемещаются в обратном направлении. Из органов управления диалоговой панели они копируются в соответствующие элементы данных класса диалоговой панели.

Метод UpdateData возвращает ненулевое значение, если обмен данными прошел успешно и нуль в противном случае. Ошибка при обмене данными может произойти, если данные копируются из диалоговой панели в элементы класса диалоговой панели и пользователь ввел неправильные данные, отвергнутые процедурой автоматической проверки данных.

При создании модальной диалоговой панели перед тем как панель появится на экране, вызывается виртуальный метод OnInitDialog класса CDialog. По умолчанию OnInitDialog вызывает метод UpdateData и выполняет инициализацию органов управления. Если вы переопределили метод OnInitDialog в своем классе диалоговой панели, в первую очередь вызовите метод OnInitDialog класса CDialog.

Метод UpdateData также вызывается некоторыми другими методами класса CDialog. Так, метод UpdateData вызывается, когда пользователь закрывает модальную диалоговую панель, нажимая кнопку OK. Заметим, что кнопка OK должна иметь идентификатор IDOK. Если пользователь нажмет на кнопку Cancel, имеющую идентификатор IDCANCEL, то диалоговая панель также закрывается, но метод UpdateData не вызывается и обмен данными не происходит.

//=====================================================

// Метод 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. Другие методы этого класса будут рассмотрены нами позже в главе посвященной автоматизированным средствам проектирования приложений.


Обработка исключительных ситуаций


При написании настоящих приложений большую роль играет обработка всевозможных ошибочных ситуаций, возникающих во время работы приложения. В качестве примера таких ошибочных ситуаций может служить неудачная попытка получить блок оперативной памяти, попытка открыть несуществующий файл и т. д.

От того насколько внимательно ваше приложение обрабатывает все ошибочные ситуации, зависит насколько надежно и устойчиво оно будет работать. Например, если приложение по мере своей работы должно получать у операционной системы большие блоки памяти и вы не учтете возможности, что оперативной памяти окажется недостаточно, приложение может вызвать срабатывание защиты памяти.

В языке Си++ реализованы специальные операторы try, throw и catch, предназначенные для обработки ошибочных ситуаций, которые называются исключениями.



Обработка командных сообщений


В разделе “Долгий путь сообщения” главы “Введение в MFC” мы начали рассказ о том, как обрабатываются командные сообщения. Теперь изучим обработку командных сообщений более подробно.

Процесс обработки командных сообщений значительно отличается от обработки других сообщений. Обычные сообщения обрабатываются только тем объектом, которому они поступили. Если таблица сообщений класса объекта не содержит обработчика сообщения, будут просмотрены таблицы сообщений его базовых классов. В том случае, если ни один из базовых классов также не содержит обработчика сообщения, выполняется обработка сообщения по умолчанию.

Судьба командных сообщений гораздо сложнее. Командное сообщение, переданное для обработки объекту приложения, может последовательно передаваться другим объектам приложения. Один из объектов, класс (или базовый класс) которого содержит обработчик этого сообщения, выполняет его обработку. Так, например, командное сообщение, переданное главному окну приложения, в конечном счете может быть обработано активным окном просмотра.

Существует стандартная последовательность объектов приложения, которым передаются командные сообщения. Каждый объект в этой последовательности может обработать командное сообщение, если в его таблице сообщений или таблице сообщений базовых классов есть соответствующая макрокоманда. Необработанные сообщения передаются дальше, другим объектам приложения.

Объекты различных классов обрабатывают командные сообщения по-разному. Например, объекты, представляющие главное окно приложения, сначала предоставляют возможность обработать полученное сообщение другим объектам, в том числе активному окну просмотра и соответствующему ему документу. Только если сообщение остается необработанным, просматривается таблица сообщений класса главного окна приложения. Если и здесь командное сообщение не обрабатывается, оно направляется другим объектам приложения.



Обработка сообщений


Как вы знаете из предыдущих томов серии “Библиотека системного программиста”, работа приложений операционной системы Windows основана на обработке сообщений. Когда пользователь работает с устройствами ввода/вывода компьютера, например клавиатурой или мышью, драйверы этих устройств создают сообщения, описывающие его действия. Каждое нажатие на клавиши клавиатуры вызывает генерацию ряда сообщений, определяющих, какая клавиша нажата. Перемещение мыши вызывает сообщения, описывающие траекторию перемещения указателя мыши и т. д. Другие сообщения могут вырабатываться операционной системой или самими приложениями.

Сообщения сначала попадают в системную очереди сообщений операционной системы. Из нее сообщения передаются приложениям, которым они предназначены, и записываются в очередь приложений. Каждое приложение имеет собственную очередь сообщений.

Приложение в цикле, который называется циклом обработки сообщений, получает сообщения из очереди приложения и направляет их соответствующей функции окна, которая и выполняет обработку сообщения. Цикл обработки сообщений обычно состоял из оператора while в котором циклически вызывались функции GetMessage и DispatchMessage:

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

оконные сообщения;

сообщения от органов управления;

команды


Общие члены объектов класса


Иногда удобно, чтобы все объекты данного класса имели общие элементы данных, которые используются совместно. За счет этого можно существенно сократить количество глобальных переменных, улучшая структуру программы.

Общие элементы данных класса следует объявить с ключевым словом static. Все общие элементы класса надо определить в тексте программы, зарезервировав за ними место в оперативной памяти:

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);



Общие замечания о ресурсах приложения


Внимательно изучив различные ресурсы приложения Single вы должны заметить, что четыре типа ресурсов имеют элементы с одинаковым идентификатором. Существует меню, строковый ресурс, таблица акселераторов и пиктограмма, которые имеют один и тот же идентификатор IDR_MAINFRAME.



Однооконный интерфейс


MFC AppWizard способен создавать шаблоны приложений, использующих однооконный или многооконный интерфейс. Приложения с однооконным интерфейсом имеют одно окно для отображения документа. Многооконные приложения способны одновременно открыть несколько окон просмотра документов.

В этой главе мы остановимся на приложениях с однооконным интерфейсом. Приложения с многооконным интерфейсом будут рассмотрены в следующей главе, которая называется “Многооконный интерфейс”.

Сейчас мы рассмотрим модель “документ - окно просмотра”, лежащую в основе как однооконных, так и многооконных приложений, созданных с использованием библиотеки классов MFC.



Окна (класс CWnd)


Практически все приложения имеют пользовательский интерфейс, построенный на основе окон. Это может быть диалоговая панель, одно окно или несколько окон, связанных вместе. Основные свойства окон представлены классом CWnd, наследованным от класса CCmdTarget.

Вы очень редко будете создавать объекты класса CWnd. Класс CWnd сам является базовым классом для большого количества классов, представляющих разнообразные окна. На рисунке 2.3 представлена только небольшая часть дерева наследования класса CWnd.

Рис. 2.3. Класс CWnd

Перечислим классы, наследованные от базового класса CWnd.

Обрамляющие окна (класс CFrameWnd)

Класс CFrameWnd представляет окна, выступающие в роли обрамляющих окон (frame window), в том числе главные окна приложения. От этого класса также наследуются классы CMDIChildWnd и CMDIFrameWnd, используемые для отображения окон многооконного интерфейса MDI. Класс CMDIFrameWnd представляет главное окно приложения MDI, а класс CMDIChildWnd - его дочерние окна MDI. Класс CMiniFrameWnd применяется для отображения окон уменьшенного размера. Такие окна обычно используются для отображения в них панели управления.

Окна органов управления

В предыдущих томах серии “Библиотека системного программиста” мы рассказывали о том, что существует ряд органов управления, встроенных в операционную систему. К ним относятся кнопки, полосы прокрутки, редакторы текста, переключатели и т. д.

Для работы с этими органами управления в библиотеке MFC предусмотрены специальные классы, наследованные непосредственно от класса CWnd.

Класс

Орган управления

CAnimateCtrl

Используется для отображения видеоинформации

CBitmapButton

Кнопка с рисунком

CButton

Кнопка

CComboBox

Список с окном редактирования

CEdit

Поле редактирования

CHeaderCtrl

Заголовок для таблицы

CHotKeyCtrl

Предназначен для ввода комбинации клавиш акселераторов

CListBox

Список

CListCrtl

Может использоваться для отображения списка пиктограмм

CProgressCtrl

Линейный индикатор

CPropertySheet

Блокнот. Может состоять из нескольких страниц

CRichEditCtrl

Окно редактирования, в котором можно редактировать форматированный текст

CScrollBar

Полоса просмотра

CSliderCtrl

Движок

CSpinButtonCtrl

Обычно используется для увеличения или уменьшения значения какого-нибудь параметра

CStatic

Статический орган управления

CTabCtrl

Набор “закладок”

CToolBarCtrl

Панель управления

CToolTipCtrl

Маленькое окно содержащее строку текста

CTreeCtrl

Орган управления, который позволяет просматривать иерархические структуры данных

<
Управляющие панели (классы CControlBar, CStatusBar, CDialogBar)

Класс CControlBar и классы, наследуемые от него, предназначены для создания управляющих панелей. Такие панели могут содержать различные органы управления и отображаются как правило в верхней или нижней части главного окна приложения.

Так, класс CStatusBar предназначен для создания панели управления. Эта панель обычно содержит ряд кнопок, дублирующих действие меню приложения.

Класс CStatusBar управляет панелью состояния. Панель состояния отображается в виде полосы в нижней части экрана. В ней приложение может отображать всевозможную информацию, например краткую подсказку о выбранной строке меню.

Большие возможности представляет управляющая панель, созданная на основе класса CDialogBar. Такая панель использует обычный шаблон диалоговой панели, который вы можете разработать в редакторе ресурсов Visual C++.

Блокнот (класс CPropertySheet)

Класс CPropertySheet представляет блокнот - диалоговую панель, содержащую несколько страниц. Отдельные страницы такого блокнота управляются объектами другого класса - CPropertyPage. Класс CPropertyPage наследуется от базового класс CDialog, который мы рассмотрим ниже.

Окна просмотра (класс CView и классы наследованные от него)

Большой интерес представляет класс CView и классы, наследуемые от него (рис. 2.4). Эти классы представляют окно просмотра документов приложения. Именно окно просмотра используется для вывода на экран документа, с которым работает приложения. Через это окно пользователь может изменять документ.

Разрабатывая приложение, вы будете наследовать собственные классы просмотра документов либо от базового класса CView, либо от одного из нескольких порожденных классов, определенных в библиотеке MFC.

Классы, наследованные от CCtrlView, используют для отображения документа готовые органы управления. Например, класс CEditView использует орган управления edit (редактор). Более подробно эти классы будут описаны позже, когда мы будем рассказывать о средствах автоматизированного программирования MFC AppWizard и ClassWizard.



Класс CScrollView  представляет окно просмотра, которое имеет полосы свертки. В классе определены специальные методы, управляющие полосами просмотра

Класс CFormView позволяет создать окно просмотра документа, основанное на диалоговой панели. От этого класса наследуются еще два класса CRecordView и CDaoRecordView. Эти классы используются для просмотра записей баз данных.



Рис. 2.4. Класс CView

Диалоговые панели (класс CDialog и классы наследованные от него)

Кроме перечисленных классов от базового класса CWnd наследуются классы, управляющие диалоговыми панелями. Если вы желаете создать диалоговую панель, вы можете наследовать класс от CDialog (рис. 2.5).

Вместе с диалоговыми панелями обычно используется класс CDataExchange. Класс CDataExchange обеспечивает работу процедур обмена данными DDX (Dialog Data Exchange) и проверки данных DDV (Dialog Data Validation) используемых для диалоговых панелей. В отличие от класса CDialog, класс CDataExchange не наследуется от какого-либо другого класса.

Когда вы создаете блокнот, состоящий из нескольких страниц, то каждая такая страница является объектом класса, наследованного от CPropertyPage.

От класса CDialog наследуется ряд классов, представляющих собой стандартные диалоговые панели для выбора шрифта, цвета, вывода документа на печать, поиска в документе определенной последовательности символов, а также поиска и замены одной последовательности символов другой последовательностью.

Чтобы создать стандартный диалог, вы можете просто определить объект соответствующего класса. Дальнейшее управление такой панелью осуществляется методами класса.



Рис. 2.5. Класс CDialog


Окно Project Workspace


Microsoft Visual C++ версии 4.0 имеет удобные средства для просмотра исходных текстов файлов проекта, кодов классов приложения, ресурсов, а также для получения справочной информации.

Откройте окно Project Workspace. Обычно оно расположено в левой части экрана, но вы можете переместить его в другое место и даже закрыть. Если окно закрыто, откройте его. Для этого выберите из меню View строку Project Workspace. Окно Project Workspace состоит из нескольких страниц. Вы можете открыть их, нажимая на соответствующие закладки. Количество страниц зависит от того, установлена ли справочная система и открыт ли проект.

Закладка

Описание

ClassView. Средство для просмотра и редактирования классов приложения

ResourceView. Позволяет просматривать и редактировать ресурсы приложения

FileView. Выполняет просмотр файлов приложения

InfoView. Справочная система Microsoft Visual C++. В нее включена информация о языке Си, Си++, библиотеки классов MFC, функций програмного интерфейса Windows

Откройте окно Project Workspace и выберите страницу FileView. Так как сначала в проекте нет ни одного файла, вы увидите пустую папку проекта. Теперь надо создать новый текстовый файл и набрать в нем исходный текст нашего первого приложения.

Чтобы создать новый файл, вы можете нажать кнопку New Source File (

) в стандартной панели управления или выбрать из меню File строку New, а затем из списка в открывшейся панели New выбрать строку Text File. Откроется новое окно текстового редактора. В нем надо набрать исходный текст приложения, представленного листингом 2.1. Сохраните набранный текст в файле под именем MFHello.cpp в каталоге проекта. Для этого выберите из меню File строку Save As.

Листинг 2.1. Файл MFHello.cpp

// Включаемый файл для 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).



Рис. 2.10. Файлы проекта MFHello

Откройте страницу ClassView в окне Project Workspace. В ней отображаются все классы, определенные в приложении и все глобальные переменные. Для каждого класса приложения можно видеть входящие в него элементы (рис. 2.11).

На странице ClassView отображается древовидная структура классов вашего приложения. Когда вы в первый раз открываете ClassView, структура классов отображается в виде закрытой папки
. Чтобы ее открыть, щелкните два раза левой кнопкой мыши по изображению папки или один раз по символу
, расположенному левее папки. В открытой папке символом
 представлены классы приложения и еще одним символом папки глобальные объекты приложения. Папку с глобальными объектами можно открыть также как папку с классами. Вы можете открыть и сами классы. Так вы сможете просмотреть элементы класса - методы и данные. Методы обозначаются символом
, а данные символом
. Если методы или данные объявлены как protected, перед ними отображается символ
, а если как private - символ
.



В нашем проекте определен только один класс CMFHelloApp. В класс CMFHelloApp входит метод InitInstance. Кроме того, определена одна глобальная переменная MFHelloApp.



Рис. 2.11. Классы проекта MFHello

Если вы используете в приложении классы или функции библиотеки классов MFC, то проект надо настроить. Выберите из меню Build строку Settings или нажмите комбинацию клавиш <Alt+F7>. На экране появится диалоговая панель Project Settings. В этой панели расположены несколько страниц, позволяющих настроить различные характеристики проекта.

Откройте страницу General. Мы привели внешний вид этой страницы на рисунке 2.12. Обратите внимание на список Microsoft Foundation Classes. По умолчанию из этого списка выбрана строка No Using MFC. Она означает, что приложение не будет использовать библиотеку MFC. Так как в приложении MFHello и всех остальных приложениях, описанных в этой книге, задействованы классы MFC, выберите из списка Microsoft Foundation Classes строку Use MFC in a Shared Dll (mfc40(d).dll) или строку Use MFC in a Static Library.

Что же выбрать - Use MFC in a Shared Dll или Use MFC in a Static Library? Оказывается программные коды библиотеки классов MFC могут использоваться приложением двумя способами. Код библиотеки MFC либо непосредственно записывается в выполнимый файл приложения, либо вызывается по мере необходимости из отдельной dll-библиотеки.

Использование для приложения dll-библиотеки немного ускоряет процесс построения проекта и позволяет создавать выполнимые файлы значительно меньшего размера. Однако сам по себе такой выполнимый файл работать не будет. Для него необходима dll-библиотека. Поэтому если приложение устанавливается и на других компьютерах, его надо распространять вместе с dll-библиотекой.

Для MFC версии 4.0 dll-библиотека хранится в файлах Mfc40d.dll и Mfc40.dll. В файле Mfc40d.dll находится отладочная версия MFC, а в файле Mfc40.dll отладочная информация отсутствует. В ходе установки Visual C++ эти dll-библиотеки записываются в системный каталог операционной системы.



Если вы забудете указать, что приложение использует MFC, то во время построения проекта на этапе установления связи (Linking...) будут обнаружены ошибки:

error LNK2001: unresolved external symbol __endthreadex

error LNK2001: unresolved external symbol __beginthreadex

Когда для создания нового приложения используется MFC AppWizard, библиотека MFC подключается автоматически. Вам не надо вручную вносить изменения в диалоговой панели Project Settings. Более подробно об использовании MFC AppWizard вы можете прочитать в следующих разделах книги.



Рис. 2.12. Диалоговая панель Project Settings

Если вы все сделали, постройте проект. Для этого вы можете выбрать из меню Build строку Build MFHello.exe - создать выполнимый файл MFHello.exe или строку Rebuild All - заново оттранслировать все исходные файлы проекта. Если в ходе построения выполнимого файла приложения нашего проекта возникли ошибки, исправьте их и заново оттранслируйте проект. После успешного построения проекта запустите полученное приложение, выбрав из меню Build строку Execute MFHello.exe.

На экране появится маленькая диалоговая панель (рис. 2.13). Название этой диалоговой панели соответствует названию выполнимого файла приложения MFHELLO. В диалоговой панели отображается текстовое сообщение “Hello, MFC!”, пиктограмма с восклицательным знаком внутри треугольника и кнопка OK. Как только вы нажмете кнопку OK, диалоговая панель закрывается и приложение завершит свою работу.



Рис. 2.13. Приложение MFHello

Посмотрим, как работает приложение MFHello на уровне исходного текста. Первая строка, не считая строки комментария, содержит директиву препроцессора #include, которая включает файл afxwin.h:

// Включаемый файл для 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 вы узнаете позже, когда мы будем рассматривать средства автоматизированной разработки приложений.


Окно просмотра


В отличие от объектов, представляющих окна типа frame (объекты классов CFrameWnd, CMDIFrameWnd и CMDIChildWnd) окно просмотра в первую очередь проверяет собственную таблицу сообщений. И только в том случае, если командное сообщение не может быть обработано, оно передается документу, связанному с данным окном просмотра. Последовательность обработки командных сообщений документом представлена ниже.



Оконные сообщения


Эта группа включает сообщения, предназначенные для обработкой функцией окна. Практически все сообщения, идентификаторы которых начинаются префиксом WM_, за исключением сообщения WM_COMMAND, относятся к этой группе.

Оконные сообщения предназначаются для обработки объектами, представляющими окна. Это могут быть практически любые объекты класса CWnd или классов, наследованных от него, например CFrameWnd, CMDIFrameWnd, CMDIChildWnd, CView, CDialog. Характерной чертой этих классов является то, что они включают идентификатор окна.

Большинство этих сообщений имеют параметры, детально характеризующие сообщение. Например сообщение WM_SIZE передается окну, когда пользователь меняет его размер и имеет параметры, определяющие новый размер окна.



Операции с файлами


В состав класса CFile входят методы, позволяющие выполнять над файлами различные операции, например копирование, переименование, удаление, изменение атрибутов.

Операционная система MS-DOS содержит команду REN, позволяющую переименовывать файлы. Класс CFile включает статический метод Rename, выполняющий функции этой команды:

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. Виртуальная версия метода не заполняет это поле

Атрибуты файла, указанные в поле m_attribute структуры CFileStatus, определяются как переменная перечислимого типа Attribute. Этот тип определен в классе CFile следующим образом:

enum Attribute {

      normal =    0x00,

      readOnly =  0x01,

      hidden =    0x02,

      system =    0x04,

      volume =    0x08,

      directory = 0x10,

      archive =   0x20

};

Атрибут

Описание

normal

Нормальный файл

readOnly

Файл, который можно открыть только для чтения

hidden

Скрытый файл

system

Системный файл

volume

Метка тома

directory

Каталог

archive

Архивный

Метод GetStatus возвращает ненулевое значение при нормальном завершении и нуль в случае ошибки. Ошибка обычно возникает, если вы указываете несуществующий файл.


Оператор присваивания


Для класса CObject описан оператор присваивания. Он описан с ключевым словом private и не имеет реализации:

private:

      void operator =( const CObject& src );

Таким образом для классов, наследованных от CObject запрещается выполнение операции копирования по умолчанию. Если такая операция необходима, вы должны явно определить ее в своем классе. Если вы не определите в своем классе оператор присваивания, но попытаетесь им воспользоваться, компилятор выдаст сообщение об ошибке.



Операторы new и delete


Оператор new создает объект заданного типа. При этом он выделяет память, необходимую для хранения объекта и возвращает указатель, указывающий на него. Если по каким-либо причинам получить память не удается, оператор возвращает нулевое значение. Оператор new позволяет сразу инициализировать созданную переменную. Приведем формат оператора new:

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;

Прямоугольные скобки указывают на то, что надо удалить массив элементов.



Операторы try, throw и catch


Оператор try открывает блок кода, в котором может произойти ошибка. Если ошибка произошла, то оператор throw вызывает исключение. Исключение обрабатывается специальным обработчиком исключений. Обработчик исключения представляет собой блок кода, который начинается оператором catch.

Допустим ваше приложение должно вычислять значение выражения res = 100 / (num * (num - 7)). Если вы зададите значение переменной num, равное 0 или 7, то произойдет ошибка деления на нуль. Участок программы, в котором может случиться ошибка, объединим в блок оператора try. Вставим перед вычислением выражения проверку переменной nem на равенство нулю и семи. Если переменная num примет запрещенные значения, вызовем исключение, воспользовавшись оператором throw.

Сразу после блока try поместите обработчик исключения catch. Он будет вызываться в случае ошибки.

Пример такой программы, получившей название Exception, мы привели в листинге 1.1. Программа Exception принимает от пользователя значение переменной num, а затем вычисляет выражение res = 100 / (num * (num - 7)) и отображает полученный результат на экране.

В случае, если пользователь введет число 0 или 7, тогда вызывается исключение throw. В качестве параметра оператору throw указывается переменная num. Заметим, что так как переменная num имеет тип long, считается что данное исключение также будет иметь тип long.

После вызова оператора throw управление сразу передается обработчику исключения соответствующего типа. Определенный нами обработчик отображает на экране строку "Exception, num = ", а затем выводит значение переменной num.

После обработки исключения, управление не возвращается в блок try, а передается оператору, следующему после блока catch данного обработчика исключения. Программа выведет на экран строку “Stop program” и завершит свою работу.

Если пользователь введет разрешенные значения для переменной num, тогда исключение не вызывается. Программа вычислит значение res и отобразит его на экране. В этом случае обработчик исключения не выполнится и управление перейдет на оператор, следующий за блоком обработки  исключения. Программа выведет на экран строку “Stop program” и завершит работу.


Листинг 1.1. Файл Exception.cpp

#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;

}


Основа структуры приложения (класс CCmdTarget)


Непосредственно от класса CObject наследуются ряд классов, которые сами являются базовыми для остальных классов MFC. В первую очередь это класс CCmdTarget, представляющий основу структуры любого приложения. Основной особенностью класса CCmdTarget и классов, наследованных от него является то, что объекты этих классов могут получать от операционной системы сообщения и обрабатывать их.

Структура классов, связанных с классом CCmdTarget представлена на рисунке 2.2.

Рис. 2.2. Класс CCmdTarget



Открытие и создание файлов


После создания объекта класса CFile можно открыть файл, вызвав метод Open. Методу Open надо указать путь к открываемому файлу и режим его использования. Прототип метода Open имеет следующий вид:

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, для работы с файлами в двоичном режиме

Необязательный параметр pError, который является указателем на объект класса CFileException, используется только в том случае, если выполнение операции с файлом вызывает ошибку. Если вы указали параметр pError и случилась ошибка, то в объект будет записана дополнительная информация.

Метод Open возвращает ненулевое значение, если файл открыт и нуль в случае ошибки. Ошибка при открытии файла может случится, например, если методу Open указан несуществующий файл.



Отладка приложения (классы CDumpContext, CMemoryState)


В отладочной версии приложения вы можете использовать класс CDumpContext. Он позволяет выдавать состояние различных объектов в текстовом виде.

Класс CMemoryState позволяет локализовать проблемы, связанные с динамическим выделением оперативной памяти. Такие проблемы обычно возникают, когда пользователь выделяет память, используя оператор new, а затем забывает ввернуть эту память операционной системе.



Панель состояния


Для управления панелью состояния используется класс CStatusBar. Чтобы создать для окна панель состояния следует выполнить следующие действия.

Создать объект класса CStatusBar. Он будет представлять панель состояния и управлять ей

Вызвать метод Create класса CStatusBar, чтобы создать панель и связать ее с объектом представляющим панель состояния

Вызвать метод SetIndicators класса CStatusBar, чтобы связать каждый индикатор панели состояния с идентификатором текстовой строки

MFC AppWizard автоматически добавляет к созданному им  приложению программный код, служащий для отображения панели состояния.

Объект m_wndStatusBar класса CStatusBar определяется как элемент класса CMainFrame. Это вполне естественно, так как панель состояния принадлежит именно окну класса CMainFrame.

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 и выше

По умолчанию MFC AppWizard устанавливает для панели управления стили CBRS_TOOLTIPS, CBRS_FLYBY, CBRS_SIZE_DYNAMIC. Вы можете изменить их по своему усмотрению.

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);


Панель управления


Класс CToolBar представляет панель управления toolbar. Обычно панель управления содержит несколько кнопок и разделителей между ними. Для панели управления определен специальный ресурс типа Toolbar.

Чтобы создать панель управления toolbar, следует выполнить следующие действия.

Создать ресурс, описывающий панель toolbar

Создать объект класса CToolBar. Он будет представлять панель управления

Вызвать метод Create класса CToolBar, чтобы создать панель и связать ее с объектом представляющим панель управления

Вызвать метод LoadToolBar класса CToolBar. Он загрузит ресурс диалоговой панели и завершит построение панели управления

Чтобы облегчить вам работу, MFC AppWizard создает для приложения и панель управления и панель состояния. Для этого он включает в состав ресурсов приложения ресурс, описывающий панель управления. В нашем приложении определен только один такой ресурс, имеющий идентификатор IDR_MAINFRAME.

Объект m_wndToolBar класса CToolBar для управления этой панелью определяется в классе CMainFrame. Этот объект объявлен как protected, поэтому обращаться к нему можно только из класса главного окна приложения CMainFrame.

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.



Панель управления и панель состояния


Наиболее интересная задача, которую выполняет метод OnCreate, заключается в отображении панелей управления и состояния. Для панели управления и панели состояния в библиотеке классов MFC предусмотрены два класса CStatusBar и CToolBar.

Классы CStatusBar и CToolBar имеют длинную цепочку наследования.

CStatusBar <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject

CToolBar   <- |

Полное описание этих классов заняло бы слишком много места, поэтому мы ограничимся рассказом о самых главных методах, необходимых для работы с панелями управления и состояния.



Панель управления toolbar


Многие современные приложения, в том числе все приложения имеющие оконный интерфейс и созданные с использованием средств MFC AppWizard, имеют панель управления. Эта панель располагается как правило ниже меню главного окна приложения и содержит ряд кнопок.

//////////////////////////////////////////////////////////////

// Панель управления 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"



Печать документа (класс CPrintInfo)


Класс CPrintInfo предназначен для управления печатью документов на принтере. Когда пользователь отправляет документ на печать или выполняет предварительный просмотр документа перед печатью, создается объект класса CPrintInfo. Он содержит различную информацию о том, какие страницы документа печатается и т. д.

Кроме описанных нами классов библиотека MFC включает большое количество классов, предназначенных для организации технологии OLE. Из-за ограниченного объема книги мы не будем рассматривать приложения, поддерживающие OLE технологию.



Перегрузка имен функций


В соответствии с правилами языка С различные функции, определенные в программе, должны иметь разные имена. Это не всегда удобно, так как могут быть несколько функций, выполняющих сходные действия, но немного отличающиеся по набору параметров.

Язык С++ позволяет иметь несколько функций с одинаковыми именами, но различным набором параметров. Такие функции называются перегруженными, так как одно и то же имя используется для обозначения различных функций.

В качестве примера рассмотрим функции Sqware, предназначенные для вычисления площади прямоугольников и квадратов:

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);

}



Перегрузка операторов


Си++ позволяет вам легко вводить новые типы данных. Так, например, вы можете определить класс для работы с комплексными числами или числами в полярной системе координат. Естественно, что удобнее всего проводить вычисления с объектами таких классов при помощи операторов, а не специальных методов или функций.

В Си++ вы можете переопределить большинство операторов языка для работы с вашими типами данных. Вот список операторов, которые вы можете переопределить:

!

=

+=

–=

!=

,

–>

–>*

&

|

( )

[ ]

new

delete

>> 

<<=

^=

&=

|=

<< 

>>=

==

~

*=

/=

%=

%

^

+

-

*

/

++

––

<=

>=

&&

Переопределение операторов вызывает перегрузку операторов. Как в случае перегруженных функций и методов, перегруженные операторы вызываются в зависимости от количества и типа их параметров. О перегруженных функциях вы можете прочитать в разделе “Перегрузка имен функций”.

Для переопределения операторов предназначено ключевое слово operator. Вы должны определить функцию или метод, имя которого состоит из ключевого слова operator и самого оператора. Параметры этой функции должны соответствовать параметрам переопределяемого оператора.

Вы можете переопределить оператор для объектов класса, используя соответствующий метод класса или дружественную функцию класса. Когда вы переопределяете оператор с помощью метода класса, то в качестве неявного параметра метод принимает ключевое слово this, являющееся указателем на данный объект класса. Поэтому если переопределяется бинарный оператор, то переопределяющий его метод класса должен принимать только один параметр, а если переопределяется унарный оператор - метод класса вообще не должен иметь параметров.

Если оператор переопределяется при помощи дружественной функции, то он должен принимать два параметра для бинарных операторов и один параметр для унарных операторов.


Существует ряд ограничений, которые вы должны учитывать при переопределении операторов:

Нельзя изменять количество параметров оператора. Например, нельзя переопределить унарную операцию как бинарную и наоборот

Нельзя изменять старшинство операторов

Нельзя определять новые операторы

Нельзя переопределять операторы принимающие в качестве параметров стандартные типы данных языка, такие как int или char

Переопределенные операторы не могут иметь параметров, используемых по умолчанию

Нельзя переопределять следующие операторы: (.), (.*), (::), (?:), а также символы, обрабатываемые препроцессором (символы комментария и т. д.).

В нашей первой книге, посвященной языку программирования Си++ и библиотеке классов MFC, мы не будем переопределять операторы. Однако мы будем вызывать операторы, уже переопределенные в классах MFC. Если вы желаете получить больше информации о методике переопределения операторов, обращайтесь к литературе, посвященной языку Си++.


Переопределение методов базового класса


В порожденном классе можно определить методы и элементы данных с именами, которые уже используются в базовом классе. Соответствующие методы и элементы данных базового класса оказываются скрыты. Чтобы обратиться к ним, необходимо указывать полное имя, включающее имя базового класса, оператор :: и имя элемента класса.



Первое приложение MFC


Первое приложение MFHello, которое мы создадим с использованием библиотеки классов MFC будет очень простое. Единственное, что оно будет делать - это отображать на экране маленькую диалоговую панель, содержащую строку “Hello, MFC!”.

Исходный текст приложения, представленный в листинге 2.1, состоит всего из двенадцати строк, не считая строк комментариев. В нашем первом приложении мы использовали единственный класс библиотеки MFC, наследованный от базового класса CWinApp.

Чтобы создать новый проект, выберите из меню File строку New. На экране появится диалоговая панель New, содержащая одноименный список New и две кнопки. Выберите из списка New строку Project Workspace. Откроется диалоговая панель New project workspace (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). В ней вы должны указать тип приложения, который будет разрабатываться, имя проекта и расположение каталога для записи в него файлов проекта.

Самые простые приложения с использованием библиотеки классов MFC мы будем создавать без использования автоматизированных средств разработки приложений MFC AppWizard. Поэтому в качестве типа приложения выберите из списка Type строку Application.

В поле Name введите имя нового проекта. Наш первый проект мы назвали именем MFHello. Расположение каталога для размещения файлов проекта отображается в поле Location. По умолчанию каталог проекта называется точно также как сам проект и будет размещен в каталоге Projects среды разработки Visual C++. Вы можете выбрать для проекта любой другой каталог, если нажмете на кнопку Browse.

Теперь нажмите кнопку Create. Будут созданы служебные файлы проекта. Они получат названия MFHello.mak, MFHello.ncb и MFHello.mdp. Файл MFHello.mdp является основным файлом проекта. В этих файлах определяется, какие исходные файлы содержит проект, указываются характеристики создаваемого приложения, конфигурация самой среды разработки Visual C++.



Пиктограмма


В файле ресурсов приложения указана единственная пиктограмма, имеющая идентификатор IDR_MAINFRAME. Эта пиктограмма содержится в файле Dialog.ico, в каталоге res.

//////////////////////////////////////////////////////////////

// Пиктограмма

IDR_MAINFRAME                                 ICON       DISCARDABLE       "res\\Dialog.ico"

Пиктограмма IDR_MAINFRAME содержит два цветных изображения с разрешением 32х32 и 16х16 пикселов (рис. 4.7). Вы можете изменить эти пиктограммы по своему усмотрению.

Рис. 4.7. Пиктограммы приложения Dialog


В файле ресурсов приложения Single определены две пиктограммы IDR_SINGLETYPE и IDR_MAINFRAME. Каждая из этих пиктограмм содержит по два изображения различного размера 32х32 и 16х16 пикселов.

//////////////////////////////////////////////////////////////

// Пиктограммы

IDR_MAINFRAME     ICON   DISCARDABLE       "res\\Single.ico"

IDR_SINGLETYPE   ICON     DISCARDABLE       "res\\SingleDoc.ico"

Пиктограмма IDR_MAINFRAME представляет минимизированное приложение (рис. 5.11). Эта же пиктограмма отображается в левом верхнем углу главного окна приложения.

Рис. 5.11. Пиктограмма IDR_MAINFRAME

Точно такая же пиктограмма используется всеми приложениями, построенными на основе MFC AppWizard, вне зависимости от типа их интерфейса с пользователем.

Пиктограмма IDR_SINGLETYPE может быть использована для представления документа, с которым работает приложение (рис. 5.12). Приложение с однооконным интерфейсом практически не использует эту пиктограмму.

Рис. 5.12. Пиктограмма IDR_SINGLETYPE



Подзадачи приложения (классы CWinThread и CWinApp)


От класса CCmdTarget наследуется класс CWinThread, представляющий подзадачи приложения. Простые приложения, которые мы  будем рассматривать в первой книге, посвященной MFC, имеют только одну подзадачу. Эта подзадача, называемая главной, представляется классом CWinApp, наследованным от класса CWinThread.



Получение дампа объекта класса


Виртуальный метод Dump позволяет получить дамп объекта данного класса:

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;

}



Порядок обработки сообщений


Ниже описаны последовательности обработки командных сообщений объектами различных классов.



Позиционирование


Чтобы переместить указатель текущей позиции файла в новое положение, можно воспользоваться одним из следующих методов класса CFile - Seek, SeekToBegin, SeekToEnd. В состав класса CFile также входят методы, позволяющие установить и изменить длину файла - GetLength, SetLength.

Когда вы открываете файл, указатель текущей позиции файла находится в самом начале файла. Когда вы читаете данные из файла, этот указатель перемещается в конец файла и указывает на данные, которые будут получены очередной операцией чтения из файла.

Чтобы переместить указатель текущей позиции файла в любое место, можно воспользоваться универсальным методом Seek. Он позволяет переместить указатель на определенное число байт относительно начала, конца файла или текущей позиции указателя:

virtual LONG Seek(LONG lOff, UINT nFrom);

     

throw(CFileException);

Параметр lOff определяет число байт, на которое надо переместить указатель текущей позиции файла. Параметр lOff может быть положительной или отрицательной величиной. Если lOff больше нуля, то указатель смещается в сторону конца файла, если lOff меньше нуля, то указатель смещается в начало файла.

Параметр nFrom определяет, относительно чего задано это смещение. В качестве nFrom можно указать три различные константы, также определенные в классе CFile:

Константа

Параметр lOff задает смещение относительно

CFile::begin

Начала файла

CFile::current

Текущей позиции файла

CFile::end

Конца файла

В случае успешного перемещения указателя файла в новую позицию метод Seek возвращает новое смещение указателя относительно начала файла. Если новая позиция указателя задана неправильно, например, вы пытаетесь переместить указатель в позицию перед началом файла, вызывается исключение.

Чтобы переместить указателя файла в начало или конец файла, наиболее удобно использовать специальные методы. Метод SeekToBegin перемещает указатель в начало файла, а метод SeekToEnd - в его конец. Приведем прототип метода SeekToBegin:

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);


Приложение Dialog


Названия файлов, входящих в проект Dialog, вы можете просмотреть на странице FileView в окне Project Workspace. В состав нашего проекта входят следующие основные файлы:

Имя файла

Описание

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

Использование этих файлов позволяет ускорить процесс повторного построения проекта. Более подробное описание файлов представлено ниже



Приложение Except


Приложение Except, исходный текст которого представлен в листинге 3.3, показывает как можно выполнить обработку исключительных ситуаций. Оно содержит блок try и несколько обработчиков исключений для объектов типа CMemoryException, CFileException, CException, а также универсальный обработчик. Если в блоке try вызывается исключение, связанное с ошибкой в файловой системе или системе распределения памяти, оно обрабатывается соответствующими блоками catch. Если исключение вызвано с объектом другого типа, но наследованным от класса CException, например CArchiveException, CNotSupportedException или CResourceException, тогда оно обрабатывается блоком catch для объектов CException. И наконец, если объект исключения не имеет базовым классом CException, оно обрабатывается в последнем блоке catch.

Листинг 3.3. Файл Except.cpp

#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 может содержать код ошибки, определенной операционной системой.


Приложение MFMenu


Приложения, которые мы рассматривали до сих пор, фактически никак не могли взаимодействовать с пользователем. Они не имели ни меню, ни панели управления. И самое главное - они не содержали обработчиков сообщений.

Сейчас мы рассмотрим приложение MFMenu, которое имеет меню и содержит обработчики сообщений, передаваемых приложению, когда пользователь открывает меню и выбирает из него строки. Для создания приложения MFMenu мы также не станем пользоваться средствами автоматизированной разработки MFC AppWizard, и наберем текст приложения вручную. Этот позволит вам лучше разобраться с механизмом сообщений.

Создайте новый проект под названием MFMenu. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Наберите в редакторе исходный текст приложения и сохраните его в файле MFMenu.cpp (листинг 2.7). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFStart. Затем включите этот файл в проект.

Листинг 2.7. Файл MFMenu.cpp

// Включаемый файл для 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));

}


Приложение с единственным окном


Первое созданное нами приложение не имело главного окна. Для вывода сообщения на экран мы использовали функцию AfxMessageBox, которая очень похожа на функцию MessageBox программного интерфейса операционной системы Windows.

Следующее приложение, которое мы будем рассматривать, немного сложнее. При запуске оно будет отображать на экране компьютера обычное окно, имеющее заголовок, системное меню и кнопки управления.

Точно так же как в приложении MFHello, в нашем втором приложении мы будем использовать класс CWinApp в качестве главного класса приложения. Для управления окном приложения мы создадим еще один класс, наследуемый от базового класса CFrameWnd, входящего в библиотеку MFС.

Создайте новый проект, как мы рассказывали выше, и назовите его именем MFStart. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Автоматизированные средства разработки приложений MFC AppWizard мы рассмотрим позже.

Наберите в редакторе исходный текст приложения и сохраните его в файле MFStart.cpp (листинг 2.2). Наш пример без учета строк комментариев состоит всего из двадцати строк исходного текста, поэтому набор текста не займет у вас много времени. Включите набранный файл в проект.

Листинг 2.2. Файл MFStart.cpp

// Включаемый файл для 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.



Рис. 2.20. Классы проекта MFStart

Постройте проект и запустите полученное приложение, выбрав из меню Build строку Execute MFStart.exe. На экране появится главное окно приложения, представлене нами на рисунке 2.21. Оно имеет стандартный заголовок с надписью Hello MFC, системное меню и кнопки для изменения размера окна. Чтобы завершить приложение, вы можете выбрать строку Close из системного меню главного окна или нажать на кнопку
.





Рис. 2.21. Приложение MFStart

Приложение MFStart очень простое. Оно состоит из одного главного окна и не содержит ни меню, ни каких либо других органов управления. Тем не менее, окно приложения MFStart обладает всеми возможностями окон Windows. Оно имеет заголовок, системное меню и кнопки управления. Вы можете изменить размер этого окна, увеличить его на весь экран и даже уменьшить до размера пиктограммы.

Так же как и у приложения MFHello, первая строка исходного текста приложения MFStart, не считая строки комментария, содержит директиву препроцессора #include, которая включает файл afxwin.h. Этот файл включается в исходные тексты всех приложений, использующих библиотеку классов MFC.

Затем мы определяем главный класс приложения, который наследуется от базового класса CWinApp. Главный класс приложения MFHello, который называется CMFStartApp, определяется следующим образом:

//=====================================================

// Класс 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.

Листинг 2.3. Файл MFStartApp.h

#include <afxwin.h>

class CMFStartApp : public CWinApp

{

public:

      virtual BOOL InitInstance();

};

Виртуальный метод InitInstance переопределен нами в файле MFStartApp.cpp. В этом же файле создается объект класса CMFStartApp, представляющий само приложение. Файл MFStartApp.cpp показан в листинге 2.4.

Листинг 2.4. Файл MFStartApp.cpp

#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.

Листинг 2.5. Файл MFStartWindow.h

#include <afxwin.h>

class CMFStartWindow : public CFrameWnd

{

public:

      CMFStartWindow();

};

И наконец, последний файл MFStartWindow.cpp модифицированного проекта MFStart показан в листинге 2.6. В этом файле определяется конструктор класса CMFStartWindow.

Листинг 2.6. Файл MFStartWindow.cpp

#include <afxwin.h>

#include "MFStartWindow.h"

CMFStartWindow::CMFStartWindow()

{

      Create(NULL, "Hello MFC");

}


Приложение с главной диалоговой панелью


В состав компиляторов Microsoft Visual C++ встроены средства, позволяющие программисту облегчить разработку приложений. В первую очередь к ним относятся MFC AppWizard и ClassWizard.

Как известно, в любом деле самое трудное - начало. Это высказывание в полной мере справедливо по отношению к разработке приложений Windows. Мы рассказывали в предыдущих книгах серии “Библиотека системного программиста” и вы могли убедиться в этом сами, что исходные тексты даже простых приложений Windows имеют большие размеры.

На создание работающего каркаса или шаблона приложения у программиста уходит слишком много времени. Обычно, чтобы не набирать исходный текст такого шаблона каждый раз, новые приложения создают на основе уже отлаженных примеров, изменяя их по своему усмотрению.

Благодаря MFC AppWizard среда разработчика Microsoft Visual C++ позволяет быстро создавать шаблоны новых приложений. При этом программисту не приходится писать ни одной строки кода. Достаточно ответить на ряд вопросов, которые задает MFC AppWizard, выбрав какое приложение вы желаете создать, и исходные тексты шаблона приложения вместе с файлами ресурсов готовы. Эти тексты можно сразу оттранслировать и получить готовый загрузочный модуль приложения.

Конечно, сегодня никакие средства автоматизированной разработки не смогут создать программу полностью без вашего участия - иначе зачем были бы нужны программисты? Прикладную часть приложения вы должны будете написать сами.

Однако MFC AppWizard окажет вам очень сильную помощь. Так, чтобы создать многооконный текстовый редактор обладающий справочной системой, в который к тому же можно включать OLE объекты и, вам не придется написать ни единой строчки кода программы. Исходные тексты такого приложения можно автоматически разработать с помощью AppWizard буквально за две минуты.

Но даже когда шаблон приложения полностью готов, Microsoft Visual C++ не оставляет вас один на один с его текстом. Встроенный в среду Visual C++ редактор ресурсов позволяет быстро создавать новые меню, диалоговые панели, добавлять кнопки к панели управления (панели toolbar).

Средства ClassView и ClassWizard позволят подключить к созданным и отредактированным ресурсам управляющий ими код. Большую часть работы по описанию и определению функций, обрабатывающих сообщения от меню, органов управления диалоговых панелей и т. д. также берут на себя ClassView и ClassWizard.



Приложение с модальной диалоговой панелью


В этом разделе мы расскажем о том, как создать простейшее приложение с единственной диалоговой панелью. Диалоговая панель будет содержать несколько кнопок, статическое текстовое поле и поле редактирования. В следующей главе мы расскажем, как создать более сложное приложение с главной диалоговой панелью при помощи средств автоматизированного проектирования MFC AppWizard и ClassWizard.

Создайте новый проект под названием MFDialog. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1). Наберите в редакторе исходный текст приложения и сохраните его в файле MFDialog.cpp (листинг 2.13).

Листинг 2.13. Файл MFDialog.cpp

// Включаемый файл для 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;

}

Создайте файл ресурсов MFDlgRes.rc и добавьте в него новую диалоговую панель. На экране откроется окно редактора диалоговой панели и панель с инструментами Controls (рис. 2.28). По умолчанию новая диалоговая панель называется Dialog и содержит две кнопки OK и Cancel.

Вы можете добавлять в диалоговую панель другие органы управления - кнопки, переключатели, поля редактирования, статические текстовые поля, рисунки. Более того в Visual C++ версии 4.0 вам становятся доступны новые органы управления - многостраничные диалоговые панели, поля для просмотра видеоинформации и т. д.



Рис. 2.28. Создание диалоговой панели

В следующей таблице мы кратко описали органы управления диалоговой панели, которые можно добавлять с помощью панели инструментов Controls.

Кнопка

Название

Описание



Select

Если вы нажмете эту кнопку, то сможете выбрать органы управления, которые уже расположены в диалоговой панели



Picture

Рисунок



Static Text

Статическое текстовое поле



Edit Box

Поле редактирования



Group Box

Прямоугольник, объединяющий группу органов управления



Button

Кнопка



Check Box

Переключатель в виде прямоугольника



Radio Button

Переключатель круглой формы (радиопереключатель)



Combo Box

Список с окном редактирования



List Box

Список



Horizontal Scroll Bar

Горизонтальная полоса просмотра



Vertical Scroll Bar

Вертикальная полоса просмотра



Animate

Окно просмотра видео



Tab Control

Позволяет размещать в диалоговой панели несколько страниц органов управления, каждая из которых имеет “закладку”. Пользователь одновременно видит закладки всех страниц и может выбрать любую из них.



Tree Control

Позволяет просматривать иерархические (древовидные) структуры данных. Такой орган управления можно использовать для отображения структуры каталогов на диске, заголовков статей справочной системы и т. д.



List Control

Может использоваться для отображения списка пиктограмм



Hot Key

Орган управления Hot Key предназначен для ввода комбинации клавиш акселераторов



Slider

Движок. Обычно используется при отображении видеоинформации. Позволяет перейти к просмотру произвольного кадра



Progress

Линейный индикатор. Позволяет отображать на экране ход какого-нибудь процесса, например процесса копирования файлов



Spin

Обычно используется для увеличения или уменьшения значения какого-нибудь параметра



Custom Control

Орган управления, внешний вид и назначение которого определяется пользователем

<


Для нашего первого приложения с диалоговой панелью вам надо добавить только одну кнопку, одно текстовое поле и одно поле редактирования.

Сначала добавьте кнопку. Для этого щелкните по изображению кнопки в панели Controls. Затем переместите указатель мыши в то место диалоговой панели, где вы желаете разместить кнопку и нажмите левую клавишу мыши. В диалоговой панели появится изображение кнопки, названное по умолчанию Button1. Выполните по ней двойной щелчок левой клавишей мыши. На экране появится панель Push Button Propeties, определяющая различные характеристики кнопки. В первую очередь вас будут интересовать поля Caption и ID. В поле Caption введите название кнопки Default, а в поле ID ее идентификатор IDC_DEFAULT. Остальные характеристики кнопки оставьте без изменения.

Находясь в редакторе ресурсов вы можете сразу попробовать как работает диалоговая панель. Найдите диалоговую панель Dialog (рис. 2.29). Если ее нет на экране, выберите из меню View строку Toolbars и в открывшейся диалоговой панели Toolbars установите переключатель Dialog. Диалоговая панель Dialog содержит ряд кнопок. Первая кнопка, на которой нарисован тумблер, позволяет проверить, как будет работать диалоговая панель.



Рис. 2.29. Панель управления Dialog

Остальные кнопки диалоговой панели Dialog позволяют задавать выравнивание органов управления друг относительно друга и относительно границ панели. Последние две кнопки устанавливают разметку на диалоговой панели. Разметка поможет вам ровнее разместить органы управления.

Вы можете изменить заголовок диалоговой панели, ее идентификатор и многие другие параметры, если сделаете двойной щелчок левой кнопкой мыши по заголовку диалоговой панели. На экране появится панель Dialog Properties, содержащая несколько страниц. Выберите страницу General (рис. 2.30).



Рис. 2.30. Характеристики диалоговой панели

В поле ID вы можете изменить идентификатор диалоговой панели, в поле Caption - заголовок диалоговой панели. В полях Font name и Font size отображается тип и размер шрифта, который используется для всех текстов в диалоговой панели. Изменить параметры шрифта можно, нажав кнопку Font. Поля X Pos и Y Pos позволяют указать начальное положение диалоговой панели на экране. Если X Pos и Y Pos содержат нулевые значения, начальное положение диалоговой панели определяется операционной системой.



Диалоговая панель может иметь собственное меню. Это меню будет отображаться непосредственно под заголовком диалоговой панели. Если вы желаете подключить к диалоговой панели меню, сначала разработайте меню и запишите его в файл ресурсов. Затем выберите в диалоговой панели Dialog Properties идентификатор данного меню из списка Menu.

Для приложения MFDialog вам надо поменять только идентификатор диалоговой панели и ее название. Измените идентификатор диалоговой панели с IDD_DIALOG1 на “DIALOGPANEL”, а ее заголовок - с Dialog на My Dialog. Остальные характеристики диалоговой панели оставьте без изменения.

Итак, диалоговая панель “DIALOGPANEL” приложения MFDialog содержит три кнопки, одно статическое текстовое поле и одно поле редактирования. В листинге 2.14 представлен фрагмент файла ресурсов в котором определяется диалоговая панель приложения.

Листинг 2.14. Фрагмент файла MFDlgRes.rc

//////////////////////////////////////////////////////////////

// Диалоговая панель

//

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.

Листинг 2.15. Файл resource.h

//{{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.



Рис. 2.31. Классы проекта MFDialog

Страница ResourceView окна Project Workspace показывает все ресурсы, входящие в проект (рис. 2.32). В приложении MFDialog определен только один ресурс - диалоговая панель, имеющая идентификатор “DIALOGPANEL”.





Рис. 2.32. Ресурсы проекта MFDialog

Постройте проект и запустите полученный выполнимый файл. На экране появится диалоговая панель My Dialog, определенная в файле ресурсов нашего проекта (рис. 2.33). Вначале поле редактирования Line Editor пустое. В нем вы можете ввести любой текст. Если вы нажмете на кнопку Default, тогда все, что вы ввели в поле редактирования Line Editor, пропадает и в нем отображается строка Start Text.

Кроме кнопки Default в диалоговой панели My Dialog находятся еще две кнопки OK и Cancel. Если вы нажмете кнопку OK, тогда диалоговая панель закрывается, а на экране появляется текстовое сообщение, содержащее текст, введенный вами в поле Line Editor. Если нажать кнопку Cancel, то диалоговая панель My Dialog также закроется, но сообщение с введенным вами текстом не появится.



Рис. 2.33. Приложение MFDialog

Рассмотренное нами приложение MFDialog можно создать значительно быстрее, если воспользоваться средствами автоматизированной разработки приложений MFC AppWizard и ClassWizard. Используя эти средства, вы будете избавлены от необходимости вручную набивать в текстовом редакторе код приложения и сможете сконцентрировать свои усилия на реализации обработчиков сообщений от органов управления диалоговой панели.


Приложение с немодальной диалоговой панелью


Процедура создания немодальной диалоговой панели несколько отличается от процедуры создания модальной диалоговой панели. Как и в случае с модальной диалоговой панелью, в первую очередь вы должны создать шаблон диалоговой панели и добавить его в файл ресурсов приложения.

Затем надо создать класс управляющий диалоговой панелью - класс диалоговой панели. Этот класс наследуется непосредственно от базового класса CDialog. В классе диалоговой панели следует определить методы для обработки сообщений от органов управления диалоговой панели.

Следующие шаги выполняются только для немодальных диалоговых панелей. Для класса диалоговой панели надо определить конструктор, объявив его как public. Чтобы открыть немодальную диалоговую панель, создайте объект класса диалоговой панели, обратившись к определенному вами конструктору. В этот момент диалоговая панель еще не появляется на экране. Для этого надо вызвать метод Create класса CDialog.

Вы можете вызывать метод Create либо непосредственно из конструктора класса диалоговой панели, либо уже после создания объекта. Если диалоговая панель имеет стиль WS_VISIBLE, она сразу появится на экране. В противном случае для этого надо вызвать метод ShowWindow. Как видите, для отображения немодальной диалоговой панели мы не использовали метод DoModal.

В классе CDialog определены два прототипа метода Create. Один позволяет указать диалоговую панель через ее текстовое имя, а другой через числовой идентификатор:

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.



Приложение Single


В этом разделе мы рассмотрим однооконное приложение, созданное с использованием средств MFC AppWizard и расскажем, как его можно совершенствовать.

Создайте новое приложение с однооконным интерфейсом и назовите его Single. При определении свойств приложения оставьте все предложения по умолчанию. Наше приложение не будет поддерживать ни технологию OLE, ни базу данных, ни сетевые технологии. За счет этого оно будет меньше размером, что позволит лучше понять структуру приложений MFC. Процедура создания приложений с использованием MFC AppWizard описана в разделе “Приложение с оконным интерфейсом” и сейчас мы на ней останавливаться не будем.

В состав проекта Single входят следующие основные файлы:

Имя файла

Описание

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

Текстовый файл, содержащий описание проекта. В нем кратко рассмотрен каждый файл, входящий в проект, перечислены классы приложения, а также представлена другая дополнительная информация

Постройте проект Single и запустите полученное приложение. На экране появиться главное окно приложения (рис. 5.10). Как видите, оно имеет меню, панели управления и состояния. Попробуйте выбрать различные строки из меню приложения.

Некоторые из строк меню приложения уже работают. Например, когда вы выбираете из меню File строку Open, на экране открывается стандартная диалоговая панель для выбора файла. Вы можете выбрать из этой панели любой файл и открыть его. Однако от этого изменится только заголовок окна приложения - в нем появится название открытого файла. Содержимое файла будет недоступно. Чтобы вы смогли просматривать и изменять содержимое открытого файла, необходимо добавить специальный код. Мы займемся этим в разделе “Простейший графический редактор” данной главы.

Рис. 5.10. Приложение Single



Приложение TestFile


Теперь мы представим вам приложение, которое использует класс CStdioFile для записи в файл информации о файловой системе компьютера. Для определения характеристик файловой системы мы воспользовались функцией GetVolumeInformation из программного интерфейса Win32.

Эта функция позволяет определить различные характеристики файловой системы, установленной на данном томе. Ниже представлен прототип функции GetVolumeInformation:

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. Эта константа не может быть использована вместе с другими

<
Параметр lpFileSystemNameBuffer содержит указатель на буфер, в который будет записан тип файловой системы. В зависимости от типа файловой системы, установленной на данном томе, это может быть FAT, NTFS или HPFS.

Размер буфера определяется параметром nFileSystemNameSize. Если вас не интересует тип файловой системы, запишите в параметр lpFileSystemNameBuffer значение NULL.

Если функция GetVolumeInformation завершилась без ошибок, она возвращает значение TRUE. В противном случае возвращается значение FALSE, а для получения дополнительной информации вы можете вызвать функцию GetLastError.

В листинге 3.1 представлен исходный текст приложения TestFile. В этом приложении используются всего несколько классов библиотеки классов MFC. Для организации самого приложения мы наследовали от базового класса CWinApp главный класс приложения CMFStartApp. Класс CWinApp был описан нами ранее в разделе “Приложение MFStart”.

В классе CMFStartApp определены два метода: InitInstance и FileSystemInfo. Метод InitInstance является переопределением виртуального метода InitInstance базового класса CWinApp. Этот метод вызывается каждый раз при запуске приложения. Мы используем метод InitInstance для того, чтобы вызвать метод FileSystemInfo и сразу завершить работу приложения.

Метод FileSystemInfo получает данные о файловой системе текущего тома и записывает их в файл fsystem.dat в текущем каталоге. Для получения информации о файловой системе используется функция GetVolumeInformation, описанная выше.

Данные, полученные функцией FileSystemInfo, временно заносятся в строку strTmpOut класса CString. При этом используется метод Format, чтобы получить форматированную запись, содержащую дополнительное текстовое описание.

Для записи в файл мы создали объект класса CStdioFile. В конструкторе мы сразу указали имя файла, который надо создать и открыть. Все операции с файлом, включая его создание, выполняются в блоке try. В случае возникновении ошибки во время создания или других операций с файлом вызывается исключение, которое будет обработано блоком catch.



Мы специально создаем файл и открываем его, используя конструктор класса CStdioFile, а не метод Open. Метод Open в случае возникновения ошибки возвращает соответствующее значение, но не вызывает исключение. Поэтому надо было бы специально проверять значение возвращаемое методом Open.

Для записи в файл мы вызываем метод WriteString, передавая ему в качестве параметра строку strTmpOut, которая содержит данные подготовленные для записи. Каждый вызов метода WriteString записывает в файл одну строку, так как в конце strTmpOut мы всегда записываем символ перевода строки.

Чтобы закрыть файл, мы используем метод Close базового класса CFile. В случае успешного завершения на экране отображается сообщение с именем файла fsystem.dat и управление возвращается методу InitInstance.

Если при работе с файлом возникла ошибка, управление передается блоку catch. В нем отображается сообщение об ошибке, а затем управление сразу возвращается методу InitInstance. Невыполненная часть блока try игнорируется.

Листинг 3.1. Файл TestFile.cpp

// Включаемый файл для 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.

Листинг 3.2. Файл fsystem.dat

Метка тома:  LIBRARY

Серийный номер: 1D794E8D

Тип файловой системы: FAT

Максимальная длина имени файла: 255

Свойства файловой системы

   FS_CASE_IS_PRESERVED

   FS_UNICODE_STORED_ON_DISK


Процедура создания однооконного приложения


Сначала вы должны создать новый проект и выбрать тип проекта AppWizard. Определите расположение каталога для размещения в нем файлов проекта и имя проекта. Когда вы создадите проект, на экране появится первая диалоговая панель MFC AppWizard. Внешний вид этой панели мы уже приводили на рисунке 4.2.

Теперь надо определить, какой тип пользовательского интерфейса будет иметь приложение. Выберите приложение с однооконным интерфейсом (Single document). Начиная с этого момента процедура создания приложений с разными пользовательскими интерфейсами будет отличаться от описанной нами в предыдущем разделе.

Нажмите кнопку Next. На экране появится следующая диалоговая панель MFC AppWizard. Если вы создаете приложение с однооконным интерфейсом, диалоговая панель будет иметь внешний вид, показанный на рисунке 5.1.

Рис. 5.1. Второй шаг MFC AppWizard

Если приложение будет работать с базами данных, то вам надо указать AppWizard, на каком уровне создаваемый шаблон приложения будет поддерживать базы данных. Сейчас мы не будем подробно останавливаться на работе с базами данных. Наше первое приложение с однооконным интерфейсом не будет работать с базами данных, поэтому переведите переключатель What database support would you like to include? в положение None. Затем нажмите кнопку Next>. На экране появится следующая диалоговая панель MFC AppWizard (рис. 5.2).

Рис. 5.2. Третий шаг MFC AppWizard

В этой диалоговой панели вам предстоит выбрать, будет ли приложение поддерживать технологию OLE, и если будет, то в каком режиме.

Первый переключатель What OLE compound document support would you like to include? определяет как приложение будет поддерживать технологию OLE. Мы описали использование этого переключателя в следующей таблице.

Переключатель

Описание

None  

Приложение не использует технологию OLE

Container 

Приложение сможет включать в свои документы, другие объекты и ссылки на них, то есть будет выступать в качестве клиента OLE

Mini-server

Обеспечивается работа основных возможностей сервера OLE. Документы или объекты, подготовленные в приложении, можно будет включать в другие приложения. Однако приложение нельзя будет использовать автономно. Объекты, подготовленные в приложении, можно будет встраивать, однако нельзя будет встроить ссылку на объект, записанный в отдельном файле

Full-server 

Обеспечивается работа всех возможностей сервера OLE. Объекты, подготовленные в приложении, можно будет включать в другие приложения или встраивать ссылку на объект, записанный в отдельном файле. Приложение можно будет использовать автономно

Both container and server 

Приложение сможет работать и как сервер и как клиент OLE

<
Следующий переключатель в диалоговой панели называется Would you like support for OLE compound files?. Положение переключателя определяет в каком формате будет сохранятся документ, подготовленный в приложении. Если переключатель находится в положении Yes, please, и документ содержит несколько OLE объектов, то остается возможность индивидуального доступа к этим объектам.

В нижней части диалоговой панели расположены еще два переключателя - OLE automation и OLE controls. Переключатель OLE automation следует включить, если вы желаете, чтобы приложение было доступно для других приложений, поддерживающих OLE automation. Переключатель OLE controls надо включить, если вы предполагаете использовать в приложение органы управления OLE.

Установив все переключатели как вам надо, нажмите кнопку Next >. На экране появится следующая диалоговая панель MFC AppWizard (рис. 5.3).



Рис. 5.3. Третий шаг MFC AppWizard

Переключатели в верхней части панели определяют основные особенности приложения, такие как использование панели управления (toolbar), справочной системы, трехмерных органов управления.

Переключатель Docking toolbar определяет, будет ли приложение иметь панель управления. Если вы включите этот переключатель, то приложение будет иметь панель управления с кнопками (рис. 5.4).



Рис. 5.4. Панель управления

По умолчанию в нем размещаются три группы кнопок. Вы можете потом добавлять или удалять кнопки из панели управления по своему усмотрению.

Первая группа из трех кнопок предназначена для управления документами: они позволяют создавать новый документ, открывать существующий документ и  сохранять открытый документ в файле.

Вторая группа из трех кнопок может быть использована для редактирования документа. Эти кнопки позволяют удалять, копировать в clipboard и вставлять из clipboard выделенный текст.

В последней, третьей группе находится кнопка, позволяющая распечатать документ и кнопка для вызова справочной системы.

Переключатель Initial status bar управляет панелью состояния status bar. Эта панель размещается в нижней части главного окна приложения. По умолчанию в этой панели отображается краткую подсказку о режиме работы приложения и о положении клавиш <CapsLock>, <NumLock> и <ScrollLock>. На рисунке 5.5 мы привели примерный вид панели состояния.



Вы сможете использовать панель состояния и для других целей. Например в этой панели можно отображать текущие дату и время, и даже небольшие пиктограммы.



Рис. 5.5. Панель состояния

Переключатель Printing and print preview определяет, сможет ли приложение распечатать подготовленный в нем документ и будет ли доступен режим предварительного просмотра распечатки на экране.

Переключатель Context-sensitive Help управляет контекстно зависимой подсказкой в вашем приложении. Если этот переключатель включен, тогда AppWizard создаст набор файлов справочных баз данных. Используя эти файлы в качестве шаблона, вы легко сможете добавить собственную справочную информацию к этой базе.

Переключатель 3D controls определяет внешний вид приложения. Если переключатель 3D controls включен, тогда пользовательский интерфейс приложения, включая главное окно приложения, дочерние окна и диалоговые панели, будет выглядеть объемным.

Библиотека классов MFC позволяет создавать приложения, поддерживающие технологию WOSA. Эта поддержка позволяет создавать приложения, способные непосредственно работать с почтовой системой, а также взаимодействовать с другими приложениями в локальной или глобальной сети через протокол TCP/IP.

Переключатель MAPI (Messaging API) управляет поддержкой почтового API. Если вы желаете, чтобы ваше приложение могло передавать и принимать почтовые сообщения (MS Mail, MS Exchenge), включите этот переключатель.

Переключатель Windows Sockets управляет поддержкой сокетов Windows. Если включить этот переключатель, созданное приложение сможет взаимодействовать с другими приложениями по протоколу TCP/IP.

Большинство приложений позволяют сохранять документы в файлах на диске. Впоследствии эти файлы можно снова открыть и продолжить с ними работать. Названия нескольких файлов, с которыми ваше приложение работало позже остальных, отображаются в меню File главного окна приложения. Чтобы открыть один из этих файлов надо выбрать его имя из меню File. По умолчанию приложение будет сохранять названия четырех файлов. Вы можете изменить это число, выбрав его в поле How many files would you like on your recent file list?.



В диалоговой панели MFC AppWizard - Step 4 of 6 располагается кнопка Advanced, позволяющая указать дополнительные характеристики приложения, такие как название главного окна приложения, расширение файлов, в которые приложение будет сохранять свои документы и т. д. Все эти характеристики можно будет настроить непосредственно в исходном тексте приложения, но гораздо удобнее сделать этой сейчас, во время работы MFC AppWizard.

Нажмите кнопку Advanced. На экране появится диалоговая панель Advanced Options, которая содержит две страницы: Document Template Strings и Window Styles. Рассмотрим их более подробно.

Страница Document Template Strings представлена нами на рисунке 5.6 и определяет характеристики документов с которыми будет работать приложение. В верхней части этой страницы расположена группа Non-localized strings. В поле File extension вы можете указать расширение, которое будет по умолчанию присваиваться файлам, созданным приложением. Мы указали в этом поле строку lis. В поле File type ID отображается идентификатор, под которым данный тип документов заносятся в регистрационную базу Windows 95.

Рассмотрим группу Localized strings. Наибольший интерес представляет поле Main frame caption. В нем можно указать заголовок главного окна приложения. По умолчанию это поле содержит имя проекта. В поле Doc type name отображается название типа документов, создаваемых приложением.

Когда вы создаете новый документ и сохраняете его в файле и когда вы открываете файл, чтобы загрузить содержащийся в нем документ, на экране появляются стандартные диалоговые панели для ввода имени файла. Чтобы в этих панелях по умолчанию отображались только имена файлов с определенным расширением, введите в поле Filter name название фильтра для имен файлов, а в поле File extension само расширение. Обычно имя фильтра формируют на основе названия данного типа документа и расширения имени файлов документа (см. поле File extension). Так, в нашем случае, используется имя фильтра Single Files (*.lis).





Рис. 5.6. Диалоговая панель Advanced Options, страница Document Template Strings

Если в приложении определено несколько шаблонов документов, тогда при создании нового документа на экране появляется диалоговая панель File New. Из этой панели можно выбрать шаблон для создания нового документа. Строка из поля File new name (OLE short name) будет показана в этой панели. В случае, если приложение поддерживает технологию OLE как сервер, то строка File new name используется в качестве короткого имени объекта OLE.

В поле File type name (OLE long name) вы можете ввести имя типа файлов. Это имя будет использоваться в стандартных панелях Open и Save As, в качестве типа файлов документов. Для приложений использующих технологию OLE поле File type name также определяет длинное имя объекта OLE.

Теперь выберите страницу Window Style диалоговой панели Advanced Options. Для этого достаточно нажать на соответствующую закладку (рис. 5.8).

Иногда при редактировании документа бывает удобно одновременно просматривать различные участки одного документа. Для этого можно открыть его в двух окнах одновременно. Однако еще удобнее разделить окно на несколько частей (рис. 5.7). Такая возможность реализована, например, в текстовом процессоре Microsoft Word for Windows.

MFC AppWizard упрощает программисту разработку таких приложений. Чтобы главное окно приложения с однооконным интерфейсом, или MDI окна многооконных приложений можно было разделить на несколько частей достаточно включить переключатель Use split window.



Рис. 5.7. Разделение окна на несколько частей

В группе Main frame styles расположены переключатели, определяющие вид главного окна приложения. Ниже приведена таблица, в которой описаны эти переключатели.

Переключатель

Описание

Thick frame

Главное окно имеет рамку, позволяющую изменить его размер

Minimize box

Главное окно содержит кнопку для сворачивания его в пиктограмму

Maximize box

Главное окно содержит кнопку для увеличения его размера до максимально возможного

System menu

Главное окно приложения будет иметь системное меню

Minimized

При запуске приложения его главное окно будет уменьшено до пиктограммы. Потом вы сможете открыть окно

Maximized

При запуске приложения его главное окно принимает максимально возможный размер

<




Рис. 5.8. Диалоговая панель Advanced Options, страница Window Styles

Если вы разрабатываете приложение, имеющее многооконный интерфейс, то вам становятся доступны переключатели из группы MDI child frame styles. Они позволяют выбрать внешний вид дочерних окон приложения. Вот краткое описание этих переключателей:

Переключатель

Описание

Thick frame

Все MDI окна приложения имеют рамку, позволяющую изменить их размер

Minimize box

MDI окна содержат кнопку для сворачивания их в пиктограмму

Maximize box

MDI окна содержат кнопку для увеличения их размера до максимально возможного

Minimized

Открываемые окна MDI будут уменьшены до пиктограммы

Maximized

Открываемые окна MDI будут иметь максимально возможный размер

Конечно, если вы не укажите в диалоговых панелях MFC AppWizard, что приложение должно иметь панель состояния status bar или справочную систему, то их можно будет добавить потом. Но для этого вам потребуется непосредственно исправлять исходный текст приложения. Значительно легче сделать это, включив несколько переключателей в панели MFC AppWizard, чем непосредственно изменять исходный текст.

Теперь вы можете перейти к следующей диалоговой панели MFC AppWizard. Для этого нажмите кнопку Next >. На экране появится панель, аналогичная панели, представленной на рисунке 1.4. В этой диалоговой панели, вы можете попросить MFC AppWizard снабдить исходный текст приложения комментариями, а также выбрать, как приложение будет использовать библиотеку классов MFC - вызывая библиотеки DLL или включая код классов непосредственно в приложение.

Теперь перейдите к последнему этапу определения свойств приложения, нажав кнопку Next >. На экране появится диалоговая панель для выбора названий классов приложения. Внешний вид этой панели уже был представлен на рисунке 5.9. Но теперь список классов будет значительно больше. Имена классов образуются от имени проекта. В нашем примере проект называется Single.

Если вы создаете приложение, имеющее однооконный интерфейс, то в списке классов будут класс приложения с именем CSingleApp, класс главного окна CMainFrame, класс документа CSingleDoc и класс для просмотра документа CSingleView.



Обратите внимание, что в качестве базового класса для CSingleView можно выбрать различные классы, определенные в библиотеке MFC.



Рис. 5.9. Выбор базового класса для класса CSingleView

Вы должны выбрать базовый класс, который лучше всего подходит для вашего приложения. Это может значительно сэкономить время, которое вы потратите на создание приложения.

Например, если вам надо создать редактор текста, который может воспринимать файлы в формате RTF, то вам достаточно наследовать класс CSingleView от базового класса CRichEditView. И все! Откомпилируйте проект и текстовый редактор готов. Вы можете открывать файлы в текстовом формате и формате RTF, редактировать их и сохранять измененные файлы.

Если вы создаете многооконное приложение, то проект будет содержать еще один класс - CChildFrame. Этот класс управляет дочерними MDI окнами приложения.

В зависимости от того, включите ли вы в приложение поддержку технологии OLE, баз данных, стандарта WOSA, в проект могут быть включены и другие классы.

Теперь, когда вы закончили заполнять диалоговые панели MFC AppWizard, нажмите кнопку Finish. На экране появится диалоговая панель New Project Information. В этой панели приводится описание приложения. Если вас что-то не устраивает, вы можете нажать кнопку Cancel и создать проект заново.

Чтобы продолжить разработку приложения, нажмите кнопку OK. MFC AppWizard приступит к построению проекта и создаст все файлы проекта. Полученный проект сразу можно оттранслировать и запустить на выполнение. Для этого вы можете воспользоваться меню Build или нажать комбинацию клавиш <Ctrl + F5>.


Просмотр дерева наследования классов


ClassView предоставляет очень интересную и полезную возможность просмотра дерева наследования классов приложения. Для этого выберите название интересующего вас класса из списка классов и откройте временное меню, нажав правую кнопку мыши. Строки Derived Classes и Base Classes позволяют просмотреть последовательность классов, порожденных от данного класса или последовательность базовых классов.

На рисунке 2.17 мы воспользовались возможностями ClassView для изучения последовательности базовых классов для основного класса приложения MFHello.

Рис. 2.17. Порядок наследования, диалоговая панель Base Classes and Members

Диалоговая панель Base Classes and Members не имеет кнопок OK и Cancel. Она закрывается в том случае, если вы выберите из нее элемент класса или переключитесь на другое окно. Иногда удобно, чтобы диалоговая панель постоянно присутствовала на экране. Вы можете “прикрепить” ее к главному окну Microsoft Visual C++. Нажмите кнопку

, она изменится на
. Теперь в любой момент времени вы сможете открыть панель Base Classes and Members.

В левой части этой панели отображается список классов в порядке наследования. Базовые классы расположены ниже и правее наследуемых классов. Вы наглядно можете определить, что основным базовым классом для CMFHelloApp является класс CObject. Из этого класса наследуются классы CCmdTarget, CWinThread, CWinApp и только затем класс CMFHelloApp, определенный в приложении.

В правой части экрана отображаются элементы класса, выбранного из списка классов. Вы можете просмотреть не только элементы классов, определенных в приложении, но также и элементы классов из библиотеки MFC. Все элементы класса разделены на три группы в зависимости от их области видимости - Public, Protected и Private.

Панель Base Classes and Members позволяет легко перейти к редактированию любого элемента класса. Для этого выберите название этого элемента из списка и сделайте по нему двойной щелчок левой кнопкой мыши.

Чтобы вам было легче отличить методы от данных класса, перед методами располагается символ f, а перед данными d. Непосредственно перед именем метода или данных расположен символ, позволяющий отличить статические и виртуальные методы. Перед названиями статических методов расположен символ

, а перед виртуальными -
.


Если список методов и данных класса слишком велик, вы можете отображать в диалоговой панели только их часть. Выберите из поля Functions типы методов, имена которых надо отобразить. Доступны следующие типы методов:

Тип метода

Отображать

All

Все методы

Virtual

Виртуальные методы

Static

Статические методы

Non- Static

Все методы, кроме статических

Non- Virtual

Все методы, кроме виртуальных

Non- Static Non-Virtual

Все методы, кроме статических и виртуальных

None

Не отображать методы класса

Аналогично, из списка Data надо выбрать типы элементов данных класса, которые надо отображать на экране. В следующей таблице описаны различные типы данных, представленные в списке Data.

Тип элементов данных

Отображать

All

Все элементы данных

Static

Статические элементы данных

Non- Static

Все данные, кроме статических

None

Не отображать элементы данных

В нижней правой части диалоговой панели Base Classes and Members отображаются названия файлов в которых определен (группа Definitions), и в которых используется (группа References) выбранный элемент. Двойной щелчок левой кнопкой мыши позволяет открыть в редакторе соответствующий файл. Курсор при этом сразу устанавливается в то место, где объявляется или используется выбранный элемент класса.


Просмотр характеристик класса


Последняя страница Class Info диалоговой панели ClassWizard позволяет просмотреть основные характеристики класса, изменить фильтр сообщений Windows, изменить внешний класс и внешние переменные.

Выберите из списка Class name имя интересующего вас класса. В группе File details отображается справочная информация о выбранном классе. Поле Header содержит название включаемого файла, в котором определен класс, поле Source - имя файла реализации класса (в нем размещаются определения методов класса и некоторых данных класса). В поле Base class отображается название базового класса, а в поле Resource - идентификатор ресурса (если он есть), обслуживаемого классом. Например, если вы исследуете класс, наследованный от класса CDialog, который обслуживает диалоговую панель, то в поле Base class выводится CDialog, а в поле Resource идентификатор диалоговой панели.

Основные характеристики классов, отображаемые в полях группы File details, нельзя изменить в автоматическом режиме. Вы можете редактировать только характеристики класса, расположенные в группе Advanced options.

В зависимости от того, какой тип объекта Windows - диалоговую панель, окно, дочернее окно и т. д., обслуживает класс, он может получать различные множества сообщений.

Список сообщений, для которых класс может содержать методы-обработчики, отображается в списке Messages на странице Message Map главной диалоговой панели ClassWizard. Так как операционная система Windows имеет очень много различных сообщений, то ClassWizard помещает в этот список только часть этих сообщений.

Список сообщений формируется в соответствии с типом объекта Windows, обслуживаемом данным классом. Отбор сообщений происходит по фильтру, который отображается в поле Message filter страницы Class Info диалоговой панели ClassWizard. Вы можете поменять этот фильтр. Вам доступны следующие фильтры сообщений:

Список Message filter

Объект, сообщения от которого должны обрабатываться

Child Window

Дочернее окно

Dialog

Диалоговое окно

MDI Child Frame

Дочернее окно MDI

Not a Window

Не окно

Topmost Frame

Окно frame window - главное окно приложения

Window

Окно

ClassWizard позволяет отобразить (привязать) органы управления на диалоговой панели или форме к внешним (foreign) объектам классов CRecordset or CDaoRecordset. Для этого можно использовать список Foreign class и поле Foreign variable. Более подробно об этой возможности мы расскажем в одной из следующих книг серии.