我正在考虑使用动态加载BPL并将主应用程序中的对象实例传递给BPL中的方法。这造成应用程序和BPL使用的问题单元。
我写了一个小小的原型来做到这一点,很好奇Delphi如何在内部管理应用程序中定义的类与BPL之间的差异。
例如,比如说一个基本的Widget类:
TmyWidget = class
private
fId:Integer;
fDescription:String;
public
procedure DoSomething1();
end;
现在,应用程序和BPL是使用包含TmyWidget类的单元构建的。后来,东西TMyWidget改变和应用程序被重建,但BPL不是(或反之亦然)。我加入另一种方法了doSomething2(),并在应用中创建TmyWidget的一个实例,并通过它的BPL进行处理和在基本的例子,它的工作原理。但它显然充满了潜在的问题。
如果另一个动态加载的BPL也使用TmyWidget,那么事情变得更加有趣。它似乎工作,但它绝对不是理想的。
主要问题是 - 如何通常将对象传递给主应用程序和DLL或BPL?我之前从未尝试过,而且可能有充分的理由,但我有这个想法适合这种方法......
我想像,最好的办法是序列化对象并在通过这些字节和在DLL反序列化/ BPL与此过程被铭记的主机和动态加载的模块之间的电势的版本差异,但我希望新SimpleSharedMem选项可能会带来这一新功能,而无需序列化的开销,但它似乎不是很有用,除非你是在保持应用程序和DLL重建上的任何共享代码更改严格...但在这个原型,应用程序会保持相当稳定,动态加载的模块将频繁更改,并将功能添加到TmyWidget。 (服务器的应用程序作为工厂建设TmyWidget的基于客户机请求和应用将通过实例以用于处理的各种模块。)
答案 0 :(得分:10)
...很好奇Delphi如何内部管理应用程序中定义的类与BPL之间的差异
Delphi通过不允许它来管理它。您不能同时在多个包中使用具有相同名称的单元:如果您这样做,则会收到类似于Package XYZ already contains ABC
的错误消息(暂时没有看到...) 。由于类型名称包含单元名称,因此在两个不同的包中不能使用相同的类型。除非它是由它的GUID定义的接口,否则这是一个不同的故事。
...通常如何将对象传递给主应用程序和DLL或BPL?
你没有将对象传递给DLL,这不是一个好主意。当您需要将对象传递给BPL时,请确保将该BPL的基类定义为第3个BPL。
实施例。您TmyWidget
的多态行为可能是使用某些虚拟方法定义的。确保您有一个TmyWidgetBase
类来定义所有这些虚拟方法,从该基类派生所有TmyWidget
并传递类型为TmyWidgetBase
的对象。确保TmyWidgetBase
类在其自己的包中。
当我尝试这样做时,我最终得到了一个微小的“引导程序”exe和许多BPL。基本上所有逻辑都在BPL中,以便于传递对象。
答案 1 :(得分:6)
我工作过的其中一个项目已成功使用了大量的运行时包十多年了,所以我将分享一些处理包的经验。
Cosmin指出不同的包装不能包含相同的单位。如果使用隐式链接,通过向另一个包的 requires 子句添加包,或者通过向Project Options中的 Runtime packages 列表添加包,编译器将执行为您工作并报告以下错误消息之一:
E2199: Packages '%s' and '%s' both contain unit '%s'
(如果您的编译项目依赖于包含相同单元的两个包)
E2200: Package '%s' already contains unit '%s'
(如果你编译一个包含一个单元的包,它已经包含在它所依赖的一个包中)
如果使用显式链接,使用LoadPackage,通常会在运行时尝试检查(尽管可以规避)并引发:
EPackageError:无法加载包 '%s' 的它包含单位'%s',即 包含在'%s'
包中
解决这些错误并不是那么困难。
如果您有两个需要使用单元的软件包,只需让其中一个包含该单元而另一个需要第一个。
如果您有两个需要使用彼此包含的单元的软件包,则必须将这些单元移动到可以依赖的新软件包。
隐式链接包具有以下优点:您可以直接访问类定义,就像它们是静态链接一样。只需将一个单元添加到您需要使用它的单元的uses子句中。编译器和运行时环境负责解决所有问题。
明确链接的包需要依赖初始化部分中的类注册。