编译c#项目,在项目属性中,平台与目标平台的区别是什么?

.NET编译、WOW64注册表访问、同时在X86和X64平台上部署应用程序

[翻译文章,原文请参考:http://www.codeproject.com/Articles/51326/Net-Compilation-registry-accessing-and-application.aspx]

多长的一个标题,不是吗?这是因为在一些情况下,你必需要做一大堆事情才能让你的.NET应用程序同时在x86和x64环境下成功运行,尤其是在你还需要使用一些非安全代码(unmanaged code)或者需要访问注册表的时候。

编译和运行.NET组件(Compiling and running assemblies)

原生代码(例如C++等等)都会被编译成为平台相关的二进制代码。这就是你在编译时候需要指定目标平台为x86或者x64的原因。在.NET平台下,情况有所不同。就与你可能已经知道的一样,你的VB或者C#代码并不会编译成为二进制代码,而是MSIL代码。这些代码加载时,JIT(Just-in-time)编译器负责把中间代码转化成为二进制代码。在这个转化过程中,编译器会根据当前操作系统来决定编译成32位代码还是64位代码。这就是你可以不给.NET组件指定目标平台,而选择"Any CPU"的原因。下面的图片是Visual Studio工程配置页的截图,你在这里可以选择目标平台。

既然如上所述,我为什么还要为.NET组件指定目标平台呢? "Any CPU"不应该是最好的选择吗? 是的。在绝大多数情况下,程序员开发的都是纯.NET应用程序,它不依赖任何非托管代码,也不需要进一步的优化。所以"Any CPU"是最好的选择(让JIT编译器动态确定编译成什么样的代码)。但是仍然有些情况你会想去指定目标平台,例如:

  • 如果你使用了任何非托管代码(它是平台相关的),就需要注意目标平台的问题。因为当JIT编译器选择了一个和你的非托管代码不兼容的模式去做编译时,你的应用程序可能无法加载这些非托管代码。
  • 如果你想对你的代码做平台相关优化。例如,你的应用程序需要做大量的RAM内存访问时,你可能只想让它运行在64位模式下。

另外,还有其他和目标平台相关的问题,比如注册表访问。我们会在这篇文章的后面讨论它。这篇博文对目标平台有进一步的讨论。

平台兼容性(Platform compatibility)

  • 如果你的.NET组件使用"Any CPU"选项编译
    • 可以运行在X86环境中(JIT编译器把它编译成X86二进制代码)
    • 可以运行在X64环境中(JIT编译器把它编译成X64二进制代码)
  • 如果你的.NET组件使用"X64"作为目标平台编译
    • 不可以运行在X86环境中
    • 可以运行在X64环境中
  • 如果你的.NET组件使用"X86"作为目标平台编译
    • 可以运行在X86环境中
    • 可以运行在X64环境中(在WOW64模式下)

WOW64模式

因为向后兼容对于产品销售很有帮助,所以Windows 64位操作系统提供了一个为运行32位应用程序的兼容模式。它被称为WOW模式(Windows on Windows)。你有使用模拟器在PC上玩过电视游戏吗?它就类似这个模拟器。WOW64是一个在64位机器上运行32位应用程序的模拟器。尽管我们还会在后面讨论一些关于WOW64模式的问题,你可以先访问这里来了解WOW模式的更多细节。

检测"Any CPU"进程的位数(Detecting the bitness of a "Any CPU" process)

当你把.NET组件编译成为"Any CPU"时,会发生什么事情呢?如果你知道这个.NET组件总是运行在X86环境下,那运行这个组件的进程也会是32位的。但是如果这个.NET组件可以运行在X86和X64环境下,那运行这个组件的进程是32位还是64位呢?更进一步说,如果它运行在X64操作系统上,JIT编译器会选择32位还是64位来编译它呢?一般来说,它是64位的。但是怎样能够检测到这个呢?很简单,如下代码所示:

1 if (IntPtr.Size == 4) 2 return eArchitecture.x86; 3 else if 4 (IntPtr.Size == 8) 5 return eArchitecture.x64;

System.IntPtr被设计用来保存内存地址的。在.NET中,它提供了一个非常方便的属性告诉我们它的长度。因此,如果它的长度是8个字节,我们就是在用64位地址。如果是4字节,我们就在32位环境中。就是如此简单!

注意:如果你的组件编译时选择X86为目标平台,运行它的进程永远都是32位。即使运行在64位环境中,它使用的是WOW64模式(模拟32位),并且仍然是32位进程

Windows 64位基础结构(Windows 64-bit infrastructure)

如上所述,Windows 64位操作系统提供了WOW64模式来允许运行X86应用程序。微软的工程师们决定不在你的硬盘上混淆32位和64位应用程序,所有X64应用程序被安装到默认的"Program Files"文件夹下面,同时,X86应用程序被安装到新的"Program Files(X86)"文件夹下面。

Windows X64注册表

在X64的Windows操作系统中,WOW64模式为32位应用程序和64位应用程序提供了分开的注册表逻辑视图(logical view),这让应用程序像访问两个独立的注册表。当为X64平台开发或者部署应用程序时,这是需要考虑的重要一点,因为X86-targeted的安装工程会以不同的方式访问注册表。简单来说,它用以下的方式工作:

Windows截获来自应用程序和组件对于注册表的调用,并且重定向到相应的注册表逻辑视图。这个过程对应用程序是透明的。可以从这里了解到注册表重定向的更多信息。

尽管下面的命名方式不是百分百正确,我们仍然分别称这两个注册表逻辑视图为32位注册表和64位注册表。

使用Visual Studio安装工程部署应用程序(Deploy Application with Visual Studio Setup Projects)

和很多人一样,如果你也是使用Visual Studio安装工程和Windows 安装包来部署你的应用程序,并且你想让你的应用程序同时兼容"X86"和"X64"平台,这里有一组你需要考虑的事情。

Visual Studio工程的目标平台

就像一般的组件一样,安装工程也有目标平台属性,当你在工程浏览器(Solution Explorer)中选中安装工程时,你可以在属性页中看到这个属性。如下图所示:

不幸的是这里没有"Any CPU"选项,所以我们必须自己选择平台。这个平台选择会影响到下面的几个方面:

  • 你应用程序的默认安装目录:"Program Files" 或者"Program Files (X86)"。
  • 如果安装工程需要修改注册表,这个会影响到安装程序是更新64位注册表还是WOW64进程中重定向的32位注册表。如果你的安装工程用到注册表,这是一个重要的事情。

所以,我应该给安装工程选择怎样的目标平台呢?它依赖于你的应用程序怎样被编译的和怎样为运行在X64环境下做准备。具体如下:

  • 如果你的应用程序编译成X86平台相关的,选择X86作为安装工程的目标平台。
  • 如果你的应用程序编译成X64平台相关的,选择X64作为安装工程的目标平台。
  • 如果你的应用程序使用"Any CPU"编译:
    • 如果你的应用程序已经为运行在X64环境下准备好了(例如,注册表访问已经考虑到WOW64模式),选择X64
    • 如果你不能确信你的应用程序已经为X64环境准备好了,选择X86

那我的注册表键值在哪里呢?如上所述,如果你的安装工作使用X86作为目标平台,它就不会修改64位注册表,而是重定向到由WOW64模式提供的32位注册表。因此,当你正常打开"regedit.exe"时,并不能找到它们。取而代之的是你需要打开这个特别的RegEdit去显示32位注册表:[Windows Installation path]\SysWOW64\regedit.exe

从C#访问注册表(Acessing the rigistry from C#)

你已经理解了Windows X64基础架构怎样设计的,以及你的应用程序怎样被安装工程安装的。现在,你可能需要学习怎样处理Window X64中两个注册表视图(Windows 64位注册表和Windows 32位注册表)。尤其是当你的应用程序以X86编译的并运行在WOW64模式下时。

常见的.NET访问注册表方式

想象你现在需要访问"LOCAL_MACHINE\Software"注册表节点下面的某个键。在.NET中,一般用下面的代码来做这件事情(使用名字空间Microsoft.Win32):

1 RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\[Your Key Here]");

这种做法在Windows X64机器中将只能访问到"64位"注册表,即使你的进程是运行在WOW64模式下面。因此,如果你的应用程序是用X86安装工程安装的,我们上面要找的注册表项就会找不到(因为安装包目标平台是X86,所有的注册表项安装时都写到"32位"注册表中)。我们真正需要访问的是由WOW64提供的"32位"注册表。

从C#访问32位WOW64注册表

到目前为止,.NET平台还没有方法让我们直接访问WOW64模式下面的32位注册表。为了实现这个功能,我们必须要使用Windows自身的API调用,导入"advapi32.dll"并使用函数RegOpenKeyEx。这个函数有一个参数"samDesired",它是用来指定访问选项的标识组合,其中一个选项(KEY_WOW64_32KEY)就是指定我们希望打开的注册表视图是WOW64下的32位注册表。

samDesired中的标识

 

Value Meaning

KEY_ALL_ACCESS (0xF003F)

Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.

KEY_CREATE_LINK (0x0020)

Reserved for system use.

KEY_CREATE_SUB_KEY (0x0004)

Required to create a subkey of a registry key.

KEY_ENUMERATE_SUB_KEYS (0x0008)

Required to enumerate the subkeys of a registry key.

KEY_EXECUTE (0x20019)

Equivalent to KEY_READ.

KEY_NOTIFY (0x0010)

Required to request change notifications for a registry key or for subkeys of a registry key.

KEY_QUERY_VALUE (0x0001)

Required to query the values of a registry key.

KEY_READ (0x20019)

Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.

KEY_SET_VALUE (0x0002)

Required to create, delete, or set a registry value.

KEY_WOW64_32KEY (0x0200)

Indicates that an application on 64-bit Windows should operate on the 32-bit registry view. For more information, see Accessing an Alternate Registry View.

This flag must be combined using the OR operator with the other flags in this table that either query or access registry values.

Windows 2000: This flag is not supported.

KEY_WOW64_64KEY (0x0100)

Indicates that an application on 64-bit Windows should operate on the 64-bit registry view. For more information, see Accessing an Alternate Registry View.

This flag must be combined using the OR operator with the other flags in this table that either query or access registry values.

Windows 2000: This flag is not supported.

KEY_WRITE (0x20006)

Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.

实现代码

"OpenSubKey"函数将会返回搜索的注册表项,它允许你指定从正常的注册表中读取还是从WOW64模式下的32位注册表中读取。下面的例子就是从WOW64模式下的32位注册表中读取注册表项:

你只需要把上面代码中的"[Key]"替换成你想要搜索的注册表项即可。

怎样让你的应用程序是注册表读取安全的(How to make your application a bulletproof registry reader)

现在,你已经有方法读取X64环境下的两个注册表了,我建议你用下面的方式来处理注册表访问问题

  1. 首先,使用.NET中的标准方式"Registry.LocalMachine.OpenSubKey"查找注册表项。这个方式能够覆盖你的程序运行在32位Windows上和运行在64位Windows上非WOW64模式的情况。
  2. 如果注册表项没有找到,用下面方式中的一种进行处理:
    1. 检测Windows版本
      1. 32位:异常,注册表没有找到
      2. 64位:尝试使用上面的代码去访问WOW64模式下的注册表。如果同样没有找到,启动异常处理
    2. 直接尝试去访问WOW64模式下的注册表,但把这段代码放到"try-catch"语句内。如果捕获到异常,或者注册表项没有找到,启动异常处理

Del.icio.us : .NET Compilation.NET编译WOW64X64X86注册表访问

备注


  • x86 将程序集编译为由兼容 x86 的 32 位公共语言运行库运行。

  • Itanium 将程序集编译为由采用 Itanium 处理器的计算机上的 64 位公共语言运行库运行。

  • x64 将程序集编译为由支持 AMD64 或 EM64T 指令集的计算机上的 64 位公共语言运行库运行。

  • anycpu(默认值)将程序集编译为在任意平台上运行。

在 64 位 Windows 操作系统上:

  • /platform:x86 编译的程序集将在 WOW64 下运行的 32 位 CLR 上执行。

  • /platform:anycpu 编译的可执行文件将在 64 位 CLR 上执行。

  • /platform:anycpu 编译的 DLL 将在与加载它的进程相同的 CLR 上执行。

有关开发在 Windows 64 位操作系统上运行的应用程序的更多信息,请参见 64 位应用程序



======================================

如果引用的库包含X86的库,或则可能需要在X86运行并且也需要支持X64位系统。

我个人为配置平台选择AnyCPU。

目标平台推荐使用X86。这样X86和X64位均可以运行,只不过在X64上是以WOW的CLR运行。

如果要在X86和X64位运行,建议所有宿主程序或者启动程序为X86优化,动态库可以为ANY CPU;

以下是我看到的一篇文章。作为佐证。


关于C#编写x86与x64程序的分析

http://blog.csdn.net/sundacheng1989/article/details/18349503

分类: 技术积累 1705人阅读 评论(1) 收藏 举报
电脑硬件CPU可以分为x86与x64, x86的机器只能安装32位的操作系统,如XP, WIN7_86, x64的机器既可以安装32位的系统,又可以安装64位的系统,只是在x64的机器上安装32位的系统,不能够很充分的利用这台机器的资源。x86程序,即适用于32为操作系统的程序,x64即适用于64位操作系统的程序。64位系统上依然可以运行32位的程序,但是这是通过WOW64来运行,通俗上讲,就是模拟出一个32位的CPU来运行这个程序

接下来是C#程序的编译运行,分为两步,第一步是编译成IL,在编写C#程序的时候,需要考虑到在最终程序需要在哪种环境下运行。Build中默认的环境是Any CPU,还有X86,X64. 这些有什么不同?根据名字,我们可能认为,编译器会根据选择的环境不同来生成不同的二进制文件。然而,C#编译器只是把代码编译成为了IL代码,以dll的形式。然后再程序运行的时候,JIT编译器才把IL代码编译为CPU能够识别的二进制码。所以,无论选择哪个环境,都不会影响dll的生成,只是在dll的头文件中加入了一些平台信息,最终运行的时候JIT会根据这些信息来编译dll给CPU处理

x86编译的dll和x64编译的dll是不能相互引用的,否则会抛出程序集不能下载的异常。Any CPU编译的dll,被哪种类型的程序调用,就会编译成哪种类型的dll.现在考虑一种情况
A项目是Console Application, B项目是Class Library, 在64位操作系统的机器上。A项目为Start Project,决定了这个程序的32位还是64位的属性。如果A设置为x86, A决定了这个程序为32位,B设置为x86, 因为两个项目都是同一类型,虽然都是32位,但可以在64位系统上运行。如果A为Any CPU, B为x86,就会出问题。因为A虽然为Any CPU,但是在64位操作系统上运行,就会转换成64位的dll,A决定了这个程序为64位,然后再调用32位的dll,就会出问题。如果A为x86, B为Any CPU, 就不会出问题。 因为A决定了这个程序为32位,调用B的时候就会把B转成32位,一切正常。

有人说,难道不可以一直把Project设置为Any CPU吗?因为现在很多的系统都是64位的,所以我们的程序设置成Any CPU, 在64位机器上跑的时候就是64位的程序,但是这些程序很多时候需要使用一些组件,比如COM组件等,而这些组件仅仅是32位的dll,根本不支持64位,当我们用64位的程序调用这些dll的时候就会有问题。所以,这时候,我们就需要把我们的程序设置成x86.

那么有人说,那都设置为x86不就可以了?那么现在我们就要保证我们的程序引用的dll都是32位的。虽然很多第三方的dll都提供32位与64位两个版本,基本上可以保证这一点。但是如果没有必要做成32位的程序,单纯是为了方便,我们前边说过64位的系统运行32位的程序,效率不高,浪费了我们的资源

展开阅读全文

没有更多推荐了,返回首页