.NET分離exe和dll在不同的目錄讓你的程序更整潔
- 2020 年 5 月 12 日
- 筆記
- .NET技術, c#, RDIFramework.NET, WinForm開發框架, 企業級控件庫, 快速開發框架, 數據庫開發應用, 數據庫開發相關技術, 計算機常用知識, 隨談, 項目與團隊管理, 項目總結
1、引言
在一個項目開發中一般都是把引用的dll放在根目錄下,隨着項目的日益增大,根目錄下的dll文件就會越來越多,合理規劃這些dll的存放地址,可以使整個項目更加的規範與美觀。這篇文章就為大家介紹關於C#如何在指定文件夾尋找文件dll的相關內容,文中通過基於RDIFramework框架WinForm版為基礎進行介紹,Web的相關dll規劃類似,希望對大家具有一定的參考學習價值。
我們框架原執行目錄下的dll存放如下,可以看到整個目錄下的文件非常多。
下面我們通過最常用的方式對dll文件進行規劃處理,使整個運行目錄更加的乾淨,規範,最終效果如下圖所示。
可以看到,上圖的整個運行目錄結構非常的清爽與整潔了。如何實現的呢?下面我們就具體講解。
2、實現方法
2.1、系統搜索dll的目錄以及順序
CLR解析一個程序集會在一個根目錄內進行搜索,整個探索過程又稱Probing,這個根目錄很顯然就是當前包含當前程序集的目錄。
AppDomainSetup這個類存儲着探索目錄的信息,其成員包括:ApplicationBase、PrivateBinPath。
程序搜索dll的順序如下(區分強名稱簽名的和沒有強名稱簽名的程序集):
沒有做強名稱簽名的程序集:
- 程序的根目錄
- 根目錄下面,與被引用程序集同名的子目錄
- 根目錄下面被明確定義為私有目錄的子目錄
- 在目錄中查找的時候,如果dll查找不到,則會嘗試查找同名的exe
- 如果程序集帶有區域性,而不是語言中立的,則還會嘗試查找以語言區域命名的子目錄
具有強名稱簽名的程序集:
全局程序集緩存
如果有定義codebase,則以codebase定義為準,如果codebase指定的路徑找不到,則直接報告錯誤
程序的根目錄
根目錄下面,與被引用程序集同名的子目錄
根目錄下面被明確定義為私有目錄的子目錄
在目錄中查找的時候,如果dll查找不到,則會嘗試查找同名的exe
如果程序集帶有區域性,而不是語言中立的,則還會嘗試查找以語言區域命名的子目錄。如下圖所示:
2.2、如何讓程序識別不同目錄下的dll?
我們看到,上面的順序無論是否有強名稱簽名,都會用到私有目錄,要實現程序識別不同目錄下的dll文件,一般有三種方式。
1、配置App.config文件的privatePath——【推薦】。
2、訂閱程序集解析事件AssemblyResolve在代碼中解析。
3、在加載使用到dll的代碼之前重置當前環境的目錄。
2.2.1、配置App.config文件的privatePath——【推薦】
這是最簡單最常用的方法,也是我們採用的方式。這兒要說明的是此方法有一定的局限性,就是沒法對dll做控制,另外無法解決第三方DllImprt
中引入的程序集不在根目錄下的問題。配置如下,多個目錄用;分隔。
<configuration>
<runtime>
<!--xmlns是必需的特性。指定程序集綁定所需的 XML 命名空間。 使用字符串「urn: 架構-microsoft-com:asm.v1」作為值。-->
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="yes"/> <!--指定運行時是否使用發佈者策略-->
<!--指定加載程序集時公共語言運行時搜索的子目錄, 其中privatePath是相對於*.exe.config文件的相對路徑,多個文件夾以分號分隔。-->
<probing privatePath="devLibs;3rdLibs;frameworkLibs"/>
</assemblyBinding>
</runtime>
</configuration>
其中privatePath是相對於*.exe.config文件的相對路徑,多個文件夾以分號分隔。當編譯後會在生成目錄下生成一個後綴為.exe.config的文件,就是相對這個文件的。
添加程序集DLL引用之後,將DLL的屬性「複製本地」設置為False。程序編譯過程中,會自動檢索Common和Security文件夾下的DLL及其依賴項。
我們框架就是使用這種方式來實現,最終的運行目錄結構效果如下。
2.2.2、訂閱程序集解析事件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"));
}
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#中修改系統環境變量的代碼:
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);
}
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值。
3、運行效果
4、參考文章
4.1、文章相關
4.2、框架相關
最好用的.NET敏捷開發框架-RDIFramework.NET V3.6版全新發佈 100%源碼授權
RDIFramework.NET — 基於.NET的快速信息化系統開發框架 — 系列目錄
RDIFramework.NET敏捷開發框架 ━ 工作流程組件介紹
RDIFramework.NET框架SOA解決方案(集Windows服務、WinForm形式與IIS形式發佈)-分佈式應用
RDIFramework.NET敏捷開發框架 ━ 工作流程組件Web業務平台
RDIFramework.NET敏捷開發框架通過SignalR技術整合即時通訊(IM)
RDIFramework.NET框架基於Quartz.Net實現任務調度詳解及效果展示
一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,大家可以通過下面的地址了解詳情。
RDIFramework.NET官方網站://www.rdiframework.net/
RDIFramework.NET官方博客://blog.rdiframework.net/
同時需要說明的,以後的所有技術文章以官方網站為準,歡迎大家收藏!
RDIFramework.NET框架由海南國思軟件科技有限公司專業團隊長期打造、一直在更新、一直在升級,請放心使用!
歡迎關注RDIFramework.net框架官方公眾微信(微信號:guosisoft),及時了解最新動態。
掃描二維碼立即關注