我想用插件开发GUI应用程序。插件包含从插件核心库中的基本表单继承而来的VCL表单。主应用程序可以选择动态加载哪个插件,然后选择显示哪个Form子类。
在用户端,我想部署main.EXE、插件核心库和许多用于不同型号的插件库。我可以向用户发布新的或修改现有的插件库,以显示新设备的新表单,而无需修改主.EXE和插件核心库。
我开发的第一个版本使用DLL方法,即插件核心库和插件都是DLL形式。用户方面一切都很好。然而,在开发人员方面,如果没有在插件核心DLL项目中定义的基本窗体,则无法链接插件DLL项目。这意味着基本表单实际上在每个插件DLL项目中都是静态链接的,如果有一天我修改基本表单并重建插件核心DLL项目,我必须重建所有插件DLL项目,并向用户重新发布插件.DLL。
在StackOverflow中搜索和询问后,我意识到VCL窗体不能跨DLL边界继承的限制是由于RTTI冲突(?)。建议的解决方案是将库从DLL修改为BPL形式,这是我开发的第二个版本。除了以下两项之外,一切都很好:
-
从插件BPL动态加载的Form与Windows任务栏中的主.EXE分离。这不是我想要的。解决方案是,我在.EXE项目中启用了"使用运行时包构建"。
-
在.EXE项目中启用"使用运行时包构建"后,我必须向用户发布其他.bpl,如vcl.bpl和>rtl.bpl[/strong>。这不是我想要的。
我想知道,以上两个问题可以同时解决吗?在我看来,如果我:,我可以解决这两个问题
- 禁用.EXE项目中的"使用运行时包构建"
- 在所有.BPL项目中启用"使用运行时包构建"
通过这种方式,.EXE可以在没有vcl.bpl和>rtl.bpl捆绑的情况下运行,并且插件.bpls可以成功加载,因为依赖单元已经是主>.EXE的一部分了?我说得对吗?但是,在所有.BPL项目选项中,"使用运行时包构建"复选框都被禁用。因此,我没有机会检查解决方案是否有效。很抱歉描述太长,由于公司的网络安全政策,我无法附上图片。
从插件BPL动态加载的Form与Windows任务栏中的主.EXE分离。这不是我想要的。解决方案是,我在.EXE项目中启用了"使用运行时包构建"。
加载BPL后,将EXE的Application.Handle
传递给BPL,并在创建任何Form实例之前将其分配给BPL自己的Application.Handle
。
或者,在Windows 7+上,您可以让EXE调用SetCurrentProcessExplicitAppUserModelID()
为其任务栏按钮建立应用程序ID。然后,BPL中的每个Form都可以使用SHGetPropertyStoreForWindow()
和IPropertyStore.SetValue(PKEY_AppUserModel_ID)
为其窗口设置相同的应用程序ID。具有相同应用程序ID的多个窗口组合在一个任务栏按钮下。
有关更多详细信息,请参阅MSDN:应用程序用户模型ID(AppUserModelID)
我想知道,以上两个问题可以同时解决吗?在我看来,如果我:,我可以解决这两个问题
禁用.EXE项目中的"使用运行时包生成"。
在所有.BPL项目中启用"使用运行时包构建"。
通过这种方式,.EXE可以在没有vcl.bpl和rtl.bpl捆绑的情况下运行,并且插件.bpl可以成功加载,因为依赖单元已经是主.EXE?我说得对吗?
没有。BPL不能像那样使用EXE的内置单元。
如果禁用"使用运行时包构建",RTL/VCL单元将静态链接到可执行文件中。这样做的问题是,给定单元的多个副本无法同时加载到内存中,因此,如果相同的RTL/VCL单元静态链接到多个BPL,甚至EXE本身,则无法将多个BPLs一起加载(甚至根本无法加载)。
如果启用"使用运行时包构建",则可执行文件将依赖于RTL/VCL BPL,然后必须部署这些BPL。
因此,如果EXE和BPL共享公共单元,则这些单元必须通过共享BPL加载,因此内存中只存在一个单元副本。在编写自定义BPL时,无法避免这种情况。这意味着,如果您使用基本的RTL功能,则至少您通常必须部署RTL.BPL,而UI则必须部署VCL.BPL。