在“任何 CPU”.NET 程序集上强制 x86 CLR

Posted

技术标签:

【中文标题】在“任何 CPU”.NET 程序集上强制 x86 CLR【英文标题】:Force x86 CLR on an 'Any CPU' .NET assembly 【发布时间】:2010-12-03 04:44:35 【问题描述】:

在 .NET 中,“平台目标:任何 CPU”编译器选项允许 .NET 程序集在 x64 机器上以 64 位运行,在 x86 机器上以 32 位运行。还可以使用“平台目标:x86”编译器选项强制程序集在 x64 机器上以 x86 运行。

是否可以运行带有“Any CPU”标志的程序集,但确定它应该在 x86 还是 x64 CLR 中运行?通常这个决定是由 CLR/OS 加载器(据我了解)根据底层系统的位数做出的。

我正在尝试编写一个可以与其他正在运行的进程交互(读取:注入代码)的 C# .NET 应用程序。 x64 进程只能注入到其他 x64 进程中,x86 也是如此。理想情况下,我想利用 JIT 编译和 Any CPU 选项来允许使用单个应用程序注入 x64 或 x86 进程(在 x64 机器上)。

这个想法是应用程序将被编译为任何 CPU。在 x64 机器上,它将作为 x64 运行。如果目标进程是 x86,它应该重新启动自身,强制 CLR 将其作为 x86 运行。这可能吗?

【问题讨论】:

【参考方案1】:

您可以使用CorFlags 应用程序了解应用程序的运行方式并对其进行静态更改。要了解应用程序将如何运行,请使用:

corflags <PathToExe>

要更改应用程序的运行方式,请使用:

corflags /32bit+  <PathToExe>

这将使 EXE 文件作为 32 位进程运行。有关程序集应如何运行的信息存储在 PE 标头中。请参阅堆栈溢出问题How to find if a native DLL file is compiled as x64 or x86?

如果你想在运行时注入代码,你必须在C++/COM 中写一个.NET profiler。 请参阅 .NET Internals: The Profiling APIProfiling (Unmanaged API Reference) 了解更多详情。

您需要实现 JitCompilationStarted 回调并在那里完成您的工作。如果您朝这个方向发展,则必须将注入 DLL 文件构建为 x86 和 x64。一旦设置了以下环境变量,本机 DLL 文件将由 CLR 加载:

Cor_Enable_Profiling=0x1
COR_PROFILER=CLSID-of-your-native-DLL-file

如果设置正确,那么 64 位版本将“看到”64 位进程,而 32 位版本将“看到”32 位进程。

【讨论】:

感谢您的信息 :) 我知道 corflags 应用程序,但想知道是否有任何方法可以在运行时以编程方式实现类似结果。 一旦进程运行,就无法改变它的上下文! 在运行时改变上下文并不意味着只是在PE头设置一个位,32位进程在WOW仿真层下运行。我看不到进程如何在运行时保存其状态,进行上下文切换并继续运行。见此链接:blogs.msdn.com/oldnewthing/archive/2008/12/22/9244582.aspx 我并不是很希望进程在运行时更改它的上下文,而是说我将程序集 a.exe 编译为 AnyCPU。它在 x64 机器上以 64 位运行。如果它需要注入 32 位,我希望它启动一个自身的新实例 (Process.Start("a.exe")) 并强制该新实例以 x86 启动【参考方案2】:

我已经有一段时间没有尝试这个了,但我相信调用程序集的进程的位数决定了它会被 JITed 为 x86 还是 x64。

因此,如果您编写一个小型控制台应用程序并将其构建为 x86,另一个构建为 x64,则运行其中一个或另一个将导致加载到进程中的其他程序集以 32 位或 64 位运行。当然,这假设您在 64 位机器上运行。

【讨论】:

是的,我知道您可以通过将其包装在 x86 启动器程序集中来强制它,但我想知道您是否可以为“任何 CPU”编译的程序集动态强制它。无论如何,谢谢,如果我找不到其他东西,可能会回到这个。会投票但没有足够的代表。 进程是 64 位或 32 位。如果程序集在 32 位进程中加载​​并且构建为 Any CPU,它将被 JITed 为 32 位,在 64 位进程中它将被 JITed 为 64。您打算如何创建托管您的程序集的程序集? 【参考方案3】:

我不确定是否可以帮助您。但这是我的经验。

我有一个主机应用程序A.exe(编译为x86),我有一个来自主机应用程序的客户端应用程序B.exe(编译为ANY CPU)。我使用System.Diagnostic.Process 类从A.exe 启动B.exe

现在的问题是,如果我将两者放在 x64 机器上,那么 A.exe 将以 x86 运行,B.exe 将以 x64 运行

但是如果A.exe调用程序集c(c.dll,编译为Any CPU),并且B.exe也调用c.dll,那么c.dll将跟随调用它的应用程序。换句话说,在 64 位机器中,当A.exe 调用它时,它的行为类似于x86 dll,而当B.exe 调用它时,它的行为类似于x64

【讨论】:

【参考方案4】:

我通过创建两个(实际上是三个)二进制文件做了类似的事情。我有一个检测我试图注入的进程是 32 位还是 64 位。然后,此过程将启动 32 位或 64 位版本的注入二进制文件(而不是像您提到的那样重新启动自身)。

这听起来很麻烦,但您可以在构建时通过构建后事件轻松实现此目的,该事件会复制您的输出二进制文件并使用CorFlags 实用程序强制副本以 32 位运行。这样您就不必在您的应用程序中部署 CorFlags 实用程序,这可能由于某种原因是不合法的。

我认为这与您最初的想法非常相似,除了两行构建事件之外真的不需要更多工作。

【讨论】:

以上是关于在“任何 CPU”.NET 程序集上强制 x86 CLR的主要内容,如果未能解决你的问题,请参考以下文章

C# IO 不适用于 x64 或仅在任何 CPU 上的 x86 配置

Visual Studio“任何 CPU”目标是啥意思?

如何在 .Net 核心 exe/程序集上运行 ildasm?

.NET 4.0 NGEN x64 程序集上的符号解析不起作用

x86 中的 BEXTR 指令是如何工作的

在 ASP.NET 应用程序的生产环境中将 x86 更改为 x64 dll