Главная » Delphi » Явная загрузка библиотек DLL

0

Несмотря на удобство метода  неявной загрузки  библиотек DLL, он не всегда бывает желательным. Предположим, что  существует  некоторая библиотека DLL, содержащая множество функций. Вполне  вероятно, что в обычном режиме работы приложение ни когда не вызовет ни одной  из функций этой  библиотеки. Получается, что  при  каждом запуске такого  приложения загрузка  данной библиотеки приводит к напрасным затра там памяти, особенно при использовании этим приложением сразу нескольких библио тек  DLL. Другим  примером может  служить  применение компонентов библиотек DLL для заполнения объектов, имеющих очень  большой размер и содержащих списки  весь ма специализированных функций, предоставляемых на выбор  для использования в кон кретных ситуациях. (Например, списки  доступных  драйверов принтеров или  подпро грамм преобразования формата файлов.) В такой  ситуации имело  бы смысл загружать каждую библиотеку DLL только  по конкретному требованию, исходящему  от приложе ния. Этот метод и называется явной загрузкой (explicitly loading) библиотек DLL.

Для иллюстрации явной загрузки  вернемся к примеру библиотеки DLL с модаль ной формой. В листинге 6.6 содержится код главной формы приложения, демонстри рующего  явную загрузку этой  DLL. Данный файл  можно  найти на прилагаемом ком пакт диске (см. также www.williamspublishing.com).

Листинг 6.6. Главная форма приложения  Calendar DLL

unit MainFfm;

interface uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls;

type

{ Сначала определяется процедурный тип данных, который должен

отражать процедуру, экспортируемую из библиотеки DLL. }

TShowCalendar = function (AHandle: THandle;

ACaption: String): TDateTime; StdCall;{ Создать новый класс исключения для отображения неудачной загрузки DLL. }

EDLLLoadError = class(Exception);

TMainForm = class(TForm) lblDate: TLabel; btnGetCalendar: TButton;

procedure btnGetCalendarClick(Sender: TObject);

end;

var

MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.btnGetCalendarClick(Sender: TObject);

var

LibHandle        : THandle;

ShowCalendar: TShowCalendar;

begin

{ Попытка загрузить DLL }

LibHandle := LoadLibrary(‘CALENDARLIB.DLL’);

try

{ Если попытка не удалась, LibHandle содержит ноль.

В этом случае передается исключение. }

if LibHandle = 0 then

raise EDLLLoadError.Create(‘Unable to Load DLL’);

{ Если выполнение программы дойдет до этого места, значит, DLL

загрузилась удачно. Теперь устанавливаем связь с

экспортируемой функцией DLL, чтобы ее можно было вызывать. }

@ShowCalendar := GetProcAddress(LibHandle, ‘ShowCalendar’);

{ Если функция импортируется успешно, устанавливаем свойство

lblDate.Caption для отображения даты, возвращаемой этой

функцией. В противном случае передается исключение. }

if not (@ShowCalendar = nil) then

lblDate.Caption := DateToStr(ShowCalendar

(Application.Handle, Caption))

else

RaiseLastWin32Error;

finally

FreeLibrary(LibHandle); // Выгрузить DLL.

end;

end;

end.

В начале  этого  модуля определяется процедурный тип данных  TShowCalendar, ко торый отображает определение функции, вызываемой из библиотеки Calendar- Lib.dll. Затем  определяется специальное исключение, которое передается при  воз никновении проблем с загрузкой библиотеки DLL. Обратите внимание: в обработчикесобытий btnGetCalendarClick() используются  три  функции  API  Win32:  LoadLi- brary(), FreeLibrary() и GetProcAddress().

Функция LoadLibrary() определена следующим образом:

function LoadLibrary(lpLibFileName: PChar): HMODULE; stdcall;

Эта функция загружает экземпляр библиотеки DLL, указанной переменной lpLibFileName, и отображает ее в адресное пространство вызывающего процесса. Если работа функции завершается успешно,  она  возвращает дескриптор экземпляра DLL, а в случае неудачи — нулевое  значение, которое и является сигналом к передаче исключения. Более  подробная информация о работе функции LoadLibrary() и воз вращаемых ей кодах ошибок  приведена в интерактивной справочной системе.

Функция FreeLibrary() определена следующим образом:

function FreeLibrary(hLibModule: HMODULE): BOOL; stdcall;

Эта функция уменьшает  счетчик экземпляров библиотеки, заданной значением переменной hLibModule. Если счетчик экземпляров примет нулевое  значение, то данная  библиотека будет удалена из памяти. Счетчик экземпляров отслеживает коли чество задач, использующих данную библиотеку DLL.

Функция GetProcAddress() определена следующим образом:

function GetProcAddress(hModule: HMODULE;

lpProcName: LPCSTR): FARPROC; stdcall

Эта функция возвращает адрес  функции внутри  библиотечного модуля, заданного значением первого параметра hModule. Этот параметр имеет  тип THandle, а его значение можно  получить, обратившись к функции LoadLibrary(). В случае  неус пешного завершения работы функции  GetProcAddress() последняя возвращает значение nil. Чтобы получить более  подробную информацию об ошибках, необхо димо обратиться к функции API GetLastError().

В обработчике события Button1.OnClick функция LoadLibrary() вызывается для загрузки  библиотеки CALDLL. При  неудачном завершении загрузки  передается ис ключение. В случае успешной  загрузки  будет выполнено обращение к функции окна GetProcAddress(), которая вернет адрес функции ShowCalendar(). Наличие симво ла оператора адреса (@) перед  переменной процедурного типа ShowCalendar не позво ляет  компилятору выдать  сообщение об ошибке  несовпадения типов.  Теперь, получен ный  адрес  функции  ShowCalendar() можно  использовать в виде  переменной типа TShowCalendar. И,  наконец, внутри   блока  finally вызывается  функция  FreeLi- brary(), гарантирующая, что загруженная библиотека будет удалена из памяти.

Таким  образом, библиотека загружается и освобождается при  каждом  вызове дан ной  функции.  Если  за  все  время   работы приложения функция  вызывается только один  раз,  то становится ясно,  что  явная  загрузка  может  сэкономить ресурсы  памяти, которых всегда так не хватает. Но если бы эта функция вызывалась достаточно часто, то повторение операций загрузки  и выгрузки  библиотеки DLL привело бы к сущест венной дополнительной нагрузке на систему.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии