КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
[Главная страница] [Delphi] [DLL] [Контакты]
Динамическая загрузка
В данном разделе будет показано, как в DLL библиотеке создать форму во время выполнения и как создать форму в режиме конструктора и подключения ее к DLL библиотеке.
Динамическая загрузка, которая известна также как динамическое подключение во время выполнения (runtime dynamic linking), является более универсальным и более сложным способом загрузтси библиотек DLL. Динамическое подключение позволяет загружать и выгружать библиотеку DLL всякий раз, когда это необходимо, не создавая при этом модуль импорта.
Чтобы динамически загрузить 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. Иван Хладни.