这个DEMO中主要是在DLL中建立了一个IDockableControl类,并在DLL的子类中写了具体的实现方法。
在主程序exe中,找到这个服务,然后调用DLL的内嵌方法,把DLL插件窗口内嵌到主程序中。
界面如下
DLL代码如下:
unit Frm_Dll; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, qstring, QPlugins, Vcl.Imaging.jpeg, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.AxCtrls, qplugins_base, qplugins_vcl_messages; type TForm_Dll = class(TForm) Panel1: TPanel; Button1: TButton; CheckBox1: TCheckBox; Edit1: TEdit; Edit2: TEdit; Image1: TImage; Label1: TLabel; private { Private declarations } protected procedure CreateParams(var Params: TCreateParams); override; public { Public declarations } end; // 创建一个接口,方法都由子类来实现 IDockableControl = interface ['{D0A4BDFA-CB29-4725-9158-C199B9C373C9}'] procedure DockTo(AHandle: HWND); stdcall; procedure HostSizeChanged; stdcall; end; // 接口方法的类 TDockableService = class(TQService, IDockableControl) protected FForm: TForm; procedure DockTo(AHandle: HWND); stdcall; procedure HostSizeChanged; stdcall; public constructor Create(const AId: TGuid; AName: QStringW); overload; override; destructor Destroy; override; end; TDockInstanceService = class(TQService) public function GetInstance: IQService; override; stdcall; end; var Form_Dll: TForm_Dll; implementation {$R *.dfm} { TDockableService } constructor TDockableService.Create(const AId: TGuid; AName: QStringW); begin // 继承 inherited Create(AId, AName); end; // 销毁 destructor TDockableService.Destroy; begin FreeAndNil(FForm); inherited; end; // 内嵌窗口 procedure TDockableService.DockTo(AHandle: HWND); begin // 创建窗体 FForm := TForm_Dll.Create(nil); FForm.BorderStyle := bsNone; FForm.ParentWindow := AHandle; FForm.DoubleBuffered := True; FForm.Show; // 设置窗口大小 HostSizeChanged; end; procedure TDockableService.HostSizeChanged; var R: TRect; begin // 取父窗口大小 GetClientRect(Winapi.Windows.GetParent(FForm.Handle), R); // 设置子窗口大小 FForm.SetBounds(0, 0, R.Width, R.Height); end; { TDockInstanceService } function TDockInstanceService.GetInstance: IQService; begin Result := TDockableService.Create(NewId, 'DockableService'); end; { TForm2 } procedure TForm_Dll.CreateParams(var Params: TCreateParams); begin inherited; Params.Style := Params.Style and (not(WS_CAPTION or WS_THICKFRAME)); // 不要标题和边框,实际上就是 BorderStyle 为 bsNone 的效果 end; initialization // 注册 /Services/Docks/Frame 服务 RegisterServices('Services/Docks', [TDockInstanceService.Create(IDockableControl, 'Frame')]); finalization // 取消服务注册 UnregisterServices('Services/Docks', ['Frame']); end.
EXE代码如下
unit Frm_Main; interface { VCL DLL或主程序需要引用 QPlugins.VCL 单元,该单元实现了替代的消息处理和派发 } uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ExtCtrls, QPlugins, QPlugins_loader_lib, QPlugins_Vcl_Messages; type TForm1 = class(TForm) Panel1: TPanel; Button1: TButton; PageControl1: TPageControl; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure PageControl1Resize(Sender: TObject); private { Private declarations } public { Public declarations } end; // DLL中的接口,方法都由DLL的子类来实现 IDockableControl = interface ['{D0A4BDFA-CB29-4725-9158-C199B9C373C9}'] procedure DockTo(AHandle: HWND); stdcall; procedure HostSizeChanged; stdcall; end; // 加强的Tabsheet TDockHostPage = class(TTabSheet) private procedure SetDockedControl(const Value: IDockableControl); protected FDockedControl: IDockableControl; public property DockedControl: IDockableControl read FDockedControl write SetDockedControl; end; var Form1: TForm1; implementation {$R *.dfm} // 按钮_新页 procedure TForm1.Button1Click(Sender: TObject); var ACtrl: IDockableControl; ATabSheet: TDockHostPage; begin // 根据'Services/Docks/Frame'找到DLL中的TDockInstanceService服务 ACtrl := PluginsManager.ByPath('Services/Docks/Frame') as IDockableControl; // 如果这个服务存在 if Assigned(ACtrl) then begin // 创建一个加强的Tabsheet,并把DLL中的TDockInstanceService服务内嵌进去 ATabSheet := TDockHostPage.Create(PageControl1); ATabSheet.PageControl := PageControl1; ATabSheet.Caption := '第 ' + IntToStr(PageControl1.PageCount) + ' 页'; ATabSheet.DockedControl := ACtrl; // 激活 PageControl1.ActivePage := ATabSheet; end else begin Application.MessageBox('Services/Docks/Frame 服务未注册,请编译DLL先。', '服务未注册', MB_OK or MB_ICONSTOP); end; end; // 创建 procedure TForm1.FormCreate(Sender: TObject); var APath: string; begin ReportMemoryLeaksOnShutdown := True; APath := ExtractFilePath(Application.ExeName); // 注册默认的 DLL 加载器,扩展名可以根据实际的情况随意修改,多个扩展名之间用逗号或分号分隔 PluginsManager.Loaders.Add(TQDLLLoader.Create(APath, '.dll')); // 启动插件注册,如果要显示加载进度,可以注册IQNotify响应函数响应进度通知 PluginsManager.Start; end; // 窗口大小调整 procedure TForm1.PageControl1Resize(Sender: TObject); var I: Integer; APage: TDockHostPage; begin // 遍历窗体 for I := 0 to PageControl1.PageCount - 1 do begin APage := PageControl1.Pages[I] as TDockHostPage; if APage.DockedControl <> nil then begin APage.DockedControl.HostSizeChanged; end; end; end; { TDockHostPage } // 接口作为参数传过来就内嵌到加强的Tabsheet中 procedure TDockHostPage.SetDockedControl(const Value: IDockableControl); begin if FDockedControl <> Value then begin FDockedControl := Value; if Assigned(Value) then begin // DLl中的内嵌窗口函数 Value.DockTo(Handle); end; end; end; end.