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

发布于 2024-10-20 19:46:57 字数 904 浏览 4 评论 0原文

我正在考虑使用动态加载 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 的工厂,并且应用程序会将实例传递给各个模块进行处理。)

I was toying around with the idea of using dynamically loading BPL's and passing object instances from the main app to a method in a BPL. This poses a problem units between used by the application and by the BPL.

I wrote a small little prototype which did this and was curious how Delphi internally manages differences between classes defined in the app vs. the BPL.

For example, say a basic Widget class like:

TmyWidget = class
private
  fId:Integer;
  fDescription:String;
public
  procedure DoSomething1();
end;

Now the app and the BPL are built using the unit containing TmyWidget class. Later, something changes in TMyWidget and the app is rebuilt, but the BPL is not (or vice-versa.) I added another method DoSomething2() and created an instance of TmyWidget in the app and passed it to the BPL for processing and in the basic example, it worked. But it's obviously fraught with potential problems.

If another dynamically loaded BPL also uses TmyWidget then things get even more interesting. It seems to work, but it definitely doesn't feel ideal.

The main question is - how does one typically pass objects to and from the main application and DLLs or BPLs? I've never attempted it before and likely for a good reason, but I've got this idea that lends itself to this approach...

I'd imagine that the best approach is to serialize the object and pass those bytes over and deserialize it in the DLL/BPL with this process being mindful of potential version differences between the host and the dynamically loaded module but I was hoping the new SimpleSharedMem option might bring this new functionality without the overhead of serialization, but it seems to be not very useful unless you are strict in keeping the app and dll rebuilt on any shared code changes...but in this prototype, the app would stay fairly constant and the dynamically loaded modules would be changing frequently with functionality being added to TmyWidget. (The server app serves as the factory for building TmyWidget's based on client requests and the app would pass instances to the various modules for processing.)

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

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

发布评论

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

评论(2

抱猫软卧 2024-10-27 19:46:57

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

Delphi 通过不允许它来管理这种差异。您不能同时在多个包中拥有同名的单元:如果您这样做,您会收到一条错误消息,内容类似于 Package XYZ已经包含 ABC (还没有看到一会儿……)。由于类型名称包含单元名称,因此两个不同的包中不能有相同的类型。除非它是由 GUID 定义的接口,但这是一个不同的故事。

...通常如何在主应用程序和 DLL 或 BPL 之间传递对象?

您不将对象传递给 DLL,这不是一个好主意。当您需要将对象传递给 BPL 时,请确保将该 BPL 的基类定义到第三个 BPL 中。

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

当我尝试这样做时,我最终得到了一个很小的“引导”exe 和很多 BPL。本质上所有逻辑都在 BPL 中,以方便传递对象。

...was curious how Delphi internally manages differences between classes defined in the app vs. the BPL

Delphi manages this by not allowing it. You can't have a unit with the same name in multiple packages at the same time: if you do, you get an error message saying something similar to Package XYZ already contains ABC (haven't seen that in a while...). Since the type name includes the unit name, you can't have the same type in two different packages. Unless it's a Interface defined by it's GUID, but that's a different story.

... how does one typically pass objects to and from the main application and DLLs or BPLs?

You don't pass objects to DLL, that's not a good idea. When you need to pass objects to a BPL, make sure the base class for that BPL is defined into an 3rd BPL.

Example. Polymorphic behavior for your TmyWidget is probably defined using some virtual methods. Make sure you have a TmyWidgetBase class that defines all of those virtual methods, derive all your TmyWidget's from that base class and pass around objects with the type TmyWidgetBase. Make sure the TmyWidgetBase class is in it's own Package.

When I attempted doing this I ended up with an tiny "bootstrap" exe and lot's of BPL's. Essentially all the logic was in BPL's, to facilitate passing objects around.

抽个烟儿 2024-10-27 19:46:57

我参与的一个项目十多年来已经成功地使用了大量的运行时包,因此我将分享一些我处理包的经验。

正如 Cosmin 指出的那样,不同的包不能包含相同的单位。如果您使用隐式链接,通过将一个包添加到另一个包的 requires 子句中,或者将一个包添加到“项目选项”中的运行时包列表中,编译器将执行以下操作:为您工作并报告以下错误消息之一:

E2199:包“%s”和“%s”均包含单元“%s”(如果您的编译项目依赖于两个包含相同的单元)

E2200:包“%s”已包含单元“%s”(如果您编译的包包含一个单元,则该单元已包含在它所依赖的包之一中)

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

EPackageError:无法加载包
“%s。”它包含单元“%s”,即
也包含在包“%s”中

解决这些错误实际上并不是那么困难。

如果您有两个包都需要使用一个单元,只需让其中一个包含该单元,另一个包需要第一个单元。
package dependency graph

如果您有两个包需要使用彼此包含的单元,则必须将这些单元移动到两者都可以依赖的新包。
dependency graph

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

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

One of the projects I've worked on has successfully used a large number of runtime packages for more than a decade now so I'll share a few of my experiences dealing with packages.

As Cosmin pointed out different packages cannot contain the same units. If you use implicit linking, by adding a package to the requires clause of another package or by adding a package to the the Runtime packages list in Project Options the compiler will do the work for you and report one of the following error messages:

E2199: Packages '%s' and '%s' both contain unit '%s' (if your compiling project that depends on two packages containing the same unit)

E2200: Package '%s' already contains unit '%s' (if your compiling a package that contains a unit this is already contained in one of the packages it depends on)

If you are using explicit linking, using LoadPackage, a check will normally be attempted a runtime (though it can be circumvented) and raise an:

EPackageError: Cannot load package
'%s.' It contains unit '%s', which is
also contained in package '%s'

Resolving these errors isn't really all that difficult.

If you have two packages that both need to use a unit just let one of them contain the unit and the other one require the first.
package dependency graph

If you have two packages that need to use each other's contained unit you'll have to move those units to an new package that both can depend on.
dependency graph

Implicitly linked packages have the advantage that you can directly access class definitions as though they were statically linked. Just add a unit to the uses clause of the unit you need to use it in. The compiler and the runtime environment take care of resolving everything.

Explicitly linked packages will need to rely on class registration in the initialization section.

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