不用Fody,支持.net2.0,C#把Dll文件打包到Exe输出单文件版应用

网上一些使用fody对c#的调用的dll文件捆包成exe文件,并不适合我,因为我的.net项目为了在低端机上使用,只安装.net2.0

 

这个改动主要分为两部分。,第一部分是先写一个自动通过资源加载dll文件的类

第二个部分就是在程序的入口处进行dll的动态加载.注意不是main函数

步骤一共分为三部,

首先第一步是把要打包进来的dll文件。直接拽到项目当中当做资源文件。

也可以放到指定的文件夹中.放入以后,修改文件的属性:

生成操作->嵌入的资源

复制到输出目录->如果较新则复制


 第二步。编写一个dll文件的动态加载器我这里已经写好啦可以直接复制拿过去用

#region 引用
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.IO;
#endregion
#region 主体
/// <summary>
/// dll动态加载器
/// </summary>
static class DllLoader
{
    #region 变量
    static Dictionary<string, Assembly> DllsDic = new Dictionary<string, Assembly>();
    static Dictionary<string, object> AssembliesDic = new Dictionary<string, object>();
    #endregion
    #region 公共函数,外部调用
    /// <summary>
    /// 加载
    /// </summary>
    public static void Load()
    {
        Console.WriteLine("注册dll方法调用");
        //获取Program所属程序集
        var ass = new StackTrace(0).GetFrame(1).GetMethod().Module.Assembly;
        //判断是否已处理
        if (AssembliesDic.ContainsKey(ass.FullName))
        {
            return;
        }
        //程序集加入已处理集合
        AssembliesDic.Add(ass.FullName, null);
        //绑定程序集加载失败事件(这里我测试了,就算重复绑也是没关系的)
        AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
        //获取所有资源文件文件名
        var res = ass.GetManifestResourceNames();
        foreach (var r in res)
        {
            if (r.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    #region 实际应用中,不把dll解开放到某个地方 也是一样可以用的.具体根据自己的情况确定是否要解开注释内容
                    //if (r.ToLower().Contains(".dll"))
                    //{
                    //    ExtractResource2File(r, GetUnpackPatch() + @"/" + r.Substring(r.IndexOf('.') + 1));
                    //}
                    #endregion
                    var s = ass.GetManifestResourceStream(r);
                    var bts = new byte[s.Length];
                    s.Read(bts, 0, (int)s.Length);
                    var da = Assembly.Load(bts);
                    //判断加载结果
                    if (DllsDic.ContainsKey(da.FullName))
                    {
                        continue;
                    }
                    DllsDic[da.FullName] = da;
                }
                catch (Exception e)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(e.Message);
                    Console.WriteLine(e.StackTrace);
                    Console.ResetColor();
                }
            }
        }
    }
    /// <summary>
    /// 解包文件
    /// </summary>
    /// <param name="resourceName">资源名</param>
    /// <param name="filename">目标文件名</param>
    #endregion
    #region 私有函数,内部使用
    static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //程序集
        Assembly ass;
        //获取加载失败的程序集的全名
        var assName = new AssemblyName(args.Name).FullName;
        //判断Dlls集合中是否有已加载的同名程序集
        if (DllsDic.TryGetValue(assName, out ass) && ass != null)
        {
            DllsDic[assName] = null;//如果有则置空并返回
            return ass;
        }
        else
        {
            throw new DllNotFoundException(assName);//否则抛出加载失败的异常
        }
    }

    static void ExtractResource2File(string resourceName, string filename)
    {
        Console.WriteLine("解压文件:{0} {1}", resourceName, filename);
        if (!System.IO.File.Exists(filename))
        {
            using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    byte[] b = new byte[s.Length];
                    s.Read(b, 0, b.Length);
                    fs.Write(b, 0, b.Length);
                }
            }
        }
    }
    /// <summary>
    /// 获取解压目录,根据自己的需求改位置,如果解压到系统文件夹,需要有相关权限,可能会被杀毒软件误报
    /// </summary>
    /// <returns></returns>
    static string GetUnpackPatch()
    {
        //string strPath = "C:";
        string strPath = AppDomain.CurrentDomain.BaseDirectory;
        if (!Directory.Exists(strPath))
        {
            Directory.CreateDirectory(strPath);
        }
        return strPath;
    }
    #endregion
}
#endregion

把这个文件直接放到项目中即可


第三步在program.cs文件中插入相应的自动加载dll文件的代码.

默认新建的一个控制台应用程序中program 这个文件中只有main函数

在main函数上方加入Program函数,并进行dllloader的调用

static Program()
        {
            Console.WriteLine("开始检查dll");
            DllLoader.Load();
            Console.WriteLine("结束检查dll");
        }

修改后的Program.cs文件是这样的:

using System;
using System.IO;
using NPOI.SS.UserModel;
using NPOI.HSSF.UserModel;

namespace Dll打包进Exe
{
    class Program
    {
        static Program()
        {
            Console.WriteLine("开始检查dll");
            DllLoader.Load();
            Console.WriteLine("结束检查dll");
        }
        static void Main(string[] args)
        {
            //Dictionary<string, object> waitJsonObj = new Dictionary<string, object>();
            //waitJsonObj.Add("ce", "测试内容");
            //waitJsonObj.Add("i", 12321321);
            //string json = Newtonsoft.Json.JsonConvert.SerializeObject(waitJsonObj);
            //Console.WriteLine(json);
            //Console.ReadLine();
            IWorkbook workbook = new HSSFWorkbook();
            ISheet sheet = workbook.CreateSheet();
            IRow row0 = sheet.CreateRow(0);
            ICell cella = row0.CreateCell(0);
            cella.SetCellValue("测试新表格的表头 行1 列a");
            SaveSheet2NewFile("c:\\ceshi.xls", sheet);
            Console.WriteLine("写入文件完成");
            Console.ReadLine();
        }
        public static int SaveSheet2NewFile(string fileName, ISheet sheet)
        {
            int i = 0;
            int j = 0;
            int count = 0;
            //IWorkbook workbook = null;
            FileStream fs = null;
            try
            {

                fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
                //if (fileName.IndexOf(".xlsx") > 0) // 2007版本
                //    workbook = new XSSFWorkbook();
                //else if (fileName.IndexOf(".xls") > 0) // 2003版本
                //    workbook = new HSSFWorkbook();

            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
                //MessageBox.Show(err.Message);
                return -1;
            }
            try
            {
                //ISheet newsheet = workbook.CreateSheet();
                sheet.Workbook.Write(fs);
                //workbook.Write(fs); //写入到excel
                fs.Close();
                return count;
            }
            catch (Exception ex)
            {
                Console.WriteLine("err:",ex.Message);
                //MessageBox.Show("Exception: " + ex.Message);
                fs.Close();
                return -1;
            }
        }
    }
}

该例子是把NPOI这个表格处理的相关dll打包到exe文件中


注意,如果dll可能会变动,比如被当做资源的a.dll是你的a.csproj项目生成的,那么你在编辑了a项目以后,a项目会生成新的dll,所以,加入到当前项目中的dll应该是在 a项目下的bin/debug或者release目录下的xx.dll,如果你的主项目还在测试中,调试模式是debug,那么资源文件选择用a项目的bin/debug下面的文件,如果是release,也要对应使用a项目的对应release生成的dll,不然可能会因为dll调试模式问题造成不能有效装载.


生成项目之前,dll还是正常引用,但是生成了以后,虽然bin/debug目录下会有相应的dll,但是不用管的,直接把生成的exe文件拿出来是可以用的.你会发现exe文件的大小是包含了自身大小和装载进去的dll文件的大小的和


还有一个补充的是,如果包进来的dll还引用了其他的dll,或者是本身这个被引用的dll所需要引用的dll的版本会和系统环境产生兼容问题,要把引用的dll引用的dll也加到资源里面.比如  NPOI的dll如果创建xlsx文件的话,xssfworkbook类需要用到CSharpCode.SharpZipLib.dll这个文件,但是系统环境和vs的debug环境不一致导致了无法创建xlsx文件.

关于:未能加载文件或程序集“ICSharpCode.SharpZipLib”或它的某一个依赖项异常的解决方案 - 追逐时光者 - 博客园

这个文章中下载的文件.


技巧: 

1.

dll也是可以打包dll在里面的

2.

动态调用dll,(使用Assembly.LoadFile的方式)时,a使用动态引用引用b,但是a项目中直接使用项目引用的方式引用了b,要把b引用先删除才能测试出来动态引用时候 b是否有依赖项不兼容.

3.动态引用dll的时候,添加bin//debug目录下的dll,移除了项目本身的引用方式对<项目>的引用的话,但是有把被引用的b项目加入到解决方案中,在b项目中设置断点也是能被检测到的.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Afterwards_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值