利用Costura.Fody制做绿色单文件程序(C#程序(含多个Dll)合并成一个Exe)

开发程序的时候常常会引用一些第三方的DLL,而后编译生成的exe文件就不能脱离这些DLL独立运行了。这样交给用户很不方便,但愿的效果是直接交付一个exe文件。git

这时候就须要借助一款名为Fody.Costura的插件。Fody.Costura是一个Fody框架下的插件,可经过Nuget安装到VS工程中。安装以后,就能够将项目所依赖的DLL(甚至PDB)文件所有打包到EXE文件里。github

使用 Costura.Fody ,这个强大之处是能够在build阶段直接将依赖的DLL合并在目标EXE中,支持非托管DLL进行mixed mode 打包。不足之处是只能将DLL合并到EXE中,不支持DLL和DLL的合并。框架

github地址:https://github.com/Fody/Costura函数

nuget地址:https://www.nuget.org/packages/Costura.Fody/ui

使用场景:最终发布只有一个EXE,将依赖的DLL合并到一个EXE中。spa

使用方法

一、在VS中,经过Nuget为目标EXE工程安装Costura.Fody。
二、从新构建项目。插件

构建完成后,到项目的输出目录下找到新生成的EXE文件,你同时会发现输出目录下仍然存在那些DLL。不过不用担忧,这个EXE已经可以独立运行了。你能够把这些DLL所有删除后再运行EXE试试。code

Fody.Costura还支持一些进阶的特性,例如:blog

  • 临时程序集文件:在运行EXE前自动,自动将DLL从EXE中解压到文件夹系统中,再经过常规的方式加载该DLL。
  • 合并不是托管的DLL:Fody.Costura能够合并不是托管的DLL,可是不会自动合。若是你的程序涉及非托管DLL,那么你须要经过修改Fody.Costura的配置文件来显示地告诉它你想合并哪些非托管的DLL。
  • 预加载DLL:Fody.Costura能够帮助你在程序启动时预先加载某些DLL,你甚至能够指定这些DLL的加载顺序。

以上这些进阶特性都须要你经过修改Fody.Costura的配置文件来实现,具体的操做步骤能够参考它的官方文档事件

实现原理介绍

当CLR试图加载一个程序集但加载失败时,它会引起AppDomain.AssemblyResolve事件。咱们的程序能够监听这个事件,而且在这个事件的处理函数中返回这个CLR试图加载的程序集,从而使程序得以继续正常运行。

Fody.Costura在构建项目时会把EXE引用到的DLL所有嵌入到EXE文件中。当程序在运行的过程当中用到其中某个DLL的时候(此时因为CLR没法找到该DLL文件,致使AppDomain.AssemblyResolve事件被触发)再从EXE文件的嵌入资源中提取所需的DLL。

下面这两个函数就是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方法就可以当即获得调用了。

与此相同原理的将DLL添加到EXE的资源(http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx);

相关文章
相关标签/搜索