Question

I would like to add cpuid functionality to my C# app. I found this interesting blog post online. I will probably need MASM to compile this but:

  1. How should I start?
  2. I suspect that I will have to compile a dll for both X86 and X64 but again I don't have a clue about how to go about such a thing (and I am a bit pressed for time).

So any help would be more than welcome!

Was it helpful?

Solution

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;
}

OTHER TIPS

I think you can build a native CLR assembly employing inline assembler syntax (__asm) in C++ code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top