基于TInterfacedClass的Delphi插件框架的内存管理
对于服务器端插件框架,我想实现公开一个返回类引用(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
内存管理器没有问题,因为 FastMM 作为 EXE 和 DLL 之间的共享内存管理器工作。但我真的对在 DLL 和 EXE 之间传递纯对象或(最糟糕的)元类的概念感到不舒服。问题是,EXE 中的
TInterfacedObject
与 DLL 中的TInterfacedObject
不一样!当然,它们可能看起来完全相同,但事实并非如此!如果您升级了 EXE 或任何 DLL 的 Delphi 版本,您将需要重建所有内容(从而失去从实现插件框架中获得的任何优势)。一个更便携的解决方案是返回一个“工厂接口”,类似于:
然后导出具有此签名的函数:
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 asTInterfacedObject
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:
then export a function with this signature:
您的代码不完整,但从您所包含的内容来看,存在一个明显的缺陷。
您似乎正在从 DLL 导出一个类 (TInterfacedClass)。当客户尝试使用不同版本的 Delphi 来使用您的类时,这会导致问题。更重要的是,如果他们想用不同的语言编写插件,这会让他们束手无策。
就我个人而言,我会选择基于 COM 的接口,它允许插件作者用任何语言创建插件。事实上,COM 的发明正是为了解决这个问题。
如果您愿意为插件和主机应用程序使用相同的编译器,并且您更喜欢将类公开给 COM 接口,那么您需要确保所有释放都是使用与分配内存相同的内存管理器执行的。最简单的方法是使用 ShareMem,这样你就安全了。
更新
Cosmin 在评论中指出了跨模块边界导出类的另一个缺陷。这基本上是你不应该做的事情。 COM 就是为此目的而设计的,它仍然应该是您的首选。 Delphi 接口与 COM 兼容,因此您可以获得二进制互操作性的相同好处,而无需创建服务器、注册 CLSID 等。
我认为您的插件应该如下所示:
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: