You need to enable JavaScript to run this app. 相同PublicKeyToken的不同版本Siemens.Engineering程序集并行使用问题
优惠活动
大模型
产品
解决方案
定价
更多

相同PublicKeyToken的不同版本Siemens.Engineering程序集并行使用问题

同强名标识Siemens.Engineering多版本共存实现方案

问题根因

CLR的程序集绑定规则明确:程序集名称、Culture、PublicKeyToken 三项一致时,无论版本号存在多大差异,都会被视为同一绑定目标,应用域全局仅允许加载单一份。所有当前遇到的异常均由该规则触发:

  • 编译阶段MSBuild无法区分两个同标识的引用优先级,因此生成“随机选择版本”的冲突警告
  • 运行时CLR受绑定规则限制,仅会匹配首个路径命中的主程序集,对应的关联依赖(如Siemens.Engineering.Contract)也只会绑定到该版本的路径,跨版本调用时就会触发TypeLoadException
  • 当前App.config中配置的两个同assemblyIdentity节点,CLR只会解析首个条目,第二个codeBase规则完全失效,无法实现双版本绑定。

落地实现步骤

1. 清理无效的配置

移除App.config中所有针对Siemens.Engineering的dependentAssembly绑定节点,这类同标识的配置不仅无法实现双版本加载,还会干扰CLR的正常绑定流程。

2. 拆分版本独立目录

不要将两个版本的主程序集及关联依赖直接放在程序输出根目录,按版本拆分独立子目录存放,避免文件冲突:

程序输出根目录/
├─ 主程序.exe
├─ v15/
│  ├─ Siemens.Engineering.dll (15版本主程序集)
│  └─ 所有15版本对应的关联依赖(含Siemens.Engineering.Contract.dll)
└─ v16/
   ├─ Siemens.Engineering.dll (16版本主程序集)
   └─ 所有16版本对应的关联依赖(含Siemens.Engineering.Contract.dll)

注意:每个版本目录必须存放该版本对应的全部依赖文件,禁止跨目录复用依赖。

3. 调整编译引用配置

保持extern alias的核心编译逻辑不变,按如下要求调整项目引用属性:

  • 将15版本Siemens.Engineering.dll引用的Aliases属性设置为Siemens_Engineering_15,取消默认的global别名
  • 将16版本Siemens.Engineering.dll引用的Aliases属性设置为Siemens_Engineering_16,取消默认的global别名
  • 两个引用的Copy Local(即项目文件中的Private)属性设置为False,避免编译时自动拷贝程序集到根目录造成文件冲突
  • 两个引用的SpecificVersion属性设置为True,让MSBuild基于完整版本号区分引用边界,消除编译阶段的版本冲突警告。

对应的csproj引用配置参考:

<Reference Include="Siemens.Engineering">
  <HintPath>Lib\Siemens.Engineering.15.dll</HintPath>
  <Aliases>Siemens_Engineering_15</Aliases>
  <Private>False</Private>
  <SpecificVersion>True</SpecificVersion>
</Reference>
<Reference Include="Siemens.Engineering">
  <HintPath>Lib\Siemens.Engineering.16.dll</HintPath>
  <Aliases>Siemens_Engineering_16</Aliases>
  <Private>False</Private>
  <SpecificVersion>True</SpecificVersion>
</Reference>

4. 挂载自定义程序集解析逻辑

在程序启动入口(Main方法最开始)挂接AppDomain.CurrentDomain.AssemblyResolve事件,绕开CLR默认的单绑定规则,根据请求加载的程序集版本,从对应子目录加载匹配的文件:

using System;
using System.Reflection;
using System.IO;

internal class Program
{
    private static void Main(string[] args)
    {
        // 注册自定义解析事件,必须在所有业务代码执行前挂载
        AppDomain.CurrentDomain.AssemblyResolve += OnCustomAssemblyResolve;

        // 正常使用extern alias声明对应版本的实例即可
        var tiaInstance15 = new Siemens_Engineering_15::Siemens.Engineering.TiaPortal(...);
        var project15 = new Siemens_Engineering_15::Siemens.Engineering.Project(...);
        var tiaInstance16 = new Siemens_Engineering_16::Siemens.Engineering.TiaPortal(...);
    }

    private static Assembly OnCustomAssemblyResolve(object sender, ResolveEventArgs args)
    {
        var requestedName = new AssemblyName(args.Name);
        // 匹配所有Siemens.Engineering前缀的程序集(含主程序集、Contract依赖等)
        if (requestedName.Name.StartsWith("Siemens.Engineering"))
        {
            // 按程序集主版本号匹配对应存放目录
            string targetDir = requestedName.Version.Major switch
            {
                15 => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "v15"),
                16 => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "v16"),
                _ => null
            };

            if (targetDir == null) return null;
            string targetFilePath = Path.Combine(targetDir, $"{requestedName.Name}.dll");
            return File.Exists(targetFilePath) ? Assembly.LoadFrom(targetFilePath) : null;
        }
        return null;
    }
}

关键注意事项

  • 自定义解析逻辑必须覆盖所有Siemens.Engineering相关的依赖程序集,不能仅解析主程序集,否则仍会触发TypeLoadException
  • 不要尝试通过篡改PublicKeyToken的方式让两个版本成为独立标识的程序集,该操作会触发强名签名校验失败,也不符合西门子TIA库的授权规则
  • 若两个版本的库依赖非托管本地组件,需要将对应版本的非托管文件同步存放到版本子目录,在初始化对应版本的实例前,通过SetDllDirectoryAPI切换非托管dll的搜索路径,避免加载错版本的本地组件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 12:54:09