На этом шаге мы рассмотрим загрузку немодальных форм из динамически подключаемых библиотек.
Однако иногда возникает необходимость отображения немодальных форм из динамически загружаемых библиотек - например, при редком использовании в приложении немодальных форм для экономии ресурсов. Если реализовать код таким же образом, как и при показе модальных диалоговых окон, то форма будет создана и, может быть, даже показана на экране. Но после этого произойдет выгрузка DLL, и вслед за этим немедленно последуют исключения, так как в памяти компьютера будет отсутствовать код для работы с элементами управления формы. Традиционное решение этой проблемы выглядит следующим образом: библиотека загружается динамически, и в качестве одного из параметров ей передается адрес функции главного приложения, которая будет вызвана при закрытии немодальной формы - обычно в обработчике события OnDestroy. Эта функция должна информировать главное приложение о необходимости выгрузки DLL из памяти компьютера, но библиотека должна выгружаться после завершения ее работы (и, следовательно, после завершения работы деструктора формы) - иначе возможно возникновение исключения из-за отсутствия кода в памяти компьютера. Это достигается путем асинхронной развязки - посылки сообщения с помощью функции PostMessage какому-либо окну приложения, обычно главной форме. Код реализации данного способа отображения немодальных форм в DLL выглядит следующим образом (приведем полный текст модуля формы, находящейся в DLL):
unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TNotifyClose = procedure; stdcall; type TForm2 = class(TForm) Label1: TLabel; procedure FormDestroy(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } public { Public declarations } FNC: TNotifyClose; end; var Form2: TForm2; procedure DynNonmodal(AppHandle:THandle; NC: Pointer); stdcall; implementation {$R *.dfm} procedure TForm2.FormDestroy(Sender: TObject); begin if Assigned(FNC) then FNC; end; procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; procedure DynNonmodal(AppHandle:THandle; NC: Pointer); stdcall; begin Application.Handle:=AppHandle; if Assigned(Form2) then Form2.Show else begin Form2:=TForm2.Create(Application); Form2.FNC:= TNotifyClose(NC); Form2.Show; end; end; end.
Приложение, использующее эту библиотеку, имеет следующий код (приведем полный текст приложения):
unit Unit_pr; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const WM_DLLUNLOAD = WM_USER+1; type TDynNonmodal=procedure(AppHandle: THandle; NC: Pointer); stdcall; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public procedure WMDLLUnload(var Message:TMessage); MESSAGE WM_DLLUNLOAD; {метод обработки сообщения} { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} var FHLib: THandle; procedure ReceiveCloseNotify; stdcall; begin Application.ProcessMessages; PostMessage(Form1.Handle, WM_DLLUNLOAD, 0, 0); end; procedure TForm1.WMDLLUnload(var Message: TMessage); begin Application.ProcessMessages; if FHLib <> 0 then FreeLibrary(FHLib); FHLib := 0; ShowMessage('Библиотека выгружена'); end; procedure TForm1.Button1Click(Sender: TObject); var DM: TDynNonmodal; begin if FHLib = 0 then FHLib := LoadLibrary('FirstLib.dll'); if FHLib > 0 then begin DM := GetProcAddress(FHLib, 'DynNonmodal'); if Assigned(DM) then DM(Application.Handle, @ReceiveCloseNotify); end; end; end.
Результат работы приложения изображен на рисунке 1:
Рис.1. Результат работы приложения
На следующем шаге мы закончим изучение этого вопроса.