«Любой дурак может написать программу, которую поймет компилятор. Хорошие программисты пишут программы, которые смогут понять другие программисты»
57. Обработка ошибок в Windows
Каждый процесс имеет соответствующий режим реакции на ошибочные состояния, который указывает системе, как приложение будет реагировать на критические ошибки. К последним относятся ошибки при работе с дисками, ошибки неготовности устройств, ошибки выравнивания и необработанные исключительные ситуации. Приложение может разрешить системе вывести соответствующее сообщение об ошибке или может обрабатывать ошибку самостоятельно. Для обработки ошибок без вмешательства пользователя надо использовать функцию SetErrorMode:
UINT SetErrorMode ( UINTuMode ); {UINT=LongWord}
Параметр uMode может принимать одно из следующих значений или их комбинацию:
Значение |
Толкование |
0 |
Обработка ошибок системой по умолчанию, при которой выводится сообщение об ошибке |
SEM_FAILCRITICALERRORS |
Система не выводит сообщение об ошибке, а посылает его (message) процессу |
SEM_NOALIGNMENTFAULTEXCEPT |
Для 64-битной версии Windows: система обрабатывает ошибки выравнивания невидимо для вызывающего процесса процесса и порожденных им другим процессов. После установки этого значения последующие попытки его отмены игнорируются |
SEM_NOGPFAULTERRORBOX |
Система не выводит сообщение об общем нарушении защиты (GP). Этот режим должен устанавливаться только отладчиками, которые обрабатывают такие ошибки самостоятельно с помощью соответствующего обработчика ИС |
SEM_NOOPENFILEERRORBOX |
Сообщение о не найденном файле не выводится, а передается процессу |
Функция SetErrorMode возвращает предыдущий режим.
Обратить внимание пользователя на ошибку можно с помощью инвертирования (flash) заголовка окна приложения:
FlashWindow(hwnd, TRUE); Sleep(500); FlashWindow(hwnd, TRUE);
MessageBeep(MB_ICONEXCLAMATION);
Функция FlashWindowEx предоставляет дополнительные возможности по управлению визуализацией оповещения пользователя об ошибке. Она имеет такой заголовок:
function FlashWindowEx(var pfwi: FLASHWINFO): BOOL;
PFLASHWINFO = ^FLASHWINFO;
FLASHWINFO = record
cbSize: UINT;
hwnd: HWND;
dwFlags: DWORD;
uCount: UINT;
dwTimeout: DWORD;
end;
Функции обычно сообщают о наличии ошибки путем возвращения значений 0, - 1 или Null, которые могут быть проанализированы вызывающей программной единицей. Кроме того, система предоставляет возможность установить внутренний код ошибки, называемый кодом последней ошибки (last-error code). Если функция завершается успешно, она, обычно, не устанавливает код ошибки; в противном случае она может сделать это с помощью функции SetLastError:
VOID SetLastError( DWORDdwErrCode);
Бит 29 кода ошибки зарезервирован для кодов ошибок приложений и позволяет отличить их от кодов ошибок системы. Значения остальных бит определяются программистом и должны быть оговорены в описании функции. Отметим, что код ошибки поддерживается системой для каждого потока в отдельности.
Получить код ошибки можно с помощью функции GetLastError:
DWORD GetLastError(VOID);
Обсуждаемые функции часто используются подпрограммами, экспортируемыми DLL, для уведомления вызывающего процесса о наличии ошибок.
Значения системных кодов сообщений (их несколько сотен) об ошибках можно узнать из файла Windows.h или из раздела MSDN System Error Codes.
Приложение для аварийного завершения работы может использовать функцию FatalAppExit:
VOID FatalAppExit(
UINT uAction, // зарезервировано
LPCTSTR lpMessageText // строка с завершающим нулем длиной до 35 символов
);
Эта функция выводит переданную строку с текстом сообщения пользователю и завершает работу приложения после закрытия окна сообщения. Использование функции для завершения приложения является крайней мерой, так как при этом файлы могут быть не закрыты и захваченные ресурсы не освобождены.
Примечание. Попытки воспользоваться функцией SetErrorMode успеха не имели (проект TestErrorHandling)
Procedure TForm1.Button1Click(Sender: TObject);
var
f : TextFile;
E : TEdit;
p : PInteger;
Begin
// SetErrorMode(SEM_FAILCRITICALERRORS);
// SetErrorMode(SEM_NOOPENFILEERRORBOX);
{ E.Text:='aaaaaaaaaaaaa'; { меняется заголовок формы, а ошибки нет!}
{ FatalAppExit(0,'Приплыли!'); { работает }
ShowMessage(IntToStr(
SetErrorMode(SEM_NOGPFAULTERRORBOX)));
E:=nil;
E.Text:='aaaaaaaaaaaaa'; { все равно ошибки нет!}
E.Font.Size:=13; {всегда ошибка "Access violation ..."}
{ p:=Addr(E);
p^:=123;}
{ AssignFile(f,'asd'); Reset(f); сообщение об ошибке выводится всегда}
End;
Обработка ошибок в СОМ.
Обработка ошибок при работе с СОМ объектами с точки зрения клиентского приложения основана на использовании значения HResult, возвращаемого функциями СОМ библиотек и методами СОМ объектов. Разработчик СОМ объекта со своей стороны должен приложить усилия для того, чтобы предоставить пользователям СОМ объектов возможность получить код ошибки и ее текстовое представление. Рассмотрим сначала механизм обработки ошибок клиентским приложением.
Значение HResult, возвращаемое большинством API-функций и методов СОМ, представляет собой 32-битовое целое число определенного формата:
Кодировка HResult
Биты |
Обозначение |
Значение |
31 |
severity code |
Бит наличия ошибки (1 – ошибка есть, 0 – ошибки нет) |
27..30 |
reserved |
Зарезервированы |
16..26 |
facility code |
Область, ответственная за ошибку |
0..15 |
Код, идентифицирующий ошибку или предупреждение |
Коды областей (facility code) зарезервированы Microsoft и являются уникальными. В настоящее время определены коды областей, приведенные в следующей таблице.
Константа |
Значение |
Описание |
FACILITY_NULL |
0 |
Общие коды состояния (типа S_OK) |
FACILITY_RPC |
1 |
Ошибки RPC |
FACILITY_DISPATCH |
2 |
Ошибки интерфейса IDispatch для позднего связывания |
FACILITY_STORAGE |
3 |
Ошибки IStorage и IStream. Коды со значением до 256, имеют тот же смысл, что и коды ошибок DOS |
FACILITY_ITF |
4 |
Коды состояния, возвращаемые большинством методов интерфейсов. Действительный смысл ошибки определяется каждым интерфейсом в отдельности. Из этого следует, что два одинаковых значения, возвращенные разными интерфейсами, могут иметь различный смысл |
FACILITY_WIN32 |
7 |
Коды ошибок функций Win32, возвращающих значения типа HResult |
FACILITY_WINDOWS |
8 |
Дополнительные коды ошибок интерфейсов, определенных Microsoft |
Для задания кодов ошибок в Windows используются константы в таком формате:
<Facility>_<Severity>_<Reason>
Здесь:
<Facility> – имя области или другой отличающий идентификатор (исключается для FACILITY_NULL);
<Severity> – буква S или E, обозначающая успех или ошибку;
<Reason> – идентификатор, поясняющий причину ошибки.
Примеры:
S_OK (FACILITY_NULL)
STG_E_FILENOTFOUND (FACILITY_STORAGE)
DISP_E_EXCEPTION (FACILITY_DISPATCH)
Эти и другие коды ошибок определены в Windows, а в Delphi они определены в файле Windows.pas.
Для анализа и обработки значений HResult можно использовать следующие функции Delphi, определенные в Windows.pas.
Функция |
Назначение |
function Succeeded(Status: HRESULT): BOOL; |
Возвращает true, если значение Status не указывает на ошибку |
function Failed(Status: HRESULT): BOOL; |
Возвращает false, если значение Status указывает на ошибку |
function IsError(Status: HRESULT): BOOL; |
Возвращает true, если значение Status указывает на ошибку |
function HResultCode(hr: HRESULT): Integer; |
Возвращает целочисленное значение аргумента |
function HResultFacility(hr: HRESULT): Integer; |
Возвращает код области facility |
function HResultSeverity(hr: HRESULT): Integer; |
Возвращает код наличия или отсутсвия ошибки severity |
function MakeResult(sev, fac, code: Integer): HResult; |
Возвращает значение HResult, полученное как комбинация кодов Severity, Facility и Reason |
Приведенные в таблице функции аналогичны соответствующим функциям и макросам, определенным в Windows.
Для получения текстового описания какой-либо ошибки можно воспользоваться файлом Windows.pas или файлом Windows.h (секции файлов OLE Error Codes) путем поиска полученного значения HResult (в шестнадцатеричной системе). Другой и более удобный способ – это использование утилиты Error Lookup из Microsoft Visual Studio Tools. Единственное входное данное для нее – это код ошибки. Вот пример ее использования:
Еще один способ получения текстового описания ошибки является использование функции Win32 API FormatMessage, главным недостатком которой является довольно громоздкий формат ее вызова.
Еще описания ошибок можно найти в файлt OleDbErr.mc (Microsoft Visual Studio). Вот фрагмент этого файла с описанием одной из ошибок:
MessageId=0x0e01 Facility=Interface Severity=CoError
SymbolicName = DB_E_ROWLIMITEXCEEDED
Language=English
Creating another row would have exceeded the total number of active
rows supported by the rowset
«56. Апартаменты и многопоточность в СОМ»
58. Разработка активных форм ActiveForm