CPUID is a giant pain and I would advise against going down that path if you can avoid it. CPUID results are different between Intel and AMD processors (at least for the interesting stuff like hyperthreading and cache topology) and are not particularly stable across different processor versions. (Newer Intel i7 processors introduce a new CPUID value (eax=0xb) which supercedes the information supported by CPUID on earlier processors).
If you can get away with it, your best bet would be to use WMI (see Win32_Processor) or GetLogicalProcessorInformation.
Either of these will be a vastly simpler and more manageable solution if they are supported on your platform (to get logical processor information either way requires WinXP sp3 or newer on the client side or Windows Server 2008 or newer on the server side).
If you really want to try your luck with CPUID, what I would recommend doing would be to create a simple stub which is capable of executing CPUID and returning the results to managed code (you will need different versions for 32-bit and 64-bit) and execute those within the context of your managed application. I do this by compiling a native application and then extracting the raw instruction bytes of my CPUID method into a byte array which can be executed from managed code.
This should get you started for 32-bit support only:
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;
}