C#开发者在开发WinForm程序、Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集,

编译后引用的dll都放在根目录下。以我个人作品 AutoProject Studio 自动化项目生成器 为例,由于需要支持SQL Server、

Oracle、MySQL、PostgreSQL、DB2、Sybase、Infomix、SQLite、Access等多种数据库,所以引用了DmProvider.dll、

IBM.Data.DB2.dll、IBM.Data.Informix.dll、MySql.Data.dll、Npgsql.dll、SQLite.Interop.dll、System.Data.dll、

System.Data.OracleClient.dll、Sybase.AdoNet4.AseClient.dll 等DLL,参考下图:

随着项目的日益增大,根目录下充满了各种各样的dll,非常的不美观。如果能够把dll按照想要的目录来存放,那么系统就美观多了。

此问题就涉及到 CLR查找和加载程序集的方式

  系统搜索dll的目录以及顺序

  CLR解析一个程序集会在一个根目录内进行搜索,整个探索过程又称Probing,这个根目录很显然就是当前包含当前程序集的目录。

  AppDomainSetup这个类存储着探索目录的信息,其成员包括: ApplicationBase 、 PrivateBinPath 。

程序搜索DLL的顺序如下(区分强名称签名、没有强名称签名的程序集)

  没有做强名称签名的程序集
  1. 程序的根目录
  2. 根目录下面,与被引用程序集同名的子目录
  3. 根目录下面被明确定义为私有目录的子目录
  4. 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
  5. 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录
  强名称签名的程序集
  1. 全局程序集缓存
  2. 如果有定义codebase,则以codebase定义为准,如果 codebase指定的路径找不到,则直接报告错误
  3. 程序的根目录
  4. 根目录下面,与被引用程序集同名的子目录
  5. 根目录下面被明确定义为私有目录的子目录
  6. 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
  7. 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录

如何让程序识别不同目录下的dll?
我们看到,上面的顺序无论是否有强名称签名看,都提到了一个名词 “私有目录”。

  方法一:配置App.config(web.config)文件的 privatePath ——【推荐】

针对该问题,微软提供了<probing> 元素, 在配置文件中自定义存储目录。

 1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <runtime>
4 <!--双核以上计算机请开启下面的选项为 true-->
5 <gcServer enabled="false" />
6 <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
7 <probing privatePath="bin;bin2\subbin;bin3" />
8 </assemblyBinding>
9 </runtime>
10 </configuration>
  • configuration:每个配置文件中的根元素,常用语言 runtime 和 .NET Framework 应用程序会使用这些文件。
  • runtime:包含程序集绑定和垃圾回收的相关信息。
  • assemblyBinding:包含有关程序集版本重定向和程序集位置的信息。
  • privatePath:必选特性。指定可能包含程序集的应用程序基目录的子目录。 用分号分隔每个子目录。

上述 privatePath中的"bin;bin2\subbin;bin3",其中bin是默认编译输出目录,bin2、bin3 是自定义目录,subbin是bin2下的子目录。

把DLL分别放入上述目录中,程序运行正常。

这是最简单的方法,当然也有一定的局限性,就是没法对DLL做控制。另外,无法解决第三方 DllImport 中引入的程序集不在根目录下的问题。

但是该方法基本解决了分目录存储的问题。

  方法二:订阅程序集解析事件 AssemblyResolve 在代码中解析

应用程序集域中支持在程序集解析时的处理: AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 。

通过这个事件,我们可以在程序集解析时,根据不同的程序集做不用的处理,比如加载x86的程序集还是64位的程序集,当然也就可以指定程序集目录了

这也正是 Assembly.Load 和 Assembly.LoadFrom 等方法的用武之地。

 1 using System;
2 using System.IO;
3 using System.Reflection;
4 using System.Windows.Forms;
5
6 namespace WindowsFormsApplication1
7 {
8 static class Program
9 {
10 /// <summary>
11 /// 应用程序的主入口点。
12 /// </summary>
13 [STAThread]
14 static void Main()
15 {
16 Application.EnableVisualStyles();
17 Application.SetCompatibleTextRenderingDefault(false);
18
19 AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
20
21 Application.Run(new Form1());
22 }
23
24 /// <summary>
25 /// 解析当前应用程序域内指定目录下的DLL
26 /// </summary>
27 /// <param name="sender"></param>
28 /// <param name="args"></param>
29 /// <returns></returns>
30 private static Assembly CurrentDomain_AssemblyResolve(object sender,ResolveEventArgs args)
31 {
32 string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,@"xx目录\");
33 path = Path.Combine(path,args.Name.Split(',')[0]);
34 path = string.Format(@"{0}.dll",path);
35 return Assembly.LoadFrom(path);
36 }
37 }
38 }
  方法三:设置 AppDomainSetup类的PrivateBinPath属性
 1 using System;
2 using System.IO;
3 using System.Reflection;
4 using System.Windows.Forms;
5
6 namespace WindowsFormsApplication1
7 {
8 static class Program
9 {
10 /// <summary>
11 /// 应用程序的主入口点。
12 /// </summary>
13 [STAThread]
14 static void Main()
15 {
16 Application.EnableVisualStyles();
17 Application.SetCompatibleTextRenderingDefault(false);
18
19 SetPrivateBinPath();//设置程序集所在的指定目录并解析DLL
20
21 Application.Run(new Form1());
22 }
23
24
25 /// <summary>
26 /// 设置程序集所在的指定目录并解析DLL
27 /// </summary>
28 private static void SetPrivateBinPath()
29 {
30 if(AppDomain.CurrentDomain.IsDefaultAppDomain())
31 {
32 string appName = AppDomain.CurrentDomain.FriendlyName;
33 var currentAssembly = Assembly.GetExecutingAssembly();
34
35 AppDomainSetup setup = new AppDomainSetup();
36 setup.ApplicationBase = Environment.CurrentDirectory;
37 setup.PrivateBinPath = "xx目录";
38 setup.ConfigurationFile = setup.ApplicationBase + string.Format("\\Config\\{0}.config",appName);
39
40 AppDomain newDomain = AppDomain.CreateDomain("NewAppDomain",null,setup);
41 int ret = newDomain.ExecuteAssemblyByName(currentAssembly.FullName);
42
43 AppDomain.Unload(newDomain);
44 Environment.ExitCode = ret;
45 Environment.Exit(0);
46 return;
47 }
48 }
49 }
50 }
  方法四:在加载使用到的DLL代码之前重置当前环境的目录

通过 Environment.CurrentDirectory=customPath ,切换目录后,在调用dll方法时运行正常。

处理 [DllImport] 中的程序集的加载,此处提供用一种方式来处理:增加环境变量。

C#代码如下:

static void AddEnvironmentPaths(IEnumerable<string> paths)
{
var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty }; string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths)); Environment.SetEnvironmentVariable("PATH", newPath);
}

CLR查找和加载程序集 z的更多相关文章

  1. CLR查找和加载程序集的方式

    C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...

  2. CLR查找和加载程序集的方式(二) 流程图

    在前一篇文章<CLR查找和加载程序集的方式(一)>中详细介绍了CLR查找和加载程序的方式,分别介绍了配置与代码的实现方式. 本篇通过一个具体的流程图来帮助大家更加直观明了深入的掌握CLR查 ...

  3. CLR查找和加载程序集的方式(一)

    C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...

  4. 【转】再谈CLR查找和加载程序集的方式

    这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面.我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助. 本文范例代码可以通过这里下载 http://files.cnblogs. ...

  5. 再谈CLR查找和加载程序集的方式

    原文:再谈CLR查找和加载程序集的方式 这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面.我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助. 本文范例代码可以通过这里下载 h ...

  6. CLR如何加载程序集以及程序集版本策略

    在项目的配置文件Web.config中,会看到<runtime>节点,以及包含在其中的<assemblyBinding>节点,这显然与程序集有关,这些节点到底何时被用到呢? 在 ...

  7. linux系统——ld-linux.so.X查找和加载共享动态库的顺序

    ld-linux.so查找共享库的顺序: Glibc安装的库中有一个为ld-linux.so.X,其中X为一个数字,在不同的平台上名字也会不同.可以用ldd查看: #ldd /bin/cat linu ...

  8. 五、CLR加载程序集代码时,JIT编译器对性能的产生的影响

    1.CLR首次加载代码造成的性能损失 四.CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,JITComplier函数(JIT编译器)会验证IL ...

  9. 重温CLR(十七)程序集加载和反射

    本章主要讨论在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息.创建类型的实例以及访问类型的成员.可利用本章讲述的内容创建动态可扩展应用程序. 反射使用的典型场景一般是由一家公司创建宿主应 ...

随机推荐

  1. scrapy-redis 更改队列和分布式爬虫

    这里分享两个技巧 1.scrapy-redis分布式爬虫 我们知道scrapy-redis的工作原理,就是把原来scrapy自带的queue队列用redis数据库替换,队列都在redis数据库里面了, ...

  2. Web前端开发最佳实践(8):还没有给CSS样式排序?其实你可以更专业一些

    前言 CSS样式排序是指按照一定的规则排列CSS样式属性的定义,排序并不会影响CSS样式的功能和性能,只是让代码看起来更加整洁.CSS代码的逻辑性并不强,一般的开发者写CSS样式也很随意,所以如果不借 ...

  3. Factroy 简单工厂

    意图 定义一个用于创建对象的接口,让子类决定实例化哪一个类. Factory Method使一个类的实例化延迟到其子类. 动机 框架使用抽象类定义和维护对象之间的关系.这些对象的创建通常也由框架负责. ...

  4. 8-12 Erratic Expansion uva12627

    题意:一开始有一个红气球  每小时后一个红气球会变成三个红气球和一个蓝气球  第k小时 a到b行之间有几个红气球 递归找规律题目 一定要注意涉及指数的时候一定要开long long 数组!!!! #i ...

  5. 002 Jupyter-NoteBook工具介绍(网页版编辑器)

    1.Jupyter-NoteBook位置 在安装完anaconda后,这个工具已经被安装完成. 2.打开 3.功能讲解 目录:C:\Users\dell,这个可以看上面控制台上的信息. 4.其余的功能 ...

  6. [js]变量与数据类型篇

    一.变量 在JavaScript中就用一个变量名表示变量,变量名是大小写英文.数字.$和_的组合,不能用数字开头.变量名也不能是JavaScript的关键字: 1.变量的声明 (1)var:申明一个变 ...

  7. CSUOJ 1868 潜在好友

    Description 小X在搬砖写一个论坛,这个时候老板突然想到一个功能,让小X今天赶快实现.大概就是如果某个人是你好友的好友那么他的头像上面会有特殊的标志.小X想不到较好的办法来解决如何验证两个人 ...

  8. 将已有的项目提交到GitHub

    1.目的: 将已有的项目提交到GitHub 2.准备工作 2.1 此教程建立在对git有初步的理解上 2.2 此教程之前需准备工作 a.熟悉git的一些基本命令和原理. b.已注册有GitHub账号. ...

  9. 1006 Sign In and Sign Out (25)(25 point(s))

    problem At the beginning of every day, the first person who signs in the computer room will unlock t ...

  10. Opencv学习笔记4:Opencv处理调整图片亮度和对比度

    一.理论基础 在数学中我们学过线性理论,在图像亮度和对比度调节中同样适用,看下面这个公式: 在图像像素中其中: 参数f(x)表示源图像像素. 参数g(x) 表示输出图像像素. 参数a(需要满足a> ...