Embedding DLLs in a compiled executable

前端 未结 16 2686
情深已故
情深已故 2020-11-21 07:07

Is it possible to embed a pre-existing DLL into a compiled C# executable (so that you only have one file to distribute)? If it is possible, how would one go about doing it?<

16条回答
  •  广开言路
    2020-11-21 07:31

    ILMerge can combine assemblies to one single assembly provided the assembly has only managed code. You can use the commandline app, or add reference to the exe and programmatically merge. For a GUI version there is Eazfuscator, and also .Netz both of which are free. Paid apps include BoxedApp and SmartAssembly.

    If you have to merge assemblies with unmanaged code, I would suggest SmartAssembly. I never had hiccups with SmartAssembly but with all others. Here, it can embed the required dependencies as resources to your main exe.

    You can do all this manually not needing to worry if assembly is managed or in mixed mode by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler. This is a one stop solution by adopting the worst case, ie assemblies with unmanaged code.

    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            string assemblyName = new AssemblyName(args.Name).Name;
            if (assemblyName.EndsWith(".resources"))
                return null;
    
            string dllName = assemblyName + ".dll";
            string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
    
            using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
            {
                byte[] data = new byte[stream.Length];
                s.Read(data, 0, data.Length);
    
                //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
    
                File.WriteAllBytes(dllFullPath, data);
            }
    
            return Assembly.LoadFrom(dllFullPath);
        };
    }
    

    The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath() is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.

    For managed dlls, you need not write bytes, but directly load from the location of the dll, or just read the bytes and load the assembly from memory. Like this or so:

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);
            return Assembly.Load(data);
        }
    
        //or just
    
        return Assembly.LoadFrom(dllFullPath); //if location is known.
    

    If the assembly is fully unmanaged, you can see this link or this as to how to load such dlls.

提交回复
热议问题