]>Обработка ошибок в Windows

Обработка ошибок в Windows

Аватар пользователя mabu

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

  1. Типы данных для возвращаемых значений
  2. Почему же произошла ошика?
  3. Ты тоже можешь это сделать
  4. Получение описания кода ошибки

Типы данных для возвращаемых значений

Большинство функций Windows возаращают следующие типы.

Процедуры
Функции типа Sub (процедуры) не возвращают значений. Такие функции всегда (или почти всегда) выполняется успешно, хотя их количество в Windows очень мало. Пример: функция ExitProcess.
Bool или Boolean
Если вызов функции оканчивается неудачно, то возвращается 0 (он же False), в остальных случаях возвращается любое другое число, отличное от нуля. Однако не пытайся сравнить это число с True, лучше просто сравнивать с нулём.
Handle
Если вызов функции оканчивается неудачно, то обычно возвращается NULL, что эквивалентно нулю, в остальных случаях возвращаемое значение идентифицирует объект, которым ты можешь манипулировать. Однако некоторые функции вместо NULL в случае ошибки возвращают константу INVALID_HANDLE_VALUE, например, функция CreateFile. В документации для каждой функции чётко указано, что именно она возвращает при ошибке: NULL или INVALID_HANDLE_VALUE.
PVOID или Any Ptr
Если вызов функции оканчивается неудачно, то возвращается NULL, в остальных случаях PVOID сообщает адрес блока данных в памяти.
Integer, Long или DWORD
Это значение — «крепкий орешек». Функции, которые возвращают значения каких‐либо счётчиков, обычно возвращают Integer, Long, или DWORD. Если по какой‐либо причине функция не сумела сосчитать то, что ты хотел, она обычно возвращает 0 или -1, всё зависит от конкретной функции. Если ты используешь одну из таких функций, проверь по документации, каким именно значением она уведомляет об ошибке.

Почему же произошла ошика?

При возникновении ошибки необходимо разобраться почему вызов данной функции оказался неудачен. За каждой ошибкой закреплён свой код — 32‐битное целое число.

Функция Windows, обнаружив ошибку, через механизм локальной памяти потока сопоставляет соответствующий код ошибки с вызывающим потоком. Это позволяет потокам работать независимо друг от друга, не вмешиваясь в чужие ошибки. Когда функция вернёт управление, её возвращаемое значение будет указывать на то, что произошла какая‐то ошибка. Какая именно — можно узнать, вызвав функцию GetLastError.

Код FreeBASIC
Declare Function GetLastError()As DWORD

Она просто возвращает числовое значение, характеризующее код ошибки.

Список кодов ошибок лежит в заголовочной файле win\winerror.bi. Здесь приведена его небольшая часть, чтобы примерно представлять, на что он похож.

Код FreeBASIC
Const ERROR_SUCCESS = 0
Const NO_ERROR = 0
Const SEC_E_OK = cast(HRESULT, &h00000000)
Const ERROR_INVALID_FUNCTION = 1
Const ERROR_FILE_NOT_FOUND = 2
Const ERROR_PATH_NOT_FOUND = 3
Const ERROR_TOO_MANY_OPEN_FILES = 4
Const ERROR_ACCESS_DENIED = 5
Const ERROR_INVALID_HANDLE = 6

Здесь показана лишь очень крошечная часть файла, на самом деле в нём более трёх с половиной тысяч строк.

Функцию GetLastError необходимо вызывать сразу же после неудачного вызова функции Windows, иначе код ошибки может быть потерян.

Некоторые функции Windows всегда завершаются успешно, но по разным причинам. Например, попытка создать объект ядра «событие» с определённым именем может быть успешна потому, что оно действительно создано, либо потому, что такой объект уже существует. Но иногда нужно знать причину успеха. Для возврата этой информации корпорация Microsoft предпочла использовать механизм установки кода последней ошибки. Так что и при успешном выполнении некоторых функций ты можешь использовать GetLastError и получать дополнительную информацию. К таким функциям относится, например, CreateEvent.

Ты наверняка спросишь, составит ли корпорация Microsoft полный список всех кодов ошибок, возможных в каждой функции. Ответ: нет. Такого списка никогда не будет, уж слишком сложно его составлять и поддерживать для всё новых и новых версий системы.

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

Ты тоже можешь это сделать

Механизм установки кода ошибки можно использовать и в собственных функциях. Предположим, ты пишешь библиотечную функцию, к которой будут обращаться другие части программы или вообще другие программы. Вызов этой функции по каким‐либо причинам может оказаться неудачным и тебе придётся тоже сообщать об этом. С этой целью ты просто устанавливаешь код последней ошибки в потоке и возвращаешь значение False, INVALID_HANDLE_VALUE, NULL или что‐то другое, более подходящее в твоём случае. Чтобы установить код последней ошибки вызывай функцию SetLastError и передай ей нужной число.

Код FreeBASIC
Declare Sub SetLastError(ByVal dwErrorCode As DWORD)

Можно использовать коды ошибок, уже определённые в winerror.bi, если они подходят. Если ты считаешь, что ни один из кодов ошибок из winerror.bi не годится для ошибки, возможной в твоей функции, можно определить свой код. Он представляет 32‐битное значение, которое разбито на поля:

Биты с 0 по 15
Код ошибки. Определяется корпорацией Microsoft или пользователем.
Биты с 16 по 27
Код подсистемы (facility code). Определяется корпорацией Microsoft.
Бит 28
Зарезервирован. Должен быть 0.
Бит 29
Корпорация Microsoft обещала, что никогда не будет его устанавливать. Следовательно, если ты определяешь собственный код ошибки, то запиши сюда 1, тогда будет гарантия, что код ошибки не будет конфликтовать с кодами, определёнными Microsoft.
Биты 30 и 31
В двоичном виде: 00 — успех, 01 — информация, 10 — предупреждение, 11 — ошибка.

Подробнее об этих полях будет рассказано в следующих статьях. На данный момент единственное важное для тебя поле — это бит 29. Чтобы гарантировать непересекаемость кодов ошибок от Microsoft, установи его в 1. В переводе на числа это означает, что твой код ошибки должен быть больше, чем &h20000000 или 536870912 в десятичном виде.

Получение описания кода ошибки

Для получения текстового описания ошибки подойдёт функция FormatMessage. Использовать её можно так:

Код FreeBASIC
#define unicode
#include once "windows.bi"

' Строка с ошибкой
Const BufferLength As Integer = 4096 - 1
Dim ErrorMessage As WString * (BufferLength + 1) = Any

' Вызов функции с неправильным параметром
GetProcessId(NULL)

' Получить код ошибки
Dim dwError As DWORD = GetLastError()

' Получить строку по коду ошибки
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), @ErrorMessage, BufferLength, NULL)

Print ErrorMessage

Поделись ссылочкой в социальных сетях