Долгие годы программы на Delphi умели загружать DLL двумя способами: статически и динамически. В Delphi 2010 появился третий способ – отсроченная загрузка.
Сделаем простенькую DLL, которая экспортирует некую функцию, например, CalcFee:
library TestDLL;
Function CalcFee(const iID: Integer): Currency;
begin
Result := ...;
end;
Exports
CalcFee;
end.
и рассмотрим все три способа.
1. Статическая загрузка.
Это самый простой способ вызвать функцию из DLL. Для его реализации необходимо всего лишь описать внешнюю функцию:
Function CalcFee (const iID: Integer): Currency; external 'TestDLL.dll';
И после этого её можно вызывать:
fFee := CalcFee(1234)
Наша DLL загружается при запуске программы и остается загруженной до завершения ее работы.
За этой простотой скрывается большая проблема – если DLL удалить или испортить, то программа просто не запустится.
2. Динамическая загрузка.
Этот способ – более продвинутый, но требует написания значительно большего количества кода:
Var
CalcFee: function(const iID: Integer): Currency;
hDLL: THandle;
begin
hDLL := LoadLibrary('TestDLL.dll');
If hDLL <> 0
then try
@CalcFee := GetProcAddress(hDLL, 'CalcFee');
If @CalcFee = nil
then ShowMessage('В TestDLL.dll не найдена функция CalcFee')
else fFee := CalcFee(1234);
finally
FreeLibrary(hDLL);
end
else ShowMessage('Ошибка при загрузке TestDLL.dll')
В этом коде используются функции Win32 API из Windows.pas:
- LoadLibrary – загружаем DLL;
- GetProcAddress – получаем адрес функции по её имени;
- FreeLibrary – выгружаем DLL из памяти.
Динамическая загрузка позволяет загружать DLL только при необходимости и выгружать её, если она больше не нужна. Второе преимущество и более важное – это возможность обработать ошибки при загрузке DLL и вызове функции. Поэтому даже без DLL или с испорченной DLL программа будет работать. Если перед загрузкой DLL ее удалить, то получим нормальное сообщение:
3. Отсроченная загрузка
В Delphi 2010 появился третий способ вызова функций из DLL – отсроченная загрузка. Как и динамическая загрузка, она позволяет загружать DLL только при необходимости (например, для экономии ресурсов), но также как при статической загрузке, функцию достаточно лишь описать, добавив директиву
delayed:
Function CalcFee(const iID: Integer): Currency; external 'TestDLL.dll' delayed;
Недостатки этого способа – это то, что DLL уже нельзя выгрузить и то, что если при вызове функции произойдет ошибка, то пользователь не узнает ее причину. В справке написано "Trying to call a delayed routine that cannot be resolved results in a run-time error (or an exception, if the SysUtils unit is loaded)". Если DLL удалить или если в DLL нет нужной функции, сообщение будет однотипным:
Если функция будет использована в консольной программе, то сообщение об ошибке будет тоже не информативным:
"Runtime error 255 at 7C812AFB "
Но спасение есть. При желании ошибки, возникающие во время загрузки DLL или вызове её функций можно перехватить и обработать с помощью процедур SetDliNotifyHook и SetDliFailureHook из юниты System.
Не знаю, как вам, но мне идея отсроченной загрузки DLL понравилась, и я буду её в будущем использовать.