Автор работы: Пользователь скрыл имя, 25 Марта 2014 в 14:49, лекция
Конспект лекций содержит описание технологии системного программирования под Windows с использованием функций Win32 API. В первой части конспекта лекций рассмотрены особенности архитектуры ОС Windows, специфика интерфейса прикладного программирования Win32, структура приложений для Windows. Подробно рассмотрены API функции и основные структуры данных для работы с дисками, каталогами, файлами. Отдельная глава посвящена структурной обработке исключений SEH.
SEH гарантирует,
что программа сможет
SEH предоставляет две основные возможности: обработку исключений (exception handling) и обработку завершения (termination handling).
Если в программе не будет какой-либо обработки исключений, непреднамеренная исключительная ситуация, например разыменование нулевого указателя или деление на нуль, приведет к немедленной остановке программы.
Поддержка SEH обеспечивается сочетанием функций Win32, поддержкой в языке, которую обеспечивает компилятор, и поддержкой времени выполнения. Конкретная форма поддержки в языке может меняться; все примеры в этой главе рассчитаны на Microsoft С.
Сначала определим, какие блоки кода нужно контролировать, и снабдим их обработчиками исключений. Можно контролировать функцию целиком или иметь отдельные обработчики исключений для разных блоков кода и функций.
Блок кода может оказаться удачным местом для обработчика исключений в ситуациях, перечисленных ниже.
Для установления контроля за блоком кода создаются следующие блоки try и except:
_try {
/* Блок контролируемого кода */
}
_except (выражение_фильтра) {
/* Блок обработки исключения */
}
Блок _try является частью обычного кода приложения. Если в блоке происходит исключение, операционная система передает управление обработчику исключения - блоку кода, связанному с ключевым словом except. Последующие действия определяются значением выражение_ фильтра.
Следует заметить, что исключение может произойти и в блоке, который вложен в блок _try; в этом случае поддержка времени выполнения «разворачивает» стек, находит обработчик исключения и передает ему управление. То же самое происходит, когда исключение возникает внутри функции, вызываемой в блоке _try.
На рис. 6.1 показано, как выявляется в стеке обработчик исключения, когда происходит исключение. Как только блок обработчика исключения завершается, управление переходит к оператору, следующему за блоком исключения, если только в обработчике не было оператора, передающего управление еще куда-нибудь.
Выражение фильтра в операторе __except вычисляется немедленно после того, как происходит исключение. Обычно это литеральная константа, вызов функции фильтра или условное выражение. Во всех случаях выражение должно возвратить одно из трех значений.
1. exception_execute_handler -— система выполняет блок except, как показано на рис. 6.1. Это обычная ситуация.
2. exception_continue_search -— система игнорирует обработчик исключения и последовательно ищет его во вложенных блоках, пока не находит.
Рис. 6.1. SEH, блоки и функции
3. EXCEPTION_CONTINUE_EXECOTION
— система немедленно
Ниже приведен простой пример, в котором обработчик исключения удаляет временный файл, если исключение происходит в теле цикла. Обратите внимание, что оператор _try может применяться к любому блоку, включая блок, связанный с оператором while, if или другим оператором ветвления. В этом примере при исключении удаляется временный файл и закрывается его дескриптор, после чего цикл продолжается
GetTempFileName (TempFile, .. .) ;
while (. . .) _try {
hFile = CreateFile (TempFi:le, ..., open_always, ...);
SetFilePointer (hFile, 0, DNULL, FILE_END) ;
…
WriteFile (hFile, ...);
i = *p; /* Может
произойти исключение
CloseHandle (hFile);
}
_except (EXCEPTION_EXECUTE_HANDLER) {
CloseHandle (hFile);
DeleteFile (TempFile);
/* Теперь в цикле выполняется следующая итерация. */
}
/* Сюда передается управление после нормального выхода из цикла.
Дескриптор файла закрыт в любом случае, а временного файла не
существует, если произошло исключение. */
Логика этого фрагмента кода следующая:
На рис. 6.2 показана последовательность событий, происходящих при возникновении исключения. Слева показан код, а числа в кружках обозначают действия, производимыe языковой поддержкой периода выполнения.
1. Происходит исключение, в данном случае деление на нуль.
2. Управление
переходит к обработчику
3. Функция
Filter определяет свои действия
в зависимости от кода
4. Код исключения в данном случае EXCEPTION_INT_DI-VIDE_BY_ZERO.
5. Функция
фильтра решает, что должен быть
выполнен обработчик
6. Выполняется обработчик исключения — код, связанный с оператором _except.
7. Управление передается из блока try-except.
Рис. 6.2. Последовательность обработки исключений
Блок except или выражение фильтра может определить конкретное исключение с помощью функции
DWORD GetExceptionCode (VOID)
Код исключения должен быть получен немедленно после исключения. Поэтому сама функция фильтра не может вызывать GetExceptionCode (это ограничение установлено в компиляторе). Обычно эта функция вызывается в выражении фильтра (см. ниже пример 6.1); кодом исключения служит параметр заданной пользователем функции фильтра, например:
__except (MyFilter (GetExceptionCode ( ))) { … }
В этой ситуации функция фильтра определяет и возвращает значение выражения фильтра, которое должно быть одним из трех значений, указанных выше. Функция может определить на основе кода исключения свое возвращаемое значение; например, фильтр может передавать исключения при операциях с плавающей запятой внешнему обработчику (возвращая exception_contiitoe_search), а нарушение доступа к памяти обрабатывать в текущем обработчике (возвращая EXCEPTION_EXECUTE_HANDLER).
Можно также вызвать эту функцию и из обработчика исключений
….
switch (GetExceptionCode ( )) { ….}
__try {
x=0;
y=5/x;
}
__except ((GetExceptionCode ( )= = EXCEPTION_ACCESS_VIOLATION) || (GetExceptionCode ( )= = EXCEPTION_INT_DIVIDE_BY_ZERO)) ?
EXCEPTION_EXECUTE_HANDLER:
switch (GetExceptionCode ( )) {
case EXCEPTION_ACCESS_VIOLATION:
//обработка нарушения доступа к памяти
…
break;
case EXCEPTION_INT_ DIVIDE_BY_ZERO:
//обработка деления целого числа на 0
…
break;
}
}
}
char gl_Buff[100];
void FuncPrim ( ) {
int x=0;
char *pBuf=NULL;
__try {
*pBuf=’M’;
x = 5/x;
}
__except (Filter1(&pBuf)) {
MessageBox(NULL, “Ошибка”, NULL, MB_OK);
}
MessageBox(NULL, “Функция выполнена”, NULL, MB_OK);
}
LONG Filter1 (char **pBuf) {
if (*pBuf = = NULL) {
*pBuf = gl_Buff;
return (EXCEPTION_CONTINUE_EXECUTION)
}
return (EXCEPTION_EXECUTE_HANDLER );
}
Первый раз исключение возникает, когда мы пытаемся поместить в буфер, на который указывает pBuf, значение «М». Процессор генерирует исключение и вычисляет выражение в фильтре исключений в блоке except. В этом блоке адрес переменной pBuf передается функции Filter1.
Получая управление, Filter1 проверяет, не равен ли pBuf значению NULL, и если да, устанавливает его так, чтобы он указывал на глобальный буфер gl_Buff. Тогда фильтр возвращает EXCEPTION_CONTINUE_EXECUTION. Обнаружив такое значение выражения в фильтре, система возвращается к инструкции, вызвавшей исключение, и пытается выполнить ее снова. На этот раз все происходит успешно, и М будет записана в первый байт буфера gl_Buff.
Когда выполнение кода продолжится, вновь возникает исключение – деление на ноль. И система вычислит выражение фильтра исключений. На этот раз Filter1 вырнет EXCEPTION_EXECUTE_HANDLER, что подскажет системе выполнить код в блоке __except, и на экране появится окно с сообщением об исключении.
GetExceptionCode может
возвращать множество
EXCEPTION_INT_DIVIDE_BY_ZERO И EXCEPTION_FLT_OVERPLOW.
Коды исключений формируются по тем же правилам, что и коды ошибок, определенные в файле WinError.h. Каждое значение типа DWORD разбивается на поля:
Таблица 6.1
Биты |
31-30 |
29 |
28 |
27-16 |
15-0 |
Содержимое |
Код степени «тяжести» (severity) |
Кем определен: Mi-crosoft или пользователем |
Зарезервированный |
Код подсистемы (facility code) |
Код исключения |
Значение |
0 – успех 1 - информация; 2 -преду-преждение 3 - ошибка |
0 –Microsoft 1- пользов. |
Должен быть равен 0 |
Определяется Microsoft |
Определяется Mic-rosoft или пользователем |
Пример. Код исключения EXCEPTION_ACCESS_VIOLATION равен
0хС0000005
С 0 0 0 0 0 0 5
1100 0000 0000 0000 0000 0000 0000 0101
Биты 30 и 31 установлены в 1, указывая, что нарушение доступа является ошибкой (поток не может продолжить выполнение).
Бит 29 равен 0, т.е. код определен Microsoft. Бит 28 равен 0 – резервный.
Биты 16 – 27 все равны 0, сообщая код подсистемы FACILITY_NULL (нарушение может произойти в любой подсистеме ОС, а не в какой-то одной).
Биты 0-15 дают значение 5, Microsoft присвоила исключению, связанному с нарушением доступа, код 5.
Когда возникает исключение, операционная система помещает в стек соответствующего потока структуры exceptION_record, CONTEXT, EXCEPTION_POINTERS.
Существует функция, вызываемая только из выражения фильтра, которая возвращает указатель на структуру EXCEPTION_POINTERS, содержащую дополнительную (в том числе зависящую от типа процессора) информацию:
LPEXCEPTION_POINTERS GetExceptionInformation (VOID)
Структура exception_pointers содержит как зависимую от процессора, так и независимую информацию, сгруппированную в двух других структурах:
typedef struct _EXCEPTION_POINTERS {
pexceptION_record ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS;
Структура exception_record содержит машинно-независимую информацию о последнем исключении. В состав структуры входят следующие элементы:
ExceptionCode – код исключения, с тем же набором значений, которые возвращаются функцией GetExceptionCode.
ExceptionFlags – флаги исключения. На данный момент определены только два значения: 0 (возобновляемое исключение) и eXCEption_noncontinuable (невозобновляемое исключение), что позволяет функции фильтра определить, следует ли пытаться продолжать выполнение.
ExceptionRecord – указатель на структуру exception_record, содержащую информацию о другом необработанном исключении. При обработке одного исключения может возникнуть другое. Когда возникает серия вложенных исключений, записи с информацией о них могут образовывать связанный список. Исключение будет вложенным, если оно генерируется при обработке фильтра. В отсутствие необработанных исключений ExceptionRecord равен NULL.
ExceptionAddress – адрес машинной команды, при выполнении которой произошло исключение.
NumberParametrs – количество параметров, связанных с исключением (0 – 15). Это число заполненных элементов в массиве ExceptionInformation. Почти для всех исключений значение этого элемента равно 0.
ExceptionInformation – массив дополнительных аргументов, описывающих исключение. Почти для всех исключений элементы этого массива не определены.
Последние два элемента структуры exception_record сообщают фильтру дополнительную информацию об исключении. Сейчас такую информацию дает только один тип исключений: eXCEption_access_violation. Все остальные дают нулевое значение в элементе NumberParametrs. Проверив этот элемент, можно узнать, надо ли просматривать массив ExceptionInformation.
Информация о работе Системное программирование в среде Win32