Quantcast
Viewing all articles
Browse latest Browse all 428

【茶包射手日記】Visual Studio 編譯自動帶入相依 DLL 問題

同事報案,在 Visual Studio 從私有 NuGet 伺服器安裝我寫的共用元件,該元件參照了 Managed ODP.NET 但沒在 NuGet Package 宣告相依性,理論上不會一併安裝 Managed ODP.NET NuGet Package,但同事發現建置後 bin 目錄卻神奇地出現 Oracle.ManagedDataAccess.dll。試著在我的電腦演練相同操作,bin 目錄並不會出現 Oracle.ManagedDataAccess.dll!很明顯這又是我不了解的「魔法」,啟動調查,試著找出 Visual Studio 自動帶出相關 DLL 的原理。

起初我懐疑有某個聰明的外掛套件從中幫忙,自動補齊該共用元件需要的第三方程式庫,Visual Studio 改用安全模式也是同樣結果,不在場證明 GET,無保請回。

接著我懐疑跟註冊 GAC 有關,推測同事的 Managed ODP.NET 有註冊 GAC,Visual Studio 能成功找到 DLL 寫入 bin,我的沒註冊 GAC 不知去哪裡找 DLL,補不了檔案。

不料,檢查 GAC C:\Windows\assembly\GAC_MSIL\Oracle.ManagedDataAccess 資料夾的結果出乎意料,我的機器有同事沒有,意味我有註冊 GAC 而同事沒有,跟前面的推論完全相反啊啊啊~(啪!我聽到清脆的打臉聲,Orz)

別怕,拎杯機靈勝過尚書大人,立刻找到合理新解釋:因為我的 Managed ODP.NET 已註冊 GAC,所以不需要複製到 bin!(咳,「事前信心十足大膽預測,事後又能娓娓道來為何失準」是「偽專家」的必要技能,我真不愧是見過大風大浪的老屁股呀)

但這裡有個問題:如果同事沒有註冊 Managed ODP.NET,Visual Studio 是怎麼找到 Oracle.ManagedDataAccess.dll 放進 bin 裡?

這類疑難雜症,交給茶包一哥 Process Monitor就對了!

開啟 Process Monitor 監控專案建置過程的 Registry 與檔案存取,我學到一件事:建置作業由 MSBuild.exe 負責,它會先搜尋共用元件所在目錄看 Oracle.ManagedDataAccess.dll 有沒有跟它放在一起(在本例為 NuGet Packages 目錄,但我沒有包進去),若沒找到 MSBuild 會接著尋找 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\ Regitry,同事在該機碼下有個 Oracle.ManagedDataAccess 指向 Oracle.ManagedDataAccess.dll,MSBuild 就靠著它找到 DLL 並複製到 bin 目錄下。

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\Oracle.ManagedDataAccess]
@="E:\\Oracle\\odp.net\\managed\\common\\Oracle.ManagedDataAccess.dll"

(我的電腦在 AssemblyFoldersEx 也有機碼指向 Managed ODP.NET,但名稱不同,叫 odp.net.managed,如下圖)

Image may be NSFW.
Clik here to view.

補充:AssemblyFoldersEx 是 Visual Studio/MSBuild 尋找第三方元件的依據,參考:如何在32/64bit環境讓Visual Studio加入參考時可在.NET頁籤瀏覽自己的元件

AssemblyFoldersEx 機碼可解釋為何同事沒註冊 GAC 也可以找到 Managed ODP.NET DLL,下一步是驗證 GAC 是否與 DLL 會不會複製到 bin 有關?用 gacutil /u Oracle.ManagedDataAccess.dll 將其從 GAC 移除,再測試我的電腦 bin 還是沒出現 Oracle.ManagedDataAccess.dll,代表「有註冊 GAC 所以不用複製到 bin」的推測又錯了…(啪!啪!)

最後,老老實實比對同事跟我的 Process Monitor 記錄,同事的狀況是從 AssemblyFoldersEx 查到路徑找到檔案就複製到 bin 下(如下圖),我則是 AssemblyFoldersEx 找到路徑尋獲 DLL 檔,又繼續去 GAC \Windows\assembly\GAC_MSIL\Oracle.ManagedDataAccess 尋找 DLL 檔,重點是都有找到檔案,但就是沒將檔案複製到 bin。

Image may be NSFW.
Clik here to view.

這讓我有個想法:莫非問題出在版本不符?檢查共用元件參照的 Managed ODP.NET 版本為 4.121.2.0,同事 AssemblyFoldersEx\Oracle.ManagedDataAccess Registry 指向的版本也是 4.121.2.0,
而我的 AssemblyFoldersEx 與 GAC 指向的版本則是 4.121.1.0,並不是共用元件要求的版本。

BINGO!

將 AssemblyFoldersEx 指向位置的版本換成 4.121.2.0,我的電腦編譯後 bin 就出現 Oracle.ManagedDataAccess.dll 了,全案宣告偵破!

歸納本案調查心得:

  1. Visual Studio 建置背後靠 MSBuild 完成
  2. MSBuild 與 Visual Studio 會藉由 SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\ Registry 尋找第三方程式庫
  3. 若發現某 DLL 參照其他程式庫,MSBuild 會試著依 DLL 所在目錄、AssemblyFoldersEx、GAC 順序找尋所參照 DLL 並複製到 bin 目錄下
  4. 找尋 DLL 時版本必須完全一致,否則視同沒找到,既不複製也不會有錯誤訊息

【後話】

MSBuild 解析參照組件過程感覺有很多學問,想找篇深入探討文件解惑。在 MSDN 部落格上看到一篇序文如獲至寶,作者提到計劃寫完一系列共六篇文章深入剖析 MSBuild 及 Visual Studio 如何解析尋找 Assembly,讓我滿心期待一探究竟,很遺憾,作者 2010 年 5 月發願後就沒了下文,徒留幾篇敲碗留言… Orz

Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 428

Trending Articles