No Fody, support. net2.0,C# package Dll files into Exe output single file version application

Posted by aktell on Sat, 20 Nov 2021 08:19:57 +0100

On the Internet, some dll files calling c# by fody are bundled into exe files, which is not suitable for me, because my. net project only installs. net2.0 for use on low-end computers

This change is mainly divided into two parts., The first part is to write a class that automatically loads dll files through resources

The second part is to dynamically load dll at the entrance of the program. Note that it is not the main function

The steps are divided into three parts,

The first step is to package the dll file. Drag it directly into the project as a resource file.

You can also put it in the specified folder. After putting it in, modify the file properties:

Build operation - > embedded resources

Copy to output directory - > copy if newer

 

  Step two. Write a dll file dynamic loader. I have written it here. You can copy it directly and use it

#region reference
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.IO;
#endregion
#region body
/// <summary>
///dll Dynamic loader
/// </summary>
static class DllLoader
{
    #region variable
    static Dictionary<string, Assembly> DllsDic = new Dictionary<string, Assembly>();
    static Dictionary<string, object> AssembliesDic = new Dictionary<string, object>();
    #endregion
    #region public function, external call
    /// <summary>
    ///Loading
    /// </summary>
    public static void Load()
    {
        Console.WriteLine("register dll Method call");
        //Gets the assembly to which the Program belongs
        var ass = new StackTrace(0).GetFrame(1).GetMethod().Module.Assembly;
        //Determine whether it has been processed
        if (AssembliesDic.ContainsKey(ass.FullName))
        {
            return;
        }
        //Add assembly to processed collection
        AssembliesDic.Add(ass.FullName, null);
        //Binding assembly loading failure event (I tested it here. It doesn't matter if you bind it repeatedly)
        AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
        //Get all resource file names
        var res = ass.GetManifestResourceNames();
        foreach (var r in res)
        {
            if (r.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    #In the practical application of region, it can also be used without opening the dll solution to a certain place. Determine whether to unlock the annotation content according to your own situation
                    //if (r.ToLower().Contains(".dll"))
                    //{
                    //    ExtractResource2File(r, GetUnpackPatch() + @"/" + r.Substring(r.IndexOf('.') + 1));
                    //}
                    #endregion
                    var s = ass.GetManifestResourceStream(r);
                    var bts = new byte[s.Length];
                    s.Read(bts, 0, (int)s.Length);
                    var da = Assembly.Load(bts);
                    //Judge loading results
                    if (DllsDic.ContainsKey(da.FullName))
                    {
                        continue;
                    }
                    DllsDic[da.FullName] = da;
                }
                catch (Exception e)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(e.Message);
                    Console.WriteLine(e.StackTrace);
                    Console.ResetColor();
                }
            }
        }
    }
    /// <summary>
    ///Unpacking file
    /// </summary>
    ///< param name = "resourcename" > resource name < / param >
    ///< param name = "filename" > target file name < / param >
    #endregion
    #region private function, used internally
    static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //Assembly
        Assembly ass;
        //Gets the full name of the assembly that failed to load
        var assName = new AssemblyName(args.Name).FullName;
        //Determine whether there is a loaded assembly with the same name in the Dlls collection
        if (DllsDic.TryGetValue(assName, out ass) && ass != null)
        {
            DllsDic[assName] = null;//If yes, set to null and return
            return ass;
        }
        else
        {
            throw new DllNotFoundException(assName);//Otherwise, an exception of loading failure will be thrown
        }
    }

    static void ExtractResource2File(string resourceName, string filename)
    {
        Console.WriteLine("Unzip file:{0} {1}", resourceName, filename);
        if (!System.IO.File.Exists(filename))
        {
            using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    byte[] b = new byte[s.Length];
                    s.Read(b, 0, b.Length);
                    fs.Write(b, 0, b.Length);
                }
            }
        }
    }
    /// <summary>
    ///Obtain the decompression directory and change the location according to your own needs. If you unzip it to the system folder, you need to have relevant permissions, which may be misreported by the anti-virus software
    /// </summary>
    /// <returns></returns>
    static string GetUnpackPatch()
    {
        //string strPath = "C:";
        string strPath = AppDomain.CurrentDomain.BaseDirectory;
        if (!Directory.Exists(strPath))
        {
            Directory.CreateDirectory(strPath);
        }
        return strPath;
    }
    #endregion
}
#endregion

Just put this file directly into the project

The third step is to insert the corresponding code to automatically load the dll file in the program.cs file

By default, there is only the main function in the program file of a new console application

Add the Program function above the main function and call dlloader

static Program()
        {
            Console.WriteLine("Start checking dll");
            DllLoader.Load();
            Console.WriteLine("End inspection dll");
        }

The modified Program.cs file is as follows:

using System;
using System.IO;
using NPOI.SS.UserModel;
using NPOI.HSSF.UserModel;

namespace Dll Pack in Exe
{
    class Program
    {
        static Program()
        {
            Console.WriteLine("Start checking dll");
            DllLoader.Load();
            Console.WriteLine("End inspection dll");
        }
        static void Main(string[] args)
        {
            //Dictionary<string, object> waitJsonObj = new Dictionary<string, object>();
            //waitJsonObj.Add("ce", "test content");
            //waitJsonObj.Add("i", 12321321);
            //string json = Newtonsoft.Json.JsonConvert.SerializeObject(waitJsonObj);
            //Console.WriteLine(json);
            //Console.ReadLine();
            IWorkbook workbook = new HSSFWorkbook();
            ISheet sheet = workbook.CreateSheet();
            IRow row0 = sheet.CreateRow(0);
            ICell cella = row0.CreateCell(0);
            cella.SetCellValue("Test the header row 1 column of the new table a");
            SaveSheet2NewFile("c:\\ceshi.xls", sheet);
            Console.WriteLine("Write file complete");
            Console.ReadLine();
        }
        public static int SaveSheet2NewFile(string fileName, ISheet sheet)
        {
            int i = 0;
            int j = 0;
            int count = 0;
            //IWorkbook workbook = null;
            FileStream fs = null;
            try
            {

                fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
                //If (filename. Indexof (". Xlsx") > 0) / / 2007 version
                //    workbook = new XSSFWorkbook();
                //Else if (filename. Indexof (". XLS") > 0) / / 2003 version
                //    workbook = new HSSFWorkbook();

            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
                //MessageBox.Show(err.Message);
                return -1;
            }
            try
            {
                //ISheet newsheet = workbook.CreateSheet();
                sheet.Workbook.Write(fs);
                //workbook.Write(fs); // Write to excel
                fs.Close();
                return count;
            }
            catch (Exception ex)
            {
                Console.WriteLine("err:",ex.Message);
                //MessageBox.Show("Exception: " + ex.Message);
                fs.Close();
                return -1;
            }
        }
    }
}

This example is to package the relevant dll processed by NPOI table into exe file

Note that if DLL is likely to change, for example, a.dll, which is regarded as resource, is generated by your a.csproj project. After editing the a project, the a project will generate new DLL, so the DLL added to the current project should be xx.dll under bin/debug or release directory under a project. If your main project is still being tested, the debug mode is bin/debug. Then select the file under bin / debug of project a for the resource file. If it is release, it should also correspond to the DLL generated by the corresponding release of project A. otherwise, it may not be loaded effectively due to the problem of DLL debugging mode

Topics: C#