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.首先回到咱们的测试工程,在测试工程主窗体下新建下面方法:

       ///  /// 对外解析dll失败时调用
              ///  ///  ///  ///  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文件则进行加载

      代码如下:

       ///  /// 该类放在程序入口
          ///  public class SysRef
          {
              ///  /// Dll文件目录
              ///  private List RefPath { get; set; }
              ///  /// 实例化
              ///  public SysRef()
              {
                  AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;//在程序调用dll前绑定这个事件(一般选在程序入口)
                  RefPath = new List();
              }
              ///  /// 设置dll路径
              ///  /// 这个参数是dll所在的文件夹路径,并不是dll本身的路径 public void SetRefPath(string Path)
              {
                  if (!Directory.Exists(Path)) return;
                  if (!RefPath.Contains(Path)) RefPath.Add(Path);
              }
              ///  /// 对外解析dll失败时调用
              ///  ///  ///  ///  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实例化放在程序入口处。实际使用过程中需要根据项目需求修改工具类的代码。今天的教学就到这里了,测试工程项目文件我放在了最后,需要的自取。

      测试工程项目文件

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