Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Microsoft visual C++ и MFC

© Александр Фролов, Григорий Фролов
Том 24, М.: Диалог-МИФИ, 1993.

[Назад] [Содеожание] [Дальше]

Исключения - класс CException

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

Для обработки исключительных ситуаций, возникающих в MFC, определен специальный класс. Сам класс CException XE "CException" является абстрактным классом. Объекты такого класса создавать нельзя. Для обработки исключительных ситуаций, возникающих в MFC, используется классы наследованные от класса CException:


CMemoryException		<- |  <-  CException
CFileException		<- |
CArchiveException		<- |
CNotSupportedException	<- |
CResourceException		<- |
CDaoException				<- |
CDBException				<- |
COleException				<- |
COleDispatchException	<- |
CUserException		<- |

Как правило, эти исключения вызываются методами классов MFC, когда возникают какие-либо ошибочные ситуации. Так, например, если вы попытаетесь открыть несуществующий файл, воспользовавшись для этого методом Open из класса CFile, то будет вызвано исключение CFileException.

Если вы желаете обрабатывать исключения, которые вызываются методами классов MFC, вы должны определить обработчики для этих исключений. Каждый такой обработчик должен представлять собой блок catch, в качестве аргумента которого надо использовать указатель на объект класса CException или указатель на объект класса, наследованного от класса CException:


try
{
	// Здесь может находится код, который вызывает исключение
}

// Обработчик для исключения типа CMemoryException
catch(CMemoryException* ptrException)
{
	// Обработка исключения ...

	// В конце удаляем объект исключения
	ptrException -> Delete;
}

Еще раз подчеркнем, что обработчик исключений MFC должен принимать указатель на объект класса CException (или класса, наследованного от CException). Этот объект создается при возникновении исключительных ситуаций внутри методов MFC. После того как этот объект окажется не нужен, ваш обработчик должен его удалить. Для этого предназначен метод Delete, определенный в классе CException. Не используйте для удаления объектов класса CException и объектов классов, наследованных от него, обыкновенный оператор delete.

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

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

Класс

Исключение вызывается

CMemoryException XE "CMemoryException"

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

CFileException XE "CFileException"

При работе с файлами

CArchiveException XE "CArchiveException"

Во время записи или восстановления объектов (Archive/Serialization)

CNotSupportedException XE "CNotSupportedException"

При обращении к неизвестный метод, который не поддерживается данным классом

CResourceException XE "CResourceException"

Ошибка при работе с ресурсами Windows

CDaoException XE "CDaoException"

Ошибка при работе с базами данных, через средства DAO

CDBException XE "CDBException"

Ошибка при работе с базами данных, через средства ODBC

COleException XE "COleException"

Ошибка при работе OLE

COleDispatchException XE "COleDispatchException"

Ошибка при работе OLE

CUserException XE "CUserException"

При обработке этого исключения на экране отображается сообщение, а затем вызывается исключение CException

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

Класс CException

Класс CException XE "CException" включает два виртуальных метода GetErrorMessage и ReportError. Эти методы позволяют получить словесное описание причины, которая привела к вызову исключения. Заметим, что методы GetErrorMessage и ReportError чисто виртуальные, поэтому они должны быть переопределены в наследуемом классе:


virtual BOOL 
GetErrorMessage(LPTSTR lpszError, UINT nMaxError, 
	PUINT pnHelpContext = NULL);

Когда вы вызываете в обработчике исключения метод GetErrorMessage, он записывает в буфер lpszError сообщение об ошибке, вызвовшей исключение. Размер буфера надо указать в параметре nMaxError. В конце сообщения всегда записывается символ двоичного нуля. Если сообщение не помещается в буфер lpszError (оно больше чем nMaxError - 1 байт), тогда в буфер записываются только nMaxError - 1 символов сообщения. В последний байт записывается двоичный нуль.

Необязательный параметр pnHelpContext может содержать указатель на переменную типа UINT, в которую будет записан идентификатор контекстной подсказки (help context ID).

Метод GetErrorMessage возвращает ненулевое значение, если сообщение об ошибке доступно, и нуль в противном случае.

Вы можете вызывать метод ReportError из обработчика исключений:


virtual int 
ReportError(UINT nType = MB_OK, UINT nMessageID = 0);

Метод ReportError отображает в диалоговой панели на экране сообщение об ошибке, вызвавшей данное исключение. Параметр nType определяет внешний вид диалоговой панели сообщения. В качестве параметра nType можно указать любую комбинацию стилей панелей сообщения, таких как MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_ICONEXCLAMATION, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONSTOP. Если вы не укажите этот параметр, тогда подразумевается стиль MB_OK, то есть панель сообщения с одной кнопкой OK.

Иногда исключение может не иметь текстового описания. Вы можете указать методу ReportError, чтобы он отображал в этом случае определенное сообщение. Текст этого сообщения надо сохранить в строковом ресурсе, а соответствующий идентификатор передать методу ReportError в качестве второго параметра nMessageID. Если вы не укажите этот параметр, тогда отображается сообщение “No error message is available”.

Метод ReportError возвращает значение типа AfxMessageBox. Оно определяет, какая кнопка была нажата в диалоговой панели с сообщением.

Методы GetErrorMessage и ReportError используют данные из ресурсов, созданных AppWizard. Поэтому они могут работать неправильно, если приложение создано без использования AppWizard.

Класс CMemoryException

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

Когда приложение пытается создать новую переменную или объект, вызывая оператор new, то в том случае, если память под него не может быть выделена, создается объект класса CMemoryException XE "CMemoryException" и вызывается соответствующее исключение.

Функции malloc не вызывают исключение CMemoryException, но вы можете проверять значение, возвращаемое функцией malloc, и если оно равно нулю, вызывать его сами.

Чтобы самому вызвать исключение, воспользуйтесь функцией AfxThrowMemoryException XE "AfxThrowMemoryException" :

void AfxThrowMemoryException();

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

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


if(GlobalAlloc(GMEM_FIXED, 1000000) == NULL) 
	AfxThrowMemoryException();

Класс CFileException

Класс CFileException XE "CFileException" предназначен для обработки исключительных ситуаций, возникающих во время создания или вызова методов класса CFile и порожденных от него классов. Этот класс описан нами в разделе “Класс CFile” и предназначается для работы с файловой системой. Естественно, при работе с файловой системой могут возникнуть самые разнообразные ошибки (исключительные ситуации): попытка открыть несуществующий файл, переполнение диска во время операции записи, ошибка чтения с диска и т. д.

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

Константа

Причина ошибки

CFileException:: none

Без ошибки

CFileException:: generic

Неопределенная ошибка

CFileException:: fileNotFound

Файл не найден

CFileException:: badPath

Задан несуществующий путь

CFileException:: tooManyOpenFiles

Открыто слишком много файлов

CFileException:: accessDenied

Доступ к файлу закрыт

CFileException:: invalidFile

Использование неправильного идентификатора (дескриптора) файла

CFileException:: removeCurrentDir

Попытка удалить текущий каталог

CFileException:: directoryFull

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

CFileException:: badSeek

Ошибка во время перемещения указателя файлов

CFileException:: hardIO

Ошибка аппаратного обеспечения компьютера

CFileException:: sharingViolation

Программа SHARE.EXE не загружена или общая область заблокирована (locked)

CFileException:: lockViolation

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

CFileException:: diskFull

Нет свободного пространства на диске

CFileException:: endOfFile

Достигнут конец файла

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

Приложение 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 XE "AfxThrowFileException" :

void AfxThrowFileException(int cause, LONG lOsError = –1); 

Параметр cause должен определять причину исключения. В качестве этого параметра можно задавать возможные значения для элемента данных m_cause из класса CFileException (см. таблицу выше). Необязательный параметр lOsError может содержать код ошибки, определенной операционной системой.

Класс CArchiveException

Исключительные ситуации, возникающие во время записи и восстановления объектов из файла, вызывают исключение CArchiveException XE "CArchiveException" .

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

Константа

Причина ошибки

CArchiveException:: none

Без ошибки

CArchiveException:: generic

Неопределенная ошибка

CArchiveException:: readOnly

Попытка записи в архивный объект, открытый для чтения

CArchiveException:: endOfFile

Обнаружен конец файла при чтении объекта

CArchiveException:: writeOnly

Попытка читать из архивного объекта, открытого для записи

CArchiveException:: badIndex

Неправильный формат файла

CArchiveException:: badClass

Попытка прочитать объект в объект неправильного типа

CArchiveException:: badSchema

Попытка чтения объекта с несоответствующей версией класса

Чтобы создать объект CArchiveException и вызвать исключение воспользуйтесь функцией AfxThrowArchiveException XE "AfxThrowArchiveException" :

void AfxThrowArchiveException(int cause); 

Параметр cause должен определять причину вызова исключения. Возможный список значений этого параметра представлен в таблице выше (см. элемент данных m_cause класса CArchiveException).

Класс CNotSupportedException

Если приложение пытается вызвать несуществующий метод класса, то вызывается исключение CNotSupportedException XE "CNotSupportedException" . Конструктор класса CNotSupportedException имеет следующий вид:

CNotSupportedException();

Однако если вы сами желаете вызвать из своего кода исключение этого типа, то вместо того, чтобы создавать объект класса CNotSupportedException вручную и передавать его оператору throw, воспользуйтесь функцией AfxThrowNotSupportedException XE "AfxThrowNotSupportedException" :

void AfxThrowNotSupportedException();

Класс CResourceException

Если в процессе работы возникают проблемы с ресурсами, например приложение пытается загрузить несуществующий ресурс, тогда вызывается исключение CResourceException XE "CResourceException" . Вы можете вызвать это исключение сами. Для этого воспользуйтесь функцией AfxThrowResourceException XE "AfxThrowResourceException" :

void AfxThrowResourceException();

Класс CUserException

Если какая-либо операция при работе приложения закончилась с ошибкой, оно может вызвать функцию AfxMessageBox, чтобы сообщить об этом пользователю, а затем вызвать исключение с объектом класса CUserException XE "CUserException" . Чтобы создать объект класса CUserException и вызвать исключение, воспользуйтесь функцией AfxThrowUserException XE "AfxThrowUserException" :

void AfxThrowUserException();
[Назад] [Содеожание] [Дальше]