效果图如下,可以反复卸载和重新加载。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.