012.Delphi插件之QPlugins,多实例内嵌窗口服务

这个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.
原文地址:https://www.cnblogs.com/tianpan2019/p/11498642.html