ILMerge替代方案,如何在EXE文件中嵌入应用程序的相关DLL?

时间:2011-03-21 18:06:38

标签: c# winforms reflection c#-4.0

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

[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

我将其Build Action设置为“Embedded Resource”。

如果我删除那段代码并通过单击一次安装程序将dll设置为包含它可以正常工作。顺便说一下,使用.NET Framework 4完整个人资料和VS2010 SP1

4 个答案:

答案 0 :(得分:3)

当它试图jit Main()方法时,抖动变为kaboom。 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());
    }

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

我怀疑您正在尝试使用非托管代码加载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已经驻留在那里。