dotnet 9 通过 AppHostRelativeDotNet 指定自定义的运行时路径 ...

打印 上一主题 下一主题

主题 1892|帖子 1892|积分 5678

进行框架依靠发布的时间,应用程序需要有 dotnet runtime 运行时才华跑起来。在 dotnet 9 之前,通常都是需要安装到系统的 Program File 文件夹下的全局 dotnet 运行时的支持。在 dotnet 9 时,引入了 AppHostRelativeDotNet 机制,允许开发者自定义依靠框架发布的应用使用的 dotnet 运行时路径
在 2022 时,我写了一个提案,允许应用程序自定义使用的 dotnet 运行时文件夹路径。详细请看 https://github.com/dotnet/runtime/issues/64430
这个提案的背景是我有很多个应用准备发布给到用户端上,如果这么多应用都走独立发布,天然会让用户的 C 盘布满重复的文件。如果是将 dotnet 运行时交给的是 Program File 文件夹下的全局文件夹,则可能会遇到各种被投毒题目,比如某次系统更新之后,应用程序就因为 .NET 环境损害而无法启动
我所在的团队那会也在迁徙一个大型的 .NET Framework 项目到 .NET 6 上,原本的项目会有多 exe 入口题目,这部分设计也改不动。多入口情况下也不得当每个入口都做独立发布,只管独立发布的重复 BCL 等文件能够在安装包内里被压缩,但是在安装到用户装备上时,解压缩出来的内容依然会撑满用户的 C 盘
为此,我所在的团队就制作和开源了 https://github.com/dotnet-campus/dotnetCampus.AppHost 项目,细节原理请参阅 如何让 .NET 程序脱离系统安装的 .NET 运行时独立运行?除了 Self-Contained 之外还有更好方法!谈 dotnetCampus.AppHost 的工作原理 - walterlv
关于我所在的团队迁徙大型项目标经验请参阅 记将一个大型客户端应用项目迁徙到 dotnet 6 的经验和决策
我那会预期的情况是如许的,我在自己控制的路径下,如 C:\Program Files\CompanyName 文件夹下,放入了自己的 DotNETRuntime[Version] 文件夹。然后再依次部署上多个应用程序,这些应用程序都是采用依靠框架(Publish framework-dependent)方式发布,总的文件夹布局情况如下
  1. C:\Program Files\CompanyName\DotNETRuntime[Version]\
  2. C:\Program Files\CompanyName\Produce1\
  3. C:\Program Files\CompanyName\Produce2\
  4. C:\Program Files\CompanyName\Produce3\
复制代码
云云即可让 Produce1 Produce2 Produce3 三个产品共用一个 dotnet 运行时
我的这个提案被 dotnet 官方采纳了,加入到 .NET Host 提升计划内里,详细请看 https://github.com/dotnet/runtime/issues/97931 。颠末了三年(现实上绝大部分时间都在讨论)的开发,终于在 dotnet 9 支持了这个功能,能够完全实现我预期的功能
此项功能被命名为 Embedded install location options for apphost ,被我翻译为嵌入 dotnet 安装路径到 AppHost 里的功能,我也对外宣称这是为依靠框架的应用自定义 .NET Runtime 文件夹路径的功能
接下来我将和大家先容此功能的用法和结果
此功能涉及到的关键属性分别如下:

  • AppHostDotNetSearch : 决定从哪里开始寻找,可选参数为 AppLocal、 AppRelative、 EnvironmentVariables 和 Global,可认为在此之前就是 Global 的值。允许设置多个参数,多个参数之间依然用 ; 分号隔开。在本文内里,焦点功能将由 AppRelative 参数实现
  • AppHostRelativeDotNet : 配置相对于 exe 的路径,这个路径将被作为 dotnet 运行时的查找路径
默认情况下,可只需设置 AppHostRelativeDotNet 属性即可。当 AppHostRelativeDotNet 属性被设置的时间,隐式设置了 AppHostDotNetSearch 属性为 AppRelative 的值。但通常来讲,可以将 AppHostDotNetSearch 属性设置为 AppHostDotNetSearch=AppRelative;Global 的值,这就意味着如果从相对路径没有找到 dotnet 运行时,将自动回滚到从 Global 全局进行查找。这里的 Global 全局即 C:\Program Files\dotnet\ 或 C:\Program Files (x86)\dotnet\ 文件夹
为了演示此功能的用法,我创建了一个名为 LinerewheldeholearjearHalllurlecayawfea 的控制台项目,控制台项目使用的是 .NET 9 默认控制台模版代码。编辑 csproj 项目文件,添加 AppHostDotNetSearch 和 AppHostRelativeDotNet 属性,修改之后的 csproj 项目文件代码大概如下。本文内容内里只给出关键代码片段,如需要全部的项目文件,可到本文末端找到本文所有代码的下载方法
  1. <Project Sdk="Microsoft.NET.Sdk">
  2.   <PropertyGroup>
  3.     <OutputType>Exe</OutputType>
  4.     <TargetFramework>net9.0</TargetFramework>
  5.     <ImplicitUsings>enable</ImplicitUsings>
  6.     <Nullable>enable</Nullable>
  7.     <AppHostDotNetSearch>AppRelative;Global;</AppHostDotNetSearch>
  8.     <AppHostRelativeDotNet>../relative/path/to/runtime</AppHostRelativeDotNet>
  9.   </PropertyGroup>
  10. </Project>
复制代码
可以看到我在 AppHostRelativeDotNet 写入的是相对于 exe 的上一层文件夹空间的 relative/path/to/runtime 路径
准备工作就此完成,接下来就是设置进行框架依靠发布。这里需要特殊说明的是 .NET Core (包含 .NET 5 和更高版本)的输出 exe 是不能实现 .NET Framework 的 AnyCpu 邪术的,在使用自定义 dotnet 运行时路径时,需要根据自己的需求,明确指定其版本。这里也需要额外说明的是,只管本文内容都在 Windows 下测试,但究竟上本文先容的 dotnet 这项新功能是可以在全平台使用的,即在 Linux 或 mac 上也实用

我的发布配置文件 FolderProfile.pubxml 代码如下
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Project>
  3.   <PropertyGroup>
  4.     <Configuration>Release</Configuration>
  5.     <Platform>Any CPU</Platform>
  6.     <PublishDir>bin\Release\net9.0\publish\win-x86\</PublishDir>
  7.     <PublishProtocol>FileSystem</PublishProtocol>
  8.     <_TargetId>Folder</_TargetId>
  9.     <TargetFramework>net9.0</TargetFramework>
  10.     <RuntimeIdentifier>win-x86</RuntimeIdentifier>
  11.     <SelfContained>false</SelfContained>
  12.     <PublishSingleFile>false</PublishSingleFile>
  13.     <PublishReadyToRun>false</PublishReadyToRun>
  14.   </PropertyGroup>
  15. </Project>
复制代码
配置完成之后,直接进行发布,此时可以看到发布创建的文件只有几个。有了这项技术就不怕发布大量工具了,有了这项技术就可以让发布的 .NET Core(包含.NET 5及更高版本)应用也和 .NET Framework 应用一样小体积占用

发布完成之后,可不能和进行独立发布(Self-Contained)一样,直接就将此分发给到用户了,咱还需要对此进行包装文件夹布局
刚才在 AppHostRelativeDotNet 写的是相对于 ../relative/path/to/runtime 文件夹,嗯,这里只能写相对文件夹路径,不能写绝对文件夹路径。那咱就需要将发布输出的文件包装为里一层文件夹,我这里选择将其放入到名为 App1 的文件夹内里,如许我如果有第二个应用,就可以放入到 App2 文件夹内里
再接着将 App1 文件夹放入到名为 App 的文件夹内里。再在 App 文件夹内里的 relative/path/to/runtime 文件夹内里放入 dotnet 运行时。云云就完成了包装文件夹布局,此时直接双击 App\App1\LinerewheldeholearjearHalllurlecayawfea.exe 就能运行了
包装完成的文件夹布局情况如下
  1. C:\LINDEXI\APP
  2. |   
  3. +---App1
  4. |       LinerewheldeholearjearHalllurlecayawfea.deps.json
  5. |       LinerewheldeholearjearHalllurlecayawfea.dll
  6. |       LinerewheldeholearjearHalllurlecayawfea.exe
  7. |       LinerewheldeholearjearHalllurlecayawfea.pdb
  8. |       LinerewheldeholearjearHalllurlecayawfea.runtimeconfig.json
  9. |      
  10. \---relative
  11.     \---path
  12.         \---to
  13.             \---runtime
  14.                 |   dotnet.exe
  15.                 |   LICENSE.txt
  16.                 |   ThirdPartyNotices.txt
  17.                 |   
  18.                 +---host
  19.                 |   \---fxr
  20.                 |       \---9.0.4
  21.                 |               hostfxr.dll
  22.                 |               
  23.                 \---shared
  24.                     \---Microsoft.NETCore.App
  25.                         \---9.0.4
  26.                                 .version
  27.                                 clretwrc.dll
  28.                                 clrgc.dll
  29.                                 clrjit.dll
  30.                                 coreclr.dll
  31.                                 createdump.exe
  32.                                 hostpolicy.dll
  33.                                 Microsoft.CSharp.dll
  34.                                 Microsoft.DiaSymReader.Native.x86.dll
  35.                                 Microsoft.NETCore.App.deps.json
  36.                                 Microsoft.NETCore.App.runtimeconfig.json
  37.                                 Microsoft.VisualBasic.Core.dll
  38.                                 Microsoft.VisualBasic.dll
  39.                                 Microsoft.Win32.Primitives.dll
  40.                                 Microsoft.Win32.Registry.dll
  41.                                 ...
  42.                                 System.Xml.XPath.dll
  43.                                 System.Xml.XPath.XDocument.dll
  44.                                 WindowsBase.dll
复制代码
大概同伴们有一个题目,那就是这里的 .NET Runtime 运行时文件夹组织是哪里来的,文件是从哪里来的。这是从 dotnet 官方下载的,下载链接是: https://dotnet.microsoft.com/zh-cn/download/dotnet/9.0
下载右边“运行应用 - 运行时”这一列的内容

运行时这一列有很多选项,详细应该下哪一个呢?这就看自己的需求了。如我只是一个简单的控制台,且准备发布的是 x86 应用,那我就应该下载 x86 二进制文件,就是如许的对应关系,先取决于要用什么框架,再决定用什么平台

下载下来的是一个 zip 压缩包,打开压缩包就可以看到这就是上文提到的 relative/path/to/runtime 文件夹内的布局,按照本文提供的方式将其解压缩就好了

额外需要说明的是,现在对于桌面应用来说是没有提供二进制包,只有安装包。即对于 WPF 和 WinForms 来说,现在只有安装包可用。那咋办呢?很简单,只需要找一个干净的系统(如假造机内),下载安装包且安装。安装完成之后,即可在 C:\Program Files\dotnet\ 或 C:\Program Files (x86)\dotnet\ 文件夹内找到安装输出的文件,将其拷贝出来放入到 relative/path/to/runtime 文件夹内即可
通过此项技术,即可让多个应用共用一个私有分发的 .NET 运行时。也可以作为单应用多 exe 入口程序的共享运行时技术实现。这项技术对于小工具项目特殊友爱,避免小工具项目要么各自带着运行时独立发布,要么被第三方或系统投毒运行时的选择
既然这可以使用私有分发的 .NET 运行时,那对于一些动手能力强的开发者来说,也可以在这内里带上自己魔改之后的 .NET 版本,实现更多风趣的功能
本文代码放在 githubgitee 上,可以使用如下命令行拉取代码。我整个代码堆栈比较巨大,使用以下命令行可以进行部分拉取,拉取速率比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行内里输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin 1e09f77a553d664872cb12324a118649ffd23ad9
复制代码
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin 1e09f77a553d664872cb12324a118649ffd23ad9
复制代码
获取代码之后,进入 Workbench/LinerewheldeholearjearHalllurlecayawfea 文件夹,即可获取到源代码
参考文档:
更多技术博客,请参阅 博客导航

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表