I have to develop plugin system for non-gui app that I'm developing. I envisioned this as core app that has basic set of features, and which is extendable with plugins.
Now, I figured out that probably best way to do this is to make plugins as DLLs, and to load them within host app. Plugins should be able to modify some parts of app core (access to certain methods/variables), and this is tricky part.
What I've thought is to make THost
class, which implements IHost
and IHostExposed
interfaces. When host loads plugin, it will pass IHostExposed
to plugin, and plugin could call methods/access to variables in that interface. Something like this:
Interface declarations:
unit uHostInterfaces;
interface
type
IHost = interface
['{BAFA98BC-271A-4847-80CE-969377C03966}']
procedure Start;
procedure Stop;
end;
// this intf will get exposed to plugin
IHostExposed = interface
['{1C59B1A9-EC7A-4D33-A574-96DF8F5A7857}']
function GetVar1: Integer;
function GetVar2: String;
procedure SetVar1(const AValue: Integer);
procedure SetVar2(const AValue: String);
property Var1: Integer read GetVar1 write SetVar1;
property Var2: String read GetVar2 write SetVar2;
end;
implementation
end.
Host class declaration:
unit uHost;
interface
uses
Winapi.Windows, Winapi.Messages,
uHostInterfaces, uInstanceController, uSettings;
type
THost = class(TInterfacedObject, IHost, IHostExposed)
private
FVar1 : Integer;
FVar2 : String;
FWindowHandle : HWND;
FInstanceController: TInstanceController;
FSettings : TSettings;
procedure WndProc(var AMessage: TMessage);
public
constructor Create;
destructor Destroy; override;
// methods from IHost
procedure Start;
procedure Stop;
// methods from IHostExposed, which get set Var1/Var2
function GetVar1: Integer;
function GetVar2: string;
procedure SetVar1(const AValue: Integer);
procedure SetVar2(const AValue: string);
end;
implementation
...
...and how i'd use it:
type
TRegisterPlugin = procedure(const AHostExposed: IHostExposed);
var
hdll : THandle;
RegisterPlugin: TRegisterPlugin;
host : IHost;
begin
host := THost.Create;
hdll := LoadLibrary('plugin.dll');
if hdll <> 0 then
begin
@RegisterPlugin := GetProcAddress(hdll, 'RegisterPlugin');
if Assigned(RegisterPlugin) then
begin
// call the plugin function and pass IHostExposed interface to it
// from there on, plugin can use this interface to interact with core app
RegisterPlugin(host as IHostExposed);
...
What I would like to hear is any suggestions about this approach, and if there are better solutions for what I'm trying to achieve?