动态加载BPL的共享代码/传递对象

Dar*_*ler 4 delphi bpl

我正在考虑使用动态加载BPL并将主应用程序中的对象实例传递给BPL中的方法.这造成应用程序和BPL使用的问题单元.

我写了一个小小的原型,它做了这个,并很好奇Delphi如何内部管理应用程序中定义的类与BPL之间的差异.

例如,说一个基本的Widget类,如:

TmyWidget = class
private
  fId:Integer;
  fDescription:String;
public
  procedure DoSomething1();
end;
Run Code Online (Sandbox Code Playgroud)

现在,应用程序和BPL是使用包含TmyWidget类的单元构建的.之后,TMyWidget中的某些内容发生了变化,应用程序被重建,但是BPL不是(反之亦然.)我添加了另一个方法DoSomething2()并在应用程序中创建了一个TmyWidget实例并将其传递给BPL进行处理并在基本的例子,它的工作原理.但它显然充满了潜在的问题.

如果另一个动态加载的BPL也使用TmyWidget,那么事情变得更加有趣.它似乎工作,但它绝对不是理想的.

主要问题是 - 如何通常将对象传递给主应用程序和DLL或BPL?我以前从来没有尝试过,而且可能有充分的理由,但我有这个想法适合这种方法......

我想最好的方法是序列化对象并将这些字节传递并在DLL/BPL中反序列化,此过程注意到主机和动态加载模块之间可能存在的版本差异,但我希望新的SimpleSharedMem选项可能会带来这个新功能而没有序列化的开销,但它似乎不是很有用,除非你严格保持app和dll重建任何共享代码更改......但在这个原型中,应用程序将保持相当稳定并且动态加载的模块将经常更改,并将功能添加到TmyWidget.(服务器应用程序充当基于客户端请求构建TmyWidget的工厂,应用程序将实例传递给各个模块进行处理.)

Cos*_*und 10

...很奇怪Delphi如何在内部管理应用程序中定义的类与BPL之间的差异

Delphi通过不允许它来管理它.您不能同时在多个包中使用具有相同名称的单元:如果您这样做,则会收到一条错误消息,说明类似的内容Package XYZ already contains ABC(暂时没有看到...).由于类型名称包含单元名称,因此在两个不同的包中不能使用相同的类型.除非它是由它的GUID定义的接口,否则这是一个不同的故事.

...通常如何将对象传递给主应用程序和DLL或BPL?

你没有将对象传递给DLL,这不是一个好主意.当您需要将对象传递给BPL时,请确保将该BPL的基类定义为第3个BPL.

例.您的多态行为TmyWidget可能是使用某些虚拟方法定义的.确保您有一个TmyWidgetBase定义所有这些虚拟方法的类,TmyWidget从该基类派生所有的类并传递具有该类型的对象TmyWidgetBase.确保TmyWidgetBase该类在其自己的包中.

当我尝试这样做时,我最终得到了一个很小的"bootstrap"exe和很多BPL的.基本上所有逻辑都在BPL中,以便于传递对象.


Ken*_*ran 6

我工作过的其中一个项目已经成功使用了大量的运行时包十多年了,所以我将分享一些处理包的经验.

正如Cosmin指出的不同包装不能包含相同的单位.如果使用隐式链接,通过向另一个包的requires子句添加包,或者通过向Project Options中的Runtime包列表添加包,编译器将为您完成工作并报告以下错误消息之一:

E2199: Packages '%s' and '%s' both contain unit '%s' (如果您的编译项目依赖于包含相同单元的两个包)

E2200: Package '%s' already contains unit '%s' (如果你编译一个包含一个单元的包,它已经包含在它所依赖的一个包中)

如果使用显式链接,使用LoadPackage,通常会在运行时尝试检查(尽管可以绕过它)并引发:

EPackageError:无法加载包'%s'.它包含单元'%s',它也包含在包'%s'中

解决这些错误并不是那么困难.

如果你有两个需要使用单元的软件包,只需让其中一个包含单元而另一个需要第一个. 包依赖图

如果你有两个需要使用彼此包含的单元的软件包,你必须将这些单元移动到一个可以依赖的新软件包. 依赖图

隐式链接包具有以下优点:您可以直接访问类定义,就像它们是静态链接一样.只需将一个单元添加到您需要使用它的单元的uses子句中.编译器和运行时环境负责解决所有问题.

显式链接的包需要依赖初始化部分中的类注册.