基于TInterfacedClass的Delphi插件框架的内存管理

发布于 2024-10-26 16:33:50 字数 590 浏览 0 评论 0原文

对于服务器端插件框架,我想实现公开一个返回类引用(TInterfacedClass)的 RegisterPlugin 方法的 DLL。

然后,主机应用程序创建此类的实例,并且实例将在主机线程的上下文中运行。 (这与 Jedi VCL 插件框架不同,后者在 DLL 或 BPL 中实例化插件并将实例返回到主机。)

到目前为止,第一次测试没有显示任何问题。但是,内存管理是否存在我应该注意的隐藏问题?由于我在这个项目中使用 Delphi 2009,FastMM4 是默认的内存管理器。

这是插件 DLL 项目的草图:

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

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

exports
  RegisterPlugin;

{ TPluginOne }
// ... plugin class implementation

begin  
end.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

韬韬不绝 2024-11-02 16:33:50

内存管理器没有问题,因为 FastMM 作为 EXE 和 DLL 之间的共享内存管理器工作。但我真的对在 DLL 和 EXE 之间传递纯对象或(最糟糕的)元类的概念感到不舒服。问题是,EXE 中的 TInterfacedObject 与 DLL 中的 TInterfacedObject 不一样!当然,它们可能看起来完全相同,但事实并非如此!如果您升级了 EXE 或任何 DLL 的 Delphi 版本,您将需要重建所有内容(从而失去从实现插件框架中获得的任何优势)。

一个更便携的解决方案是返回一个“工厂接口”,类似于:

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

然后导出具有此签名的函数:

function RegisterPlugin: IFactoryInterface;

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;
删除会话 2024-11-02 16:33:50

您的代码不完整,但从您所包含的内容来看,存在一个明显的缺陷。

您似乎正在从 DLL 导出一个类 (TInterfacedClass)。当客户尝试使用不同版本的 Delphi 来使用您的类时,这会导致问题。更重要的是,如果他们想用不同的语言编写插件,这会让他们束手无策。

就我个人而言,我会选择基于 COM 的接口,它允许插件作者用任何语言创建插件。事实上,COM 的发明正是为了解决这个问题。

如果您愿意为插件和主机应用程序使用相同的编译器,并且您更喜欢将类公开给 COM 接口,那么您需要确保所有释放都是使用与分配内存相同的内存管理器执行的。最简单的方法是使用 ShareMem,这样你就安全了。

更新

Cosmin 在评论中指出了跨模块边界导出类的另一个缺陷。这基本上是你不应该做的事情。 COM 就是为此目的而设计的,它仍然应该是您的首选。 Delphi 接口与 COM 兼容,因此您可以获得二进制互操作性的相同好处,而无需创建服务器、注册 CLSID 等。

我认为您的插件应该如下所示:

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.

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文