我读了这篇文章,发现虚拟库接口的概念很适合运行时加载DLL.但是它们似乎不适用于Win32.这是真的?如果是这样:为什么?我没有看到将这个想法与.NET联系起来的原因.
编辑:我在这里大部分都是罗伯已经写过的.:-)
我不是在插件或类似之后 - 它是关于普通的旧Win32 DLL.让我感兴趣的是让编译器处理在运行时加载DLL的所有细节的想法 - 不需要为DLL中的每个函数调用GetProcAddress等.
在我看来,到目前为止,这三个答案已经完全忽略了你的问题.那,或者我有.你问为什么Win32 Delphi没有像Supports
Hallvard的文章谈到的神奇功能那样的东西,不是吗?即,给定DLL的名称和接口的类型信息的函数返回使用从DLL导出的独立函数实现该接口的对象.
Hydra似乎都是关于从Win32程序调用.Net代码,而不是从DLL导入函数.TJvPluginManager
要求插件DLL导出一个特殊的自注册函数,管理器在加载DLL时会调用该函数,并且该函数必须返回TJvPlugin
该类的实例,因此插件DLL必须用Delphi或C++ Builder编写.Supports
另一方面,该函数适用于以任何语言编写的任何DLL.如果你愿意,你可以在kernel32上使用它.
我不知道为什么Win32 Delphi没有这样的东西.也许CodeGear没有看到太多的需求,因为Delphi和Turbo Pascal在没有它的情况下已经存在了很长时间.
编写一个像这样工作的函数当然是可能的,我不认为它会比.Net版本更难编写,除非Microsoft的.Net库已经提供了大部分内容而Delphi只是包装它们升级到一个方便的调用函数,看起来像Supports
Delphi多年来的其他几个重载版本.
在Win32中实现该功能将有几个步骤.(我只提供了一个必要的草图,因为我现在还没有Delphi的正在运行的副本.问得很好,也许我会找到更多细节.)首先,你需要确保类型接口的信息至少包含其方法的未修饰名称.然后,Supports
需要为接口中的每个方法生成一个函数存根(除了_AddRef,_Release和QueryInterface).假设调用约定是stdcall
:存根将是这样的.
asm // Pop the return address, // discard the "this" pointer, // and restore the return address pop eax pop ecx push eax jmp AddressOfFunction end;
当Supports
生成每个存根时,它将填写实际的函数地址,从GetProcAddress
使用相应接口方法的名称调用获得.该stdcall
调用约定是容易将保鲜膜像; cdecl
有点累赘; register
颈部疼痛.
一旦生成了所有存根,就需要生成一个看起来像是实现给定接口的"对象".它不一定是实际的类.在编译时,Supports
不知道它将被要求实现的接口的布局,因此拥有一个类不会做太多.
最后一步是提供的实现_AddRef
,_Release
和QueryInterface
._AddRef
会不起眼的; _Release
是FreeLibrary
引用计数达到零时你打电话的地方; QueryInterface
不会做太多,除了声称它支持IUnknown
和给出的接口Supports
.
Delphi过去常常带有一个示例程序,该程序演示了实现一个没有任何类的接口.这一切都是通过记录和函数指针完成的(毕竟这最终都是接口).Delphi还附带了相应的代码来实现类,这部分是为了说明Delphi可以做多少容易.我现在找不到演示程序的名称,但我确定它仍然在某处.