Несмотря на то что DLL не имеет собственной формы, с ее помощью можно вызывать формы из связанных с библиотекой модулей. Для этого в библиотеке используется ссылка uses на связанные модули-формы и объявляются экспортируемые из DLL подпрограммы, в которых реализуется вызов соответствующих форм.
В следующих примерах (TestDLLForm.dpr) иллюстрируется техника включения в DLL формы и использования ее в вызывающей программе (листинги 8, 9 и 10).
Текст DLL
library DLLWithForm;
uses
SysUtils,
Classes,
DLLFormU in 'DLLFormU.pas' {DLLForm};
{$R *.RES}
exports
ShowModalForm, ShowForm, FreeForm;
begin
end.
Текст формы в DLL
unit DLLFormU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;
type
TDLLForm = class(TForm)
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
CallForm: THandle; //Дескриптор вызывающей формы
public
{ Public declarations }
end;
// Объявление экспортируемых подпрограмм
function ShowModalForm: Integer;
procedure ShowForm(Appl, Form: THandle);
procedure FreeForm;
var
DLLForm: TDLLForm;
implementation
{$R *.DFM}
function ShowModalForm: Integer;
// Модальный вызов
begin
DllForm := TDllForm.Create(Application);
Result := DLLForm.ShowModal;
DLLForm.Free;
end;
procedure ShowForm(Appl, Form: THandle);
// Немодальный вызов
begin
Application.Handle := Appl; // Замена объекта Application
DllForm := TDllForm.Create(Application);
// Запоминаем дескриптор вызывающего окна для посылки
// ему сообщения о закрытии
CallForm := Form;
DLLForm.Show
end;
procedure FreeForm;
// Уничтожение формы
begin
DLLForm.Free
end;
procedure TDLLForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if CallForm<>0 then
SendMessage(CallForm, wm_User, 0, 0)
end;
end.
Текст вызывающей программы
unit TestMainU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TTestMain = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
// Следующая процедура обрабатывает сообщение WM_USER,
// которое посылает форма из DLL в момент своего закрытия
procedure WMUser(var Msg: TMessage); message WM_USER;
end;
var
TestMain: TTestMain;
implementation
{$R *.DFM}
function ShowModalForm: Integer; External 'DLLWithForm';
procedure ShowForm(Appl, Form: THandle); External 'DLLWithForm';
procedure FreeForm; External 'DLLWithForm';
procedure TTestMain.Button1Click(Sender: TObject);
// Модальный вызов
begin
Button2.Enabled := False;
label1.Caption := 'ModalResult = '+IntToStr(ShowModalForm);
label1.Show; // Показываем результат вызова
Button2.Enabled := True
end;
procedure TTestMain.Button2Click(Sender: TObject);
// Немодальный вызов
begin
Button1.Enabled := False;
Button2.Enabled := False;
Button3.Enabled := True;
label1.Hide;
ShowForm(Application.Handle, Self.Handle);
end;
procedure TTestMain.Button3Click(Sender: TObject);
// Закрыть форму
begin
FreeForm;
Button1.Enabled := True;
Button2.Enabled := True;
Button3.Enabled := False;
end;
procedure TTestMain.WMUser(var Msg: TMessage);
// Сообщение из формы DLL о ее закрытии
begin
Button3.Click
end;
end.
Модуль формы DLLForm, помещенной в DLL, ссылается на стандартный модуль Forms и, таким образом, получает свой глобальный объект Application, который ничего «не знает» о глобальном объекте вызывающей программы. В режиме модального вызова это не имеет особого значения, поскольку модальное окно блокирует работу вызывающей программы. В режиме немодального вызова следует синхронизовать действия объектов, в противном случае минимизация главного окна, например, не приведет к минимизации окна DLL. Синхронизация достигается тем, что дескриптор объекта Application DLL заменяется соответствующим дескриптором вызывающей программы.
При показе формы в немодальном режиме она может быть закрыта либо вызвавшей ее программой, либо щелчком по собственной системной кнопке Закрыть. В последнем случае она должна каким-то образом известить вызывающую программу об этом событии. Для этого используется стандартный механизм посылки Windows-сообщения. Сообщение должно иметь адрес — дескриптор окна, для которого оно предназначено. Вот почему вторым параметром обращения к функции ShowForm в DLL передается и в поле CallForm запоминается дескриптор окна вызывающей программы. Обработчик события OnClose формы проверяет это поле и, если оно определено, посылает вызвавшему окну сообщение с индексом WM_USER. В вызывающей программе предусмотрен обработчик этого сообщения, в котором реализуются необходимые действия.