开发程序的时候常常会引用一些第三方的DLL,而后编译生成的exe文件就不能脱离这些DLL独立运行了。git
可是,不少时候咱们本想开发一款只须要一个exe就能完美运行的小工具。那该怎么办呢?github
下文介绍一种超简单的方法,不用写一行代码就可轻松实现。框架
这里咱们须要用到一款名为Fody.Costura的工具。Fody.Costura是一个Fody框架下的插件,可经过Nuget安装到VS工程中。安装以后,就能够将项目所依赖的DLL(甚至PDB)文件所有打包到EXE文件里。函数
构建完成后,到项目的输出目录下找到新生成的EXE文件,你同时会发现输出目录下仍然存在那些DLL。不过不用担忧,这个EXE已经可以独立运行了。你能够把这些DLL所有删除后再运行EXE试试。工具
另外,Fody.Costura还支持一些进阶的特性,例如:ui
以上这些进阶特性都须要你经过修改Fody.Costura的配置文件来实现,具体的操做步骤能够参考它的官方文档。spa
好了,Fody.Costura的使用方式已经介绍完了。若是你对Fody.Costura的实现原理感到好奇,能够接着往下看。插件
当CLR试图加载一个程序集但加载失败时,它会引起AppDomain.AssemblyResolve事件。咱们的程序能够监听这个事件,而且在这个事件的处理函数中返回这个CLR试图加载的程序集,从而使程序得以继续正常运行。code
Fody.Costura在构建项目时会把EXE引用到的DLL所有嵌入到EXE文件中。当程序在运行的过程当中用到其中某个DLL的时候(此时因为CLR没法找到该DLL文件,致使AppDomain.AssemblyResolve事件被触发)再从EXE文件的嵌入资源中提取所需的DLL。xml
下面这两个函数就是Fody.Costura实现这部分逻辑的代码。
public static void Attach() { var currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += (s, e) => ResolveAssembly(e.Name); }
public static Assembly ResolveAssembly(string assemblyName) { if (nullCache.ContainsKey(assemblyName)) { return null; } var requestedAssemblyName = new AssemblyName(assemblyName); var assembly = Common.ReadExistingAssembly(requestedAssemblyName); if (assembly != null) { return assembly; } Common.Log("Loading assembly '{0}' into the AppDomain", requestedAssemblyName); assembly = Common.ReadFromEmbeddedResources(assemblyNames, symbolNames, requestedAssemblyName); if (assembly == null) { nullCache.Add(assemblyName, true); // Handles retargeted assemblies like PCL if (requestedAssemblyName.Flags == AssemblyNameFlags.Retargetable) { assembly = Assembly.Load(requestedAssemblyName); } } return assembly; }
能够看到,Attach方法监听了AppDomain.AssemblyResolve事件。当CLR没法成功加载某个程序集时, AssemblyResolve事件处理函数会被执行。AssemblyResolve会尝试经过Common.ReadFromEmbeddedResources方法从已加载的程序集的嵌入资源中获取目标程序集,并返回给CLR。
看到这里,你可能会问,Attach方法是在何时执行的呢?
实际上是这样的,对于C#语言来讲,CLR隐藏了一个大招——CLR能够在每一个模块(每一个程序集都含有一个或多个模块)加载以前执行一些初始化的代码。可是很遗憾,C#语言没法控制这部分代码。Fody.Costura则是在内部将IL代码直接注入到EXE程序集内部模块的初始化函数中,而这部分IL代码其实就是执行了Attach方法。这样一来,EXE程序集被加载后,Attach方法就可以当即获得调用了。
以上就是Fody.Costura实现原理的简单介绍。
问题1:安装完Costura.Fody以后再编译解决方案,报错:FodyWeavers.xml文件找不到;或者编译后发现执行文件EXE仍是只有几十KB的大小(并未包含DLL文件)。
报错缘由:Costura.Fody是依赖Fody的,一般在安装Fody时,FodyWeavers.xml经过 NuGet的内容功能进行部署。然而,在高版本NuGet ,没有自动建立FodyWeavers.xml文件。
解决方法:本身手动添加FodyWeavers.xml文件。将鼠标移动到你的解决方案上面,单击右键,依次选择“添加” - “新建项目” - “ XML文件 ”,注意在新建XML文件时将文件命名为“ FodyWeavers.xml “,而后将下面这段代码复制到 FodyWeavers.xml文件里面便可。
<?xml version="1.0" encoding="utf-8" ?> <Weavers> </Weavers>
问题2:最近在用VS2013去生成独立EXE文件的时候报错:"Fody is only supported on MSBuild 16 and above. Current version: 15."
报错缘由:由于VS2013版本过旧,如今Fody到5.0+,Costura.Fody到4.0+,不支持低版本VS。听说是有意为之,但愿你们都使用最新的VS。
解决方法:卸载掉项目中的Fody和Costura.Fody,而后手动安装低版本的Fody和Costura.Fody。
能够在VS的程序包管理器控制台中输入Install-Package Fody -Version 4.2.1来安装4.2.1版本的Fody,而后输入Install-Package Costura.Fody -Version 3.3.3来安装3.3.3版本的Costura.Fody。