Системное программирование в среде Win32

Автор работы: Пользователь скрыл имя, 25 Марта 2014 в 14:49, лекция

Краткое описание

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

Прикрепленные файлы: 1 файл

Win32_лек_часть1.doc

— 811.00 Кб (Скачать документ)

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

SEH предоставляет две основные возможности: обработку исключений  (exception handling) и обработку завершения (termination handling).

 

6.1. Исключения и их обработчики

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

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

Блоки Try  и  Except

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

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

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

 

Для установления контроля за блоком кода создаются следующие блоки 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. Управление  переходит к обработчику исключения, где определяется значение выражения фильтра. Сначала вызывается GetExceptionCode, и возвращенное ей значение передается как параметр в функцию Filter.

3. Функция Filter  определяет свои действия  в зависимости от кода исключения.

4. Код исключения  в данном случае EXCEPTION_INT_DI-VIDE_BY_ZERO.

5. Функция  фильтра решает, что должен быть  выполнен обработчик исключения, и поэтому возвращает значение exception_execute_handler.

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 ( )) {  ….}

 

Пример 6.1. Значение фильтра  EXCEPTION_EXECUTE_HANDLER  и EXCEPTION_CONTINUE_SEARCH

__try {

x=0;

y=5/x;

}

 

__except ((GetExceptionCode ( )= = EXCEPTION_ACCESS_VIOLATION) || (GetExceptionCode ( )= = EXCEPTION_INT_DIVIDE_BY_ZERO)) ?

EXCEPTION_EXECUTE_HANDLER:EXCEPTION_CONTINUE_SEARCH) {

switch  (GetExceptionCode ( )) {

case EXCEPTION_ACCESS_VIOLATION:

//обработка  нарушения доступа к памяти

break;

case EXCEPTION_INT_ DIVIDE_BY_ZERO:

//обработка  деления целого числа на 0

break;

}

}

}

Пример 6.2. Использование значения фильтра EXCEPTION_CONTINUE_EXECUTION

 

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_access_violatiom — попытка читать или записывать по виртуальному адресу, к которому процесс не имеет доступа;
  • EXCEPTION_DATATYPE_MISALIGNMENT — попытка считать или записать невыровненные данные. Многие типы процессоров требуют, например, чтобы данные типа dword были выровнены по 4-байтовым границам;
  • exception_noncontinuable —  значение выражения фильтра было exception_continue_execution, но после данного исключения продолжение невозможно.

 

  • Исключения, вызываемые функциями распределения памяти, HeapAlloc и HeapCreate, если в них используется флаг HEAP_GENERATE_EXCEPTIONS. Значение кода будет status_no_memory  либо EXCEPTION_ACCESS_VIOLATION.
  • Определяемый пользователем код исключения, генерируемый функцией RaiseException, которая рассматривается ниже.
  • Множество разнообразных кодов по арифметическим операциям (особенно с плавающей запятой), например:

EXCEPTION_INT_DIVIDE_BY_ZERO И EXCEPTION_FLT_OVERPLOW.

  • Исключения, используемые отладчиками, например exception_breakpoint и EXCEPTION_SINGLE_STEP.

 

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

 

Функция GetExceptionInformation

Когда возникает исключение, операционная система помещает в стек соответствующего потока структуры 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