007.Delphi插件之QPlugins,插件的卸载和重新加载

效果图如下,可以反复卸载和重新加载。QPlugins这个插件,还没弄明白,摸索着跟着DEMO写

主窗口代码如下

unit Frm_Main;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  QPlugins,
  qplugins_base,
  qplugins_params,
  qplugins_loader_lib,
  Vcl.ComCtrls,
  qplugins_formsvc,
  qplugins_vcl_formsvc;

{ 要安全的移除一项服务,则使用该服务的模块必需实现IQNotify接口,并对NID_PLUGIN_UNLOADING
  通知的响应,并在通知里移除掉到服务的引用,以便插件能够被安全的释放。

  如果需要在某个插件注册后,引用某项服务,可以响应NID_PLUGIN_LOADED通知,在其中查询新的
  服务实例并记录它
}
type
  TForm_Main = class(TForm, IQNotify)
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    mmLogs: TMemo;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
    // 服务接口
    FHoldService: IQService;
    // 在通知发生时,通知响应函数接口。IQNotify接口中定义的函数,子类来实现这个函数
    procedure Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean); stdcall;
  public
    { Public declarations }
  end;

var
  Form_Main: TForm_Main;

implementation

{$R *.dfm}

// 按钮_重新加载
procedure TForm_Main.Button1Click(Sender: TObject);
var
  AFileName: string;
  ALoader: IQLoader;
begin
  // DLL插件的全名
  AFileName := ExtractFilePath(Application.ExeName) + '插件.dll';
  // 如果DLL文件存在
  if FileExists(AFileName) then
  begin
    // ByPath通过路径获取指定的服务接口实例
    ALoader := PluginsManager.ByPath('/Loaders/Loader_DLL') as IQLoader;
    if Assigned(ALoader) then
    begin
      // 加载服务
      ALoader.LoadServices(PWideChar(AFileName));
    end;
  end;
end;

// 按钮_卸载插件
procedure TForm_Main.Button2Click(Sender: TObject);
var
  AModule: HMODULE;
  ALoader: IQLoader;
  // 取服务模块
  function ServiceModule: HMODULE;
  begin
    if Assigned(FHoldService) then
      Result := FHoldService.GetOwnerInstance
    else
    begin
      Result := 0;
    end;
  end;

begin
  // 取服务模块
  AModule := ServiceModule;
  // 如果模块存在
  if AModule <> 0 then
  begin
    // 通过路径获取指定的服务接口实例
    ALoader := PluginsManager.ByPath('/Loaders/Loader_DLL') as IQLoader;
    if Assigned(ALoader) then
    begin
      // 卸载这个服务
      ALoader.UnloadServices(AModule);
    end;
  end;
end;

// 按钮_显示窗体
procedure TForm_Main.Button3Click(Sender: TObject);
var
  // 窗体服务的接口
  AFormService: IQFormService;
begin
  // 获取指定路径的服务实例(如果是多实例,则返回一个用于提供服务的新实例)
  if GetService('Services/Forms/DynamicLoadForm', IQFormService, AFormService) then
  begin
    // 嵌入窗体到父窗口的特定的位置
    AFormService.DockTo(TabSheet2.Handle, faContent);
  end;
end;

// 创建
procedure TForm_Main.FormCreate(Sender: TObject);
begin
  //
  with PluginsManager as IQNotifyManager do
  begin
    // Subscribe订阅通知
    Subscribe(NID_PLUGIN_LOADING, Self);
    Subscribe(NID_PLUGIN_UNLOADING, Self);
    Subscribe(NID_PLUGIN_LOADED, Self);
  end;
  // 加载同目录DLL
  PluginsManager.Loaders.Add(TQDLLLoader.Create(ExtractFilePath(Application.ExeName), '.dll'));
  // 启动所有的加载器加载支持的插件
  PluginsManager.Start;
end;

// 销毁
procedure TForm_Main.FormDestroy(Sender: TObject);
begin
  with PluginsManager as IQNotifyManager do
  begin
    // 取消订阅通知
    Unsubscribe(NID_PLUGIN_LOADING, Self);
    Unsubscribe(NID_PLUGIN_UNLOADING, Self);
    Unsubscribe(NID_PLUGIN_LOADED, Self);
  end;
end;

// 在通知发生时,通知响应函数接口。IQNotify接口中定义的函数,子类来实现这个函数
procedure TForm_Main.Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean);
var
  AParam: IQParam;
begin
  if Assigned(AParams) then
  begin
    // 根据传入的参数,进行不同的输出
    case AId of
      // 加载
      NID_PLUGIN_LOADING:
        begin
          // 取DLL插件全路径
          AParam := AParams.ByName('File');
          mmLogs.Lines.Add('正在加载插件 ' + ParamAsString(AParam) + ' ...');
        end;
      // 加载完成
      NID_PLUGIN_LOADED:
        begin
          // 服务接口赋值
          FHoldService := PluginsManager.ByPath('Services/HoldService');
          if Assigned(FHoldService) then
          begin
            mmLogs.Lines.Add('HoldService 已经成功加载');
          end;
        end;
      // 卸载
      NID_PLUGIN_UNLOADING:
        begin
          // 取DLL插件全路径
          AParam := AParams.ByName('Instance');
          if Assigned(AParam) and (FHoldService.GetOwnerInstance = AParam.AsInt64) then
          begin
            FHoldService := nil;
            AParam := AParams.ByName('File');
            mmLogs.Lines.Add('正在卸载插件' + ParamAsString(AParam) + ',移除关联服务 ...');
          end;
        end;
    end;
  end;
end;

end.

DLL的界面代码如下

unit Frm_Dll;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  QPlugins,
  qplugins_vcl_formsvc,
  Vcl.StdCtrls;

type
  TForm_Dll = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form_Dll: TForm_Dll;

implementation

{$R *.dfm}

// 按钮_卸载服务
procedure TForm_Dll.Button1Click(Sender: TObject);
begin
  // 卸载服务
  UnloadServices(HInstance, False);
end;

initialization

// 初始化时,加载插件
RegisterFormService('Services/Forms', 'DynamicLoadForm', TForm_Dll, False);

finalization

// 卸载插件
UnregisterServices('Services/Forms', ['DynamicLoadForm']);

end.

自带了一个pas文件,不知道是干嘛用的

unit holdservice;


interface

uses
  qstring,
  qplugins,
  qplugins_params;

type
  // 这个只是用来测试,实际上什么也不干
  THoldService = class(TQService)
  end;

implementation

initialization

// 注册
RegisterServices('/Services', [THoldService.Create(NewId, 'HoldService')]);

finalization

// 注销
UnregisterServices('/Services', ['HoldService']);

end.
原文地址:https://www.cnblogs.com/tianpan2019/p/11495684.html