Delphi: DLL библиотеки

КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"

[Главная страница] [Delphi] [DLL] [Контакты]

Динамические подключаемые библиотеки


Динамическая загрузка

В данном разделе будет показано, как в DLL библиотеке создать форму во время выполнения и как создать форму в режиме конструктора и подключения ее к DLL библиотеке.

Динамическая загрузка, которая известна также как динамическое подключение во время выполнения (runtime dynamic linking), является более универсальным и более сложным способом загрузтси библиотек DLL. Динамическое подключение позволяет загружать и выгружать библиотеку DLL всякий раз, когда это необходимо, не создавая при этом модуль импорта.

Чтобы динамически загрузить DLL, потребуется вылолнить следующие действия:

  1. Объявить процедурную переменную или процедурный тип, описывающий подпрограмму, которую вы хотите вызвать.
  2. Вызвать функцию LoadLibrary для загрузки библиотеки DLL.
  3. Вызвать функцию GetProcAddress для получения указателя на подпрограмму в библиотеке DLL.
  4. Вызвать подпрограмму.
  5. Вызвать функцию FreeLibrary для выгрузки библиотеки DLL.

Теперь мы попробуем создать новую библиотеку DLL, которая будет содержать формы VCL и экспортировать перегруженные подпрограммы. Для начала создайте новую библиотеку DLL (назовите се, скажем, FormLib, хотя делать это необязательно), а затем создайте и экспортируйте подпрограмму, которая будет динамически генерировать и отображать пустую форму. Вспомните, что вы должны добавить модуль Forms в список uses библиотеки DLL, чтобы иметь возможность работать с формами VCL.

Листинг 1. Подпрограмма из библиотеки DLL, которая создает и отображает пустую форму


library FormLib;

uses
  SysUtils,
  Classes,
  Forms;

{$R *.res}

procedure ShowDLLForm;
begin
  with TForm.Create(Application) do
  try
    ShowModal;
  finally
    Free;
  end;
end;

exports ShowDLLForm;

begin
end.

Теперь, когда у вас есть библиотека DLL, добавьте новый проект VCL Forms в проектную группу и поместите на форму кнопку.

 Для динамической загрузки файла FormLib.dll мы воспользуемся обработчиком события OnClick кнопки.

Первое, что потребуется сделать — это объявить процедурную переменную, имеющую тот же список параметров, что и подпрограмма, которую ны хотите вызвать. Поскольку подпрограмма ShowDLLForrm в файле FormLib.dll не имеет параметров, вы должны объявить переменную процедурного типа. Ниже показано объявление процедурной переменной, которую мы собираемся использовать:

procedure TForm1.Button1Click(Sender: TObject);
var
  DLLRoutine: procedure;
begin
end;

Чтобы загрузить библиотеку DLL, вы должны вызвать функцию LoadLibrary и передать имя файла библиотеки DLL в качестве параметра lpLibFileName. Если функция будет выполнена успешно, она вернет дескриптор библиотеки DLL. Вы должны сохранить результат выполнения этой функции, поскольку выгрузить библиотеку DLL или найти в ней подпрограммы без ее дескриптора невозможно. Далее показан фрагмент кода для вызова функции LoadLibrary:

procedure TForm1.Button1Click(Sender: TObject);
var
  DLLRoutine: procedure;

begin
  DLLHandle := LoadLibrary('FormLib.dll');
end;

Прежде чем продолжить работу далее, давайте напишем блок trу-finally, код которого будет отвечать за выгрузку библиотеки DLL даже в случае возникновения ошибок. Чтобы выгрузить библиотеку DLL из оперативной памяти, вызовите функцию FreeLibrary и передайте дескриптор библиотеки DLL в качестве параметра hLibModule;

procedure TForm1.Button1Click(Sender: TObject);
var
  DLLRoutine: procedure;

begin
  DLLHandle := LoadLibrary('FormLib.dll');
  try
  finally
    FreeLibrary(DLLHandle);
  end; // Конец блока try..finally

Единственное, что нам осталось сделать — это вызвать функцию GetProccAddress в блоке try, чтобы получить адрес подпрограммы ShowDLLForm. Функция GetProcAddress принимает два параметра: дескриптор DLL и имя подпрограммы, которую вы хотите найти.

DLLRoutine := GetProcAddress(DLLHandle, 'ShowDLLForm');

Прежде чем вызывать подпрограмму, на которую указывает переменная DLLRoutine, вы должны проверить, является ли действительным адрес, на который указывает переменная DLLRoutine, так как функция GetProcAddress вернет значение nil, если не сможет найти указанную подпрограмму в библиотеке DLL.

В листинге 2 показан вызов подпрограммы из динамически загружаемой библиотеки DLL.

Листинг 2. Вызов процедуры из динамически загружаемой библиотеки DLL


procedure TForm1.Button1Click(Sender: TObject);
var
  DLLRoutine: procedure;
  DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary('FormLib.dll');
  try
    { DLLRoutine указывает на процедуру ShowDLLForm в файле FormLib.dll }
    DLLRoutine := GetProcAddress(DLLHandle, 'ShowDLLForm');

    { Вызываем процедуру ShowDLLForm }
    if Assigned(DLLRoutine) then
      DLLRoutine
    else
      MessageDlg('The specified routine cannot be found.', mtInformation,
                  [mbOK], 0)
  finally
    FreeLibrary(DLLHandle);
  end; // Конец блока try..finally
end;

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

В окне Project Manager правой кнопкой мыши щелкнуть по FormLib.dll и вполнить команды AddNew, Form.

На созданной форме расположить две метки Label1 и Label2.

В Label1 будет выводиться название DLL файла в котором находится форма Form2. В Label2 будет выводится названия EXE файла из которого вызывается DLL библиотека.

Изменения, которые необходимо внести в библиотеку FormLib показаны в листинге 3.

Листинг 3. Подключение библиотеки DLL к главному приложению, для получения названия EXE файла.


library FormLib;

uses
  SysUtils,
  Classes,
  Forms, Windows,
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

procedure ShowDLLForm; overload;
begin
  with TForm.Create(Application) do
  try
    ShowModal;
  finally
    Free;
  end;
end;

procedure ShowDLLForm(HostHandle: THandle); overload;
var
  OrigHandle: THandle;
  DLLName: array[0..255] of Char;
begin
  OrigHandle := Application.Handle;
  try
    { Подключаемся к главному приложению }
    Application.Handle := HostHandle;

    Form2 := TForm2.Create(Application);
    try
      { Получаем имя файла библиотеки DLL }
      GetModuleFileName(HInstance, DLLName, 255);

      { Отображаем имена файлов библиотеки DLL и главного приложения на форме }
      Form2.Label1.Caption := 'DLL: ' + ExtractFileName(DLLName);
      Form2.Label2.Caption := 'Host: ' + ExtractFileName(Application.ExeName);
      Form2.ShowModal;
    finally
      Form2.Free;
    end;
  finally
    Application.Handle := OrigHandle;
  end;  // Конец блока try..finally
end;

exports ShowDLLForm, ShowDLLForm(HostHandle: THandle) name 'ShowDLLFormEx';

begin
end.

Чтобы получить имя EXE файла внутри DLL библиотеки, вы должны передать дескриптор Application.Handle (или дескриптор главной формы) вызывающего приложения библиотеке DLL, и присвоить его свойству Application.Handle библиотеки DLL до того, как создавать в ней форму.

Легко заметить, что код в листинге 3 демонстрирует также способ экспорта перегруженных подпрограмм. При экспорте перегруженной подпрограммы необходимо включить список ее параметров в список exports и с помощью директивы name изменить ее имя.

Чтобы вызвать перегруженную подпрограмму ShowDLLForm, которая принимает параметр THandle, вы должны создать другую процедурную переменную с тем же списком параметров и вызвать ее по имени, которое определяет директива name (в данном случае это ShowDLLFormEx]. Этот код представлен в листинге 4.

На главной форме расположить еще одну кнопку Button2.

Листинг 4. Правильный подход к отображению формы VCL, находящейся в библиотеке DLL.


procedure TForm1.Button2Click(Sender: TObject);
var
  DLLRoutine: procedure(HostHandle: THandle);
  DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary('FormLib.dll');
  try
    { DLLRoutine указывает на процедуру ShowDLLFormEx в файле FormLib.dll }
    DLLRoutine := GetProcAddress(DLLHandle, 'ShowDLLFormEx');

    { Вызываем процедуру ShowDLLFormEx }
    if Assigned(DLLRoutine) then
      DLLRoutine(Handle) { Передаем дескриптор формы }
    else
      MessageDlg('The specified routine cannot be found.', mtInformation,
                  [mbOK], 0)
  finally
    FreeLibrary(DLLHandle);
  end; // Конец блока try..finally
end;

Результат выполнения этого обновленного кода для отображения форм VCL, находящихся в библиотеке DLL, показан на рисунке ниже.


Исходный код проекта (Delphi XE)

Используемая литература: Внутренний мир Borland Delphi 2006. Иван Хладни.