【主要內容】
@將源代碼編譯爲託管模塊
@將模塊組合爲程序集
@共享程序集(強簽名)
【概念闡述】
@將源代碼編譯爲託管模塊
1、在.NET框架裏,我們可以用任何支持CLR(Common Language Runtime)的編程語言來創建源代碼文件,然後用相應的編譯器來做
語法檢查和源代碼分析,不管使用的是何種編程語言及編譯器,最後生成的結果都是一個託管模塊(Managed Module)。
高級語言代碼 --> 託管模塊(包含IL和元數據的PE文件)--> CPU指令(運行時從IL(Intermediary Language中間語言)編譯產生)
2、託管模塊是一個需要CLR才能執行的標準PE(可移植可執行Portable Executable)文件。
PE文件是一個非常重要的概念,它由PE表頭、CLR表頭、元數據和IL(中間語言)代碼四部分組成。
PE表頭:主要指出了文件的類型,GUI(圖形用戶)、CUI(控制檯)或是DLL(在.NET中DLL特指程序集文件的一種形式,不同於以前的動態鏈接庫文件)。
另外該表頭還包括一個時間標記用於表示文件創建的時間。對於僅包含IL代碼的模塊該表頭的大部分信息會被忽略。對於包含有本地CPU代碼的模塊,
該表頭還會包含有關本地CPU代碼的一些信息。
CLR表頭:包含標識託管模塊的一些信息(可以被CLR或者一些使用工具解析)。這些信息包括託管模塊所需要的CLR版本號,一些標記,託管模塊入口點
方法(MethodDef)元數據標記,以及有關託管模塊的元數據、資源、強命名標記和其他一些意義不是太大的信息的位置和尺寸。
【元數據】(表):該表用於描述代碼中用到的類型和成員,描述的類型和成員有兩類,一是源代碼中定義,一是引用的。元數據總是和包含IL代碼的文件相關聯,實際上元數據總是和這些代碼一起被嵌入到同一個exe/dll文件中,編譯器總是同時產生元數據和IL代碼,並且總是同時將它們嵌入到生產的託管模塊中,兩者始終保持同步。
元數據的用處:
@元數據省去了源代碼編譯時對頭文件和庫文件的需求。這是因爲在含有實現類型和成員的IL代碼文件中,已經包含了所有被引用的類型和成員的信息,
編譯器可以直接從託管模塊中讀取元數據來獲得這些信息。
@Visual Studio.NET的智能感知(Intellisense)功能是通過分析元數據來實現的。
@CLR代碼驗證過程可以利用元數據來確保代碼僅執行“安全”的操作。
@利用元數據,可以將一個對象的字段序列化後發送到遠程機器,然後在遠程機器上執行反序列化,從而重新創建對象和它的狀態。
@利用元數據,垃圾收集器可以追蹤對象的生存期。對於任何對象,垃圾收集器都能通過元數據來確定該對象的類型,並且可以獲知該對象的哪些字段引
用了其他對象。
IL代碼:編譯器在編譯源代碼是產生的指令,CLR運行時將它們編譯成本地CPU指令。
3、【程序集】
程序集(assembly)是一個抽象的概念,它是一個或多個託管模塊,以及一些源文件的邏輯組合。
可以通過Assembly Linker(【AL.exe】)將這些文件組合到一個程序集中。
4、IL(Intermediary Language中間語言)
高級語言編譯成的IL代碼,可以用微軟提供的【ILDasm.exe】進行反編譯。可以直接用IL來編寫代碼,微軟也提供了一個彙編器【ILAsm.exe】供使用。
高級語言提供的都只是CLR全部功能的一個子集,IL彙編語言包含CLR全部功能,當我們選擇的語言沒有提供我們需要的CLR的某些功能時,則可以選擇IL
彙編語言,或另一個提供了該功能的高級語言來編寫這部分程序。
【實驗步驟】
=========
@查看元數據
=========
1)用notepad新建文件D:\mytest\App.cs並輸入以下實驗代碼
/////////////////////////////////////////////////////////////////////////// using System; // 自定義類型 App。 public class App{ private Int32 afield; static public void Main(System.String[] args){ // 引用類型 System.Console。 System.Console.WriteLine("Hi"); } // 構造函數 public App(Int32 fi){ afield = fi; } // 析構函數 ~App(){} // 重載操作符 public static Boolean operator == (App a1, App a2){ return true; } public static Boolean operator != (App a1, App a2){ return false; } public static App operator + (App a1, App a2){ return null; } // 屬性 public Int32 Aproperty{ get{ return afield;} set{ afield = value;} } // 索引器 public String this[Int32 x]{ get{ return null; } set{} } event EventHandler AnEvent; } /**////////////////////////////////////////////////////////////////////////////
2)打開【SDK命令提示】,輸入命令:
D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>cd d:\mytest
D:\mytest>【csc.exe 】/out:App.exe /t:exe /r:MScorLib.dll App.cs
3)使用 ILDAsm.exe 來查看元數據
D:\mytest>ildasm.exe App.exe
【視圖】--> 【元信息】--> 【顯示】
或使用快捷鍵:Ctrl + M
=========================
@將源代碼編譯爲模塊並加入到程序集中
=========================
1)建立三個源代碼文件
//========================== //D:\mytest\ModuleDemo.cs using System; public class ModuleDemo{ public static void Main(string[] args){ ModuleA a = new ModuleA(); ModuleB b = new ModuleB(); Console.WriteLine(a.ToString() + "\n" + b.ToString()); } } //D:\mytest\ModuleA.cs public class ModuleA{ } //D:\mytest\ModuleB.cs public class ModuleB{ private ModuleA moduleA; } //==========================
將ModuleA.cs和ModuleB.cs編譯爲模塊
D:\mytest>csc /t:module ModuleA.cs
D:\mytest>csc /t:module ModuleB.cs
ModuleB.cs(2,10): error CS0246: 找不到類型或命名空間名稱“ModuleA”(是否缺少 using 指令或程序集引用?)
D:\mytest>csc /t:module /addmodule:ModuleA.netmodule ModuleB.cs
將ModuleDemo.cs編譯爲程序集
D:\mytest>csc /t:exe ModuleDemo.cs.
ModuleDemo.cs(3,3): error CS0246: 找不到類型或命名空間名稱“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(3,19): error CS0246: 找不到類型或命名空間名稱“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,3): error CS0246: 找不到類型或命名空間名稱“ModuleB”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,19): error CS0246: 找不到類型或命名空間名稱“ModuleB”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(5,3): error CS0103: 當前上下文中不存在名稱“Console”
雖然B中添加了ModuleA.netmodule,同時ModuleDemo.cs編譯時加入了ModuleB.cs,但仍需要添加ModuleA.netmodule。
D:\mytest>csc /t:exe /r:MSCorLib.dll /addmodule:ModuleB.netmodule ModuleDemo.cs
ModuleDemo.cs(4,3): error CS0246: 找不到類型或命名空間名稱“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,19): error CS0246: 找不到類型或命名空間名稱“ModuleA”(是否缺少 using 指令或程序集引用?)
D:\mytest>csc /t:exe /r:MSCorLib.dll /addmodule:ModuleA.netmodule,ModuleB.netmodule ModuleDemo.cs
執行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB
將ModuleA.netmodule和ModuleB.netmodule移動到新建文件夾D:\mytest\mymodule下
D:\mytest>ModuleDemo.exe
未處理的異常: System.IO.FileNotFoundException: 未能加載文件或程序集“ModuleA.netmodule”或它的某一個依賴項。系統找不到指定的文件。
文件名:“ModuleA.netmodule” ---> System.IO.FileNotFoundException: 系統找不到指定的文件。 (異常來自 HRESULT:0x80070002)在 ModuleDemo.Main(String[] args)
建立配置文件
D:\mytest\ModuleDemo.exe.config
<?xml version="1.0"?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="mymodule"/> </assemblyBinding> </runtime> </configuration>
錯誤同上。該配置文件用來查找程序集的,模塊必須依賴於程序集。參考【程序集與模塊管理】。
將ModuleA.cs和ModuleB.cs分別編譯爲dll文件。
D:\mytest>csc /t:library ModuleA.cs
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs
新建文件夾:D:\mytest\mydll
將ModuleA.dll移動到D:\mytest\mydll
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs
error CS0006: 未能找到元數據文件“ModuleA.dll”
修改剛纔建立的配置文件
<probing privatePath="mydll"/>
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs
error CS0006: 未能找到元數據文件“ModuleA.dll”
說明該配置文件對編譯器不起作用。用如下命令纔可以。
D:\mytest>csc /t:library /r:mydll\ModuleA.dll ModuleB.cs
將生成的ModuleB.dll也移動到mydll文件夾下。
D:\mytest>csc /t:exe /r:MSCorLib.dll,ModuleA.dll,ModuleB.dll ModuleDemo.cs
error CS0006: 未能找到元數據文件“ModuleA.dll”
error CS0006: 未能找到元數據文件“ModuleB.dll”
要使用如下命令編譯。
D:\mytest>csc /t:exe /r:MSCorLib.dll,mytest\ModuleA.dll,mytest\ModuleB.dll ModuleDemo.cs
執行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB
修改配置文件
<probing privatePath="myxxx"/>
D:\mytest>ModuleDemo
未處理的異常: System.IO.FileNotFoundException: 未能加載文件或程序集“ModuleA, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null”或它的某一個依賴項。
系統找不到指定的文件。文件名:“ModuleA, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null”在 ModuleDemo.Main(String[] args)
警告: 程序集綁定日誌記錄被關閉。
要啓用程序集綁定失敗日誌記錄,請將註冊表值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD)設置爲 1。
注意: 會有一些與程序集綁定失敗日誌記錄關聯的性能損失。
要關閉此功能,請移除註冊表值 [HKLM\Software\Microsoft\Fusion!EnableLog]。
修改配置文件
<probing privatePath="mydll"/>
執行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB
刪除mydll下的ModuleA.dll和ModuleB.dll,將mymodule下的ModuleA.netmodule和ModuleB.netmodule移動到mydll下,然後執行命令:
D:\mytest>al.exe /out:mydll\ModuleAB.dll /t:library mydll\ModuleA.netmodule mydll\ModuleB.netmodule
D:\mytest>csc /t:exe /r:MSCorLib.dll,mydll\ModuleAB.dll ModuleDemo.cs
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB
刪除ModuleA.netmodule後再執行命令:
D:\mytest>ModuleDemo.exe
未處理的異常: System.IO.FileNotFoundException: 未能加載文件或程序集“ModuleB.netmodule”或它的某一個依賴項。系統找不到指定的文件。
文件名:“ModuleB.netmodule” ---> System.IO.FileNotFoundException: 系統找不到指定的文件。 (異常來自 HRESULT:0x80070002) 在 ModuleDemo.Main(String[] args)
撤銷刪除。
=====================
@將程序集安裝到全局程序集緩存
=====================
新建源代碼文件 NamedDemo.cs
public class NamedDemo{ }
編譯爲dll文件。
D:\mytest>csc /t:library NamedDemo.cs
D:\mytest>【gacutil】 /i NamedDemo.dll
將程序集添加到緩存失敗: 試圖安裝沒有強名稱的程序集
獲取密鑰文件。
創建一個名爲MyCompany.keys(名稱自定)的文件,生成一對以二進制而是存儲的共有密鑰和私有密鑰。
D:\mytest>【SN】 -k MyCompany.keys
密鑰對被寫入 MyCompany.keys
從密鑰對中提取公鑰,並將其複製到一個單獨的文件MyCompany.Publickey(名稱自定)中。
D:\mytest>SN -p MyCompany.keys MyCompany.Publickey
公鑰被寫入 MyCompany.Publickey
生成強簽名的程序集。
修改源代碼。
using System.Reflection; [assembly:AssemblyKeyFile("MyCompany.keys")] public class NamedDemo{ }
D:\mytest>csc /t:library NamedDemo.cs
NamedDemo.cs(2,11): warning CS1699: 使用命令行選項“/keyfile”或適當的項目設置代替“AssemblyKeyFile”
根據提示,修改源代碼
public class NamedDemo{ }
使用私鑰進行程序集簽名,執行命令:
D:\mytest>csc /t:library /keyfile:MyCompany.keys NamedDemo.cs
D:\mytest>gacutil /i NamedDemo.dll
程序集已成功添加到緩存中
D:\mytest>gacutil /u NamedDemo.dll
找不到與以下內容匹配的程序集: NamedDemo.dll
卸載的程序集數 = 0
失敗次數 = 0
D:\mytest>gacutil /u NamedDemo
程序集: NamedDemo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=faeeaad7fd870aaa, processorArchitecture=MSIL
已卸載: NamedDemo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=faeeaad7fd870aaa, processorArchitecture=MSIL
卸載的程序集數 = 1
失敗次數 = 0
查看公鑰
D:\mytest>sn -tp MyCompany.Publickey
公鑰爲...
公鑰標記爲 faeeaad7fd870aaa
注意此處顯示的公鑰標記與卸載程序集時顯示的PublickeyToken是一樣的。
不能查看私鑰。
D:\mytest>sn -tp MyCompany.keys
未能將密鑰轉換爲標記 -- 程序集“(null)”的公鑰無效。
不能使用公鑰加密。
D:\mytest>csc /t:library /keyfile:MyCompany.Publickey NamedDemo.cs
error CS1548: 對程序集“d:\mytest\NamedDemo.dll”簽名時加密失敗
--“密鑰文件“d:\mytest\MyCompany.Publickey”缺少簽名所需的私鑰”
【參考資源】
SDK命令提示 http://msdn.microsoft.com/zh-cn/library/ms229859(VS.80).aspx
csc.exe http://msdn.microsoft.com/zh-cn/library/2fdbz5xd(VS.80).aspx
程序集 http://msdn.microsoft.com/zh-cn/library/hk5f40ct.aspx
ILAsm.exe http://msdn.microsoft.com/zh-cn/library/496e4ekx.aspx
ILDasm.exe http://msdn.microsoft.com/zh-cn/library/f7dy01k1.aspx
元數據 http://msdn.microsoft.com/zh-cn/library/4y7k7c6k.aspx
AL.exe http://msdn.microsoft.com/zh-cn/library/c405shex(VS.80).aspx
定位程序集 http://msdn.microsoft.com/zh-cn/library/yx7xezcf.aspx
程序集與託管模塊 http://www.cnblogs.com/JoeDZ/archive/2008/06/25/1229662.html
gacutil.exe http://msdn.microsoft.com/zh-cn/library/ex0ss12c.aspx
sn.exe http://msdn.microsoft.com/zh-cn/library/k5b5tt23.aspx
《Microsoft.NET框架程序設計》
《C#高級編程》(第四版)
學習筆記《Microsoft.NET框架程序設計》(Blog):http://blog.csdn.net/difall/