Шаг 315.
Создание внутрипроцессных серверов автоматизации. Создание и использование DLL. Экспорт дочерних форм из DLL

    На этом шаге мы рассмотрим экспортирование дочерних форм из DLL.

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

    Следует принять во внимание тот факт, что дочерняя форма может быть задействована в нескольких формах главного приложения с различающимся жизненным циклом. Поэтому помимо функции, которая будет создавать дочернюю форму, необходимо иметь функцию для ее разрушения и возврата занятых ресурсов операционной системе. При этом опять же, поскольку возможно существование нескольких дочерних форм, при создании каждой формы главному приложению должен быть сообщен ее идентификатор, который необходимо сохранить и использовать при вызове деструктора данной дочерней формы. Исходя из этих теоретических предпосылок, можно приступить к созданию библиотеки, способной экспортировать дочерние формы. Приведем полный текст DLL (она содержит одну форму Form1):

library ChildDLL;

uses
  SysUtils,
  Forms,
  Types,
  Classes, Windows, Dialogs,
  UnitDLLForm1 in 'UnitDLLForm1.pas' {Form1};

{$R *.res}

function SetParent(hWndChild: HWnd; hWndNewParent: HWnd):HWnd;
  stdcall; external 'user32.dll' name 'SetParent';

function CreateCustomWindow(ParentHandle:integer;
       DataRect:TRect; var WinHandle:THandle):integer;
        stdcall; export;
//Метод возвращает дескриптор формы, который
//должен быть передан деструктору
var
  FD:TForm1;
begin
  Result := 0;
  WinHandle := 0;
  try
    FD := TForm1.Create(nil);
    Result := integer(FD);
    WinHandle := FD.Handle;
    if ParentHandle<>0 then begin
      SetParent(WinHandle,ParentHandle);
      with FD do begin
        SetWindowPos(Handle, HWND_TOP, DataRect.Left,
        DataRect.Top, DataRect.Right-DataRect.Left,
        DataRect.Bottom-DataRect.Top, SWP_SHOWWINDOW);
        Show;
      end;
    end;
  except
    On E:exception do
    MessageDlg(E.Message,mtError,[mbOK],0);
  end;
end;

procedure DeleteCustomWindow(WinID:integer); stdcall; export;
begin
  try
    if WinID<>0 then TForm1(WinID).Free;
  except
    On E:exception do
    MessageDlg(E.Message,mtError,[mbOK],0);
  end;
end;

exports
  CreateCustomWindow,
  DeleteCustomWindow;
begin
end.

    На форму TForm1 помещают элементы управления, которые необходимо пока-зать в главном приложении (рисунок 1). Обычно свойство BorderStyle дочерних форм устанавливают равным bsNone.


Рис.1.Форма в виде DLL, экспортируемая как дочерняя

    Далее, дочерним формам в качестве параметров функции следует передавать прямоугольную область родительского окна, в которой они размещаются. Это означает, что дочерняя форма должна иметь хорошо отлаженные обработчики событий, которые связаны с изменением ее размеров. Как и все предыдущие примеры, код показа дочерних форм следует сделать языково-независимым - а это значит, что необходимо использовать функции Windows API для изменения свойств их родителей. Такая функция определена в модуле user32.dll - SetParent. Другая функция Windows API - SetWindowPos - требуется для изменения границ формы. Возвращаемым параметром является указатель на объект. Однако задействовать его сразу приложение не может - оно должно запомнить этот указатель и использовать при вызове функции DeleteCustomWindow, - поэтому сохраняется возможность применения данного проекта в приложениях, написанных не на Delphi.

    Еще один полезный параметр - дескриптор окна формы - передается как вариантный параметр функции CreateCustomWindow. Он может быть использован главным приложением для посылки сообщений форме, динамического изменения размеров, атрибутов видимости и т.д. посредством вызова соответствующих функций Windows API.

    Код основного приложения для тестирования данной DLL выглядит следующим образом (приведем полный текст модуля):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FHLib:THandle;
    FChildID:integer;
    FChildHandle:THandle;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

type
  TCreateCustomWindow=function(ParentHandle:integer;
    DataRect:TRect; var WinHandle:THandle):integer; stdcall;
  TDeleteCustomWindow=procedure(WinID:integer); stdcall;


procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:=caFree;
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  CreateW:TCreateCustomWindow;
begin
  FHLib:=LoadLibrary('ChildDLL.dll');
  if FHLib<>0 then begin
    CreateW:=GetProcAddress(FHLib,'CreateCustomWindow');
    if Assigned(CreateW) then 
     FChildID:=CreateW(Handle,ClientRect,FChildHandle);
  end;
end;

procedure TForm2.FormDestroy(Sender: TObject);
var
  DeleteW:TDeleteCustomWindow;
begin
  if (FChildID>0) and (FHLib<>0) then begin
    DeleteW:=GetProcAddress(FHLib,'DeleteCustomWindow');
    if Assigned(DeleteW) then DeleteW(FChildID);
  end;
  if FHLib<>0 then FreeLibrary(FHLib);
end;

end.

    На форму TForm2 в этом проекте никаких элементов управления не помещается. Главная форма приложения (на рисунке 2 форма Form1) содержит одну кнопку, при щелчке на которой происходит немодальное отображение формы TForm2:

  with TForm2.Create(Self) do Show;
Текст этого приложения вместе с DLL можно взять здесь (420,9 Кб).

    При создании второй формы выполняется загрузка DLL, и форма, созданная в DLL, становится дочерней для TForm2. Можно создать несколько экземпляров TForm2. При разрушении конкретного экземпляра разрушается и дочернее окно на нем - для этого используется сохраненный ранее параметр FChildlD.


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

    Казалось бы, аналогичную методику можно использовать для экспорта из DLL других элементов управления, среди предков которых нет класса TForm. Однако при попытке вызвать функцию SetParent непосредственно для элемента управления происходит генерация исключения, связанного с отсутствием родительского окна у элемента управления, и в результате он не отображается на форме.

    В COM DLL часто используют дочерние формы. Однако код их создания абсолютно не похож на представленный здесь. Дочерние формы COM DLL - это элементы управления ActiveX.

    На следующем шаге мы рассмотрим реализацию внутрипроцессного сервера автоматизации.




Предыдущий шаг Содержание Следующий шаг