You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Prism技术问题:如何仅当DLL存在时才加载模块

问题描述

我正在使用Prism和Unity开发WPF应用程序,需要实现一个可选模块的加载逻辑:

  • 无该模块DLL时程序能正常启动
  • 把模块DLL复制到输出目录并重启后,模块能自动加载

当前使用的XamlModuleCatalog配置如下:

<Modularity:ModuleCatalog
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Modularity="clr-namespace:Prism.Modularity;assembly=Prism.Wpf">

    <Modularity:ModuleInfo 
                Ref="file://TrayManager.UI.dll"
                ModuleName="UIModule" 
                ModuleType="TrayManager.UI.UIModule, TrayManager.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
                
    <Modularity:ModuleInfo 
                Ref="file://TrayManager.PasswordEncryption.dll"
                InitializationMode="OnDemand"
                ModuleName="PasswordEncryptionModule" 
                ModuleType="TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

</Modularity:ModuleCatalog>

遇到的问题:

  • 把PasswordEncryptionModule的InitializationMode设为OnDemand时,程序能正常启动,但模块永远不会被加载
  • 设为WhenAvailable时,只有当DLL在输出目录中时程序才能启动

App.xaml.cs代码如下:

protected override Window CreateShell()
{
    _logger.Debug("Shell created");
    return Container.Resolve<MainWindow>();
}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{

}

protected override IModuleCatalog CreateModuleCatalog() {
    return new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative));
}

请问哪里操作出错了?


解决方案

核心问题分析

你的配置存在两个关键问题:

  1. WhenAvailable模式的强制检查:当设置该模式时,Prism启动阶段会验证模块DLL是否存在,不存在则直接抛出异常导致启动失败。
  2. OnDemand模式的被动性:该模式下Prism不会自动加载模块,必须手动触发加载操作;同时静态XAML配置的模块,即使DLL后来被添加,Prism也不会主动检测。

方案一:启动时动态检测并注册可选模块

修改CreateModuleCatalog方法,先加载XAML中的固定模块,再手动检查可选模块DLL是否存在,存在则添加到模块目录并设置自动加载:

protected override IModuleCatalog CreateModuleCatalog()
{
    // 加载XAML中定义的基础模块
    var catalog = new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative));
    
    // 检查可选模块DLL是否存在于程序目录
    var moduleDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TrayManager.PasswordEncryption.dll");
    if (File.Exists(moduleDllPath))
    {
        catalog.AddModule(new ModuleInfo
        {
            ModuleName = "PasswordEncryptionModule",
            ModuleType = "TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
            Ref = $"file://{moduleDllPath}",
            InitializationMode = InitializationMode.WhenAvailable
        });
    }
    
    return catalog;
}

这个方案的优势:

  • 无DLL时程序正常启动,不会加载该模块
  • 复制DLL到目录后重启,程序自动检测并加载模块

方案二:使用目录扫描自动发现模块

如果需要支持多个可选模块,可结合DirectoryModuleCatalog扫描程序目录自动发现模块:

protected override IModuleCatalog CreateModuleCatalog()
{
    // 加载XAML中的固定模块
    var xamlCatalog = new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative));
    
    // 创建目录扫描目录,扫描当前程序目录下的模块
    var directoryCatalog = new DirectoryModuleCatalog
    {
        ModulePath = AppDomain.CurrentDomain.BaseDirectory
    };
    
    // 合并两个模块目录
    var aggregateCatalog = new AggregateModuleCatalog();
    aggregateCatalog.AddCatalog(xamlCatalog);
    aggregateCatalog.AddCatalog(directoryCatalog);
    
    return aggregateCatalog;
}

需要给可选模块类添加特性声明,让Prism扫描时识别:

[Module(ModuleName = "PasswordEncryptionModule", InitializationMode = InitializationMode.WhenAvailable)]
public class PasswordEncryptionModule : IModule
{
    // 模块初始化逻辑
    public void Initialize()
    {
        // ...
    }
}

方案三:保留XAML配置并手动触发加载(不推荐)

如果坚持用XAML配置,可给可选模块添加Required="False"属性,避免无DLL时启动失败,然后手动触发加载:
修改XAML配置:

<Modularity:ModuleInfo 
            Ref="file://TrayManager.PasswordEncryption.dll"
            InitializationMode="OnDemand"
            Required="False"
            ModuleName="PasswordEncryptionModule" 
            ModuleType="TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

在Shell初始化后手动加载模块:

protected override void InitializeShell()
{
    base.InitializeShell();
    
    var moduleManager = Container.Resolve<IModuleManager>();
    var moduleCatalog = Container.Resolve<IModuleCatalog>();
    
    var targetModule = moduleCatalog.Modules.FirstOrDefault(m => m.ModuleName == "PasswordEncryptionModule");
    if (targetModule != null && !targetModule.IsLoaded)
    {
        moduleManager.LoadModule("PasswordEncryptionModule");
    }
}

内容的提问来源于stack exchange,提问作者N0m4n904

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 19:35:26