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

时间:2008-09-24 11:34:03

标签: .net windows

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

12 个答案:

答案 0 :(得分:32)

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

答案 1 :(得分:8)

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

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

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

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

稍微偏离主题:您可以使用NGEN将.NET应用程序编译为本机EXE,但它仍然依赖于.NET运行时。本机编译的优点是可以预编译一些东西,但我从未见过这种微小的性能提升值得打扰的情况。

答案 2 :(得分:5)

有一个名为.NET Reactor的第三方工具可以为您执行此操作。我没有使用过该工具,因此不确定它的工作情况。

答案 3 :(得分:5)

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

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

答案 4 :(得分:2)

如上所述,您可以使用ILMerge

但是,如果您使用the free Phoenix protector,这也可能更容易,这也可以保护您的代码。

答案 5 :(得分:2)

现在是 2021 年,对此的支持已经突飞猛进。

如果您已跳转到 .NET 5(支持 Windows 窗体!),您可以制作一个甚至嵌入本机二进制文件的单个文件 exe。您必须从命令行运行发布命令,但它会生成一个带有任何配置或内容文件的单个 exe。 dotnet sdk 不需要存在于目标机器上。

dotnet.exe publish YourProject.csproj -f net5.0 -o package/win-x64 -c Release -r win-x64 /p:PublishTrimmed=true /p:TrimMode=Link /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true

一些注意事项:

  • PublishTrimmed 可以设置为 false 以在大量反射使用的情况下消除代码删除。
  • TrimMode 可以设置为 copyusedlink。更多详情请访问:https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained
  • -f 参数指定框架版本,即 net5.0 等。
  • -c 参数是配置,通常是 Release
  • -r 参数是要为其构建的运行时,可以是 win-x86win-x64linux-x64。也可能有 ARM 的选项。
  • -o 参数是输出文件夹。

完整参考:https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish

答案 6 :(得分:1)

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

答案 7 :(得分:1)

Jeffrey Richter在他的book excerpt中写道,可以注册一个带有application domain’s 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);
      }
   };

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

答案 8 :(得分:0)

您不能将其拆分为多个程序集(通常是Visual Studio解决方案中的项目)。无论如何都需要.NET Framework,没有办法 - 而且这是正确的方法 - 嵌入它。

答案 9 :(得分:0)

是的,您可以创建一个EXE文件。两篇好文章是:

  1. How to include all related DLLs into a single EXE file
  2. .NET runtime -- single EXE file

答案 10 :(得分:0)

如果程序集只有托管代码,

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

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

您可以手动执行所有操作,无需担心程序集是否受管理或处于混合模式,方法是将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);
        };
    }
}

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

对于托管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.

如果程序集完全不受管理,您可以看到this linkthis如何加载此类DLL文件。

答案 11 :(得分:0)

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

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

.netshrink main window from the product's page