.NET分離exe和dll在不同的目錄讓你的程式更整潔
阿新 • • 發佈:2020-05-12
# 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)