【问题标题】:ILMerge alternative, how to embed application’s dependent DLLs inside an EXE file?ILMerge 替代方案,如何将应用程序的依赖 DLL 嵌入到 EXE 文件中?
【发布时间】:2011-03-21 18:06:38
【问题描述】:

As stated here 我试图将 dll 嵌入到 exe 应用程序中以便仅分发一个 exe,但是当我尝试在安装了完整 .NET 4 的 xp 机器上运行我的应用程序时,它只是崩溃而没有错误,我将以下代码放在main方法上

[STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";

                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            };

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmrPrincipal());
        }

我有一个名为 dlls 的文件夹,我在其中放置

Functions.Shared.dll
Alex.UI.dll
Alex.Controls.dll

并且我将其构建操作设置为“嵌入式资源”。

如果我删除那段代码并设置要通过单击安装程序包含的 dll,它可以正常工作。顺便说一句,我使用 .NET Framework 4 Full profile 和 VS2010 SP1

【问题讨论】:

  • 使用 ClickOnce 时合并 DLL 毫无意义。它不介意多个 DLL。相反,下载更新的内容更少。
  • 好吧,我不会使用 ClickOnce 来分发我的应用程序,我只想分发 exe 文件
  • 好的,将其命名为 setup.exe,就像安装项目创建的那个一样。如有必要,还负责安装 .NET、创建快捷方式以帮助您的用户入门、文件关联等。
  • 好吧好吧,你让我知道了 :) 你的答案是肯定的,但无论如何代码有什么问题?有什么想法吗?

标签: c# winforms reflection c#-4.0


【解决方案1】:

当它尝试 jit Main() 方法时,jitter 变得很糟糕。 AssemblyResolve 尚未注册,先有鸡还是先有蛋的问题。您只能使用 Main 中可用的类型,因此您必须远离 frmrPrincipal。使用一点辅助方法可以解决这个问题,但是现在您必须使用 [MethodImpl] 来抑制内联。不允许 ngen.exe 完成它的工作,你也是在打你的脚。

using System.Runtime.CompilerServices;
...
    [STAThread]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            // etc..
        }
        AvoidJitterBombing();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void AvoidJitterBombing() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new frmrPrincipal());
    }

【讨论】:

  • 我需要更多关于如何修复它的解释,需要一个代码示例或更好的指导,这是我第一次遇到反射:(
  • 我正试图说服你放弃它。显然,这方面做得不好。至少找出 ngen.exe 可以为您的程序做哪些美妙的事情。谷歌搜索得很好。
  • 嗯,我了解 sgen.exe 的作用,(感谢提示)但是有什么方法可以使这段代码工作吗?还是通过 C# 编写 CLR 的作者是错的?
  • 好吧,当里希特的名字被拖入泥潭时,我不得不放弃。帖子已更新。
  • 好的,它起作用了,但在我接受你的回答之前,请告诉我我是否正确理解发生了什么,因为你禁用内联 sgen 不会优化代码,并且从 frmrPrincipal 派生的任何代码都会受到伤害我的应用程序性能?
【解决方案2】:

我最近玩过这个,从这个或一段非常相似的代码开始,我认为resourceName 的构造是错误的。尝试在 resolve 方法的开头放置一个断点,然后在正在执行的程序集上调用 GetManifestResourceNames() 以查看它实际为您的嵌入式资源提供的名称。

我怀疑它可能是“dlls.Functions.Shared”等等,而不是“AssemblyLoadingAndReflection.Functions.Shared”

【讨论】:

    【解决方案3】:

    您可以看看.NETZ,我们用于 WinForms 应用程序部署以及 ClickOnce。

    【讨论】:

    • 你应该download源代码并用.NET Framework 4.0编译,因为他们下载页面上的二进制文件只用.NET 1.x和2.0编译过。
    【解决方案4】:

    我怀疑您正在尝试使用非托管代码加载 dll。对于托管 dll,只需在内存中读取和加载程序集即可,或者您可以从某个位置加载,甚至无需读取。对于混合模式程序集,我只能通过将字节写入文件并从其位置加载来取得成功。

    class Program
    {
        [STAThread]
        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);
            };
    
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmrPrincipal());
        }
    }
    

    其中Program 是类名。为了避免先有鸡还是先有蛋的问题,您必须确保在访问程序集之前声明处理程序,并且不要在加载(程序集解析)部分中访问程序集成员(或实例化任何必须处理程序集的东西)。还要注意确保GetMyApplicationSpecificPath() 不是任何临时目录,因为临时文件可能会被其他程序或您自己尝试擦除(不是说它会在您的程序访问 dll 时被删除,但至少它是一个麻烦。 AppData 的位置很好)。另请注意,您每次都必须写入字节,您不能只是从位置加载,因为 dll 已经存在于那里。

    【讨论】:

      猜你喜欢
      • 2021-03-15
      • 1970-01-01
      • 2011-10-26
      • 2011-01-21
      • 1970-01-01
      • 1970-01-01
      • 2019-11-11
      • 2020-10-05
      • 1970-01-01
      相关资源
      最近更新 更多