English 中文(简体)
一个.NET窗口应用程序可以压缩成一个.exe文件吗?
原标题:
  • 时间:2008-09-24 11:34:02
  •  标签:

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

问题回答

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

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

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

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

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

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

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

现在是2021,对这一点的支持已经有了突飞猛进的改善。

如果你已经跳转到.NET 5(它支持Windows窗体!),你可以制作一个单独的exe文件,甚至可以嵌入本机二进制文件。您必须从命令行运行publish命令,但它将生成一个带有任何配置或内容文件的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 can be set to false to eliminate code removal in case of heavy reflection use.
  • TrimMode can be set to copyused or link. More details here: https://learn.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained.
  • The -f parameter specifies the framework version, i.e. net5.0, etc.
  • The -c parameter is configuration, usually Release.
  • The -r parameter is the runtime to build for, can be win-x86, win-x64 and linux-x64. There may be options for ARM as well.
  • The -o parameter is the output folder.

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

有一个名为.NET Reactor可以为您做到这一点。我没有使用过该工具,所以不确定它的效果如何。

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

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

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

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

正如前面所说,您可以使用ILMerge

但是,如果您使用免费的Phoenix保护程序,它也保护您的代码。

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

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

您可以尝试NBox实用程序。

َ

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

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

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

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

其中<code>程序</code>是类名。这里的关键是将字节写入文件并从其位置加载。为了避免鸡和蛋的问题,必须确保在访问程序集之前声明处理程序,并且不要访问加载(程序集解析)部分内的程序集成员(或实例化必须处理程序集的任何东西)。此外,请注意确保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.

如果程序集是完全非托管的,您可以看到此链接关于如何加载此类DLL文件。

是的,您可以将dotnetwindows应用程序压缩为一个.exe文件。

您只需遵循以下流程:

  1. Goto Manage NuGet Packages right clicking on your project.
  2. Install Costura.Fody
  3. Clean Debug folder inside the bin folder(Delete all files and folder).
  4. Run the project.
  5. Goto debug folder, then to app.publish folder, you will get a single exe file here.

VS2022净6.0。

在PS1文件中使用此命令(例如,调用文件publishSingleFile.PS1):

$startFolder = $PSScriptRoot
$container = $startFolder + "/SingleFileExe"
dotnet publish --runtime win-x86 --self-contained true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --output $container

然后将文件放在projectName.sln文件的同一文件夹中。右键点击ps1文件,然后选择“使用powershell运行”(如果不可用,您必须首先在窗口上启用powershell),这将在您的项目中创建一个名为“SingleFileExe”的文件夹,其中包含您的单个文件。

您可以删除projectName.xml和projectName.pdb文件。您将拥有包含所有内容的projectName.exe文件。

干杯





相关问题
热门标签