C# 程序引用不同路径的DLL文件最详细教程

前言

        最近我准备发布软件,但是点开软件目录的一瞬间感觉有点傻眼,整个目录的dll文件,让人看着一塌糊涂,反正我看着就要吐了。

        相信做.net应用程序开发的伙伴都有这种感觉,因为项目的需求或者某个功能的需求,需要引用外部的dll文件,随着时间的累积,程序目录下面的dll文件越来越多,而且是各种各样的,最终自己都看不下去了,那么我们有没有什么办法把dll按照文件夹分类存放呢?不用着急,请花10分钟耐心读完本文,一定会解决你的问题的。

Dll引用分类

        1.正式教学之前,咱们得先创建一个测试工程(控制台,类库,Winform都行),名字就叫做"Dll路径配置",这里我创建的是一个Winform程序,如下图:

测试工程项目文件我会放在文章最后

        2.首先准备好我们需要引入的dll文件,这里博主简单的准备了一个测试所用的dll:FileCopy.dll,如下图:

        3.接着打开我们的测试工程的编译目录(Debug目录),这里假设我们需要把FileCopy.dll放在"依赖项"这个文件夹下,我们首先创建一个名为“依赖项”的文件夹,如下图:

  

        4.接着将FileCopy.dll复制到“依赖项”文件夹下,如下图:

        5.在项目里添加对“FileCopy.dll"的引用,如下图:

        6.在引用里修改”FileCopy"的属性,将“复制本地”改为“False”(这个一定要记着

        7.给咱们的主窗口添加一个按钮,如下图:

        8.在按钮的事件内调用FileCopy内的函数,如下图:

         (如果您创建的不是Winfrom程序,可自定义函数调用FileCopy内的函数,为什么一定要调用呢?其实是这样的,程序在加载dll的时候只会加载正在运行的代码块所调用的dll,如果不调用dll内的功能,程序是不会将dll加载到内存中的)

        9.启动程序,运行刚才的代码块(本例中直接点击按钮

        10.接着报错啦!!!!

        PS:这里为什么会报错呢?下面我解释一下(这里要认真看,理解为什么报错)

        我们知道,.net程序在生成的时候,我们引用的dll文件都会自动复制到我们程序的编译目录下面(默认的是Debug目录下面),所以程序运行的时候也会默认在程序运行目录(Debug目录)下面去查找所依赖的dll文件,本例中咱们依赖的是“FileCopy.dll”,但是"FileCopy.dll“并不在Debug目录下面(因为第6步的时候咱们已经把FileCopy.dll的属性>是否复制本地改为了False,所以生成的时候是不会复制到Debug目录下的),而是在文件夹“依赖项"下面,如图:

        所以,直接改dll文件夹添加引用这种方式行不通。 

怎么实现引用指定位置的DLL文件

        关于如何简化程序dll目录的方法,网上有很多文章,比如:修改Config文件,修改环境变量,这里咱们都不赘述,直接讲我实现的方法:

  • 直接订阅程序集解析事件 AppDomain.CurrentDomain.AssemblyResolve 事件

        这个事件在这里可以简单的理解为,主程序在调用程序目录下的dll失败时所要做的事情,需要值得注意的是:这个事件必须在主程序使用dll内的函数之前订阅才有效,通常这个事件订阅放在程序入口(网上也有这个方法,但是一般不会告诉你这点,这点很重要)

  • AssemblyResolve事件内,使用Assembly.LoadFrom()方法加载正确的dll路径        

        1.首先回到咱们的测试工程,在测试工程主窗体下新建下面方法:

        /// <summary>
        /// 对外解析dll失败时调用
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string dllName = args.Name.Split(',')[0];//获取主程序正在调用的dll名称

            string ExcutePath = Path.GetDirectoryName(Application.ExecutablePath);//获取程序执行的目录

            string dllFile = ExcutePath + "\\依赖项\\" + dllName + ".dll";//获取dll路径

            return System.Reflection.Assembly.LoadFrom(dllFile);

        }

2.接着在窗体初始化处订阅事件,订阅事件的代码如下:

  AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;//在程序调用dll前绑定这个事件(一般选在程序入口)

3.窗口整体代码如图所示:

4.这个时候再运行程序,点击按钮

5.这时候我们发现,程序没有报错了,并且按照咱们的代码正常运行了,再来看看咱们的程序目录:

        这次程序生成目录下同样的没有"FileCopy.dll”文件,不过程序却能正常运行了,证明咱们成功了,不过这样就完了吗?答案是:这样确实就完了!,就这么简单!

应用进阶

        咱们已经实现了如何让程序引用指定路径的dll文件,但是实际开发过程中,咱么可能会有很多dll文件,那么上面的代码明显不满足需求了,总不能把所有的dll文件都放在同一个路径下面吧?

        要是有多个文件夹呢?下面咱们以上面代码为基础,将其做一个升级,让其支持多个目录。

实现思路如下:

1.首先咱们新建一个工具类类,命名为:SysRef,并在类实例化的时候订阅AppDomain.CurrentDomain.AssemblyResolve事件

2.创建列表用来记录不同的路径

3.在事件响应的时候到每个路径下寻找dll,如果存在dll文件则进行加载

代码如下:

 /// <summary>
    /// 该类放在程序入口
    /// </summary>
    public class SysRef
    {
        /// <summary>
        /// Dll文件目录
        /// </summary>
        private List<string> RefPath { get; set; }
        /// <summary>
        /// 实例化
        /// </summary>
        public SysRef()
        {
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;//在程序调用dll前绑定这个事件(一般选在程序入口)
            RefPath = new List<string>();
        }
        /// <summary>
        /// 设置dll路径
        /// </summary>
        /// <param name="Path">这个参数是dll所在的文件夹路径,并不是dll本身的路径</param>
        public void SetRefPath(string Path)
        {
            if (!Directory.Exists(Path)) return;
            if (!RefPath.Contains(Path)) RefPath.Add(Path);
        }
        /// <summary>
        /// 对外解析dll失败时调用
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string dllName = args.Name.Split(',')[0];//dll程序集名称

            //遍历每个路径查找dll
            foreach (var item in RefPath)
            {
                string dllFile = item + "\\" + dllName + ".dll";
                //如果路径存在则直接引用dll程序集
                if (File.Exists(dllFile))
                {
                    return System.Reflection.Assembly.LoadFrom(dllFile);
                }

            }
            throw new Exception(string.Format("未找到程序:{0}", dllName));
        }

    }

4.有了工具类以后,使用起来更方便了,再次回到咱们的测试工程,删除之前的事件响应代码和事件订阅代码,在窗口实例化处添加下面代码:

            //获取dll文件所在的文件夹
            string dllPath = Path.GetDirectoryName(Application.ExecutablePath) + "\\依赖项";
            //新建管理类
            SysRef sysRef = new SysRef();
            sysRef.SetRefPath(dllPath);//注册地址(有多个文件夹地址的时候可以多次调用该方法注册)

 5.启动程序并测试效果

6.程序同样正常运行,再看看主程序目录,依旧没有我们引用的dll,但是程序正常运行

PS:再次强调!工具类SysRef必须在主程序使用dll内的函数之前实例化才有效,最好是将SysRef实例化放在程序入口处。实际使用过程中需要根据项目需求修改工具类的代码。今天的教学就到这里了,测试工程项目文件我放在了最后,需要的自取。

测试工程项目文件

百度云盘地址:百度网盘 请输入提取码

  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值