7.Явная загрузка библиотек
Предыдущие примеры объединяет одна особенность — динамическая библиотека загружается автоматически при старте программы. В этом просматриваются два недостатка.
- Загрузка программы немного теряет в скорости (не сильно, но все же), а функции из библиотеки могут вообще не понадобиться за все время выполнения программы.
- Может понадобиться поставлять программу в укороченном варианте без некоторых функций (без каких-либо DLL-файлов), но это не получится, потому что программа на этапе загрузки будет выдавать ошибку о том, что библиотека не найдена.
Задача: научиться использовать явную загрузку библиотеки и в определенный момент.
В предыдущем примере будем использовать явную загрузку библиотеки для вызова функции ShowAbout. В принципе, если делать явную загрузку, то это нужно делать для всех функций и процедур библиотеки, потому что если вы оставите хотя бы одну неявной, то библиотека все равно будет грузиться на этапе старта программы, а явная загрузка будет только повторять уже выполненные действия. Для примера мы взяли только одну функцию, а процедуру попробуйте перевести на явную загрузку самостоятельно. Тем более что для этого не надо делать много изменений.
Итак, загрузите приложение, написанное в прошлом разделе главы, которое использует динамическую библиотеку. В основном модуле уберите объявление функции ShowAbout. Теперь в разделе type напишите объявление нового типа:
ShowA = function (Handle: THandle):LongInt; stdcall;
Здесь мы объявляем новый тип ShowA, который является процедурным типом с параметрами функции ShowAbout из динамической библиотеки. Параметры должны быть точными, как при объявлении.
Переходиим к обработчику события OnClick для первой кнопки, где мы показывали окно. Код, который вы должны здесь написать, приведен в листинге 1.4.
Листинг 1.4. Код явной загрузки библиотеки
procedure TForm1.Button1Click(Sender: TObject);
var
DLLHandle: THandle;
sa:ShowA;
begin
if f > 0 then
FreeAbout(f);
DLLHandle := LoadLibrary(‘ProjectDLL. DLL’);
if DLLHandle = 0 then
exit; //Библиотека не загрузилась
@sa := GetProcAddress(DLLHandle, ‘ShowAbout’);
if @sa = nil then
exit; //Функция не найдена
f := sa(Handle);
FreeLibrary(DLLHandle);
end;
Здесь объявлены две локальные переменные:
- DLLHandle - будет храниться указатель на загруженную библиотеку;
- sa - имеет тип ShowA, т. е. тип функции из библиотеки.
В начале кода выполняется уже знакомая проверка переменной f. Если она больше нуля, то окно уже показывалось, и нужно освободить память от старого окна, прежде чем создавать новое.
Дальше вызывается функция LoadLibrary. Эта API-функция загружает указанную в качестве параметра динамическую библиотеку в память. Результатом выполнения является указатель на загруженную библиотеку. Этот указатель мы сохраняем в переменной DLLHandle. После этого нужно проверить, если указатель DLLHandle равен нулю, то библиотека не загрузилась.
Теперь требуется получить адрес функции ShowAbout в загруженной памяти, чтобы мы могли выполнить функцию. Для этого вызывается функция GetProcAddress. Функции нужно передать два параметра:
- указатель на загруженную библиотеку;
- имя искомой процедуры.
Результатом будет адрес искомой функции, и мы его сохраняем по адресу переменной @sa. Теперь sa указывает на адрес, по которому загружена процедура ShowAbout из динамической библиотеки. Единственное, что надо проверить, — корректность адреса. Если он равен nil, то процедура не найдена (возможно, что это старая версия библиотеки или неправильно указано имя).
Если все нормально, то мы вызываем функцию через переменную f := sa(Handle), почти так же, как это делалось раньше. Результат выполнения функции сохраняется в переменной f.
Последняя строчка кода выгружает динамическую библиотеку из памяти - FreeLibrary. Точнее сказать, на этом этапе реальной выгрузки не происходит. Функция только сообщает системе о том, что больше библиотека программе не требуется. Если эту библиотеку использует другая программа (одну библиотеку могут использовать одновременно несколько программ), то она останется в памяти, пока та не сообщит о ненужности загруженного DLL-файла.


