【发布时间】:2008-09-24 11:34:02
【问题描述】:
我对 .NET 桌面应用程序不太熟悉(使用 Visual Studio 2005)。是否可以让整个应用程序从单个 .exe 文件运行?
【问题讨论】:
-
您是想摆脱用户安装 .Net 框架的需要,还是只想将几个 dll 链接到主 .exe 文件?
我对 .NET 桌面应用程序不太熟悉(使用 Visual Studio 2005)。是否可以让整个应用程序从单个 .exe 文件运行?
【问题讨论】:
是的,您可以使用ILMerge 工具。也可以使用as a NuGet package。
【讨论】:
今天,在 2015 年,您可以为此使用 Costura.Fody。使用它相当于将 NuGet 包添加到您的项目并重新编译。我不确定它是否适用于问题中的 Visual Studio 2005,但也不是 2008 年。我在一些项目中使用过它,效果很好。
Fody 是一个通用的Code Weaver,它是免费和开源的。它允许对已编译的 .NET 代码进行后处理以增强其功能的基本思想。例如,日志记录可以添加到每个方法调用等。在我们的例子中,它将依赖的 DLL 文件打包到程序集资源中,以便它们可以在程序运行时加载,就像它们是独立的依赖项一样。
【讨论】:
是的。在 .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 运行时。原生编译的好处是有些东西可以预编译,但我从未见过这样的微小性能提升值得费心的情况。
【讨论】:
有一个名为.NET Reactor 的第三方工具可以为您执行此操作。我没有使用过这个工具,所以不确定它的效果如何。
【讨论】:
我使用.NETZ .NET 开源可执行打包程序将 EXE 和 DLL 文件打包成单个 EXE 文件。下面是一个命令行示例,说明如何将 DLL 文件打包到一个文件中:
netz -s application.exe foo.dll bar.dll
【讨论】:
现在是 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 可以设置为copyused 或link。更多详情:https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained。-f参数指定框架版本,即net5.0等-c参数为配置,一般为Release。-r 参数是要构建的运行时,可以是win-x86、win-x64 和linux-x64。 ARM 也可能有一些选项。-o 参数是输出文件夹。完整参考:https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish
【讨论】:
Jeffrey Richter在他的book excerpt中写道,可以注册一个带有application domain’sResolveAssembly事件的回调方法,使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。
不过,如果您使用the free Phoenix protector 可能会更容易,这也可以保护您的代码。
【讨论】:
我自己使用的是.netshrink,它完全符合您的需求。它将主程序集和额外程序集(DLL 文件)打包到一个可执行映像中。
我已经使用它一年了,我不会再使用 ILMerge(它总是在某个时候崩溃......)。
【讨论】:
您可以尝试NBox 实用程序。
َ
【讨论】:
ILMerge 可以将程序集合并为一个程序集,前提是该程序集只有托管代码。您可以使用命令行应用程序,或添加对 EXE 文件的引用并以编程方式合并。对于 GUI 版本,有 Eazfuscator 和 .Netz,两者都是免费的。付费申请包括BoxedApp和SmartAssembly。
如果您必须将程序集与非托管代码合并,我建议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.
【讨论】:
是的,您可以将 dotnet windows 应用程序压缩成单个 .exe 文件。
您只需遵循以下流程:
【讨论】: