为什么在C#应用程序中IL代码被打包进exe文件中?

24

我试图通过对C#可执行文件进行ILDASM和ILASM的往返操作来重新生成一个exe文件。据我所知,由ILDASM生成的.il文件足以生成.exe文件。

我很好奇为什么.NET框架设计时要使用exe文件进行部署,而不是将.il文件部署给用户。难道C#编译器不能生成.il文件并让JIT编译器直接使用该.il文件作为输入吗?这是因为操作系统需要.exe扩展名来调用加载器,还是因为文件大小或性能考虑?

注:这个问题并没有实际意义。我问这个问题是为了让我的概念更加清晰,因为我相信我缺乏很多知识。


1
+1 好问题。相比之下,这更接近于Java部署的工作方式 - 对吗?.jar文件在运行之前必须转换为字节码(据我所知),这会影响其启动时间。我是Java的新手,所以Java大师们,请纠正任何错误陈述。 - harpo
你是在询问文件内容还是文件扩展名? - SLaks
Slaks - 我想了解两个方面的内容,信息越详细越好。 - paseena
4个回答

20

为了迎合.NET,增加另一种扩展名是没有任何意义的。

.NET可执行文件是PE文件,它们提供了最少量的本地代码来引导正确版本的CLR并将IL加载到内存中并移交给CLR。

Windows本地知道如何处理PE文件,并且在EXE中构建的间接机制也不需要了解.NET。

使用.il文件,您需要向Windows注册扩展名,然后确保加载正确的CLR版本-据我所知,您只能将扩展名与一个可执行文件关联。

要支持多个CLR版本,您需要某种中介来检查您的.il文件以确定要加载哪个CLR...之后事情变得复杂和脆弱。

将所有这些打包到PE中可以优雅地解决这些问题。

尽管这是一篇较旧的文章,但当前.NET Framework的原则仍然相同:

Win32可移植可执行文件格式深入探讨,第2部分

关键部分“ .NET标题”解释了如何工作:

Microsoft .NET环境下生成的可执行文件首先是PE文件。然而,在大多数情况下,.NET文件中的普通代码和数据都很少。.NET可执行文件的主要目的是将.NET特定信息(如元数据和中间语言(IL))加载到内存中。此外,.NET可执行文件链接到MSCOREE.DLL。这个DLL是.NET进程的起点。当.NET可执行文件加载时,它的入口点通常是一个小的代码存根。该存根只是跳转到MSCOREE.DLL中的导出函数(_CorExeMain或_CorDllMain)。从那里开始,MSCOREE接管,并开始使用可执行文件中的元数据和IL。这种设置类似于Visual Basic(.NET之前)中应用程序使用MSVBVM60.DLL的方式。.NET信息的起点是IMAGE_COR20_HEADER结构,该结构当前在.NET Framework SDK和更高版本的WINNT.H中定义的CorHDR.H中定义。IMAGE_COR20_HEADER由DataDirectory中的IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR条目指向。图10显示了IMAGE_COR20_HEADER的字段。元数据、方法IL和其他指向IMAGE_COR20_HEADER的内容的格式将在后续文章中描述。

2
值得注意的是,从XP开始,CLR引导代码不再使用。它必须存在,因为这就是.NET exes的规格,但操作系统加载程序知道如何加载.NET可执行文件。 - Damien_The_Unbeliever
2
@damien - 你有那个信息的链接吗? - Kev

5

.exe文件更小,而且它们遵循现有的标准PE格式,使Windows集成更简单。

Java需要注册.jar扩展名并将其与单个JRE路径中的java.exe关联。

相比之下,由于.Net程序集也是普通的Windows可执行文件,因此.Net不需要注册任何文件关联。相反,.Net EXE包含定位正确版本的运行时并调用它来执行EXE的代码。
这样,多个版本的运行时可以在同一台机器上共存,而无需一个单独的加载器打开一个.il文件,找出它是哪个版本,然后使用正确的版本运行它。

另外,解析速度较慢;.il文件会执行得更慢,因为运行时需要解析IL。


1
更不用说.exe.il更为人所知了。"嘿,这个东西是个程序!" - BoltClock
1
@Bolt:这并没有阻止 Java。 - SLaks
3
@Kev:阅读 CIL 字节码比解析字符串更快更容易。 - SLaks
@SLaks:你能双击一个 .jar 文件吗? - oɔɯǝɹ
2
@slaks - 好的,我重新阅读了一下,我明白你的观点了,但我认为OP将IL汇编语句与编译器生成的结果MSIL混淆了。 - Kev
显示剩余3条评论

3
早期版本的Windows没有对.NET提供操作系统级别的支持。这导致出现了一些替代方案:
  1. 一个exe文件,它加载.NET CLR,然后使用它来运行IL。缺点是你无法区分包含.NET IL和本机exe的exe文件。如果您想以减少的.NET权限运行它,则这很重要。
    程序在一眼看上去与本地程序不可区分,这可能会导致更高的接受度。
    它还允许用.NET程序替换本地程序,而无需更改所有调用它的代码。
  2. 一种包含二进制IL的新文件格式。类似于Java的jar文件。双击时,它们将使用相关联的程序打开。类似于“rundotnet.exe myprogram.net”。
    这会导致假定程序可执行文件和逻辑程序之间存在一对一映射的程序出现问题。这包括防火墙和任务栏分组功能。在任务管理器中,您看不到多个“rundotnet.exe”对应的内容,...
    还有混合本机和.NET代码的程序集也无法使用此解决方案。
    这样做的最大优势是,在执行之前,程序代码可以由.NET运行时进行验证。
  3. 带有新文件扩展名的PE文件。这是我最喜欢的方法。在旧版本的Windows中,您可以简单地添加一个注册表项来将其作为普通程序运行,在新版本中,您可以给它特殊处理。这避免了2和3的问题。
有了内置的操作系统支持,就可以避免大多数单独文件格式的问题。选择1而不是2是可以理解的,但我不知道他们为什么没有选择3。我猜想他们喜欢所有(现代)可执行文件具有相同的文件扩展名。

0
.Net runtime/JIT使用IL的字节码表示。这类似于将汇编编译成机器码。与保持IL作为"可执行"代码相比,它对数据的完整性影响较小且更容易验证。此外,它还会使文件变得更大。

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