Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

memcpy(pbDup, pbSrc, cb);

}

__except (EXCEPTION_EXECUTE_HANDLER)

{

free(pbDup);

pbDup = NULL;

}

return(pbDup);

}

Эта функция создает буфер в памяти и копирует в него байты из исходного бло ка. Затем она возвращает адрес этого дубликата (или NULL, если вызов закончился неудачно). Предполагается, что буфер освобождается вызывающей функцией — ког да необходимость в нем отпадает Это первый пример, где в блоке except понадобится какойто код. Давайте проанализируем работу этой функции в различных ситуациях.

Если в пираметр pbSrc передается некорректный адрес или если вызов malloc завершается неудачно (и дает NULL), memcpy возбуждает нарушение доступа А это приводит к выполнению фильтра, который передает управление блоку except. Код в блоке except освобождает буфер памяти и устанавливает pbDup в NULL, чтобы вызвавший эту функцию поток узнал о cc неудачном завершении. (Не забудьте, что стандарт ANSI С допускает передачу NULL функции free.)

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

Глобальная раскрутка

Когда фильтр исключений возвращает EXCEPTION_EXECUTE_HANDLER, системе при ходится проводить глобальную раскрутку Она приводит к продолжению обработки всех незавершенных блоков try-finally, выполнение которых началось вслед за блоком tryexcept, обрабатывающим данное исключение. Блок-схема на рис. 24-2 поясняет, как система осуществляет глобальную раскрутку Посматривайте на эту схему, когда бу дете читать мои пояснения к следующему примеру

Рис. 24-2. Так система проводит глобальную раскрутку

void FuncOSTimpy1()

{

// 1 Что-то делаем здесь

...

__try

{

//2 Вызываем другую функцию

FuncORen1();

//этот код никогда не выполняется

}

__except (/* 6 Проверяем фильтр исключений */ EXCEPTION_EXECUTE,HANDLER)

{

// 8 После раскрутки выполняется атот обработчик

MessageBox(....);

}

// 9 Исключение обработано - продолжаем выполнение

...

}

void FuncORen1()

{

DWORD dwTemp = 0;

// 3. Что-то делаем здесь

...

__try

{

//4. Запрашиваем разрешение на доступ к защищенным данным

WaitForSingleObject(g_nSem, INFINITE);

//5. Изменяем данные, и здесь генерируется исключение

g_dwProtectedData = 5 / dwTemp;

}

__finally

{

//7. Происходит глобальная раскрутка, так как

//фильтр возвращает

FXCFPTTON_EXECUTE_HANDLER

// Даем и другим попользоваться защищенными данными

ReleaseSemaphore(g_hScm, 1, NULL);

}

// сюда мы никогда не попадем

...

}

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

FuncOStimpy1 начинает выполнение со входа в свой блок try и вызова FuncORen1. Последняя тоже начинает со входа в свой блок try и ждет освобождения семафора. Завладев им, она пытается изменить значение глобальной переменной g_dwProtected Data. Деление на нуль возбуждает исключение. Система, перехватив управление, ищет блок try, которому соответствует блок except. Поскольку блоку try функции FuncORenl соответствует 6лок finally, система продолжает поиск и находит блок try в FuncOStim py1, которому как раз и соответствует блок except.

Тогда система проверяет значение фильтра исключений в блоке except функции

FuncOStimpy1. Обнаружив, что оно — EXCEPTION_EXECUTE_HANDLER, система начи нает глобальную раскрутку с блока finally в функции FuncORen1. Заметьте: раскрутка происходит до выполнения кода из блока except в FuncOStimpy1. Осуществляя глобаль ную раскрутку, система возвращается к последнему незавершенному блоку try и ищет теперь блоки try, которым соответствуют блоки finally. В нашем случае блок finally находится в функции FuncORen1.

Мощь SEH по-настоящему проявляется, когда система выполняет код finally в Func ORen1. Из-за его выполнения семафор освобождается, и поэтомудругой поток полу чает возможность продолжить работу. Если бы вызов ReleaseSemapbore в блоке finally отсутствовал, семафор никогда бы не освободился.

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

Вот тук и работает структурная обработка исключений. Вообще-то, SEH — штука весьма трудная для понимания: в выполнение Вашего кода вмешивается операцион ная система Код больше не выполняется последовательно, сверху вниз; система уста навливает свой порядок — сложный, но все же предсказуемый. Поэтому, следуя блок схемам на рис, 24-1 и 24-2, Вы сможете уверенно применять SEH.

Чтобы лучше разобраться в порядке выполнения кода, посмотрим на происходя щее под другим углом зрения. Возвращая EXCEPTION_EXECUTE_HANDLER, фильтр сообщает операционной системе, что регистр указателя команд данного потока дол жен быть установлен на код внутри блока except Однако зтот регистр указывал на код внутри блока try функции FuncORen1. А из главы 23 Вы должны помнить, что всякий раз, когда поток выходит из блока try, соответствующего блок finally, обязательно вы полняется код в этом блоке finally. Глобальная раскрутка как раз и является тем меха низмом, который гарантирует соблюдение этого правила при любом исключении.

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

Глобальную раскрутку, осуществляемую системой, можно остановить, если в блок finally включить оператор return. Взгляните:

void FuncMonkey()

{

__try

{

FuncFish();

}

__except (EXCEPTION_EXECUTE_HANDLER)

{

MessageBeep(0);

}

MessageBox(...);

}

void FuncFish()

{

FuncPheasant();

MessageBox(...);

}

void FuncPheasant()

{

__try

{

strcpy(NULL, NULL);

}

__finally

{

return;

}

}

При вызове strcpy в блоке try функции FuncPheasant из-за нарушения доступа к памяти генерируется исключение Как только это происходит, система начинает про сматривать код, пытаясь найти фильтр, способный обработать данное исключение. Обнаружив, что фильтр в FuncMonkey готов обработать его, система приступает к глобальной раскрутке Она начинается с выполнения кода в блоке finally функции FuncPheasant. Ho этот блок содержит оператор return. Он заставляет систему прекра тить раскрутку, и FuncPheasant фактически завершается возвратом в FuncFish, кото рая выводит сообщение на экран Затем FuncFish возвращает управление FuncMonkey, и та вызывает MessageBox.