Calling the __cpuid MSVC function via C# a DLL Pinvoke (C++, C#)

Here is the latest piece of code I wish to share with everyone since I run into this myself, I am sure others will be interested also.

The C++ Part

Firstly you will need to create a new DLL project in Visual Studio. You will also need to add this line into the cpp file for your project:

1
#include <intrin.h>

This will allow you to access the intrinsic __cpuid call.

Next, I use a custom structure to hold the data returned by the function call. I do this mainly for readability since it helps when referencing. The code for the structure is as follows and should be added to the header file.

1
2
3
4
5
6
7
struct CPUInfo
{
    int EAX;
    int EBX;
    int ECX;
    int EDX;
};

Simple enough eh? Good. Now we need to construct a function call that will wrap the results from a __cpuid call into the structure above. Again, this is quite simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extern "C" __declspec(dllexport) CPUInfo cpuID(int infoType)
{
    CPUInfo i;
    int results[4] = { 0, 0, 0, 0 };
    __try
    {
        __cpuid(results, infoType);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {}

    i.EAX = results[0];
    i.EBX = results[1];
    i.ECX = results[2];
    i.EDX = results[3];

    return i;
}

Finally you will need the following function definition for the header file:

1
extern "C" __declspec(dllexport) CPUInfo cpuID(int infoType);

As you can see. The above function also checks for any exceptions – it needs this as some older CPUs do not support the cpuid call. In these cases it will simply return a CPUInfo structure filled with 0’s so that the code doesn’t break anything.

Now. You will need to compile the DLL and then you can use it in your C# project using the following method.

The C# Part

First. Create an analogue of the C++ CPUInfo class in C#. This is really simple to do and the following is what is needed, for those who do not wish to try themselves:

1
2
3
4
5
6
7
8
[StructLayout(LayoutKind.Sequential)]
struct CPUInfo
{
    public int EAX;
    public int EBX;
    public int ECX;
    public int EDX;
}

Finally you will need the pinvoke definition. Depending on the name of the DLL (I called mine HardwareInfo.dll) the following code will be slightly different:

1
2
3
4
5
6
7
/// <summary>
/// This pinvoke method will return the result from a __cpuid call, if supported.
/// </summary>
/// <param name="infoType">A code that indicates what information this instruction retrieves.</param>
/// <returns>A CPUInfo structure giving the result from the query, or an empty structure if not supported.</returns>
[DllImport("HardwareInfo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern CPUInfo _cpuid(uint infoType);

Hopefully that wasn’t too bad. Here is an example of how you would use the above code:

1
2
CPUInfo example = cpuID(0x00000000);
uint maxSupportedStandardLevels = (uint)(example.EAX & 0xffff);

The above code will tell you the maximum supported level of call that is supported on the current processor.

Notes:

Just remember that you will need to add a reference to the System.Runtime.InteropServices namespace if you are going to use this code.

As always, comments, improvements and suggestions are welcome.