我想为我的C#应用程序添加cpuid功能。我在网上发现this有趣的博客文章。我可能需要MASM来编译这个,但是:编译用于C#的X86/X64程序集#
- 我该如何启动?
- 我怀疑我将不得不为X86和X64编译一个dll,但我不知道如何去处理这样的事情(而且我有点时间紧张)。
所以,任何帮助将超过欢迎!
我想为我的C#应用程序添加cpuid功能。我在网上发现this有趣的博客文章。我可能需要MASM来编译这个,但是:编译用于C#的X86/X64程序集#
所以,任何帮助将超过欢迎!
CPUID是一个巨大的痛苦,我会建议不要沿着这条路走下去,如果你能避免它。英特尔和AMD处理器之间的CPUID结果不同(至少对于超线程和缓存拓扑结构等有趣的内容),并且在不同处理器版本之间并不是特别稳定。 (较新的Intel i7处理器引入了一个新的CPUID值(eax = 0xb),它取代了早期处理器上CPUID支持的信息)。
如果你能摆脱它,你最好的选择是使用WMI(见Win32_Processor)或GetLogicalProcessorInformation。
如果在您的平台上支持这些解决方案(要获得逻辑处理器信息,要求WinXP sp3或更新版本的客户端或Windows Server 2008或更新的服务器端,这两种解决方案中的任何一种都将成为一种更简单,更易于管理的解决方案)。
如果你真的想用CPUID来试试你的运气,我建议做的就是创建一个能够执行CPUID并将结果返回给托管代码的简单存根(你将需要不同版本的32位和64位),并在托管应用程序的上下文中执行这些操作。我通过编译本机应用程序来完成此任务,然后将我的CPUID方法的原始指令字节抽取到可从托管代码执行的字节数组中。
这应该让你开始只有32位支持:
using System;
using System.Runtime.InteropServices;
static class Program {
static void Main() {
//Allocate the executable buffer on a distinct page
// rather than just pinning it in place because we
// need to mark the page as executable.
// Failing to do this would cause NX-enabled machines
// to have access violations attempting to execute.
IntPtr pExecutableBuffer = VirtualAlloc(
IntPtr.Zero,
new IntPtr(CPUID_32.Length),
AllocationType.MEM_COMMIT | AllocationType.MEM_RESERVE,
MemoryProtection.PAGE_EXECUTE_READWRITE
);
Marshal.Copy(CPUID_32, 0, pExecutableBuffer, CPUID_32.Length);
CPUID executeHandler = (CPUID)Marshal.GetDelegateForFunctionPointer(
pExecutableBuffer, typeof(CPUID));
CPUID_Args args = new CPUID_Args();
args.eax = 0;
executeHandler(ref args);
Console.WriteLine("eax: {0} ebx: {1} ecx: {2} edx: {3}",
args.eax,
args.ebx,
args.ecx,
args.edx);
VirtualFree(
pExecutableBuffer,
IntPtr.Zero,
FreeType.MEM_RELEASE);
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void CPUID(ref CPUID_Args args);
private static readonly byte[] CPUID_32 = new byte[] {
0x53, // push ebx
0x57, // push edi
0x8B, 0x7C, 0x24, 0x0C, // mov edi,dword ptr [esp+0Ch]
0x8B, 0x07, // mov eax,dword ptr [edi]
0x8B, 0x4F, 0x08, // mov ecx,dword ptr [edi+8]
0x0F, 0xA2, // cpuid
0x89, 0x07, // mov dword ptr [edi],eax
0x89, 0x5F, 0x04, // mov dword ptr [edi+4],ebx
0x89, 0x4F, 0x08 , // movdword ptr [edi+8],ecx
0x89, 0x57, 0x0C , // mov dword ptr [edi+0Ch],edx
0x5F, // pop edi
0x5B, // pop ebx
0xC2, 0x04, 0x00 // ret
};
[Flags]
enum AllocationType {
MEM_COMMIT = 0x1000,
MEM_RESERVE = 0x2000,
}
[Flags]
enum MemoryProtection {
PAGE_EXECUTE_READWRITE = 0x40,
}
[Flags]
enum FreeType {
MEM_RELEASE = 0x8000
}
[DllImport("kernel32.dll")]
static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
IntPtr dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool VirtualFree(
IntPtr lpAddress,
IntPtr dwSize,
FreeType dwFreeType);
}
[StructLayout(LayoutKind.Sequential)]
struct CPUID_Args {
public uint eax;
public uint ebx;
public uint ecx;
public uint edx;
}
我想你可以在C++代码中使用内联汇编语法(__asm
)构建一个本地CLR程序集。
我似乎记得这在X64模式下无法正常工作。 – Kris 2009-11-03 20:16:13
哦,我在Windows上与C#/ C++混淆了一段时间,这肯定是win32。 – 2009-11-03 20:39:48
现在这是一个有用的答案:) 你是第一个提到这可能是32位和64位之间的真正区别。你能否就这些差异在哪里提供一些额外的指导? 您的一般策略似乎与此相符: http://devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx 可能您有任何想法,我可以如何正确编译位模式? – Kris 2009-11-03 20:48:20
我刚刚意识到,您的答案中的代码可能只能用于32位,因为X64位编译不允许内联asm? – Kris 2009-11-03 20:51:13
该代码可以在运行32位操作系统的64位机器上运行(这可能不是您要求的)。真正支持64位更复杂......首先,您必须决定是否支持IA64或仅支持x64/AMD64 /无论您想调用它。然后,您需要一个不同版本的字节数组(称为CPUID_64)和代码,以确保在您要调用CPUID时执行正确版本的字节数组。然而,这个例子本身并没有使用“内联汇编”。 x86正在动态加载和执行,它不是二进制文件的可执行部分。 – StarPacker 2009-11-03 20:57:22