CLR查找和加载程序集 z
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的顺序如下(区分强名称签名、没有强名称签名的程序集)
没有做强名称签名的程序集 |
- 程序的根目录
- 根目录下面,与被引用程序集同名的子目录
- 根目录下面被明确定义为私有目录的子目录
- 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
- 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录
强名称签名的程序集 |
- 全局程序集缓存
- 如果有定义codebase,则以codebase定义为准,如果 codebase指定的路径找不到,则直接报告错误
- 程序的根目录
- 根目录下面,与被引用程序集同名的子目录
- 根目录下面被明确定义为私有目录的子目录
- 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
- 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录
如何让程序识别不同目录下的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的更多相关文章
- CLR查找和加载程序集的方式
C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...
- CLR查找和加载程序集的方式(二) 流程图
在前一篇文章<CLR查找和加载程序集的方式(一)>中详细介绍了CLR查找和加载程序的方式,分别介绍了配置与代码的实现方式. 本篇通过一个具体的流程图来帮助大家更加直观明了深入的掌握CLR查 ...
- CLR查找和加载程序集的方式(一)
C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...
- 【转】再谈CLR查找和加载程序集的方式
这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面.我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助. 本文范例代码可以通过这里下载 http://files.cnblogs. ...
- 再谈CLR查找和加载程序集的方式
原文:再谈CLR查找和加载程序集的方式 这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面.我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助. 本文范例代码可以通过这里下载 h ...
- CLR如何加载程序集以及程序集版本策略
在项目的配置文件Web.config中,会看到<runtime>节点,以及包含在其中的<assemblyBinding>节点,这显然与程序集有关,这些节点到底何时被用到呢? 在 ...
- linux系统——ld-linux.so.X查找和加载共享动态库的顺序
ld-linux.so查找共享库的顺序: Glibc安装的库中有一个为ld-linux.so.X,其中X为一个数字,在不同的平台上名字也会不同.可以用ldd查看: #ldd /bin/cat linu ...
- 五、CLR加载程序集代码时,JIT编译器对性能的产生的影响
1.CLR首次加载代码造成的性能损失 四.CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,JITComplier函数(JIT编译器)会验证IL ...
- 重温CLR(十七)程序集加载和反射
本章主要讨论在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息.创建类型的实例以及访问类型的成员.可利用本章讲述的内容创建动态可扩展应用程序. 反射使用的典型场景一般是由一家公司创建宿主应 ...
随机推荐
- CCF CSP 201503-1 图像旋转
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201503-1 图像旋转 问题描述 旋转是图像处理的基本操作,在这个问题中,你需要将一个图像逆 ...
- Netty 实现SSL安全连接(wss://)
原文:Netty5使用自签证书实现SSL安全连接 在客户端是https:// 协议下,使用ws:// 协议连接会报错的,得需要使用wss:// 连接.(ip连接失败时使用域名连接) netty创建服务 ...
- Eclipse反编译插件的安装
步骤: 1.已经安装了Eclipse,如我的Eclipse目录:C:\Programs\JAVA\eclipse 2.反编译插件包:eclipse 反编译插件 jad 3.3.0.zip 3.解压反编 ...
- [xsy3343]程序锁
题意:有两个序列,序列中数字$\in\{-1,0,1\}$ 有两个指针,初始时分别指向两个序列的开始位置,有一个初始为$0$的数$a$,重复以下过程直到两个指针都指向序列末尾后 如果一个指针指向末尾后 ...
- hdu 4118 dfs
题意:给n个点,每个点有一个人,有n-1条有权值的边,求所有人不在原来位置所移动的距离的和最大值.不能重复 这题的方法很有看点啊,标记为巩固题 Sample Input 1 4 1 2 3 2 3 2 ...
- MyBatis -- generator 逆向工程
一.引言 官网文档:http://www.mybatis.org/generator/index.html 通过使用官方提供的mapper自动生成工具,mybatis-generator-core-1 ...
- 【洛谷】1477:[NOI2008]假面舞会【图论】
P1477 [NOI2008]假面舞会 题目描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会. 今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具 ...
- 移动web开发经验总结(1)
1.<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, minimum-sca ...
- py脚本打包exe可执行文件
python3以上版本打包exe需要扩展软件:cx_freeze 下载地址:http://cx-freeze.sourceforge.net/ 1)安装后在\Python32\Scripts\cxfr ...
- python开发_csv(Comma Separated Values)_逗号分隔值_常用导入导出格式_完整版_博主推荐
## 最近出了一趟差,是从20号去的,今天回来...# 就把最近学习的python内容给大家分享一下...#''' 在python中,CSV(Comma Separated Values),从字面上面 ...