将dll库从Resource加载到当前域(在主exe文件中嵌入dll)

时间:2012-11-18 19:44:46

标签: c# dll byte exe

我正在尝试使用以下代码在运行时加载dll库,这样我就不必为用户提供大量的dll文件以及主要的可执行文件。我已将所有dll文件作为嵌入资源包含在内,并且在参考部分中我已将其包括在内并将 CopyLocal 属性设置为false。但这里的问题是:1。所有的DLL都被复制到Bin \ Debug文件夹
2。我收到 FileNotFoundException
我做了很多搜索以解决这些问题,最后我就在这里。我得到了类似的代码here,但仍然无法做任何事情。我应该怎么做才能防止这种异常......? 是否有更好的方法为 Windows窗体应用程序(非WPF)做同样的事情 ...... ??

using System;
using System.Linq;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Generic;
using System.IO;

namespace MyNameSpace
{
    static class Program
    {
        static int cnt;
        static IDictionary<string, Assembly> assemblyDictionary;
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
            if (cnt != 1)
            {
                cnt = 1;
                Assembly executingAssembly = Assembly.GetExecutingAssembly();
                string[] resources = executingAssembly.GetManifestResourceNames();
                foreach (string resource in resources)
                {
                    if (resource.EndsWith(".dll"))
                    {
                        using (Stream stream = executingAssembly.GetManifestResourceStream(resource))
                        {
                            if (stream == null)
                                continue;

                            byte[] assemblyRawBytes = new byte[stream.Length];
                            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
                            try
                            {
                                assemblyDictionary.Add(resource, Assembly.Load(assemblyRawBytes));
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show("Failed to load: " + resource + " Exception: " + ex.Message);
                            }
                        }
                    }
                }
                Program.Main();
            }
            if (cnt == 1)
            {
                cnt = 2;
                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
                Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
                Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new MainForm());
            }
        }

        private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
        {            
            AssemblyName assemblyName = new AssemblyName(args.Name);

            string path = assemblyName.Name + ".dll";

            if (assemblyDictionary.ContainsKey(path))
            {
                return assemblyDictionary[path];
            }
            return null;
        }
    }
}

如果我在代码中使用了不必要的东西,那么你可以告诉我正确的方法...... 我是 Windows Form Application v4.0 项目的学生,我的论文将被提交。

2 个答案:

答案 0 :(得分:1)

如果仍然是必须执行此操作的情况,则使用此OnResolveAssembly方法。如果您不想,则无需将它们预加载到数组中。这将在第一次实际需要时加载它们。

然后只是:

  • some.assembly.dll 文件添加到项目中。
    • 可能不是对项目输出的引用
    • 但是作为DLL项目结果的文件。
  • 在文件属性中将其标记为资源

    // This function is not called if the Assembly is already previously loaded into memory.
    // This function is not called if the Assembly is already in the same folder as the app.
    //
    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
    {
        var thisAssembly = Assembly.GetExecutingAssembly();
    
        // Get the Name of the AssemblyFile
        var assemblyName = new AssemblyName(e.Name);
        var dllName = assemblyName.Name + ".dll";
    
        // Load from Embedded Resources
        var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
        if (resources.Any())
        {
            // 99% of cases will only have one matching item, but if you don't,
            // you will have to change the logic to handle those cases.
            var resourceName = resources.First();
            using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
            {
                if (stream == null) return null;
                var block = new byte[stream.Length];
    
                // Safely try to load the assembly.
                try
                {
                    stream.Read(block, 0, block.Length);
                    return Assembly.Load(block);
                }
                catch (IOException)
                {
                    return null;
                }
                catch (BadImageFormatException)
                {
                    return null;
                }
            }
        }
    
        // in the case the resource doesn't exist, return null.
        return null;
    }
    

-Jesse

PS:这来自http://www.paulrohde.com/merging-a-wpf-application-into-a-single-exe/

答案 1 :(得分:0)

尝试以下方法:

  • 对于每个.dll资源:
    • 如果AppDomain.Current.BaseDirectory上的文件已经存在,则继续使用下一个资源
    • 否则将资源保存到AppDomain.Current.BaseDirectory。在try-catch中执行此操作,如果失败,请通知用户。要成功完成此步骤,您需要在安装文件夹(通常是“Program Files”的子文件夹)上进行写访问。这将通过以管理员身份运行程序来解决,这是第一次只有文件系统上写入文件。
  • 如果您的VS项目引用了程序集,那么您不必自己加载它们。要了解这项工作的原因,您需要了解how assemblies are located by the CLR
  • 否则,您需要使用以stringAssemblyName作为参数的Assembly.Load之一自行加载每个装配。
相关问题