Как я писал раньше, Delphi 2010 научилась вызывать функций из DLL новым способом – с помощью отсроченной загрузки DLL.
Одним из недостатков этого способа является то, что если при вызове функции произойдет ошибка, то пользователь не узнает ее причину. В случае если DLL не существует, так же как и если в DLL нет нужной функции, то сообщение в программе будет однотипным:
События, которые происходят при отложенной загрузке DLL и вызове её функций, можно перехватить и обработать самому. Установить свой обработчик на эти события позволяет функция SetDliNotifyHook. Эта функция работает с параметрами процедурного типа DelayedLoadHook описанного в юните System:
DelayedLoadHook = function (dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;
где:
• dliNotify – это событие которое происходит с DLL:
- dliNoteStartProcessing – намёк, что будет работа по загрузке DLL;
– dliNotePreLoadLibrary – идёт загрузка DLL;
– dliNotePreGetProcAddress – идёт попытка получения адреса вызываемой функции;
– dliNoteEndProcessing – окончание работы по загрузке функции из DLL;
– dliFailLoadLibrary – ошибка при загрузке DLL;
– dliFailGetProcAddress – ошибка при получении адреса вызываемой функции.
• pdli – указатель на структуру типа TDelayLoadInfo с полями:
– cb – размер структуры;
– pidd – данные в необработанной форме;
– ppfn – указатель на адрес функции, которая загружена;
– szDll – название DLL;
– dlp – название или номер загружаемой функции;
– hmodCur – информация о DLL (запись типа TDelayLoadProc);
– pfnCur – указатель на адрес функции, которая будет вызвана;
– dwLastError – номер полученной ошибки.
Более подробно о функции SetDliNotifyHook и связанных с ее работой типах вы можете почитать в справке Delphi 2010. А мы приступим к написанию собственного обработчика ошибок при отложенной загрузке DLL и вызове её функций.
Т.к. нас интересует только перехват и обработка ошибок, то воспользуемся урезанной версией функции SetDliNotifyHook – SetDliFailureHook, которая устанавливает обработчик событий только на события возникающие при ошибке: dliFailLoadLibrary и dliFailGetProcAddress. Чтобы не писать перехватчик в каждом новом проекте, вынесем его код в отдельную юниту.
Unit dliHandler;
Interface
Uses
SysUtils;
Type
// Ошибка при работе с отсроченной загрузкой DLL
EDliFailure = class(Exception)
ErrorCode: Integer;
constructor Create(const sMessage: String;
const iErrorCode: Integer);
end;
Implementation
constructor EDliFailure.Create(const sMessage: String; const iErrorCode: Integer);
begin
inherited Create(sMessage);
ErrorCode := iErrorCode;
end;
// Функция возвращает имя или номер импортируемой функции
function ImportName(const AProc: TDelayLoadProc): String;
begin
if AProc.fImportByName
then Result := AProc.szProcName
else Result := '#' + IntToStr(AProc.dwOrdinal);
end;
// Обработчик ошибок генерирующий "красивую" ошибку
function DelayedLoadHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;
begin
If dliNotify = dliFailGetProcAddress
then Raise EDliFailure.Create('В ' + pdli.szDll + ' не найдена функция ' + ImportName(pdli.dlp), 2)
else Raise EDliFailure.Create('Ошибка при загрузке ' + pdli.szDll, 1); //dliFailLoadLibrary}
end;
Var
LOldFailureHook: TDelayedLoadHook;
Initialization
// Устанавливаем свой обработчик
LOldFailureHook := SetDliFailureHook(DelayedLoadHook);
Finalization
// На всякий случай вернем старый обработчик
SetDliFailureHook(LOldFailureHook);
end.
Добавляем dliHandler в любой проект и наслаждаемся автоматической обработкой ошибок при работе с отсроченной загрузкой DLL во всем проекте. Теперь вместо непонятного сообщения "External exception" пользователь увидит:
• если не удалось загрузить DLL
• если не удалось получить адрес функции
При необходимости, можно обработать ошибку и для каждого конкретного случая вызова функции (специально для этого в dliHandler я определил класс EDliFailure):
Try
...
fFee := CalcFee(1234)
...
Except
on E: EDliFailure do
Case E.ErrorCode of
1: HandleDliFailLoadLibrary;
2: HandleDliFailGetProcAddress;
End
else Raise
End;
где, HandleDliFailLoadLibrary и HandleDliFailGetProcAddress – специфические для данного конкретного случая обработчики ошибок.
Как видите, научить программу выводить понятные пользователю сообщения об ошибке легко.
Комментариев нет:
Отправить комментарий