.NET Core项目推荐实现方式参考文档:
.NET Core 3.1 和 .NET 5 控制台程序发布成独立.exe可以执行程序的方法
1、安装引用Fody和Costura.Fody
1)使用Nuget界面管理器
直接分别搜索 "Fody" 和 “Costura.Fody”,找到对应的点安装即可。
相关文档:VS(Visual Studio)中Nuget的使用
2)使用Package Manager命令安装
PM> Install-Package Fody PM> Install-Package Costura.Fody
注意:如果安装最新版报编译器版本错误,可以降低 Fody 和 Costura.Fody版本,如Fody为4.2.1
,Costura.Fody为3.3.3
。
2、FodyWeavers.xml配置文件
FodyWeavers.xml是配置文件,通过修改CosturaFodyWeavers.xml 中的节点来访问所有配置选项,默认配置如下,
<Weavers> <Costura/> </Weavers>
1)CreateTemporaryAssemblies
将嵌入的文件复制到磁盘,然后再将它们加载到内存中。对于要从物理文件加载程序集的某些场景很有用。默认为false
。
<Costura CreateTemporaryAssemblies='true' />
2)IncludeDebugSymbols
配置是否还嵌入引用程序集的.pdb文件,默认为true。
<Costura IncludeDebugSymbols='false' />
3)IncludeRuntimeReferences
配置.NET Core所使用的内嵌依赖项的运行时文件夹是否被内嵌。默认为true。
<Costura IncludeRuntimeReferences='false' />
4)UseRuntimeReferencePaths
配置运行时程序集是嵌入其完整路径还是只嵌入其程序集名称。.NET Framework项目默认为false
,.NET Core项目默认为true
。
<Costura UseRuntimeReferencePaths='true' />
5)DisableCompression
默认情况下,嵌入式程序集是压缩的,加载时则不压缩。可以使用此选项关闭压缩。
<Costura DisableCompression='true' />
6)DisableCleanup
作为Costura的一部分,嵌入式组件不再包含在构建中。这种Cleanup可以关闭。默认为false
。
<Costura DisableCleanup='true' />
7)LoadAtModuleInit
默认情况下,Costura将作为模块初始化的一部分加载。该选项禁用该行为。确保在代码的某个地方调用了CosturaUtility.Initialize()
。默认为true
。
<Costura LoadAtModuleInit='false' />
8)IgnoreSatelliteAssemblies
默认情况下,Costura将使用名为'resources.dll'的程序集作为附属资源,并将输出路径放在前面。该配置项禁用该行为。
DLL项目程序集名称以'.resources'结尾,当设置为false时,*.resources.dll
将导致错误。默认为false
,配置如下,
<Costura IgnoreSatelliteAssemblies='true' />
9)ExcludeAssemblies / ExcludeRuntimeAssemblies
要从默认操作 "嵌入所有复制本地引用" 中排除的程序集名称列表。
不要在名称中包含.exe
或.dll
。
不能用IncludeAssemblies定义。
可以对部分程序集名称匹配使用通配符。例如,System.*
将排除所有以System.
开头的程序集。通配符只能在条目的末尾使用,因此,例如,System.*.Private.*
将不起作用。
可以使用多行配置:
<Costura> <ExcludeAssemblies> Foo Bar </ExcludeAssemblies> </Costura>
或者配置在一行使用|分隔,如下,
<Costura ExcludeAssemblies='Foo|Bar' />
10)IncludeAssemblies / IncludeRuntimeAssemblies
从默认操作“嵌入所有复制本地引用”中包含的程序集名称列表。
不要在名称中包含.exe
或.dll
。
不能用ExcludeAssemblies
/ incluuntimeassemblies
定义。
可以在名称的末尾使用通配符进行部分匹配。
可以使用多行配置:
<Costura> <IncludeAssemblies> Foo Bar </IncludeAssemblies> </Costura>
或者配置在一行使用|分隔,如下,
<Costura IncludeAssemblies='Foo|Bar' />
10)Unmanaged32Assemblies 和 Unmanaged64Assemblies
不能以与托管程序集相同的方式加载非托管程序集。
为了帮助Costura识别哪些程序集是Mixed-mode,以及在什么环境中加载它们,应该将它们的名称包含在一个或两个列表中。
不要在名称中包含.exe
或.dll
。
可以在名称的末尾使用通配符进行部分匹配。
可以使用多行配置:
<Costura> <Unmanaged32Assemblies> Foo32 Bar32 </Unmanaged32Assemblies> <Unmanaged64Assemblies> Foo64 Bar64 </Unmanaged64Assemblies> </Costura>
或者配置在一行使用|分隔,如下,
<Costura Unmanaged32Assemblies='Foo32|Bar32' Unmanaged64Assemblies='Foo64|Bar64' />
3、配置预加载程序集顺序
Costura可以自动加载本地库。要包含本机库,请将其作为嵌入式资源包含在项目中称为costura32
或costura64
的文件夹中,这取决于库的bittyness。
还可以选择指定预加载库的加载顺序。当从磁盘混合模式使用临时程序集时,程序集也会被预加载。
要指定预加载程序集的顺序,请向配置中添加PreloadOrder
元素。
可以使用多行配置:
<Costura> <PreloadOrder> Foo Bar </PreloadOrder> </Costura>
或者配置在一行使用|分隔,如下,
<Costura PreloadOrder='Foo|Bar' />
4、CosturaUtility
CosturaUtility
是一个类,它允许在自己的代码中手动初始化Costura系统。主要用于模块初始化器不起作用的场景,比如库和Mono。
要使用,请在代码中尽可能早地调用CosturaUtility.Initialize()
。例如,
class Program { static Program() { CosturaUtility.Initialize(); } static void Main(string[] args) { ... } }
5、单元测试框架
大多数单元测试框架需要.dll
文件来发现和执行单元测试。可能需要将如下配置添加到测试组件中。
<Weavers> <Costura ExcludeAssemblies='TargetExe|TargetExeTest' CreateTemporaryAssemblies='true' DisableCleanup='true'/> </Weavers>
6、读取Costura.Fody内嵌压缩的程序集
合成之后被压缩的程序集需要读取,可以参考如下代码,
using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Text.RegularExpressions; namespace ConsoleApp1 { class Program { static void CopyTo(Stream source, Stream destination) { int count; var array = new byte[81920]; while ((count = source.Read(array, 0, array.Length)) != 0) { destination.Write(array, 0, count); } } static Stream LoadStream(string fullname) { FileStream stream = default(FileStream); if (fullname.EndsWith(".zip")) { using (stream = new FileStream(fullname, FileMode.Open)) { using (var compressStream = new DeflateStream(stream, CompressionMode.Decompress)) { var memStream = new MemoryStream(); CopyTo(compressStream, memStream); memStream.Position = 0; return memStream; } } } return stream; } static void Main(string[] args) { Stream stream; Stream file; string RefilePath = @"^.+[^\\]+\\"; string fullname; string newFile; for (int i = 0; i < args.Count(); i++) { fullname = args[i]; newFile = Regex.Replace(fullname, "\\.zip$", string.Empty); Console.Write("{0} -> {1}\r\n", Regex.Replace(fullname, RefilePath, string.Empty), Regex.Replace(newFile, RefilePath, string.Empty)); try { stream = LoadStream(fullname); using (file = File.Create(newFile)) { CopyTo(stream, file); } } catch (Exception ex) { Console.Write("{0}", ex.ToString()); } } } } }
注意:如果其它静态的资源文件,可以在VS中文件属性"生成操作" 选择 "Resource",然后在读取资源文件。