ETABS插件集成Prism模块时透明代理转InnerModuleInfoLoader类型错误
问题描述
开发的是运行在C盘安装的ETABS软件上的插件,插件dll文件名为Combinito.dll,WPF应用基于Prism框架实现,ETABS要求所有插件需以cPlugin函数作为入口点。
未引入模块时插件运行正常,给仅包含空白测试窗口的基础插件添加模块后,编译可以通过,但运行到bootstrapper.Run()步骤时触发运行时错误,错误信息如下:
Exception thrown: 'System.InvalidCastException' in Prism.Wpf.dll An exception of type 'System.InvalidCastException' occurred in Prism.Wpf.dll but was not handled in user code Unable to cast transparent proxy to type 'InnerModuleInfoLoader'.
通过Fusion++工具抓取ETABS启动并运行插件时的dll加载日志,发现Prism.wpf.dll被触发两次加载,第二次加载失败,失败日志如下:
LOG: DisplayName = Prism.Wpf, Version=8.1.97.5141, Culture=neutral, PublicKeyToken=40ee6c3a2184dc59 (Fully-specified) LOG: Appbase = file:///C:/Program Files/Computers and Structures/ETABS 19/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = ETABS.exe Calling assembly : (Unknown). === LOG: This bind starts in default load context. LOG: Using application configuration file: C:\Program Files\Computers and Structures\ETABS 19\ETABS.exe.Config LOG: Using host configuration file: LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config. LOG: GAC Lookup was unsuccessful. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf.DLL. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf/Prism.Wpf.DLL. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf.EXE. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf/Prism.Wpf.EXE. LOG: All probing URLs attempted and failed.
第一次成功加载的日志对比:
LOG: Appbase = file:///C:/Program Files/Computers and Structures/ETABS 19/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = ETABS.exe Calling assembly : Combinito, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. === LOG: This bind starts in LoadFrom load context. WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load(). LOG: Using application configuration file: C:\Program Files\Computers and Structures\ETABS 19\ETABS.exe.Config LOG: Using host configuration file: LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config. LOG: Post-policy reference: Prism.Wpf, Version=8.1.97.5141, Culture=neutral, PublicKeyToken=40ee6c3a2184dc59 LOG: GAC Lookup was unsuccessful. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf.DLL. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf/Prism.Wpf.DLL. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf.EXE. LOG: Attempting download of new URL file:///C:/Program Files/Computers and Structures/ETABS 19/Prism.Wpf/Prism.Wpf.EXE. LOG: Attempting download of new URL file:///C:/Users/moustafa.el-sawy/source/repos/Combinito/Combinito/bin/Debug/Prism.Wpf.DLL. LOG: Assembly download was successful. Attempting setup of file: C:\Users\moustafa.el-sawy\source\repos\Combinito\Combinito\bin\Debug\Prism.Wpf.dll
可以看到失败的加载请求中调用程序集为unknown,且没有尝试到插件所在目录查找dll。
当前Visual Studio解决方案包含两个项目:模块项目和主插件项目Combinito(作为Shell)。
相关代码
cPlugin.cs核心代码
Bootstrapper bootstrapper = new Bootstrapper(); bootstrapper.Run();
Bootstrapper.cs代码
public class Bootstrapper : PrismBootstrapperBase { protected override IContainerExtension CreateContainerExtension() { return new UnityContainerExtension(); } protected override DependencyObject CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.Register<MainWindowViewModel>(); } protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); } protected override IModuleCatalog CreateModuleCatalog() { // Use local directory as module catalog return new DirectoryModuleCatalog { ModulePath = "Modules" }; } }
模块类代码
using Combinito.Modules.LCGenerator.Views; using Prism.Ioc; using Prism.Modularity; using Prism.Regions; namespace Combinito.Modules.LCGenerator { /// <summary> /// Register components of module with Unity/Prism /// </summary> [Module(ModuleName = "ModuleA")] public class LCGeneratorModule : IModule { public void Initialize() { } public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { } } }
解决方案思路
- 注册程序集解析事件解决探测路径问题:宿主程序ETABS的默认AppBase是自身安装目录,不会主动探测插件目录的dll,在
cPlugin函数最开头注册AssemblyResolve事件,手动从插件目录加载缺失的程序集:
// cPlugin.cs入口最开头添加 Assembly currentAssembly = Assembly.GetExecutingAssembly(); string pluginDir = Path.GetDirectoryName(currentAssembly.Location); AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { string assemblyName = new AssemblyName(args.Name).Name + ".dll"; string assemblyPath = Path.Combine(pluginDir, assemblyName); if (File.Exists(assemblyPath)) { return Assembly.LoadFrom(assemblyPath); } // 额外探测Modules目录下的程序集 string moduleAssemblyPath = Path.Combine(pluginDir, "Modules", assemblyName); if (File.Exists(moduleAssemblyPath)) { return Assembly.LoadFrom(moduleAssemblyPath); } return null; };
- 修正模块目录为绝对路径:原代码中
ModulePath = "Modules"是相对路径,会被解析为ETABS安装目录下的Modules文件夹,需要改为插件目录下的Modules绝对路径:
protected override IModuleCatalog CreateModuleCatalog() { string pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string modulePath = Path.Combine(pluginDir, "Modules"); return new DirectoryModuleCatalog { ModulePath = modulePath }; }
- 关闭模块加载的跨域代理逻辑:透明代理转换错误是因为Prism默认加载模块时会做跨域检查,你可以直接改用手动注册模块的方式规避自动探测的跨域问题:
protected override IModuleCatalog CreateModuleCatalog() { var catalog = new ModuleCatalog(); catalog.AddModule(typeof(LCGeneratorModule).Name, typeof(LCGeneratorModule).AssemblyQualifiedName); return catalog; }
内容的提问来源于stack exchange,提问作者MElSawy
相关产品推荐
相关产品推荐

