如何控制C#引用DLL的位置
想必C#的開發者都遇到過這個問題,引用的dll都放在根目錄下,隨著專案的日益增大,根目錄下充滿了各種各樣的dll,非常的不美觀。
如果能夠把dll按照想要的目錄來存放,那麼系統就美觀多了,以下是我常用的程式各檔案的分佈:
- 【3rdLibs】
- NLog.dll
- Newtonsoft.Json.dll
- ……
- 【MyLibs】
- 【Resources】
- 【Images】
- Excecutable.exe
- Excecuteble.exe.config
網上有很多的文章述說這個,比如使用Assembly.Load,但是沒有說明在程式中怎麼使用,也沒有給出具體的程式碼。這裡我結合自己多年的實踐經驗,再把整個流程和方法詳細敘述一遍,以便各位看官有個具體的體會。
系統搜尋dll的目錄以及順序
CLR解析一個程式集會在一個根目錄內進行搜尋,整個探索過程又稱Probing,這個根目錄很顯然就是當前包含當前程式集的目錄。
AppDomainSetup這個類儲存著探索目錄的資訊,其成員包括:ApplicationBase
、PrivateBinPath
。
程式搜尋dll的順序如下(區分強名稱簽名的和沒有強名稱簽名的程式集):
沒有做強名稱簽名的程式集:
- 程式的根目錄
- 根目錄下面,與被引用程式集同名的子目錄
- 根目錄下面被明確定義為私有目錄的子目錄
- 在目錄中查詢的時候,如果dll查詢不到,則會嘗試查詢同名的exe
- 如果程式集帶有區域性,而不是語言中立的,則還會嘗試查詢以語言區域命名的子目錄
具有強名稱簽名的程式集:
- 全域性程式集快取
- 如果有定義codebase,則以codebase定義為準,如果codebase指定的路徑找不到,則直接報告錯誤
- 程式的根目錄
- 根目錄下面,與被引用程式集同名的子目錄
- 根目錄下面被明確定義為私有目錄的子目錄
- 在目錄中查詢的時候,如果dll查詢不到,則會嘗試查詢同名的exe
- 如果程式集帶有區域性,而不是語言中立的,則還會嘗試查詢以語言區域命名的子目錄
如何讓程式識別不同目錄下的dll?
我們看到,上面的順序無論是否有強名稱簽名看,都提到了一個名詞“私有目錄”
方法一:配置App.config檔案的privatePath
——【推薦】
這是最簡單的方法,當然也有一定的侷限性,就是沒法對dll做控制,另外,無法解決第三方
DllImprt
中引入的程式集不在根目錄下的問題,不過無論怎麼說,這個都基本解決了問題。配置如下,多個目錄用;分隔
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="3rdLib;MyLibs;SubFolder\Sub.dll"/> </assemblyBinding> </runtime>
方法二:訂閱程式集解析事件AssemblyResolve
在程式碼中解析
應用程式集域中支援在程式集解析時的處理:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
。通過這個事件,我們可以在程式集解析時,根據不同的程式集做不用的處理,比如載入x86的程式集還是64位的程式集,當然也就可以指定程式集目錄了
這也正是
Assembly.Load
和Assembly.LoadFrom
等方法的用武之地。
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { AssemblyName assemblyName = new AssemblyName(args.Name); return Assembly.LoadFrom(Path.Combine(baseDirectory, "3rdLibs")); }
方法三:在載入使用到dll的程式碼之前設定重置當前環境的目錄
這個方法就是通過
Environment.CurrentDirectory=customPath
,這樣,在呼叫dll方法時,因為目錄已經切換到了
這是一個取巧的方法,不是很實用,要來回切換程式集目錄,但是在某些情況下非常好用
如何處理[dllImport]
中的程式集的載入
自己寫dllImport
如果是自己寫,那麼久好控制了,可以直接指定相對的目錄
DllImport(3rdLibs\NLog.dll)
。不過這種方法不一定可靠,在某些系統硬是載入不了,如果使用了dllImport還是,推薦下面的另外一種方法。
引用的C#的外掛又使用了dllImport
這是很多文章都沒有提及的:
因為無法更改路徑,那麼只能夠使用上述特殊的方法,更改當前程式的路徑
當然,還有更省事一點的做法,就是在系統環境中,增加一條記錄,指向要載入的dll的所在目錄。因為C++的程式碼中,Windows目錄和Windows\System32目錄以及環境變數設定的目錄都是搜尋路徑之一。
這裡提供怎麼從C#中修改系統環境變數的程式碼:
static void AddEnvironmentPaths(IEnumerable<string> paths) { var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty }; string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths)); Environment.SetEnvironmentVariable("PATH", newPath); }