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

.NET 8 WPF升级:Azure DevOps流水线部署后MainWindow XAML加载失败

问题背景

正在将基于.NET Framework 4.8.1的Prism模块化WPF应用升级到.NET 8,各模块在Azure DevOps分库管理:

  • 第一个模块(含控件)本地及流水线构建运行均正常;
  • 第二个模块(含MainWindow,依赖第一个)本地及流水线构建运行均正常;
  • 第三个模块依赖前两个,本地构建正常,但使用流水线生成的NuGet包时运行失败:加载MainWindow的XAML时触发FileNotFoundException,替换为本地构建的第二个模块则恢复正常。

对比两个版本的第二个模块,发现MainWindow资源一致,仅流水线版本的资源URI带有版本号。怀疑是流水线配置问题,但无法查看其配置,请问Azure DevOps流水线是否存在会导致DLL或其资源不可信的配置?

报错信息

System.IO.FileNotFoundException: 无法加载文件或程序集“{2nd}, Version={x.y.z.aabcd}, Culture=neutral, PublicKeyToken=null”。系统找不到指定的文件。

调用栈

[托管到本地转换]  
    System.Private.CoreLib.dll!System.Reflection.RuntimeAssembly.InternalLoad(System.Reflection.AssemblyName assemblyName, ref System.Threading.StackCrawlMark stackMark, System.Runtime.Loader.AssemblyLoadContext assemblyLoadContext, System.Reflection.RuntimeAssembly requestingAssembly, bool throwOnFileNotFound)    未知
    System.Private.CoreLib.dll!System.Reflection.Assembly.Load(System.Reflection.AssemblyName assemblyRef)  未知
    PresentationCore.dll!System.Windows.Navigation.BaseUriHelper.GetLoadedAssembly(string assemblyName, string assemblyVersion, string assemblyKey) 未知
    PresentationFramework.dll!MS.Internal.AppModel.ResourceContainer.GetResourceManagerWrapper(System.Uri uri, out string partName = "views/mainwindow.xaml", out bool isContentFile = false)   未知
    PresentationFramework.dll!MS.Internal.AppModel.ResourceContainer.GetPartCore(System.Uri uri = {System.IO.Packaging.PackUriHelper.ValidatedPartUri}) 未知
    System.IO.Packaging.dll!System.IO.Packaging.Package.GetPartHelper(System.Uri partUri)   未知
    System.IO.Packaging.dll!System.IO.Packaging.Package.GetPart(System.Uri partUri) 未知
    PresentationFramework.dll!System.Windows.Application.GetResourceOrContentPart(System.Uri uri)   未知
    PresentationFramework.dll!System.Windows.Application.LoadComponent(object component = {{MyFailingModule}.Views.MainWindow}, System.Uri resourceLocator = {System.Uri})  未知
    {MyFailingModule}.dll!{MyFailingModule}.Views.MainWindow.InitializeComponent()  未知
    {MyFailingModule}.dll!{MyFailingModule}.Views.MainWindow.MainWindow()   未知
    [本地到托管转换]  
    [本地到托管转换]  
    System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(object obj, System.Reflection.BindingFlags invokeAttr = Default)    未知
    Unity.Container.dll!Unity.Processors.ConstructorDiagnostic.GetResolverDelegate.AnonymousMethod__0(ref Unity.Builder.BuilderContext c = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null)   未知
    Unity.Container.dll!Unity.Processors.MemberProcessor<System.Reflection.FieldInfo, object>.GetResolver.AnonymousMethod__0(ref Unity.Builder.BuilderContext c = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null)    未知
    Unity.Container.dll!Unity.Processors.MemberProcessor<System.Reflection.PropertyInfo, object>.GetResolver.AnonymousMethod__0(ref Unity.Builder.BuilderContext c = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null) 未知
    Unity.Container.dll!Unity.Processors.MemberProcessor<System.Reflection.MethodInfo, object[]>.GetResolver.AnonymousMethod__0(ref Unity.Builder.BuilderContext c = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null) 未知
    Unity.Container.dll!Unity.UnityContainer.OptimizingFactory.AnonymousMethod__0(ref Unity.Builder.BuilderContext c = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null)   未知
    Unity.Container.dll!Unity.Strategies.BuildPlanStrategy.PreBuildUp(ref Unity.Builder.BuilderContext context = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null) 未知
    Unity.Container.dll!Unity.UnityContainer.ExecuteValidatingPlan(ref Unity.Builder.BuilderContext context = 正在解析: {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"},  Name: null)    未知
    Unity.Container.dll!Unity.UnityContainer.Unity.IUnityContainer.Resolve(System.Type type = {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"}, string name = null, Unity.Resolution.ResolverOverride[] overrides = {Unity.Resolution.DependencyOverride[0]})  未知
    Unity.Abstractions.dll!Unity.UnityContainerExtensions.Resolve(Unity.IUnityContainer container = "Container[43]", System.Type t = {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"}, Unity.Resolution.ResolverOverride[] overrides = {Unity.Resolution.DependencyOverride[0]})   未知
    Prism.Unity.Wpf.dll!Prism.Unity.UnityContainerExtension.Resolve(System.Type type = {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"}, (System.Type Type, object Instance)[] parameters = {(System.Type, object)[0]})    未知
    Prism.Unity.Wpf.dll!Prism.Unity.UnityContainerExtension.Resolve(System.Type type = {Name = "MainWindow" FullName = "{MyFailingModule}.Views.MainWindow"})   未知
    Prism.dll!Prism.Ioc.IContainerProviderExtensions.Resolve<{MyFailingModule}.Views.MainWindow>(Prism.Ioc.IContainerProvider provider = {Prism.Unity.UnityContainerExtension}) 未知
    {MyFailingModule}.dll!{MyFailingModule}.Bootstrapper.CreateShell()  未知
    Prism.Wpf.dll!Prism.PrismBootstrapperBase.Initialize()  未知
    Prism.Wpf.dll!Prism.PrismBootstrapperBase.Run() 未知
>   {MyApp}.dll!{MyApp}.App.OnStartup.AnonymousMethod__1_0() Line 35    C#
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  未知
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler = null)  未知
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()   未知
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)   未知
    System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)   未知
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext = {MS.Internal.CulturePreservingExecutionContext}, System.Threading.ContextCallback callback, object state)    未知
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()   未知
    WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()  未知
    WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) 未知
    WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(nint hwnd = 0x0000000000070e22, int msg, nint wParam = 0x0000000000000000, nint lParam = 0x0000000000000000, ref bool handled = false) 未知
    WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) 未知
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  未知
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler = null)  未知
    WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)   未知
    WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(nint hwnd = 0x0000000000070e22, int msg, nint wParam = 0x0000000000000000, nint lParam = 0x0000000000000000)  未知
    [本地到托管转换]  
    [本地到托管转换]  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame})  未知
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)   未知
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)  未知
    {MyApp}.dll!{MyApp}.App.Main() Line 52  C#
分析与解决方案

1. NuGet包版本化与引用绑定问题

流水线可能启用了自动版本号生成(如使用$(Build.BuildNumber)作为NuGet版本号),导致第二个模块的版本号每次构建都变化。而第三个模块引用时未正确处理版本绑定,或XAML资源引用中硬编码了带版本号的URI,导致运行时无法匹配加载正确版本的程序集。

解决思路:

  • 检查第三个模块的NuGet引用是否设置了浮动版本(如[x.y.z, x.y.z+)),或在app.config/appsettings.json中添加程序集绑定重定向,将特定版本的第二个模块重定向到实际加载的版本;
  • 确保XAML中资源引用使用相对URI而非带版本的绝对Pack URI,避免版本号变化导致引用失效。

2. 流水线构建的程序集签名/强命名配置

如果流水线中开启了强命名签名,而本地构建未启用,会导致流水线生成的程序集元数据与本地版本不一致,影响加载。即使报错显示PublicKeyToken=null,也可能存在签名过程中的异常导致程序集身份不匹配。

解决思路:

  • 确认流水线是否配置了SignFile任务或在项目文件中启用了<SignAssembly>true</SignAssembly>,若本地未同步此配置,需统一签名设置;
  • 若不需要强命名,可在流水线中禁用签名配置,保持与本地构建一致。

3. NuGet包打包时的资源嵌入问题

流水线打包NuGet包时,可能未正确嵌入XAML资源或相关依赖项,导致运行时资源无法被找到。比如:

  • 打包任务未包含所有必要的输出文件(如.resources.dll或XAML编译后的二进制资源);
  • 使用了dotnet pack的--no-build参数,导致资源未被正确更新到包中。

解决思路:

  • 检查流水线的dotnet pack或NuGetPack任务配置,确保包含项目的所有输出文件,尤其是资源文件;
  • 确保打包前执行了完整的dotnet build,且构建配置(Debug/Release)与本地一致。

4. 程序集加载上下文的差异

.NET 8中程序集加载上下文(AssemblyLoadContext)与.NET Framework有差异,流水线构建的包可能在部署时被放置在不同目录,导致加载器无法找到依赖。此外,若流水线使用了发布单文件或裁剪优化,可能会剥离必要的依赖或资源。

解决思路:

  • 检查流水线是否启用了单文件发布或裁剪功能,若启用需确保依赖项被正确包含;
  • 确认第三个模块的输出目录中存在第二个模块的DLL,且版本与引用匹配。

5. 代码签名与信任问题

如果流水线对程序集进行了代码签名(如使用Authenticode签名),而运行环境未信任该签名证书,可能导致程序集被加载器阻止。此情况通常触发FileLoadException,但仍需排查。

解决思路:

  • 确认流水线是否配置了代码签名任务,若有需确保运行环境信任该签名证书;
  • 临时禁用签名测试是否解决问题。

内容的提问来源于stack exchange,提问作者Andrei Iulian Martoiu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:44:53