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

кнопки OK и Cancel. A если этого параметра нет — только кнопка ОК. Как только пользователь щелкнет кнопку OK, функция UnbandledExceptionFilter вернет

EXCEPTION_EXECUTE_HANDLER. Щелчок кноп ки Cancel (если она ссть) вызывает переход на следующий этап.

WINDOWS 98

ВWindows 98 упомянутые параметры хранятся не в реестре, а в файле Win.ini.

7.На этом этапе UnhandledExceptionFilter запускает отладчик как дочерний про цесс. Но сначала создает событие со сбросом вручную в занятом состоянии и наследуемым описателем. Затем извлекает из реестра значение параметра Debugger и вызывает sprintf для вставки идентификатора процесса (получен ного через функцию GetCurrentProcessd) и описателя события в командную строку. Элементу lpDesktop структуры STARTUPINFO присваивается значение "Winsta0\\Default", чтобы отладчик был доступен в интерактивном рсжимс на рабочем столе. Далее вызывается CreateProcess со значением TRUE в парамет ре bInherttHandles, благодаря чему отладчик получает возможность наследовать описатель объекта "событие" После этого UnhandledExceptionFilter ждет завер шения инициализации отладчика, вызвав WaitForSingleObjectEx с передачей ей описателя события. Заметьте, что вместо WaitForSingleObject используется Wait ForSingleObjectEx Это заставляет поток ждать в "тревожном* состоянии, кото рое позволяет ему обрабатывать все поступающие AРС-вызовы.

8.Закончив инициализацию, отладчик освобождаетсобытие, и поток Unbandled ExceptionFilter пробуждается. Теперь, когда процесс находится под управлени ем отладчика, UnbandledExceptionFilter возвращает EXCEPTION_CONTINUE_ SEARCH.

Обратите внимание: все, что здесь происходит, точно соответствует этапу 3.

Исключения и отладчик

Отладчик Microsoft Visual C++ предоставляет фантастические возможности для отлад ки после исключений Когда поток процесса вызывает исключение, операционная система немедленно уведомляет об этом отладчик (если он, конечно, подключен). Это уведомление называется "первым предупреждением" (first-chance notification). Реаги руя на него, отладчик обычно заставляет поток искать фильтры исключений. Если все фильтры возвращают EXCEPTION_CONTINUE_SEARCH, операционная система вновь уведомляет отладчик, но на этот раз даст "последнее предупреждение" (last-chance notification). Существование этих двух типов предупреждений обеспечивает больший контроль за отладкой при исключениях

Чтобы сообщить отладчику, как реагировать на первое предупреждение, исполь зуйте диалоговое окно Exceptions отладчика.

Как видите, оно содержит список всех исключений, определенных в системе. Для каждого из них сообщаются 32-битный код, текстовое описание и ответные действия отладчика. Я выбрал исключение Access Violation (нарушение доступа) и указал для него Stop Always.

Теперь, если поток в отлаживаемом процессе вызовет это исключе ние, отладчик выведет при первом предупреждении следующее окно.

К этому моменту поток еще не получал шанса на поиск фильтров исключений Сейчас я могу поместить в исходный код точки прерывания, просмотреть значения переменных или проверить стек вызовов потока Пока ни один фильтр не выполнял ся — исключение произошло только что Когда я попытаюсь начать пошаговую от ладку программы, на экране появится новое окно

Кнопка Cancel вернст нас в отладчик Кнопка No заставит поток отлаживаемого процесса повторить выполнение неудавшейся машинной команды При большинстве исключений повторное выполнение команды ничего не даст, так как вновь вызовет исключение Однако, если исключение было сгенерировано с помощью функции RaiыeException, это позволит возобновить выполнение потока, и он продолжит рабо ту, как ни в чем ни бывало Данный метод может быть особенно полезен при отладке программ на С++ получится так, будто оператор throw никогда не выполнялся (К обработке исключений в С++ мы вернемся в конце главы)

И, наконец кнопка Yes разрешит потоку отлаживаемого процесса начать поиск фильтров исключений Если фильтр исключения, возвращающий EXCEPTION_EXE CUTE_HANDLER или EXCEPTION_CONTINUE_EXECUTION, найден, то все хорошо и поток продолжает работу Еспи же все фильтры вернут EXCEPTION_CONTINUE_

SEARCH, огладчик получит последнее предупреждение и выведет окно с сообщением, аналогичным тому, которое показано ниже

Здесь Вам придется либо начать отладку, либо закрыть приложение.

Я продемонстрировал Вам, что случится, ссли ответным действием отладчика выбран вариант Stop Always Но для большинства исключений по умолчанию предла гается варианг Stop If Not Handled B этом случае отладчик, получив первое предуп реждение, просто сообщает о нсм в своем окне Output.

После этого отладчик разрешит потоку искать подходящие фильтры и, только если исключение не будет обработано, откроет следующее окно

NOTE

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

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

определяемые Вами программные исключения. От Вас требуется лишь указать уникальный числовой код исключения, сго название и ответное действие от ладчика, а затем, щелкнув киопку Add, добавить это повое исключение в список По смотрите, как это сделал я, определив собственное исключение.

Программа-пример Spreadsheet

Этя программа, «25 Spreadsheet.exe» (см. листинг на рис. 25-1), демонстрирует, как передавать физическую память зарезервированному региону адресного простран ства — но не всему региону, а только его областям, нужным в данный момент. Алго ритм опирается на структурную обработку исключений. Файлы исходного кода и ре сурсов этой программы находятся в каталоге 25-Spreadshect на компакт-диске, при лагаемом к книге. После запуска Spreadsheet на экране появляется диалоговое окно, показанное ниже.

Программа Spreadsheet резервирует регион для двухмерной таблицы, содержащей 256 строк и 1024 колонки, с размером ячеек по 1024 байта Если бы программа за ранее передавала физическую память под всю таблицу, то ей понадобилось бы 268 435 456 байтов, или 256 Мб. Поэтому для экономии драгоценных ресурсов про грамма резервирует в своем адресном пространстве регион размером 256 Мб, нс пе редавая ему физическую память.

Допустим, пользователь хочет поместить значение 12345 в ячейку на пересечении строки 100 и колонки 100 (как на предыдущей иллюстрации) Кактолько он щелкнет кнопку Write Cell, программа попытается записать это значение в указанную ячейку таблицы.

Естественно, это вызовет нарушение доступа. Но, так как я использую в про грамме SEH, мой фильтр исключений, распознав попытку записи, выведет в нижней части диалогового окна сообщение «Violation: Attempting to Write», передаст память под нужную ячейку и заставит процессор повторить выполнение команды, возбудив шей исключение Теперь значение будет сохранено в ячсйкс таблицы, поскольку этой ячейке нередана физическая память.

Проделаем еще один эксперимент. Попробуем считать значение из ячейки на пересечении строки 5 и колонки 20 Этим мы вновь вызовем нарушение доступа. На этот раз фильтр исключений не передаст память, а выведет в диалоговом окне сооб щение «Violation: Attempting to Read». Программа корректно возобновит свою работу после неудавшейся попытки чтения, очистив поле Value диалогового окна.

Третий эксперимент: попробуем считать значение из ячейки на пересечении стро ки 100 и колонки 100. Так как этой ячейке передана физическая память, никаких ис ключений не возбуждается, и фильтр не выполняется (что положительно сказывается на быстродействии программы). Диалоговое окно будет выглядеть следующим об разом.

Ну и последний эксперимент запишем значение 54321 в ячейку на пересечении строки 100 и колонки 101 Эта операция пройдет успешно, без исключений, потому что данная ячейка находится на тоЙ жс странице памяти, что и ячейка (100, 100) В подтверждение этого Вы увидите сообщение "No Violation raised" в нижней части диалогового окна

В своих проектах я довольно часто пользуюсь виртуальной памятью и SEH. Как то раз я решил создать шаблонный С++-класс CVMArray, который инкапсулирует все, что нужно для использования этих механизмов Его исходный код содержится в фай ле VMArrayh (он является частью программы-примера Spreadsheet) Вы можете рабо тать с классом CVMArray двумя способами Во-первых, просто создать экземпляр это го класса, передав конструктору максимальное число элементов массива Класс авто матически устанавливает действующий на уровне всего процесса фильтр необрабо танных исключений, чтобы любое обращение из любого потока к адресу в виртуаль ном массиве памяти заставляло фильтр вызывать VirtualAlloc (для передачи физичес кой памяти новому элементу) и возвращать EXCEPTION_CONTINUE_EXFCUTION Ta кое применение класса CVMArray позволяет работать с разреженной памятью (sparse storage), не забивая SEH-фреймами исходный код программы Единственный недоста

ток в том, что Ваше приложение не сможет возобновить корректную работу, если по каким то причинам передать память не удается

Второй способ использования CVMArray — создание производного С++-класса Производный класс даст Вам все преимущества базового класса, и, кроме того, Вы