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

быстродействие программы — в зависимости от типа процессора — может снизиться весьма значительно.

Funcfurter1

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

DWORD Funcfurter1()

{

DWORD dwTemp;

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

...

__try

{

//2. Запрашиваем разрешение на доступ

//к защищенным данным, а затем используем их

WaitForSingleObject(g_hSem, INFINITE); dwTemp = Funcinator(g_dwProtectedData);

}

_finally

{

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

RelcaseSemaphore(g_hSem, 1, NULL);

}

//4. Продолжаем что-то делать

return(dwTemp);

}

Допустим, в функции Funcinator, вызванной из блока try, — «жучок», из-за которо го возникает нарушение доступа к памяти. Без SEH пользователь в очередной раз уви дел бы самое известное диалоговое окно Application Error. Стоит его закрыть — за вершится и приложение Если бы процесс завершился (из-за пеправильногодоступа к памяти), семафор остался бы занят — соответственно и ожидающие его потоки не получили бы процессорное время. Но вызов ReleaseSemaphore в блоке finаllу гаранти рует освобождение семафора, дажс ссли нарушение доступа к памяти происходит в какой-то другой функции.

Раз обработчик завершения — такое мощное средство, способное перехватывать завершение программы из-за неправильного доступа к памяти, можно смело рассчи тывать ична то, что оно также перехватит комбинации setjump/longump и элементар ные операторы типа break и continue.

Проверьте себя: FuncaDoodleDoo

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

DWORD FuncaDoodleDoo()

{

DWORD dwTerrip = 0;

while (dwTemp < 19)

{

__try

{

if (dwTemp == 2) continue; if (dwTemp == 3) break;

}

__finally { dwTernp++; }

dwTemp++;

}

dwTemp += 10; return(dwTemp);

}

Проанализируем эту функцию шаг за шагом. Сначала dwTemp приравнивается 0. Код в блоке try выполняется, но ни одно из условий в операторах if не дает TRUE, и поток управления естественным образом переходит в блок finаllу, где dwTemp увели чивается до 1. Затем инструкция после блока finаllу снова увеличивает значение dwTemp, приравнивая его 2.

На следующей итерации цикла dwTemp равно 2, поэтому выполняется оператор continue в блоке try, Без обработчика завершения, вызывающего принудительное вы полнение блока finаllу перед выходом из try, управление было бы передано непосред ственно в начало цикла while, значение dwTemp больше бы не менялось — и мы в бесконечном цикле! В присутствии же обработчика завершения система обнаружи вает, что оператор continue приводит к преждевременному выходу из try, и передает управление блок finаllу. Значение dwTemp в нем увеличивается до 3, но код за этим

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

Теперь обрабатываем третий проход цикла. На этот раз значение выражения в первом if равно FALSE, а во втором — TRUE. Система снова перехватывает нашу по пытку прервать выполнение блока try и обращается к коду finаllу. Значение dwTemp увеличивается до 4. Так как выполнен оператор break, выполнение возобновляется после тела цикла. Поэтому код, расположенный за блоком finаllу (но в теле цикла), не выполняется. Код, расположенный за телом цикла, добавляет 10 к значению dwTemp, что дает в итоге 14, — это и есть результат вызова функции. Даже не стану убеж дать Вас никогда не писать такой код, как в FuncaDoodleDoo. Я-то включил continue и break в середину кода, только чтобы продемонстрировать поведение обработчика завершения.

Хотя обработчик завершения справляется с большинством ситуаций, в которых выход из блока try был бы преждевременным, он не может вызвать выполнение бло ка finally при завершении потока или процесса. Вызов ExitThread или ExitProcess сра зу завершит поток или процесс — без выполнения какого-либо кода в блоке finаllу. То же самое будет, если Ваш поток или процесс погибнут из-за того, что некая програм ма вызвала

TerminateThread или TerminateProcess. Некоторые функции библиотеки С (вроде abort). в

свою очередь вызывающие ExitProcess, тоже исключают выполнение блока finаllу. Раз Вы

не можете запретитьдругой программе завершение какого-либо из своих потоков, или процессов, так хоть сами не делайте преждевременных вызо вов ExitThread и ExitProcess.

Funcenstein4

Рассмотрим еще один сценарий обработки завершения.

DWORD Funcenstein4()

{

DWORD dwTemp;

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

...

__try

{

//2. Запрашиваем разрешение на доступ

//к защищенным данным, а затем используем их

WaitForSingleObject(g_hSem, INFINITE); g_dwProtectedData = 5;

dwTemp = g_dwProtectedData;

//возвращаем новое значение return(dwTemp);

}

__finally

{

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

ReleaseSemaphore(g_hSem, 1, NULL);

return(103);

}

//продолжаем что-то делать - этот код

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

dwTemp = 9;

return(awTemp);

}

Блок try в Funcenstein4 пытается вернуть значение переменной dwTemp (5) функ ции, вызвавшей Funcenstein4. Как мы уже отметили при обсуждении Funcenstein2, попытка преждевременного возврата из блока try приводит к генерации кода, кото рый записывает возвращаемое значение во временную переменную, созданную ком пилятором. Затем выполняется код в блоке finаllу. Кстати, в этом варианте Funcenstein2 я добавил в блок finаllу оператор return. Вопрос: что вернет Funcenstein4 — 5 или 103? Ответ: 103, так как оператор return в блоке finаllу приведет к записи значения 103 в ту же временную переменную, в которую занесено значение 5. По завершении блока finаllу текущее значение временной переменной (103) возвращается функции, вызвав шей Funcenstein4

Итак, обработчики завершения, весьма эффективные при преждевременном вы ходе из блока try, могут дать нежелательные результаты именно потому, что предотв ращают досрочный выход из блока try. Лучше всего избегать любых операторов, спо собных вызыать преждевременный выход из блока try обработчика завершения. А в идеале —

удалить все операторы return, continue, break,goto (и им подобные) как из блоков try, так и из блоков finally. Тогда компилятор сгенерирует код и более компак-. тный (перехватывать преждевременные выходы из блоков try не понадобится), и бо лее быстрый (на локальную раскрутку потребуется меньше машинных команд). Да и читать Ваш код будет гораздо легче.

Funcarama1

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

BOOL Funcarama1()

{

HANDLE hFile = INVALID_HANDLE_VALUE;

PVOID pvBuf = NULL;

DWORD dwNumBytesRead;

BOOL fOk;

hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ,

FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

lf (hFile == INVALID_HANDLE_VALUE)

{

return(FALSE);

}

pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRTTE);

if (pvBuf == NULL)

{

CloseHandle(hFile);

return(FALSE);

}

fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);

if (!fOk || (dwNumBytesRead == 0))

{

VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT); CloseHandle(hFile);

return(FALSE);

}

// что-то делаем с данными

...

// очистка всех ресурсов

VirtuallFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);

CloseHandle{hFile); return(TRUE);

}