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

36
我不太熟悉使用Visual Studio 2005的.NET桌面应用程序。是否可以将整个应用程序运行在单个.exe文件中?

3
你是想同时摆脱用户需要安装 .Net 框架的需求,还是只想将几个 DLL 文件与主 .exe 文件链接在一起? - Joel Coehoorn
13个回答

0

是的,您可以将dotnet Windows应用程序压缩成单个.exe文件。

只需按照以下步骤操作:

  1. 右键单击项目,在“管理NuGet包”中进行管理。
  2. 安装Costura.Fody。
  3. 清理bin文件夹中的Debug文件夹(删除所有文件和文件夹)。
  4. 运行该项目。
  5. 前往Debug文件夹,然后进入app.publish文件夹,在这里您将获得一个单独的exe文件。

0

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

如果您需要合并带有非托管代码的程序集,我建议使用SmartAssembly。我从未在SmartAssembly中遇到过任何问题,但在其他所有工具中都遇到了问题。在这里,它可以将所需的依赖项作为资源嵌入到您的主EXE文件中。

您可以手动完成所有这些操作,无需担心程序集是托管的还是混合模式的,只需将DLL文件嵌入到您的资源中,然后依靠AppDomain的Assembly 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.

如果程序集完全是非托管的,你可以查看这个链接或者这个链接来了解如何加载这样的DLL文件。


0

VS2022 .net 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运行”(如果不可用,则必须先在Windows上启用powershell),这将在项目内创建一个名为“SingleFileExe”的文件夹,并将您的单个文件放置其中。

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

干杯。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接