你所构建的每个程序集可以是一个可执行程序, 或者一个包含一组类型的DLL, 这个DLL被其他可执行程序使用. 当然, CLR负责管理这些程序集中的代码的执行, 这意味着.NET Framework必须安装在宿主机器上. 微软已经创建了软件包, 你可以免费地将.NET Framework安装在客户的机器上, 一些版本的Windows在发布时就已经安装了.NET Framework.
你可以通过在%SystemRoot%/system32目录中查找MSCorEE.dll文件来判断是否安装了.NET Framework, 然而, 一台机器可以同时安装几个版本的.NET Framework. 如果你想确定你所安装的.NET Framework的版本, 在下面的注册表关键字下检查以”v”字母开头的子关键字.
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/.NETFramework/policy
从.NET Framework的2.0版本开始, 微软也发布命令行方式的工具clrver.exe来给出安装在一台机上的所有版本的CLR. 这个工具也能显示出当前正在运行的进程所使用的CLR的版本, 通过-all开关或者传递进程ID来显示你所感兴趣的进程.
在开始讲述CLR如何载入程序之前, 我们需要讨论一下32-bit和64-bit版本的Windows操作系统. 如果你的程序集文件只包含类型安全的托管代码, 你的代码将可以运行在32-bit和64-bit版本的Windows上, 而不需要修改代码. 实际上, 通过编译器产生的EXE/DLL文件可以运行在32-bit的Windows上, 也可以运行在x64和IA64版本的64-bit Windows上! 换句话说, 一个文件可以运行在任何机器上, 只要这台机器安装了.NET Framework.
在极端少见的情况下, 开发者打算将写的代码只运行在特定版本的Windows上, 开发者使用了非安全的代码或者集成了非托管的代码, 而这些代码是为特定CPU工作的. 为了帮助这样的开发者, C#编译器提供了一个/platform命令行开关, 这个开关允许你指定生成的程序集是否只能运行在x86机器上的32-bit版本的Windows, x64机器上的64-bit版本的Windows, 或者Intel Itanium机器上的64-bit Windows. 如果你不打算制定平台, 默认的情况是任何的CPU都可以运行, 这也表明生成的程序集可以运行在任何版本的Windows上. Visual Studio的用户可以通过项目的属性页设置一个项目的目标平台, 点击Build分页面, 然后选择目标平台列表的一个选项.
图1-3 通过Visual Studio设置目标平台
>>注意: Visual Studio没有在列表中显示出Itanium目标平台, 除非你运行VSTS版本的Visual Studio, 因为我运行的是Visual Studio Professional Edition, Itanium没有出现在列表中.
依赖于平台开关, C#编译器产生的程序集或者包含PE32或者PE32+header, 编译器也将产生所需要的CPU architecture (或者未知的)放在header中. 微软发布了两个命令行工具, DumpBin.exe和CorFlag.exe, 你可以使用它来检查编译器产生的header的信息.
当运行一个可执行文件时, Windows检查这个EXE文件头来决定应用程序是否需要32-bit或者64-bit的地址空间. 一个带有PE32 header的文件可以运行在32-bit或者64-bit的地址空间中, 一个带有PE32+ header的文件需要64-bit的地址空间. Windows还检查嵌入在header中的CPU的architecture信息来确保文件是否和计算机的CPU类型匹配. 最近, 64-bit版本的Windows提供了一个技术, 允许32-bit的Windows应用程序运行在其上面. 这个技术被称为WoW64 (for Windows on Windows64). 这个技术甚至允许带有x86 native代码的32-bit的应用程序运行在Itanium机器上, 因为WoW64技术能模拟x86指令集, 当然这需要重大性能的代价.
表1-2显示了两件事情. 第一, 当你指定不同的/platform命令行开关开指定C#编译器时, 你得到的托管模块的类型. 第二, 应用程序如何运行在各种版本的Windows上.
表1-2 /platform对生成的模块和Runtime的影响
/platform 开关 | 生成的 托管模块 | X86 Windows | X64 Windows | IA64 Windows |
anycpu (默认) | PE32/agnostic | 以32位应用程序运行 | 以64位应用程序运行 | 以64位应用程序运行 |
x86 | PE32/x86 | 以32位应用程序运行 | 以WoW64位应用程序运行 | 以WoW64位应用程序运行 |
x64 | PE32+/x64 | 不能运行 | 以64位应用程序运行 | 不能运行 |
Itanium | PE32+/Itanium | 不能运行 | 不能运行 | 以64位应用程序运行 |
在Windows检查EXE文件头之后, 决定创建一个32位进程, 或者64位进程, 或者WoW64位进程, Windows载入x86, X64或者IA64版本的MSCorEE.dll到进程的地址空间中. 在x86版本的Windows上, x86版本的MSCorEE.dll可以在C:/Windows/System32目录下找到. 在x64或者IA64版本的Windows上, x86版本的MSCorEE.dll可以在C:/Windows/SysWoW64目录下找到,, 而64位版本(x64或者IA64)的MSCorEE.dll可以在C:/Windows/System32目录下找到(这是为了向后兼容的原因). 然后进城的主线程调用定义在MSCorEE.dll中的一个方法. 这个方法初始化CLR, 载入EXE程序集, 然后调用入口点方法(Main). 在这个入口点, 托管的应用程序开始运行.
>>注意: 通过7.0或者7.1版本的C#编译器构建的程序集包含一个PE32 header, CPU-architecture是agnostic. 然而, 在载入时, CLR认为这些程序集只是x86版本的. 对于可执行文件, 这增加了应用程序运行在64位操作系统的可能性, 因为可执行文件将在WoW64中载入.
如果非托管的应用程序调用LoadLibrary来载入一个托管的程序集, Windows知道要载入并初始化CLR (如果还没载入CLR) 来处理程序集中的代码. 当然, 在这种场合, 进程已经正在运行了, 这将限制程序集的可用性. 例如, 一个托管的程序集是以/platform:x86开关编译的, 那么它将不能被载入到一个64位的进程中, 然而一个用这种开关编译的可执行文件可以被载入到运行着64位版本Windows的WoW64.