返回

解决 .NET 8 应用缺少 PresentationFramework 无法运行问题

windows

解决 .NET 8 应用因缺少 PresentationFramework 程序集而无法运行的问题

背景介绍

一些 .NET 8 应用程序的用户可能会遭遇应用无法启动的情况,并伴随 System.IO.FileLoadException 异常,提示找不到 PresentationFramework 程序集或其版本不匹配。即使该程序集未被应用直接引用,问题仍然存在。 这个问题在部分设备出现,复现比较困难。开发环境中可能运行正常,用户的开发环境中即使安装了相同版本的 .NET SDK 也有可能会报错,

问题分析

这个问题可能由多个方面因素导致,主要集中在 .NET 运行时环境配置、依赖项解析以及程序集加载机制。
直接引用中并没有PresentationFramework, 有可能是MahApps.Metro的依赖导致的。

1. 依赖库的间接引用

尽管项目代码中并未直接引用 PresentationFramework,但项目依赖项如 MahApps.Metro 可能会依赖此程序集。即使未明确列出,MahApps.Metro 可能在构建或运行时引入 PresentationFramework 的特定版本,从而产生对 PresentationFramework 的需求。

2. 目标框架与运行时环境

项目文件将目标框架设定为 net6.0-windows7.0。构建时使用指定的 .NET SDK 版本构建,在运行时环境安装的也是 .NET 8的 SDK 版本。
该项目的 global.json 文件中声明 sdk版本为 8.0.401, 这表示应用应当使用该版本 .NET SDK 构建。项目目标框架和构建使用的 .NET SDK 保持一致,这应该是正确配置,不太可能是此处的问题。但如果项目和特定依赖项兼容的版本要求不匹配。需要检查兼容性,有必要进行升级。

3. 全局程序集缓存 (GAC)

GAC 是一个由 .NET Framework 使用的计算机范围内的程序集缓存。安装在 GAC 中的程序集可以在机器上的不同 .NET 应用程序之间共享。用户的系统显示 PresentationFramework 的多个版本都已注册。
开发机器上只有一个4.0.0版本。
从理论上说,应用程序不应从 GAC 中加载 PresentationFramework 程序集,因为项目被设定为面向 .NET 8,且 PresentationFramework 在较新版本的 .NET SDK 中已经从标准库中移除。
但 GAC 中版本的不匹配有可能引发问题,尤其是某些库依赖于特定的 .NET Framework 版本。

4. 构建过程

开发构建过程中使用 Debug 或 Release 模式,这些模式可以引入不同的构建路径或依赖处理行为,也可能会影响运行时。

解决方案

1. 清理和重新构建

清除所有构建中间产物,如 binobj 文件夹,然后重新构建整个项目。有时,这些目录中的残留文件会导致不一致行为。

步骤:

  1. 关闭所有相关的 IDE 实例。
  2. 手动删除项目目录下的 binobj 文件夹。
  3. 使用命令行 dotnet clean 清理解决方案或项目。
  4. 使用命令行 dotnet build 重新构建。

命令行指令:

dotnet clean
dotnet build

2. 显式引用 PresentationFramework

虽然不应直接引用此程序集,但在项目中显式添加特定版本的 PresentationFramework NuGet 包引用可能会覆盖隐式解析,从而帮助解决版本不匹配问题。需要明确使用的是哪个版本,是应用本身支持的版本。

步骤:

  1. 通过 NuGet 包管理器将 Microsoft.WindowsDesktop.App.WPF 添加到项目中。请确保选择与用户的 .NET 运行时环境匹配的版本。或选择应用本身以及依赖支持的版本,如果开发环境构建出的应用正常运行,可以考虑与开发环境使用同样的版本进行配置。
  2. 清理并重新构建项目。

操作说明:

  • 打开项目,在解决方案资源管理器中右键单击“依赖项”,选择“管理 NuGet 程序包”。
  • 在“浏览”选项卡中搜索 “Microsoft.WindowsDesktop.App.WPF” 。
  • 选择正确版本安装。

3. 检查并更新依赖项

项目依赖库可能和当前的运行时环境有冲突,检查是否有更新的版本,可以兼容 .NET 8,避免使用不匹配的依赖库版本。

步骤:

  1. 打开xxx.csproj 文件.
  2. 逐个检查每个PackageReference,到 NuGet 仓库或 GitHub 主页寻找其发布版本以及依赖说明。
  3. 选择升级有必要的库。

代码示例:
假设升级MahApps.Metro3.0.0

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0-windows7.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <!-- ... 其他引用 -->
        <PackageReference Include="MahApps.Metro" Version="3.0.0" />
        <!-- ... 其他引用 -->
    </ItemGroup>

</Project>

4. 审查项目设置和运行时配置

MahApps.Metro等第三方包如果存在冲突的话,有必要修改目标运行时环境的版本。尝试更改成对应的 .NET 6版本。
使用 net6.0-windows 替换当前 TargetFramework 避免引用更高版本.NET依赖导致不一致。
确保本地 global.json 文件与 CI/CD 环境使用的版本匹配(如果存在),并且目标运行时在构建过程的所有阶段正确使用。

步骤:

  1. 修改xxx.csproj中的 <TargetFramework>
  2. (可选)修改 global.json 中 .NET 版本号以匹配更改。

代码示例:

xxx.csproj:

<TargetFramework>net6.0-windows</TargetFramework>

global.json:

{
  "sdk": {
    "version": "6.0.401"
  }
}

5. 部署方式

尝试改变构建部署的流程和模式,来减少环境问题。如果之前使用的框架依赖型发布,那么这次可以使用独立发布(Self-Contained Deployment),
xxx.csproj 添加 RuntimeIdentifier 配置构建独立的二进制部署。这样的话部署可以包括运行时环境,减少运行时找不到库的可能。

步骤:

  1. 编辑项目文件 xxx.csproj ,添加或修改 RuntimeIdentifier 属性。
  2. 使用 -r 参数与特定运行时标识符一起发布。

代码示例:

<PropertyGroup>
  <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

命令行指令:

dotnet publish -c Release -r win-x64 --self-contained true

这样部署,二进制文件体积会更大。

安全建议:

  • 始终验证下载的程序包和依赖项的完整性和来源。使用受信任的 NuGet 源,并对重要库启用代码签名。
  • 确保用于构建的环境隔离并免受污染。理想情况下,应使用干净、经过管理的构建代理,并始终清除构建缓存。

解决因缺少 PresentationFramework 程序集而导致的 .NET 应用启动问题需要谨慎分析依赖关系和目标框架,了解构建与部署的不同阶段的行为。 通过系统地审查上述各个方面,进行必要调试后通常能准确定位到具体问题,完成修正。 修正完重新构建测试可以正常运行,用户使用更新后的构建也能消除该错误,应用能正常加载。