手持搅拌棒 食谱:.net中的dll搜索路径

来源:百度文库 编辑:中财网 时间:2024/04/26 15:49:17

.net中的dll搜索路径

分类: .net(C#/CLI/DLR) 2008-02-11 19:46 870人阅读 评论(0) 收藏 举报    一直以为.net的dll搜索路径和VC一样,设么Path路径、系统路径什么的。但是居然发现不一样。可惜不是计算机专业,只好乱找了。我印象当中,dll的路径要么是exe目录,要么是GAC,要么是config文件指定的目录。可惜config只能提供给exe使用,dll没有cinfig(可是MSDN上说可以,但是我不知道怎么弄)。
   
   第一部分(概念)
   托管模块(Managed Module)
   托管模块是一个需要CLR才能执行的标准Windows可移植可执行(portable executable,简称PE)文件。
  
   元数据(Metadata)
   简单的讲,元数据就是一个数据表的集合,在这些表中,其中一些用于
   描述托管模块中所定义的内容(比如所定义的类型和它们的成员),另外还有一些
   用于描述托管模块中所引用的内容(比如被引用的类型和它们的成员)。
   URL: ms-help://MS.MSDN
   QTR.2004APR.1033/cpguide/html/cpconmetadataoverview.htm
  
   程序集清单(Assembly Manifest
   程序集清单是另外一些元数据表的集合。这些表描述了组成程序集的文件,
   程序集所有文件中实现的公有导出类型,以及一些程序集相关的资源文件或数据文件。
   ms-help://
   MS.MSDNQTR.2004APR.1033/cpguide/html/cpconAssemblyManifest.htm
  
   1.程序集(Assembly)的概念:
   首先:程序集是一个或多个托管模块,以及一些资源文件的逻辑组合。因为它是一个逻辑上的组合,所以程序集的逻辑表示和物理表示可以相互分离。如何将代码和资源划分到不同的文件中完全取决于我们。例如,我们可以将一些很少使用的类型或资源放在一个单独的Assembly Module中,然后根据需要(比如第一次用到的时候),从web上下载它们。如果没有用到,它们将不会被下载。这样既节省磁盘空间,也减少了安装时间。程序集允许我们将文件的部署分解开来,同时又将所有的文件看作一个单独的集合。
   其次:因为CLR是直接和程序集打交道的,所以程序集也是组件复用,以及实施安全策略和版本策略的最小单元(安全策略,版本信息等都只能是加在程序集上)。
  
   注意:程序集是一个逻辑组合,它可以包含很多个文件。大多数程序集(比如使用Visual Studio.NET创建的那些)一般都是单文件程序集,也就是只有一个.exe或者.dll文件(目前.NET的程序集只有这两种格式)。在这种情况下,程序集清单(manifest)直接嵌入到单文件程序集中。但是,你也可以用“程序集生成工具”(Al.exe)来创建多文件程序集。也可以只创建一个只包含清单的程序集。
  
   2.强命名程序集(Strong Name Assembly)的概念
   因为不同的公司可能会开发出有相同名字的程序集来,如果这些程序集都被复制到同一 个相同的目录下,最后一个安装的程序集将会代替前面的程序集。这就是著名的Windows “DLL Hell”出现的原因。
  
   很明显,简单的用文件名来区分程序集是不够的,CLR需要支持某种机制来唯一的标识一个程序集。这就是所谓的强命名程序集。
  
   一个强命名程序集包含四个唯一标志程序集的特性:文件名(没有扩展名),版本号,语言文化信息(如果有的话),公有秘钥。
  
   这些信息存储在程序集的清单(manifest)中。清单包含了程序集的元数据,并嵌入在程序集的某个文件中。
  
   下面的字符串标识了四个不同的程序集文件:
  
   “MyType, Version=1.0.1.0,
   Culture=neutral, PublicKeyToken=bf5779af662fc055”
  
   “MyType, Version=1.0.1.0,
   Culture=en-us, PublicKeyToken=bf5779af662fc055”
  
   “MyType, Version=1.0.2.0,
   Culture=neturl, PublicKeyToken=bf5779af662fc055”
  
   “MyType, Version=1.0.2.0,
   Culture=neutral, PublicKeyToken=dbe4120289f9fd8a”
  
   如果一个公司想唯一的标识它的程序集,那么它必须首先获取一个公钥/私钥对,然后将共有秘钥和程序集相关联。不存在两个两个公司有同样的公钥/私钥对的情况,正是这种区分使得我们可以创建有着相同名称,版本和语言文化信息的程序集,而不引起任何冲突。
  
   与强命名程序集对应的就是所谓的弱命名程序集。(其实就是普通的没有被强命名的程序集)。两种程序集在结构上是相同的。都使用相同的PE文件格式,PE表头,CLR表头,元数据,以及清单(manifest)。二者之间真正的区别在于:强命名程序集有一个发布者的公钥/私钥对签名,其中的公钥/私钥对唯一的标识了程序集的发布者。利用公钥/私钥对,我们可以对程序集进行唯一性识别、实施安全策略和版本控制策略,这种唯一标识程序集的能力使得应用程序在试图绑定一个强命名程序集时,CLR能够实施某些“已确知安全”的策略(比如只信任某个公司的程序集)。
  
   3.如何创建强命名程序集(Strong Name Assembly)
  
   创建一个强命名程序集首先需要获得一个用强命名实用工具
   (Strong Name Utility,即SN.exe,.NET SDK自带)产生的密钥。
   下面简要介绍一下SN.exe的一些用法。 要产生一个公钥/私钥对:
  
   a)SN –k MyCompany.Keys
   该命名告诉SN.exe创建一个名为MyCompany.keys的文件。MyCompany.keys文件将包含以对以二进制格式存储的公有密钥和私有密钥。
  
   b)查看公有密钥
   首先生成一个只包含公有密钥的文件: SN –p
   MyCompany.keys MyCompany.PublicKey
   然后用-tp参数查看:SN –tp MyCompany.PublicKeys
   Public key is
  
   00240000048000009400000006020000002400005253413
  
   10004000001000100bb7214723ffc13901343df4b9c464ebf
  
   7ef4312b0ae4d31db04a99673e8163768cc0a2a7062e731d
  
   beb83b869f0509bf8009e90db5c8728e840e782d2cf928dae
  
   35c2578ec55f0d11665a30b37f8636c08789976d8ee9fe9a5
  
   c4a0435f0821738e51d6bdd6e6711a5acb620018658cce93
  
   df37d7e85f9a0104a5845053995ce8
  
   Public key token is 2dc940d5439468c2
  
   创建好了公钥/私钥对,创建强命名程序集就很容易了。只需要把System.Reflection.AssemblyKeyFileAttribute特性加入到源代码中就可以了:?[assembly:AssemblyKeyFile("MyCompany.keys")]
  
   说明:公钥/私钥对文件的扩展名可以是任意的(也可以没有),因为编译的时候都是以元数据的格式读取的。
  
   4.程序集的部署方式
   一个程序集有两种部署方式:
   a)私有方式 和应用程序部署在同一目录下的程序集称作私有部署程序集弱命名程序集只能进行私有部署
  
   b)全局方式
   全局部署方式将程序集部署在一些CLR已确知的地方,当CLR搜索程序集时,它会知道到这些地方去找。强命名程序集既可以进行私有部署,也可以进行全局部署。
  
   程序集种类
   是否可以进行私有部署
   是否可以进行全局部署
  
   普通程序集
   是
   否
  
   强命名程序集
   是
   是
  
  
   5.如何部署强命名程序集(Strong Name Assembly)和GAC
   a)GAC的概念
   如果一个Assembly要被多个应用程序访问,那么他就必须放在一个CLR已确知的目录下,并且CLR在探测到有对该Assembly的引用时,它必须能自动到该目录下寻找这个程序集。这个已确知的目录称作GAC(Global Assembly Cache),就是全局程序集缓存。它一般位于下面的目录下::/Windows/Assembly/GAC。
   GAC的作用就是提供给CLR一个已知的确定的目录去寻找引用的 程序集
  
   b)GAC的内部结构
   GAC是一个特殊的结构化的目录,用Windows Explorer浏览你会以为它只是一个包含很多程序集的普通目录。其实不是这样的,在命令行下查看,你会发现它实际上包含很多子目录,子目录的名字和程序集的名称是相同的但它们都不是实际的程序集实际的程序集位于程序集名对应的目录下。比如进入GCFWK子目录,我们会发现其中又有很多的子目录。 机器内每一个安装到GAC的GCFWK.dll在GCFWK中都会有一个子目录。 
   
   这里只 有一个目录表明只有一个版本的GCFWK程序集被安装。实际的程序集保存在每一个对应的版本目录下。目录的名称以下划线的形式分割为“(Version)_(Culture)_(PublicKeyToken)”。
  
   GCFWK的语言文化信息为netture,就表示为0.0.0__bf5779af662fc055”。 表示得意义是: “GCFWK, Version=1.0.0.0, Culture=neutral,PublicKeyToken=bf5779af662fc055” 如果语言文化信息为”ja”,就表示”1.0.0.0_ja_bf5779af662fc055”
  
   表示得意义是: “GCFWK, Version=1.0.0.0, Culture=ja, PublicKeyToken=bf5779af662fc055”
  
   c)部署强命名程序集到GAC
  
   GAC包含很多子目录,这些子目录是用一种算法来产生的,我们最好不要手动将程序集拷贝到GAC中,相反,我们应使用工具来完成这样的工作。因为这些工具知道GAC的内部结构J
  
   在开发和测试中,最常用的工具就是GACUtil.exe。 在GAC中注册程序集跟COM注册差不多,但相对更容易:
   1.把程序集添加到GAC中: GACUtil /i sample.dll (参数/i是安装的意思)
   2.把程序集移出GAC GACUtil /u sample.dll (参数/u就移除的意思)
   注意:不能将一个弱命名程序集安装到GAC中。
   如果你试图把弱命名程序集加入到GAC中,会收到错误信息:”
   Failure adding assembly to the cache: Attempt to install an assembly without a strong name”
   d)强命名程序集的私有部署
  
   把程序集安装到GAC有几个好处。首先,GAC使得很多程序可以共享程序集,这从整体上减少了使用的物理内存;其次,我们很容易将一个新版的程序集部署到GAC中,并通过一种发布者策略(差不多就是一种重定向方法,比如将原来引用版本为1.0.0.0程序集的程序,通过更改它的配置文件,转而让程序去引用版本为2.0.0.0的程序集)来使用新版本;最后,GAC还提供了对不同版本程序集的并存(side-by-side)管理方式。但是,GAC的安全策略通常只允许管理员更改,同时,向GAC中安装程序集也破坏了.NET框架的简单拷贝部署的许诺。
  
   除了向GAC或者以私有部署方式部署强命名程序集之外,我们还可以将强命名程序集部署在仅为一小部分程序知道的某个任意目录下。配置每一个应用程序的XML配置文件,让它们指向一个公有目录,这样,在运行时,CLR将知道到哪里去找这个强命名程序集。但这样又有可能会引发”DLL Hell”的问题,因为没有哪个程序可以控制这个程序集何时被卸载。这在.NET中也是不被鼓励的。
  
   6.并行执行(Side - By - Side)
   这里是一个强命名程序集的例子:
   首先有一个App.exe程序集,它绑定这一个版本为2.0.0.0的Calculus.dll程序集和一个版本为3.0.0.0的AdvMath.dll程序集。而AdvMath.dll程序集同时又绑定着一个版本为1.0.0.0的Calculus.dll的程序集。   
   An application that requires different versions of the Calculus.dll assembly
  
   CLR能够将名程相同但路径不同的多个文件加载到同一个地址空间,这在.NET中称为并存执行(Side-By-Side)执行,它是解决Windows中”DLL Hole”问题的关键技术
  
   比如.NET Framework 1.0 和.NET Framework 1.1都可以同是在一台机器上运行,这用到的就是并存执行(Side-By-Side)执行。
  
   更多参考:
   ms-help://MS.MSDNQTR.2004APR.1033/cpguide/html/cpconSide-by-SideExecutionTop.htm
   http://www.microsoft.com/china/msdn/archives/library/dnnetdep/
   html/sidexsidenet.asp
  
   7.CLR如何解析类型引用
   CLR在解析一个被引用的类型时,它可以在以下三个地方的其中之一找到该类型:
   ·同一个文件?
   对同一个文件中类型的访问在编译时就已经确定下来了,CRL直接从该文件中加载被引用的类型。完成加载后,程序将继续运行。
   ·不同的文件,相同的程序集
   CLR首先确保被引用的文件在当前程序集清单中的FileDef表内。CLR然后会在加载程序集清单文件的目录中查找被引用的文件。该文件被加载的同时,CLR会检查它的散列值以确保文件的完整性,之后便会找到相应的类型成员。完成加载后,程序将继续运行
   ·不同的文件,不同的程序集
   当被引用的程序集在一个不同的程序集文件中时,CLR会首先加载包含被引用程序集的清单所在的文件。如果该文件没有包含所需要的类型,CLR会根据此清单文件加载适当的文件。这样也会找到相应类型的成员。完成加载后,程序将继续运行。
  
   如果在解析类型引用的过程中出现任何错误,比如文件找不到,文件不能被加载,散列值不匹配等等,系统将会抛出相应的异常。 
   
  
   
   程序集种类
   是否可以引用 弱命名程序集
   是否可以引用 强命名程序集
  
   普通程序集
   是
   是
  
   强命名程序集
  
   是 
    配置文件概述:
应用程序配置文件是标准的 xml 文件,xml 标记和属性是区分大小写的。它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序。配置文件的根节点是configuration。我们经常访问的是appsettings,它是由.net预定义配置节。我们经常使用的配置文件的架构是象下面的形式。先大概有个印象,通过后面的实例会有一个比较清楚的认识。下面的“配置节”可以理解为进行配置一个xml的节点。

 常见配置文件模式:

            //配置节声明区域,包含配置节和命名空间声明
               

              //配置节声明
                    //定义配置节组
                 
       //配置节组中的配置节声明
        //预定义配置节
          //配置节设置区域

 2.        只有appsettings节的配置文件及访问方法

下面是一个最常见的应用程序配置文件的例子,只有appsettings节。


    <appsettings>
        connectionstring" value="user id=sa;data source=.;password=;initial catalog=test;provider=sqloledb.1;" />
       
   

下面来看看这样的配置文件如何方法。

string _connectionstring=configurationsettings.appsettings["connectionstring"];

使用configurationsettings类的静态属性appsettings就可以直接方法配置文件中的配置信息。这个属性的类型是namevaluecollection。

 3.        自定义配置文件
3.1 自定义配置节

一个用户自定义的配置节,在配置文件中分为两部分:一是在配置节中声明配置节(上面配置文件模式中的“

”),另外是在之后设置配置节(上面配置文件模式中的“”),有点类似一个变量先声明,后使用一样。声明一个配置文件的语句如下:

 


声明新配置节,即可创建新配置节。

name:自定义配置节的名称

type:自定义配置节的类型,主要包括system.configuration.singletagsectionhandler、system.configuration.dictionarysectionhandler、system.configuration.namevaluesectionhandler。

不同的type不但设置配置节的方式不一样,最后访问配置文件的操作上也有差异。下面我们就举一个配置文件的例子,让它包含这三个不同的type。



   
       


       

       

   
   
   
   
       
   

   
       
   
   

我们对上面的自定义配置节进行说明。在声明部分使用

声明了一个配置节它的名字叫test1,类型为singletagsectionhandler。在设置配置节部分使用     设置了一个配置节,它的第一个设置的值是hello,第二个值是world,当然还可以有更多。其它的两个配置节和这个类似。
下面我们看在程序中如何访问这些自定义的配置节。我们用过configurationsettings类的静态方法getconfig来获取自定义配置节的信息。

public static object getconfig(string sectionname);

下面是访问这三个配置节的代码:

            //访问配置节test1
            idictionary idtest1 = (idictionary)configurationsettings.getconfig("test1");
            string str = (string)idtest1["setting1"] +" "+(string)idtest1["setting2"];
            messagebox.show(str);        //输出hello world

            //访问配置节test1的方法2
            string[] values1=new string[idtest1.count];
            idtest1.values.copyto(values1,0);
            messagebox.show(values1[0]+" "+values1[1]);    //输出hello world
           
            //访问配置节test2
            idictionary idtest2 = (idictionary)configurationsettings.getconfig("test2");
            string[] keys=new string[idtest2.keys.count];
            string[] values=new string[idtest2.keys.count];
            idtest2.keys.copyto(keys,0);
            idtest2.values.copyto(values,0);
            messagebox.show(keys[0]+" "+values[0]);
           
            //访问配置节test3
            namevaluecollection nc=(namevaluecollection)configurationsettings.getconfig("test3");
            messagebox.show(nc.allkeys[0].tostring()+" "+nc["hello"]);    //输出hello world

通过上面的代码我们可以看出,不同的type通过getconfig返回的类型不同,具体获得配置内容的方式也不一样。 配置节处理程序
 返回类型
 
singletagsectionhandler
 systems.collections.idictionary
 
dictionarysectionhandler
 systems.collections.idictionary
 
namevaluesectionhandler
 systems.collections.specialized.namevaluecollection
 

  3.2 自定义配置节组
配置节组是使用元素,将类似的配置节分到同一个组中。配置节组声明部分将创建配置节的包含元素,在元素中声明配置节组,并将属于该组的节置于元素中。下面是一个包含配置节组的配置文件的例子:

 



   
       
           


       
   
   
   
       
           
       

   


 下面是访问这个配置节组的代码:
            namevaluecollection nc=(namevaluecollection)configurationsettings.getconfig("testgroup/test");
            messagebox.show(nc.allkeys[0].tostring()+" "+nc["hello"]);    //输出hello world