6
votes

For a server-side plugin framework I would like to implement DLLs which expose a RegisterPlugin method that returns a class reference (TInterfacedClass).

The host application then creates the instance(s) of this class, and instances will run in the context of the host thread(s). (This is different for example from the Jedi VCL plugin framework which instantiates the plugin in the DLL or BPL and returns the instance to the host.)

First tests showed no problems so far. However, are there hidden problems with memory management I should be aware of? As I am using Delphi 2009 for this project, FastMM4 is the default memory manager.

Here a sketch of the plugin DLL project:

library ExamplePlugin;
uses
  ...
type
  TPluginOne = class(TInterfacedObject, ...)
  ...
  end;

function RegisterPlugin: TInterfacedClass; stdcall;
begin
  Result := TPluginOne;
end;

exports
  RegisterPlugin;

{ TPluginOne }
// ... plugin class implementation

begin  
end.
2

2 Answers

7
votes

No problems with memory manager, because FastMM works as a shared memory manager between the EXE and the DLL. But I'm really not comfortable with the concept of passing pure objects, or (worst) metaclasses between the DLL and the EXE. The problem is, TInterfacedObject from the EXE is not the same as TInterfacedObject from the DLL! Sure, they might look exactly the same, but they're not! And if you ever upgrade the version of Delphi for the EXE or for any of the DLL's, you'll need to rebuild everything (thus losing whatever advantage you gained from implementing a Plugin framework).

A much more portable solution would be to return a "Factory Interface", something along the lines of:

IFactoryInterface = interface
[GUId-goes-here]
  function MakeWhateverInterfaceYouNeed: IUnknownDerivate
end;

then export a function with this signature:

function RegisterPlugin: IFactoryInterface;
2
votes

Your code is incomplete but from what you have included there is one obvious flaw.

You appear to be exporting a class (TInterfacedClass) from a DLL. This is going to cause problems when clients try to consume your class with a different version of Delphi. What's more it's going to leave them helpless if they want to write plug-ins in a different language.

Personally I'd go for a COM based interface which will allow plug-in authors to create plug-ins in any language. This is in fact the very problem that COM was invented to solve.

If you are happy to be constrained to using the same compiler for plug-ins and host app, and you prefer to expose classes to COM interfaces, then you need to make sure that all deallocation is performed with the same memory manager as allocated the memory. The simplest way is to use ShareMem and then you'll be safe.

UPDATE

Cosmin points out in a comment another flaw with exporting classes across module boundaries. It's basically something that you shouldn't do. COM was designed for this very purpose and it still should be the first choice for you. Delphi interfaces which are COM compatible so you can get the same benefits of binary interoperability without having to create servers, register CLSID's etc.

I think your plug-in should look like this:

library ExamplePlugin;

type
  TPluginOne = class(TInterfacedObject, IPlugin)
  [GUID]
  public
    constructor Create(const Host: THostApp);
  end;

function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall;
begin
  Result := TPluginOne.Create(Host);
end;

exports
  RegisterPlugin;

begin  
end.