.NET Windows 应用程序可以压缩成单个 .exe 吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/126611/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:03:40  来源:igfitidea点击:

Can a .NET windows application be compressed into a single .exe?

.netwindows

提问by public static

I am not too familiar with .NET desktop applications (using Visual Studio 2005). Is it possible to have the entire application run from a single .exe file?

我不太熟悉 .NET 桌面应用程序(使用Visual Studio 2005)。是否可以从单个 .exe 文件运行整个应用程序?

回答by mattlant

Yes, you can use the ILMergetool.

是的,您可以使用ILMerge工具。

回答by Andrew Savinykh

Today, in 2015, you can use Costura.Fodyfor this. Using it amounts to adding the NuGet package to your project and recompiling. I'm not sure if it works with Visual Studio 2005 as in the question, but then it's not year 2008 either. I've used it in a few of my projects and it worked great.

今天,在 2015 年,您可以为此使用Costura.Fody。使用它相当于将 NuGet 包添加到您的项目并重新编译。我不确定它是否适用于问题中的 Visual Studio 2005,但也不是 2008 年。我在我的几个项目中使用过它,效果很好。

Fodyis a general purpose Code Weaverthat is free and open source. The basic idea that it allows post-processing of the compiled .NET code to enhance it with features. For example, logging can be added to every method call, etc. In our case, it's packaging the dependent DLL files into the assembly resources, so that they could be loaded during the program runtime as if they were stand-alone dependencies.

Fody是一个通用的Code Weaver,它是免费和开源的。它的基本思想是允许对已编译的 .NET 代码进行后处理以增强其功能。例如,可以将日志记录添加到每个方法调用等。在我们的例子中,它将依赖的 DLL 文件打包到程序集资源中,以便它们可以在程序运行时加载,就好像它们是独立的依赖项一样。

回答by MusiGenesis

Yes. In .NET you can have your entire application encapsulated as a single EXE file.Just make sure your solution only has one Windows Application project in it (and no other projects except for setups).

是的。在 .NET 中,您可以将整个应用程序封装为单个 EXE 文件。只需确保您的解决方案中只有一个 Windows 应用程序项目(除设置外没有其他项目)。

The EXE file created by your .NET project will not be a stand-alone executable file, but the only dependency it will have will be the .NET runtime(unless you add references to other DLL assemblies). If you use .NET 2.0 (which I recommend), the runtime comes preinstalled on newer PCs and is very easy and quick to set up on older machines (installer is about 23 MB).

.NET 项目创建的 EXE 文件不是独立的可执行文件,但它唯一的依赖项是 .NET 运行时(除非您添加对其他 DLL 程序集的引用)。如果您使用 .NET 2.0(我推荐),则运行时预装在较新的 PC 上,并且在较旧的计算机上设置起来非常简单快捷(安装程序大约 23 MB)。

If your app does need to reference other assemblies (like a data access DLL or a .NET class library DLL project), you could use one of the tools referenced by other posters here to combine your EXE and all referenced DLLs into a single EXE file. However, conventional practice would dictate simply deploying the EXE file along with any dependent DLLs as separate files. In .NET, deployment of dependent DLLs is pretty simple: just put them in the same folder as the EXE file on the client machine, and you're done.

如果您的应用程序确实需要引用其他程序集(如数据访问 DLL 或 .NET 类库 DLL 项目),您可以使用此处其他海报引用的工具之一将您的 EXE 和所有引用的 DLL 组合成一个单一的 EXE 文件. 然而,传统的做法是简单地将 EXE 文件与任何依赖的 DLL 一起部署为单独的文件。在 .NET 中,依赖 DLL 的部署非常简单:只需将它们与客户端计算机上的 EXE 文件放在同一个文件夹中,就完成了。

It is good practice to deploy your application (whether one file or many) as a single-file installer(either a setup.EXE or setup.MSI). .NET comes with deployment project templates that can create installers for you fairly easily.

将应用程序(无论是一个文件还是多个文件)部署为单文件安装程序(setup.EXE 或 setup.MSI)是一种很好的做法。.NET 附带部署项目模板,可以相当轻松地为您创建安装程序。

Slightly off-topic: You could use NGEN to compile your .NET application as a native EXE, but it would still be dependent upon the .NET runtime. The advantage of native compilation is that some things can be pre-compiled, but I've never seen a situation where this tiny performance increase is worth the bother.

稍微偏离主题:您可以使用 NGEN 将您的 .NET 应用程序编译为本机 EXE,但它仍将依赖于 .NET 运行时。原生编译的好处是可以预编译一些东西,但是我从来没有见过这种微小的性能提升值得费心的情况。

回答by Vertigo

I have used the .NETZ.NET open source executable packer to pack EXE and DLL files into single EXE file. Here is a command line example for how to pack DLL files into one file:

我使用.NETZ.NET 开源可执行打包程序将 EXE 和 DLL 文件打包成单个 EXE 文件。下面是一个关于如何将 DLL 文件打包成一个文件的命令行示例:

netz -s application.exe foo.dll bar.dll

回答by Phil Wright

There is a third party tool called .NET Reactorthat can do this for you. I have not used the tool and so am not sure how well it works.

有一个名为.NET Reactor 的第三方工具可以为您执行此操作。我没有使用过这个工具,所以不确定它的效果如何。

回答by Roger Ween

As it has been said, you can use ILMerge.

如前所述,您可以使用ILMerge

It may be easier however, if you use the free Phoenix protector, which also protects your code.

但是,如果您使用免费的 Phoenix 保护器,它可能会更容易,它也可以保护您的代码。

回答by Roger Ween

You can try the NBoxutility.

您可以尝试使用NBox实用程序。

?

?

回答by Marco Manelli

I'm using .netshrinkmyself, and it does exactly what you need. It packs the main and extra assemblies (DLL files) into one executable image.

我自己正在使用.netshrink,它完全符合您的需求。它将主程序集和额外程序集(DLL 文件)打包到一个可执行映像中。

I've been using it for one year already, and I'm not going back to ILMerge (it always crashes at some point...).

我已经使用它一年了,我不会回到 ILMerge(它总是在某个时候崩溃......)。

.netshrink main window from the product's page

来自产品页面的 .netshrink 主窗口

回答by surfmuggle

Jeffrey Richterwrote in his book excerptthat a callback method with the application domain'sResolveAssembly event can be registered to enable the CLRto find third-party assemblies and DLL files during program initialization:

Jeffrey Richter在他的书摘录中写道,可以注册具有应用程序域的ResolveAssembly 事件的回调方法,以使CLR在程序初始化期间查找第三方程序集和 DLL 文件:

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

Disclaimer: I have not used this myself. As far as I understood, the client still needs to have the .NET framework installed.

免责声明:我自己没有使用过这个。据我了解,客户端仍然需要安装 .NET 框架。

回答by nawfal

ILMergecan combine assemblies to one single assembly provided the assembly has only managed code. You can use the commandline application, or add reference to the EXE file and programmatically merge. For a GUI version, there is Eazfuscator, and also .Netz, both of which are free. Paid applications include BoxedAppand SmartAssembly.

ILMerge可以将程序集合并为一个程序集,前提是该程序集只有托管代码。您可以使用命令行应用程序,或添加对 EXE 文件的引用并以编程方式合并。对于 GUI 版本,有Eazfuscator.Netz,两者都是免费的。付费应用程序包括BoxedAppSmartAssembly

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 file.

如果您必须将程序集与非托管代码合并,我建议使用SmartAssembly。我从来没有在SmartAssembly 上打过嗝,但在其他所有的时候。在这里,它可以将所需的依赖项作为资源嵌入到您的主 EXE 文件中。

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

您可以手动完成所有这些操作,无需担心程序集是托管还是混合模式,方法是将 DLL 文件嵌入您的资源中,然后依赖 AppDomain 的程序集ResolveHandler。这是采用最坏情况的一站式解决方案,即具有非托管代码的程序集。

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

Where Programis the class name. The key here is to write the bytes to a file and load from its location. To avoid a chicken-and-egg problem, you have to ensure you declare the handler before accessing the 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 temporary directory since temporary 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 file, but at least it's a nuisance. AppData is a good location). Also note that you have to write the bytes each time; you can't load from location just because the DLL file already resides there.

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

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

对于托管 DLL 文件,您不需要写入字节,而是直接从 DLL 文件的位置加载,或者只是读取字节并从内存中加载程序集。像这样左右:

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 linkor thisas to how to load such DLL files.

如果组件是完全不受管理的,你可以看到这个链接为如何加载这个DLL文件。