1. 程式人生 > >C#將託管dll嵌入exe檔案

C#將託管dll嵌入exe檔案

用vs2017用C#建立WinForm小程式,程式內用到了連線MySql的MySql.Data.dll和處理Json串的Newtonsoft.Json.dll兩個dll,糾結於僅僅小程式釋出時就要附帶這麼多dll,著實令人著急,網上搜索發現解決方案,參考了文章

https://blog.csdn.net/lin381825673/article/details/39122257

https://blog.csdn.net/call_me_lzm/article/details/51501620

將dll嵌入exe,總結起來方案有:

  1. 使用ILMerge等工具將dll嵌入exe中
  2. 將dll內嵌於exe中,並在首次執行時將其釋放出來
  3. 將dll內嵌於exe中,並在使用到dll時,將其載入在記憶體中

對於1,經過初步瞭解,應該是要生成exe後,通過該工具將dll嵌入。這樣每次修改程式都要重複這個操作,感覺太繁瑣,所以直接跳過,尋找別的方案(當然也可能是我不夠了解帶來的誤解,如果有了解的同學歡迎指出);對於2,雖然可以滿足分發的時候只發佈一個exe檔案,但一旦執行起來又甩出一堆dll,看著還是不爽;3就是我採納的方案,下面細說。

一、仍然需要將兩個dll引入專案(否則程式碼編譯不通過),dll檔案也是必須的(執行時需要呼叫)。(這一步可能已經做過了,那麼看下面兩步)


二、雙擊Resources.resx,選擇新增現有檔案,把所需DLL新增進來


這時可以看到多了

    

三、新增AssemblyResolve處理函式

        System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", "");
            dllName = dllName.Replace(".", "_");
            if (dllName.EndsWith("_resources")) return null;
            System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
            byte[] bytes = (byte[])rm.GetObject(dllName);
            return System.Reflection.Assembly.Load(bytes);
        }

        public Form1()//看清楚這是窗體本來的初始化函式
        {
            //在InitializeComponent()之前呼叫
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            InitializeComponent();
        }

可以在CurrentDomain_AssemblyResolve中設定斷點檢視,會發現當程式中首次呼叫到MySql.Data.dll或者Newtonsoft.Json.dll的方法時會回撥該函式。並且生成exe檔案後,刪除dll檔案單獨執行exe,也不會再報找不到dll的錯誤了。一旦dll被載入到記憶體中,就會一直存在。所以只有首次找不到dll時會去載入,其他時候就不用了。

在試驗過程中遇到了問題,當第二步新增資源時只把Newtonsoft.Json.dll新增進去,而不管MySql.Data.dll時,程式釋出時沒有dll仍然會報錯,除錯了好久,把所有dll全部加入資源,才成功了!