原文地址:https://blog.alswl.com/2011/01/gac/
GAC
GAC是什么?是用來干嘛的?GAC的全稱叫做全局程序集緩存,通俗的理解就是存放各種.net平台下面需要使用的dll的地方。GAC的具體目錄在windows/ assembly。
喜歡使用破解軟件的朋友可能會經常使用替換DLL這招,這個DLL雖然和.net下面的DLL有所不同,但大致起的作用還是一樣的:使用共同的動態鏈接庫來提高庫復用 ,減少文件體積。在.net平台之前,程序們都把可能用到的公共DLL存放到system32文件夾下。由於版本的不一致及其他原因(將控制信息存放到注冊表),造成 了很有名的DLL Hell。
最近在讀俞甲子的《程序員的自我修養》,中間花了很大的篇幅描述程序編譯中 的鏈接這個過程。而比較巧合的是,我的《C#與.NET 3.5高級程序設計》也正好讀到程序集這一塊。微軟使用了GAC來避免DLL重蹈覆轍。現在,我就來部署一個簡單的通用類庫到GAC中。
獲取強名稱(簽名)
並不是隨便一個DLL都可以放到GAC中,如果這樣的話,和之前的DLL Hell也沒什么區別了。GAC使用一個強名稱(簽名)的方式來區分不同的DLL。每個簽名 都是獨一無二的公鑰私鑰對。現在我們使用SDK的sn工具為我的DUtil庫生成一個snk簽名。
打開SDK中的Visual Studio 2008 命令提示,鍵入sn -k DUtil.snk,結果如下:
F:WorkSpace.netDUtilbinDebug>sn -k DUtil.snk
Microsoft (R) .NET Framework 強名稱實用工具 版本 3.5.21022.8
Copyright (c) Microsoft Corporation. All rights reserved.
密鑰對被寫入 DUtil.snk
這樣我們就得到了一個snk簽名文件。
為程序集設置簽名
現在我們得到了一個簽名文件,但是還沒有把這個簽名應用到程序中,在程序的assembly.cs文件中加入[assembly: AssemblyKeyFile(“dutil.snk”)],這個dutil.snk可以是絕對路徑,我使用的是主目錄中的snk文件。
編譯解決方案,生成的.dll文件就包含了簽名。
此時編譯器會出現一個警告:
警告 1 使用命令行選項“/keyfile”或適當的項目設置代替“AssemblyKeyFile” F:WorkSpace.netDUtilPropertiesAssemblyInfo.cs 38 12 DUtil
既然VS建議我們用選項設定,那我們就用項目選項頁設定key。如下圖所示:
選擇sha256 加密等級更高
PS:我測試過,生成DLL之后,就算刪除簽名使用的snk文件,依然可以正常使用。即snk文件只有在編譯時候使用。
安裝到GAC
安裝到GAC有兩種辦法,一種是把DLL拖入windows/assembly目錄下(不是復制粘貼,是拖動),另外一種是使用gacutil。
在Win7下,拖動的話會遇到權限問題,如下圖所示
在gacutil下面也會遇到如下錯誤
F:WorkSpace.netDUtilbinDebug>gacutil -i DUtil.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.21022.8
Copyright (c) Microsoft Corporation. All rights reserved.
將程序集添加到緩存失敗: 拒絕訪問。您可能沒有執行此任務的管理憑據。請與您的系統管
理員聯系以獲得幫助。
解決的辦法就是進入命令行時候用管理員身份進入,執行之后顯示如下
F:WorkSpace.netDUtilbinDebug>gacutil -i DUtil.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.21022.8
Copyright (c) Microsoft Corporation. All rights reserved.
程序集已成功添加到緩存中
這時候查看windows/assembly目錄,就會發現DUtil已經在里面了。
用TotalCommander打開windows/assembly,會發現里面有GAC/GAC_32/GAC_MSIL這幾個文件夾,我的DUtil安裝到c: WindowsassemblyGAC_MSILDUtil.1.0.0__35f4c1ba225b3cc6DUtil.dll,這個路徑包含了版本,簽名,通過 這種方式,就能避免版本和錯誤dll的問題了。
使用GAC的dll
打開任意一個項目,添加引用時候,選擇編譯出來的DUtil.dll即可。查看具體屬性,就會發現,這個DLL並未復制到本地,因為它可以從GAC中獲取了。
這時候如果我們刪除引用地方的DLL,會發現程序依然可以正確跑起來,說明我們的DLL確實來自GAC。(Update:當引用DLL文件存在時候,優先引用此DLL ,不存在才引用GAC的DLL)
PS:不要嘗試去打開windows/assembly目錄來引用,你會發現你徒勞的。
卸載GAC中的DLL
一句話搞定
C:Windowssystem32>gacutil -u DUtil
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.21022.8
Copyright (c) Microsoft Corporation. All rights reserved.
程序集: DUtil, Version=0.1.0.0, Culture=neutral, PublicKeyToken=35f4c1ba225b3cc6
, processorArchitecture=MSIL
已卸載: DUtil, Version=0.1.0.0, Culture=neutral, PublicKeyToken=35f4c1ba225b3cc6
, processorArchitecture=MSIL
卸載的程序集數 = 1
失敗次數 = 0
這樣就把特定目標的DLL卸載了。
參考文章
除了上文提到的兩本書,還參考了MSDN中的815808文章,文章名如何在 Visual C# .NET 中將程序集安裝到全局程序集緩存中。