Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
шпора 97.doc
Скачиваний:
0
Добавлен:
23.01.2020
Размер:
778.75 Кб
Скачать

Параметры

Порт завершения ввода-вывода — это совокупность дескрипторов файлов, откры­тых в режиме OVERLAPPED. Параметр FileHandle — это асинхронный дескриптор, который добавляется в порт. Если в этом параметре указать значение INVALID_HANDLE_VALUE, функция создает новый порт завершения ввода-вывода и возвращает его как возвращаемое значение. Следующий параметр, ExistingCompletionPort, в этом случае должен иметь значение NULL.

ExistingCompletionPort — порт, созданный при первом вызове, и порт, в кото­рый добавляется дескриптор из первого параметра. Если добавление прошло успешно, функция также возвращает дескриптор порта, при неудаче возвращается NULL.

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

NumberOfConcurrentThreads указывает максимальное число потоков, которые могут работать одновременно. Все потоки вне этого предела, ожидающие сигнала порта, остают­ся заблокированными. Если этот параметр равен нулю, пределом будет число процессоров в системе.

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

Дескрипторы, связанные с портом, не должны использоваться в функциях ReadFileEx или WriteFileEx. В документации Microsoft не рекомендуется совместно использовать файлы или другие объекты с помощью других открытых дескрипторов.

Лекция 13. Тема: Безопасность объектов Win32

Цели системы безопасности

У системы безопасности две основные цели: проверка уровня доступа и контроль за действиями клиента. Чтобы выполнить первую цель, необходимо убедиться в том, что пользователь обладает правом доступа к защищаемому объекту. Если это не так, попытка доступа должна окончиться неудачей, а пользователь должен получить сообщение «доступ запрещен» (access denied). Вторая функция системы безопасности — аудит, то есть слежение за действиями клиента — выполняется далеко не всегда. Аудит подразумевает документирование в файле журнала действий клиента, связанных с доступом к тому или иному защищаемому объекту. Администраторы могут включать или выключать систему аудита, они также могут настроить ее таким образом, чтобы документировались не все попытки доступа, а только те, которые были связаны с нарушением нрав доступа и в результате оказались безуспешными.

Права и привилегии

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

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

Атрибуты безопасности

Почти любой объект, созданный с помощью системного вызова Create, в первую очередь имеет параметр атрибутов безопасности. Поэтому программы могут защищать файлы, процессы, потоки, события, семафоры, именованные каналы и т.д.

Создание безопасности начинается с включения в вызов Create структуры SECURITY_ATTRIBUTES. Важный элемент структуры SECURITY_ATTRIBUTES – указатель на дескриптор безопасности, в котором представлены сведения о владельце объекта и пользователях, которым предоставлены или запрещены различные права.

Отдельный процесс идентифицируется своим маркером доступа, который определяет пользователя-владельца и принадлежность к группе. Когда процесс пытается обратиться к объекту, ядро Windows NT может опознать процесс по этому маркеру и на основании информации в дескрипторе безопасности решить, имеет ли процесс требуемые права на обращение к объекту.

Атрибуты безопасности имеют следующее определение:

Typedef struct _ SECURITY_ATTRIBUTES {

DWORD nLength; // Размер структуры

LPVOID lpSecurityDescriptor; // Дескриптор безопасности,

// контролирующий доступ к объекту

BOOL bInheritHandle; // Разрешает наследование дескриптора

// (handle)

// дочерним процессом

} SECURITY_ATTRIBUTES;

Элемент nLength должен иметь значение sizeof (SECURITY_ATTRIBUTES).

Обзор элементов безопасности: дескриптор безопасности.

Дескриптор безопасности инициализируется функцией InitializeSecurityDescriptor.

Структура SECURITY_DESCRIPTOR хранит в себе информацию, связанную с защитой некоторого объекта от несанкционированного доступа. В этой структуре, которую называют дескриптором безопасности, содержится информация о том, какие пользователи обладают правом доступа к объекту и какие действия эти пользо­ватели могут выполнить в отношении этого объекта.

Дескриптор безопасности объекта хранит в себе следующую информацию;

- SID (security identifier) владельца объекта;

- SID основной группы владельца объекта;

- список разграничительного контроля доступа (Discretionary Access Control List, DACL-дискреционный список управления доступом);

- системный список управления доступом (System Access Control List, SACL);

- управляющая информация (например, сведения о том, как списки ACL пере­дают информацию дочерним дескрипторам безопасности);

Список DACL определяет, кто обладает (и кто не обладает) правом доступах объекту. Список SACL определяет, информация о каких действиях вносится в файл журнала.

Функция InitializeSecurityDescriptor инициализирует указанный вами дескриптор таким образом, что в нем отсутствует DASL, SACL, владелец и основная группа владельца, а все управляющие флаги установлены в значение FALSE, При этом дескриптор имеет абсолютный формат. Что это значит? Дескриптор в абсолютном (absolute) формате содержит лишь указатели на информацию, связанную с защитой объекта. В отличие от этого дескриптор в относительном (self-relative) формате включает в себя всю необходимую информацию, которая располагается в памяти последовательно поле за полем. Таким образом, абсолютный дескрип­тор нельзя записать на диск (так как при последующем чтении его с диска все указатели потеряют смысл), а относительный дескриптор — можно.

Windows позволяет преобразовывать абсолютный дескриптор в относительную форму и обратно. Обычно это требуется лишь в случае, если вы записываете дес­криптор на диск и считываете дескриптор с диска. Системные вызовы, требую­щие передачи указателя на дескриптор безопасности, работают только с дескрип­торами в абсолютном формате.

Преобразование осуществляется при помощи вызовов MakeSelfRelativeSD и MakeAbsoluteSD. Преобразовать абсолютную форму в относительную несложно. Однако обратное преобразование (из относительной в абсолютную) обычно выполняется в несколько этапов.

Функции SetSecurityDescriptorOwner и SetSecurityDescriptorGroup связывают SID с дескрипторами безопасности. Списки ACL инициализируются с помощью функции InitializeAcl и затем связываются с дескриптором безопасности функциями SetSecurityDescriptorDacl или SetSecurityDescriptorSacl.

Списки контроля доступа

Каждый ACL представляет собой набор (список) элементов управления доступом (access control entry — ACE). Существует два типа элементов управления доступом: для разрешения и для запрещения доступа.

Сначала ACL инициализируется функцией InitializeAcl, а затем в него добавляются элементы управления доступом. Каждый АСЕ содержит идентификатор SID и маску доступа, которая определяет предоставляемые или запрещаемые права. Типичные права доступа — GENERIC_READ и DELETE.

Для добавления элементов управления доступом в разграничительные ACL служат две функции: AddAccessAllowedAce и AddAccessDeniedAce. Первая из этих функций предназначена для добавления к SACL, что вызывает аудит доступа к указанному SID.

И наконец, для удаления элементов управления доступом служит функция DeleteAce, а для обращения к ним — GetAce.

Использование безопасности объектов Win32

Каждый процесс также имеет идентификаторы SID (в маркере доступа), на основании которых ядро системы определяет, разрешен ли доступ и надо ли вести аудит. Маркер доступа также может давать владельцу некоторые привилегии (неотъемлемую способность выполнять операции, которая имеет более высокий приоритет, чем права в ACL). Благодаря этому администратор может обладать привилегиями чтения и записи во все файлы, даже не имея определенных прав в списках ACL файлов.

Легко увидеть, что происходит, когда процесс выдает запрос на обращение к объекту. Во-первых, процесс имеет некоторые привилегии на основании своего удостоверения пользователя и его принадлежности к группе. Эти привилегии записаны в идентификаторах SID.

Если идентификаторы пользователя и группы не разрешают доступа, ядро системы ищет права доступа в ACL. Первый элемент, который определенно предоставляет или запрещает требуемую службу, имеет решающее значение. Поэтому важен порядок, в котором элементы управления доступом вводятся в ACL. Часто АСЕ, запрещающие доступ, помещаются в начало списка, чтобы пользователь, определенно лишенный доступа, не получил его на основании членства в группе, имеющей такой доступ. Но можно смешивать элементы разрешения и запрещения, чтобы получить желательную семантику. АСЕ, запрещающий все права, может быть последним в списке; это гарантирует, что всем пользователям, явно не упомянутым в АСЕ, доступ будет запрещен.

Права объекта и доступ к объекту

Объект, например файл, получает свои права при создании, хотя они могут быть изменены позже. Процесс запрашивает доступ к объекту в тех случаях, когда он хочет использовать дескриптор, например при обращении к CreateFile. Запрос дескриптора содержит в одном из своих параметров необходимый доступ, например GENERIC_READ. Если процесс имеет права на получение требуемого доступа, запрос проходит успешно. Разные дескрипторы одного и того же объекта могут иметь различный доступ. Для флагов доступа применяются те же значения, которые используются для предоставления или запрещения прав при создании ACL.

Инициализация описателя безопасности

Работа с дескриптором начинается с его инициализации. При этом в параметре psd необходимо указать адрес правильной структуры SECURITY_DESCRIPTOR. Эти структуры не прозрачны для программиста, и для работы с ними применяются определенные функции.

Параметру dwRevision присваивается значение константы SECURITY_DES-CRIPTOR_REVISION

BOOL InitializeSecurityDescriptor (

PSECURITY_DESCRIPTOR psd, DWORD dwRevision)

Идентификаторы безопасности

Идентификаторы SID применяются в Win32 для опознания пользователей и групп. Программа может искать SID по имени учетной записи, которая соответствует пользо­вателю, группе, домену и т.д. Учетная запись может находиться в удаленной системе.

BOOL LookupAccountName ( LPCTSTR lpszSystem,

LPCTSTR lpszAccount, PSID psid, LPDWORD lpcbSid,

LPTSTR lpszReferencedDomain, LPDWORD lpcchReferencedDomain,

PSID_NAME_USE psnu)

Параметры

lpszSystem и lpszAccount указывают на имена системы и учетной записи. Часто lpszSystem присваивается значение NULL, чтобы указать на локальную систему.

psid — возвращаемая информация, которая имеет размер *lpcbSid. Если буфер недостаточно велик, функция завершается с ошибкой, возвращая требуемый размер.

lpszReferencedDomain — строка длиной *lpcchReferencedDomain символов. Параметр длины должен быть инициализирован в размер буфера (сбои обрабатывают­ся обычным протоколом). Возвращаемое значение определяет домен, в котором нахо­дится искомое имя. Имя учетной записи "Administrators" возвращает "BUILTIN", тогда как имя учетной записи пользователя возвращает то же самое имя пользователя.

Параметр psnu указывает на переменную SID_NAME_USE (перечислимого типа) и может проверяться на такие значения, как SidTypeWellKnownGroup, SidTypeUser, SidTypeGroup и т.д.

BOOL LookupAccountSid ( LPCTSTR lpszSystem, PSID psid,

LPTSTR lpszAccount, LPDWORD lpcchName,

LPTSTR lpszReferencedDomain,

LPDWORD lpcchReferencedDomain, PSID_NAME__USE psnu)

Функция LookupAccountSid позволяет проделать обратный процесс, указав SID и получив имя учетной записи. Именем учетной записи может быть любое доступное для процесса имя. Некоторые имена, такие как Everyone (Все), хорошо известны. Имя учетной записи пользователя данного процесса (данного сеанса) можно получить с помощью функции

BOOL GetUserName ( LPTSTR lpBuffer, LPDWORD lpcchBuffer)

Имя пользователя и его длина возвращаются обычным способом. Создавать SID и управлять ими можно с помощью таких функций, как InitializeSid и AllocateAndInitializeSid. Но примеры ограничены лишь идентификаторами, полученными по именам учетных записей.

Как только SID станут известны, они могут быть введены в инициализированный дескриптор безопасности.

BOOL SetSecurityDescriptbrOwner ( PSECURITY__DESCRIPTOR psd,

PSD psidOwner, BOOL fOwnerDefaulted)

BOOL SetSecurityDescriptorGroup ( PSECURITY__DESCRIPTOR psd,

PSID psidGroup, BOOL fGroupDefaulted)

Здесь psd указывает на соответствующий описатель безопасности, а psidOwner или psidGroup) — это адрес SID владельца (группы). Параметр fOwnerDefaulted или (fGroupDefaulted) указывает, что нужно использовать заданную по умолчанию информацию.

Функции GetSecurityDescriptbrOwner и GetSecurityDescriptorGroup возвращают SID (владельца или группы) указанного описателя безопасности.

Управление списками ACL

Рассмотрим, как управлять списками ACL, как связать ACL с дескриптором безопасности и как добавлять в него элементы контроля доступа.

Работа начинается с инициализации структуры ACL. К ACL нельзя обратиться непосредственно. Про­грамма должна только предоставить буфер, который будет играть роль ACL; его содержимым управляет функция.

BOOL InitializeAcl ( PACL pAcl, DWORD cbAcl,

DWORD dwAclRevision)

Здесь pAcl — адрес предоставленного программистом буфера размером в cbAcl байтов. Размер ACL в 1 Кбайт более чем достаточен для большинства задач. Параметр dwAclRevision должен иметь значение ACL_REVISION

После инициализации в список добавляются элементы контроля доступа в желательном порядке. Для этого предназначены две следующие функции:

BOOL AddAccessAllowedAce ( PACL pAcl, DWORD dwAclRevision,

DWORD dwAccessMask, PSID pSid)

BOOL AddAccessDeniedAce ( PACL pAcl, DWORD dwAclRevision,

DWORD dwAccessMask, PSID pSid)

Здесь pAcl указывает на ту же структуру ACL, которая была инициализирована функцией InitializeAcl, a dwAclRevision снова имеет значение ACL_REVISION. Параметр pSid указывает на SID, например полученный из функции LookupAccountName.

Маска доступа (dwAccessMask) определяет права, которые будут предоставлены или запрещены пользователю или группе, указанной идентификатором SID. Предва­рительно определенные значения маски зависят от типа объекта.

В заключение следует связать ACL с дескриптором безопасности. Для разграничительного ACL применяется следующая функция:

BOOL SetSecurityDescriptbrDacl ( PSECURITY_DESCRIPTOR psd,

BOOL fDaclPresent, PACL pAcl, BOOL fDaclDefaulted )

Если значение fDaclPresent равно TRUE, это указывает, что в структуре pAcl имеется ACL; если FALSE — функция игнорирует все заданное содержимое рАс1.

Если последний флаг fDaclDefaulted равен FALSE, это означает, что ACL гене­рируется программистом. В противном случае ACL формируется принятым по умол­чанию механизмом, таким как наследование, и значение fDaclDefaulted должно быть равно FALSE.

Чтение и изменение дескрипторов безопасности

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

BOOL GetFileSecurity ( LPCTSTR lpszFileName,

SECURITY_INFORMATION secInfo,

PSECURITY__DESCRIPTOR psd, DWORD cbSd,

LPDWORD lpcbLengthNeeded)

BOOL SetFileSecurity ( LPCTSTR lpszFileName,

SECURITY__INFORMATION secInfo,

PSECURITY_DESCRIPTOR psd)

Параметры

secInfoконстанта перечислимого типа, принимающая такие значения, как OWNER_SECURITY_INFORMATION, GROUP_SECURITY_INFORMATION, DACL_SECURITY_INFORMATION и SACL_SECURITY_INFORMATION

Этот параметр указывает, какую часть описателя безопасности следует получить или установить. Значения могут объединяться при помощи поразрядного "или".

Лучший способ выяснить размер буфера возврата для GetFileSecurity— вызвать эту функцию дважды. При первом вызове в параметре cbSd просто укажите 0. После выделения буфера вызовите функцию второй раз.

Само собой разумеется, что для выполнения этих операций нужны соответствую­щие разрешения на файл. Например, для успешного выполнения SetFileSecurity необходимо иметь разрешение WRITE_DAC или быть владельцем объекта.

Функции GetSecurityDescriptorOwner и GetSecurityDescriptorGroup могут извлекать идентификаторы SID из дескрипторов безопасности, полученных функцией GetFileSecurity. Для получения АСL служит следующая функция:

BOOL GetSecurityDescriptorDacl ( PSECURITY_DESCRIPTOR psd,

LPBOOL fDaclPresent, PACL *pAcl, LPBOOL lpfDaclDefaulted)

Ее параметры почти идентичны параметрам SetSecurityDescriptorDacl, с той лишь разницей, что флаги не устанавливаются, а возвращаются, указывая, имеется ли данный АСL и был ли он установлен по умолчанию или пользователем.

Чтобы расшифровать АСL, надо узнать, сколько элементов АСЕ он содержит.

BOOL GetAclInformation ( PACL pAcl,

LPVOID pAclInformation, DWORD cbAclInfo,

ACL_INFORMATION_CLASS dwAclInfoClass)

В большинстве случаев класс информации АСL dwAclInfoClass принимает значение AclSizeInformation, а параметр AclInformation — это структура типа ACL_SIZE_INFORMATION. Еще одним значением класса может быть Acl- RevisionInformation.

Структура ACL_SIZE_INFORMATION содержит три элемента: наиболее важный из них, AceCount, показывает, сколько элементов содержится в списке. Чтобы опреде­лить, достаточно ли велик АСL, посмотрите значения элементов AclBytesInUse и AclBytesFree этой структуры.

BOOL GetAce ( PACL pAcl, DWORD dwAceIndex, LPVOID *pAce)

Эта функция позволяет получить элементы АСЕ (их общее количество уже извест­но) по индексу. Параметр рАсе указывает на структуру АСЕ, в которой есть элемент Header, содержащий, в свою очередь, элемент АсеТуре. Тип элемента можно прове­рить на значения ACCESS_ALLOWED_ACE_TYPE и ACCESS_DENIED_ACE_TYPE.

Лекция 14. Тема: Структурная обработка исключений

Структурная обработка исключительных ситуаций (Structured Exception Handling — SEH) в Win32 представляет собой надежный механизм, позволяющий приложениям отвечать на неожиданные события, такие как исключительные ситуации при адреса­ции, сбои при выполнении арифметических операций и системные ошибки. Кроме того, SEH делает возможным завершение программы из любой точки в блоке кода, а также автоматически выполняет указанные программистом действия и восстановле­ние при ошибках.

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

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

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

Блоки Try и Except

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

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

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

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

  • Выполняется много действий с массивами, и индексы массивов могут выйти за доступные пределы.

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

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

В примерах как этой главы, так и всей книги для установления контроля за бло­ком кода создаются следующие блоки try и except:

try { /* Блок контролируемого кода */ }

except {выражение_фильтра) { /* Блок обработки исключения */ }

Заметьте, что try и except — ключевые слова, распознаваемые компилятором.

Блок try является частью обычного кода приложения. Если в блоке происходит исключение, операционная система передает управление обработчику исключения — блоку кода, связанному с ключевым словом except. Последующие действия определяются значением выражение_фильтра.

Следует заметить, что исключение может произойти и в блоке, который вложен в блок try; в этом случае поддержка времени выполнения "разворачивает" стек, на­ ходит обработчик исключения и передает ему управление. То же самое происходит, когда исключение возникает внутри функции, вызываемой в блоке try.

Выражения фильтра и их значения

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

  1. EXCEPTION_EXECUTE_HANDLER — система выполняет блок except. Это обычная ситуация.

  2. EXCEPTION_CONTINUE_SEARCH — система игнорирует обработчик исключения и последовательно ищет его во вложенных блоках, пока не находит.

  3. EXCEPTION_CONTINUE_EXECUTION — система немедленно возвращает управление в точку, в которой произошло исключение. После некоторых исключений продолжение невозможно, и, если программа пытается продолжить работу, не­ медленно генерируется другое исключение.

Коды исключений

Блок except или выражение фильтра может определить конкретное исключение с помощью функции

DWORD GetExceptionCode (VOID)

Код исключения должен быть получен немедленно после исключения. Поэтому сама функция фильтра не может вызывать GetExceptionCode (это ограничение уста­новлено в компиляторе). Обычно эта функция вызывается в выражении фильтра, где кодом исключения служит параметр заданной пользовате­лем функции фильтра.

except (MyFilter (GetExceptionCode ())) { }

В этой ситуации функция фильтра определяет и возвращает значение выражения фильтра, которое должно быть одним из трех значений, указанных выше. Функция может определить на основе кода исключения свое возвращаемое значение; напри­мер, фильтр может передавать исключения при операциях с плавающей запятой внешнему обработчику (возвращая EXCEPTION_CONTINUE_SEARCH), а нарушение дос­тупа к памяти обрабатывать в текущем обработчике (возвращая EXCEPTION_EXECUTE_HANDLER).

GetExceptionCode может возвращать множество различных значений кодов исключений. Все эти коды подразделяются на несколько категорий.

• Программные нарушения, например:

- EXCEPTION_ACCESS_VIOLATION — попытка читать или записывать по виртуальному адресу, к которому процесс не имеет доступа;

- 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_OVERFLOW.

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

Существует также альтернативная функция, вызываемая только из выражения фильтра, которая возвращает дополнительную (в том числе зависящую от типа про­цессора) информацию:

LPEXCEPTION_POINTERS GetExceptionInformation (VOID)

Структура EXCEPTION_POINTERS содержит как зависимую от процессора, так и независимую информацию, сгруппированную в двух других структурах:

typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord;

} EXCEPTION_POINTERS;

EXCEPTION_RECORD содержит элемент для ExceptionCode, с тем же набором зна­чений, которые возвращаются функцией GetExceptionCode. Еще один элемент EXCEPTION_RECORD — ExceptionFlags — имеет значение 0 или EXCEPTION_NONCONTINUABLE, что позволяет функции фильтра определить, следует ли пытаться продолжать выполнение. Другие элементы включают адрес виртуальной па­мяти ExceptionAddress и массив параметров ExceptionInformation. В случае EXCEPTION_ACCESS_VIOLATION первый элемент указывает, было ли это нарушение при записи (1) или при чтении (0), а второй элемент — адрес виртуальной памяти.

Второй элемент EXCEPTION_POINTERS — ContextRecord — содержит зависимую от процессора информацию. Есть разные структуры для каждого типа процессора; информацию об этом можно найти в <winnt.h>.

Ошибки и исключения

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

С другой стороны, исключение может возникнуть почти в любом месте, и провер­ку на исключение сделать невозможно или неудобно. Примеры — деление на нуль и нарушения доступа к памяти.

Тем не менее различие между ошибками и исключениями иногда стирается. Win32 может генерировать исключения в ходе резервирования памяти функциями НеарАllос и HeapCreate, если объема памяти недостаточно. Программы также могут вызывать собственные исключения с определенными программи­стом кодами с помощью функции RaiseException.

Обработчики исключений позволяют выходить из внутренних блоков или функций по логике программы, не прибегая для передачи управления к goto или longjmp. Эта возможность особенно ценна, если блок кода обращается к таким ресурсам, как от­крытые файлы, память или объекты синхронизации, так как обработчик может осво­бодить их; кроме того, можно продолжить выполнение программы после обработчика исключения, не завершая ее. Также программа может восстанавливать при выходе из блока состояние системы.

Генерируемые пользователем исключения

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

VOID RaiseException ( DWORD dwExceptionCode,

DWORD dwExceptionFlags, DWORD cArguments, LPDWORD lArguments)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]