1. 程式人生 > >.NET分離exe和dll在不同的目錄讓你的程式更整潔

.NET分離exe和dll在不同的目錄讓你的程式更整潔

# 1、引言 在一個專案開發中一般都是把引用的dll放在根目錄下,隨著專案的日益增大,根目錄下的dll檔案就會越來越多,合理規劃這些dll的存放地址,可以使整個專案更加的規範與美觀。這篇文章就為大家介紹關於C#如何在指定資料夾尋找檔案dll的相關內容,文中通過基於RDIFramework框架WinForm版為基礎進行介紹,Web的相關dll規劃類似,希望對大傢俱有一定的參考學習價值。 我們框架原執行目錄下的dll存放如下,可以看到整個目錄下的檔案非常多。 ![原框架執行目錄結構](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181237963-935817878.png) 下面我們通過最常用的方式對dll檔案進行規劃處理,使整個執行目錄更加的乾淨,規範,最終效果如下圖所示。 ![框架執行目錄規劃後的結構](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181251245-812542728.png) 可以看到,上圖的整個執行目錄結構非常的清爽與整潔了。如何實現的呢?下面我們就具體講解。 # 2、實現方法 ## 2.1、系統搜尋dll的目錄以及順序 CLR解析一個程式集會在一個根目錄內進行搜尋,整個探索過程又稱Probing,這個根目錄很顯然就是當前包含當前程式集的目錄。 AppDomainSetup這個類儲存著探索目錄的資訊,其成員包括:ApplicationBase、PrivateBinPath。 程式搜尋dll的順序如下(區分強名稱簽名的和沒有強名稱簽名的程式集): **沒有做強名稱簽名的程式集:** > 1. 程式的根目錄 > 2. 根目錄下面,與被引用程式集同名的子目錄 > 3. **根目錄下面被明確定義為私有目錄的子目錄** > 4. 在目錄中查詢的時候,如果dll查詢不到,則會嘗試查詢同名的exe > 5. 如果程式集帶有區域性,而不是語言中立的,則還會嘗試查詢以語言區域命名的子目錄 **具有強名稱簽名的程式集:** > 1. 全域性程式集快取 > > 2. 如果有定義codebase,則以codebase定義為準,如果**codebase指定的路徑找不到,則直接報告錯誤** > > 3. 程式的根目錄 > > 4. 根目錄下面,與被引用程式集同名的子目錄 > > 5. **根目錄下面被明確定義為私有目錄的子目錄** > > 6. 在目錄中查詢的時候,如果dll查詢不到,則會嘗試查詢同名的exe > > 7. 如果程式集帶有區域性,而不是語言中立的,則還會嘗試查詢以語言區域命名的子目錄。如下圖所示: > ![程式集資訊](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181314946-927165705.png) ## 2.2、如何讓程式識別不同目錄下的dll? 我們看到,上面的順序無論是否有強名稱簽名,都會用到**私有目錄**,要實現程式識別不同目錄下的dll檔案,一般有三種方式。 > 1、配置App.config檔案的privatePath——【推薦】。 > > 2、訂閱程式集解析事件AssemblyResolve在程式碼中解析。 > > 3、在載入使用到dll的程式碼之前重置當前環境的目錄。 ### 2.2.1、配置App.config檔案的privatePath——【推薦】 這是最簡單最常用的方法,也是我們採用的方式。這兒要說明的是此方法有一定的侷限性,就是沒法對dll做控制,另外無法解決第三方`DllImprt`中引入的程式集不在根目錄下的問題。配置如下,多個目錄用;分隔。 ```xml
``` 其中privatePath是相對於*.exe.config檔案的相對路徑,多個資料夾以分號分隔。當編譯後會在生成目錄下生成一個字尾為.exe.config的檔案,就是相對這個檔案的。 新增程式集DLL引用之後,將DLL的屬性“複製本地”設定為False。程式編譯過程中,會自動檢索Common和Security資料夾下的DLL及其依賴項。 我們框架就是使用這種方式來實現,最終的執行目錄結構效果如下。 ![框架執行目錄規劃後的結構](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181251245-812542728.png) ### 2.2.2、訂閱程式集解析事件AssemblyResolve在程式碼中解析。 應用程式集域中支援在程式集解析時的處理: ```c# AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; ``` 通過這個事件,我們可以在程式集解析時,根據不同的程式集做不用的處理,比如載入x86的程式集還是64位的程式集,當然也就可以指定程式集目錄了。這也正是`Assembly.Load`和`Assembly.LoadFrom`等方法的用武之地。 ```c# Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { AssemblyName assemblyName = new AssemblyName(args.Name); return Assembly.LoadFrom(Path.Combine(baseDirectory, "3rdLibs")); } ``` ### 2.2.3、在載入使用到dll的程式碼之前重置當前環境的目錄。 這個方法是通過`Environment.CurrentDirectory=customPath`,這樣在呼叫dll方法時,因為目錄已經切換到了我們指定的目錄下,就可以實現相應的dll正確的載入。這是一個**取巧**的方法不是很實用,需要來回切換程式集目錄,但是在某些情況下非常好用。 ## 2.3、如何處理[dllImport]中的程式集的載入 針對dllImport也分為幾種情況。 >
自己寫`dllImport` > > 引用的C#的外掛又使用了`dllImport` ### 2.3.1、自己寫的dllImport 如果是自己寫的就非常好控制了,可以直接指定相對的目錄`DllImport(3rdLibs\NLog.dll)`。不過這種方法不一定可靠,在某些系統載入不了,如果使用了dllImport還是,推薦下面的介紹的方法(引用的C#的外掛又使用了`dllImport`)。 ### 2.3.2、引用的C#的外掛又使用了`dllImport` 因為無法更改路徑,那麼只能夠使用上述特殊的方法,更改當前程式的路徑 當然,還有更省事一點的做法,就是在系統環境中,增加一條記錄,指向要載入的dll的所在目錄。因為C++的程式碼中,Windows目錄和Windows\System32目錄以及環境變數設定的目錄都是搜尋路徑之一。 這裡提供怎麼從C#中修改系統環境變數的程式碼: ```c# static void AddEnvironmentPaths(IEnumerable paths) { var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty }; string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths)); Environment.SetEnvironmentVariable("PATH", newPath); } ``` 以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值。 # 3、執行效果 ![框架執行效果1](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181336181-853577527.png) ![框架執行效果2](https://img2020.cnblogs.com/blog/157572/202004/157572-20200418181341199-612902814.png) # 4、參考文章 ## 4.1、文章相關 - [用於執行時的程式集繫結元素](https://docs.microsoft.com/zh-cn/dotnet/framework/configure-apps/file-schema/runtime/assemblybinding-element-for-runtime?redirectedfrom=MSDN) - [淺談.NET中程式集的動態載入](http://www.cnblogs.com/brucebi/archive/2013/05/22/Assembly_Load.html) - [註冊使用GAC—Global Assembly Cache(.NET)](https://www.cnblogs.com/Ferry/archive/2010/12/17/1908817.html) - [再談CLR查詢和載入程式集的方式,查詢程式集](http://www.cnblogs.com/chenxizhang/archive/2013/03/14/2959688.html) - [深入理解CLR類載入機制](http://www.cnblogs.com/baihmpgy/archive/2013/02/27/CLR_Loader_And_OSGi.html) - [C#程式集載入方法](https://blog.csdn.net/podded/article/details/3158687) ## 4.2、框架相關 [最好用的.NET敏捷開發框架-RDIFramework.NET V3.6版全新發布 100%原始碼授權](http://blog.rdiframework.net/article/237) [RDIFramework.NET — 基於.NET的快速資訊化系統開發框架 — 系列目錄](http://blog.rdiframework.net/article/190) [RDIFramework.NET敏捷開發框架 ━ 工作流程元件介紹](http://blog.rdiframework.net/article/233) [RDIFramework.NET框架SOA解決方案(集Windows服務、WinForm形式與IIS形式釋出)-分散式應用](http://blog.rdiframework.net/article/189) [微信公眾號開發系列-玩轉微信開發-目錄彙總](http://blog.rdiframework.net/article/216) [史上最全面的SignalR系列教程-目錄彙總](http://blog.rdiframework.net/article/230) [RDIFramework.NET敏捷開發框架 ━ 工作流程元件Web業務平臺](http://blog.rdiframework.net/article/234) [RDIFramework.NET敏捷開發框架通過SignalR技術整合即時通訊(IM)](http://blog.rdiframework.net/article/229) [RDIFramework.NET框架基於Quartz.Net實現任務排程詳解及效果展示](http://blog.rdiframework.net/article/221) [RDIFramework框架整合微信開發應用效果展示](http://blog.rdiframework.net/article/219) ----- 一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,大家可以通過下面的地址瞭解詳情。 RDIFramework.NET官方網站:http://www.rdiframework.net/ RDIFramework.NET官方部落格:http://blog.rdiframework.net/ 同時需要說明的,以後的所有技術文章以官方網站為準,歡迎大家收藏! RDIFramework.NET框架由海南國思軟體科技有限公司專業團隊長期打造、一直在更新、一直在升級,請放心使用! 歡迎關注RDIFramework.net框架官方公眾微信(微訊號:guosisoft),及時瞭解最新動態。 掃描二維碼立即關注 ![微訊號:guosisoft](http://doc.rdiframework.net/blog/article/20180822094544955.png)