close


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),就是全局程序集緩存。 它一般位於下面的目錄下:<System Drive>:\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會根據此清單文件加載適當的文件。 這樣也會找到相應類型的成員。 完成加載後,程序將繼續運行。

如果在解析類型引用的過程中出現任何錯誤,比如文件找不到,文件不能被加載,散列值不匹配等等,系統將會拋出相應的異常。
程序集種類
是否可以引用弱命名程序集
是否可以引用強命名程序集

普通程序集



強命名程序集

第二部分(例子)

下面是一個完整的例子來逐一說明上面所提到的概念,以加深理解。
整個例子包含7個文件(在主目錄下):
主目錄為…/Assembly ----源程序目錄
…/Assembly/Bin ---- 編譯結果輸出的目錄,也就是應用程序主目

錄。



文件名
類型
說明

App.cs
Code源文件
主程序,包含程序入口,
屬於namespace1

ClassA.cs
Code源文件
類型A,包含一個靜態方法,
屬於namespace1

ClassB.cs
Code源文件
類型B,包含一個靜態方法,
屬於namespace2

AssemblyInfo.cs
Code源文件
包含程序集簽名信息,
版本信息等

App.Key
公鑰/私鑰對文件
用來給程序集簽名,
生成強命名程序集

App.PublicKey
只包含共有密鑰
只儲存共有密鑰,
用SN.exe來查看

App.exe.config
Xml格式配置文件
App.exe的應用程序配置文件

源代碼
App.cs
namespace namespaceA {
public class App {
static void Main (string[] args) {
System.Console.WriteLine(ClassA.ShowMe());
System.Console.WriteLine(namespaceB.ClassB.ShowMe());
}
}
}
ClassA.cs
namespace namespaceA {
public class ClassA {
public static string ShowMe()
{
return "This is ClassA";
}
}
}

ClassB.cs
namespace namespaceB {
public class ClassB {
public static string ShowMe() {
return "This is ClassB";
}
}
}
AssemblyInfo.cs
////////////////////////////////////////////////// //////////////////////////////
// Module: AssemblyInfo.cs
////////////////////////////////////////////////// //////////////////////////////
using System.Reflection;
////////////////////////////////////////////////// //////////////////////////////
// Set CompanyName, LegalCopyright, and LegalTrademarks
[assembly: AssemblyCompany("App Company")]
[assembly: AssemblyCopyright("Copyright (C) 2004 @ App Company")]
[assembly: AssemblyTrademark("App is a test only program")]
////////////////////////////////////////////////// //////////////////////////////
// Set ProductName and ProductVersion
[assembly: AssemblyProduct("App Product")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]
////////////////////////////////////////////////// //////////////////////////////
// Set FileVersion and AssemblyVersion

[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyTitle("App type assembly")]
[assembly: AssemblyDescription("App Aassembly is a test only assembly")]

////////////////////////////////////////////////// //////////////////////////////
// Set Culture
[assembly: AssemblyCulture("")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("App.key")]
[assembly: AssemblyKeyName("")]
App.key和App.PublicKey是二進制格式存儲的,不能直接查看。 後面例子中會用到

1. 把源代碼編譯為託管模塊(Managed Module)
csc /out:bin/classA.module /t:module classA.cs
參數:
/out: 輸出路徑
/t: 輸出格式。 可以有四種,分別是:
library ---- DLL程序集
exe ---- 控制台可執行程序(也是程序集的一種)
winexe ---- Windows可執行程序(同樣也是程序集的一種)
module ---- 託管模塊(程序集的一部分)

說明: 託管模塊的的擴展名可以是任意的(也可以沒有),因為編譯的時候都是以元數據的格式讀取的。

2. 把源代碼編譯為程序集(Assembly)
l 把ClassB編譯為一個單文件程序集
csc /out:bin/classB.dll /t:library classB.cs
l 把App.cs,ClassA.module和ClassB.dll編譯為一個多文件程序集
csc /out:bin/App.exe /t:exe app.cs /addmodule:bin/classA.module /r:bin/classB.dll
參數:
/addmodule: 把託管模塊添加到程序集中/r: 添加引用說明: 上面生成的程序集因為沒有經過公鑰/私有簽名,所以生成的是非強命名類型的程序集。 生成的程序集App.exe的清單中只包含對classA.module託管模塊的說明,並不包classA.module的元數據,所以App.exe和classA.moudle必須在同一目錄中。 App.exe在運行時,如果用到對classA.module中類型的引用,則會去classA.moudel文件進行查找,如果classA.moude文件不存在,則會引發System.IO.FileNotFoundException。 如果App.exe不會用到class.module中的類型,則classA.module存不存在都不會對App.exe的執行產生任何影響(這就是上面提到的Assembly的好處之一,Assembly只是一個邏輯上的組合)。
App.exe還用到了對ClassB.dll的引用,因為classB.dll不是一個強命名類型,所以它只能進行私有部署,可以和App.exe放在一起,也可以放在主目錄下的其他子目錄下。 (後面通過應用程序更改配置文件,可以重定向指classB.dll的引用)。

3. 更改應用程序配置文件(App.exe.config),重定向對classB.dll的引用。

現在App.exe,classA.moudle和classB.dll都在Bin目錄下,app.exe在運行時會找到所有它需要的類型,所以運行正常。

如果把在Bin目錄下新建一個目錄,比如sub,並把classB.dll移動到sub目錄下,再運行App.exe就會出錯。 同樣會引發System.IO.FileNotFoundException錯誤,因為App.exe運行時需要的classB類型找不到。 這時候就需要更改添加(如果沒有)或更改應用程序配置文件,應用程序配置文件是一個xml格式的配置文件,和web.config文件的作用差不多,是配置應用程序運行時的行為的。

注意:配置文件的名字必須是應用程序名字再加一個.config,且必須在同一目錄下
詳細信息參考: ms-help://MS.MSDNQTR.2004APR.1033/cpguide/html/
cpconnetapplicationconfigurationscenarios.htm

App.exe.config文件的內容:
<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBindingxmlns="urn:schemas-microsoft-com:asm.v1">
<probingprivatePath="sub"/>
</assemblyBinding>
</runtime>
</configuration> 當App.exe運行時,它就會在主目錄下的sub目錄中找到classB.dll,然後繼續行。
說明:
當CLR需要定位一個程序集時,它將掃描應用程序的幾個子目錄,下面是才CLR掃描一個程序集時的順序:
…/Assembly/Bin/classB.DLL.
…/Assembly/Bin/classB/classB.DLL.
…/Assembly/Bin/sub/classB.DLL.
…/Assembly/Bin/sub/classB/classB.DLL.
…/Assembly/Bin/classB.EXE.
…/Assembly/Bin/classB/classB.EXE.
…/Assembly/Bin/sub/classB.EXE.
…/Assembly/Bin/sub/classB/classB.EXE.

注意: 如果App.exe引用的是強命名程序集,CLR會首先在GAC中查找,然後才按照上面的順序查找。

4. 創建和查看公鑰/私鑰對文件
創建公鑰/私鑰對文件可以用.NET SDK自帶的工具(SN.exe)來創建。 首先,創建一個公鑰/私鑰對文件
SN -k App.key
然後,用這個文件創建只包含共有密鑰的文件:
SN -p App.key App.publickey
然後用-tp參數查看
SN –tp App.publickey
5. 創建強命名程序集
有了公鑰/私鑰對,創建強命名程序集就很容易了。 只需要把
System.Reflection.AssemblyKeyFileAttribute特性加到源代碼中就可以了。
[assembly: AssemblyKeyFile("App.key")]
一般都加到AssemblyInfo.cs文件中。
現在重新build classB.cs,得到的將是一個強命名的程序集:
csc /out:bin/classB.dll /t:library classB.cs AssemblyInfo.cs

用ILDasm.exe查看,你會發現,在Assembly中的Public Key會有一大串值,這個就是程序集的公有密鑰,它保證了整序集的唯一性。
[img][/img]
6. 把強命名的程序集classB.dll加入到GAC中。
使用工具GACUtil.exe
把classB.dll加入到GAC中:
GACUtil /I classB.dll
刪除掉classB.dll,然後重新Build App.exe:
csc /out:bin/app.exe /t:exe app.cs /addmodule:bin/classA.module /r:classB.dll
App.exe運行正確,表明classB.dll已經成功加入到GAC中,成為一個共享程序集了。 程序集相互引用的規則:

程序集種類
是否可以引用弱命名程序集
是否可以引用強命名程序集

普通程序集



強命名程序集




把classB.dll移出GAC: GACUtil /u classB
注意:
移出的時候不能指定擴展名(因為在GAC中每一程序集對於對應的都是一個目錄,而不是實際的程序集)。

如果classB.dll不是一個強命名的程序集,而你想把app.exe build為一個強命名的,就會出錯:
error CS1577: Assembly generation failed -- Referenced assembly 'ClassB' does not have a strong name
你可以試一試。 J

7. 並行執行(Side-By-Side)的例子
http://www.microsoft.com/china/msdn/archives/library/
dnnetdep/html/sidexsidenet.asp


轉載:http://www.cnblogs.com/cqumsegao/articles/414847.html

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 UM程式研究日誌 的頭像
    UM程式研究日誌

    UM程式研究日誌

    UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()