1

我正在尝试使用以下代码在运行时加载 dll 库,这样我就不必向用户提供大量 dll 文件以及主可执行文件。我已将所有 dll 文件作为嵌入式资源包含在内,并且在参考部分中我已包含它们并将CopyLocal属性设置为 false。但这里的问题是:
1. 所有 dll 都被复制到 Bin\Debug 文件夹
2. 我得到FileNotFoundException
我做了很多搜索来解决这些问题,最后我在这里。我在这里得到了类似的代码,但仍然无能为力。我应该怎么做才能防止这种异常......??有没有更好的方法来为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项目,以便提交我的论文。

4

2 回答 2

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;
    }
    

-杰西

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

于 2013-10-17T00:54:06.803 回答
0

尝试以下操作:

  • 对于每个 .dll 资源:
    • 如果文件已经存在AppDomain.Current.BaseDirectory然后继续下一个资源
    • 否则将资源保存到AppDomain.Current.BaseDirectory. 在 try-catch 中执行此操作,如果失败,请通知用户。要成功完成此步骤,您需要对安装文件夹(通常是“Program Files”的子文件夹)具有写入权限。这将通过第一次以管理员身份运行程序来解决,仅将文件写入文件系统。
  • 如果程序集被您的 VS 项目引用,那么您不必自己加载它们。要了解这项工作的原因,您需要了解CLR 如何定位程序集
  • 否则,您将需要使用以 a或 and作为参数的Assembly.Load之一自己加载每个程序集。stringAssemblyName
于 2012-11-19T09:11:19.757 回答