14

I want to call via c#/PInvoke the GetLogicalProcessorInformation function, but I'm stuck with SYSTEM_LOGICAL_PROCESSOR_INFORMATION struct and CACHE_DESCRIPTOR struct.

How should I define these structs for correct usage?

Main problems:
1. SYSTEM_LOGICAL_PROCESSOR_INFORMATION has union in its definition
2. SYSTEM_LOGICAL_PROCESSOR_INFORMATION has ULONGLONG in its definition
3. CACHE_DESCRIPTOR has WORD and DWORD in its definition.

Can you help me with these structures?

Dennis
  • 20,275
  • 4
  • 64
  • 80
VMAtm
  • 27,943
  • 17
  • 79
  • 125

2 Answers2

18

Updated: fixed the structure marshalling which has to be done manually.

This is quite a messy P/invoke. Even when you have the structs and the union defined, it's non-trivial to call the function because you have to marshal the structures manually.

[StructLayout(LayoutKind.Sequential)]
public struct PROCESSORCORE
{
    public byte Flags;
};

[StructLayout(LayoutKind.Sequential)]
public struct NUMANODE
{
    public uint NodeNumber;
}

public enum PROCESSOR_CACHE_TYPE
{
    CacheUnified,
    CacheInstruction,
    CacheData,
    CacheTrace
}

[StructLayout(LayoutKind.Sequential)]
public struct CACHE_DESCRIPTOR
{
    public byte Level;
    public byte Associativity;
    public ushort LineSize;
    public uint Size;
    public PROCESSOR_CACHE_TYPE Type;
}

[StructLayout(LayoutKind.Explicit)]
public struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
    [FieldOffset(0)]
    public PROCESSORCORE ProcessorCore;
    [FieldOffset(0)]
    public NUMANODE NumaNode;
    [FieldOffset(0)]
    public CACHE_DESCRIPTOR Cache;
    [FieldOffset(0)]
    private UInt64 Reserved1;
    [FieldOffset(8)]
    private UInt64 Reserved2;
}

public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
    RelationProcessorCore,
    RelationNumaNode,
    RelationCache,
    RelationProcessorPackage,
    RelationGroup,
    RelationAll = 0xffff
}

public struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
    public UIntPtr ProcessorMask;
    public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
    public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}

[DllImport(@"kernel32.dll", SetLastError=true)]
public static extern bool GetLogicalProcessorInformation(
    IntPtr Buffer,
    ref uint ReturnLength
);

private const int ERROR_INSUFFICIENT_BUFFER = 122;

public static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] MyGetLogicalProcessorInformation()
{
    uint ReturnLength = 0;
    GetLogicalProcessorInformation(IntPtr.Zero, ref ReturnLength);
    if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
    {
        IntPtr Ptr = Marshal.AllocHGlobal((int)ReturnLength);
        try
        {
            if (GetLogicalProcessorInformation(Ptr, ref ReturnLength))
            {
                int size = Marshal.SizeOf(typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
                int len = (int)ReturnLength / size;
                SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] Buffer = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[len];
                IntPtr Item = Ptr;
                for (int i = 0; i < len; i++)
                {
                    Buffer[i] = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)Marshal.PtrToStructure(Item, typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
                    Item += size;
                }
                return Buffer;
            }
        }
        finally
        {
            Marshal.FreeHGlobal(Ptr);
        }
    }
    return null;
}

static void Main(string[] args)
{
    SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] Buffer = MyGetLogicalProcessorInformation();
    for (int i=0; i<Buffer.Length; i++)
    {
        Console.WriteLine(Buffer[i].ProcessorMask);
    }
}
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I've got the following compile error (on .NET 1.1) `Operator '+=' cannot be applied to operands of type 'System.IntPtr' and 'int'`. Can I use the `int*` instead of IntPtr? – VMAtm Aug 07 '11 at 15:05
  • I tried to use the following: `Item = new IntPtr(Item.ToInt32() + size);` and it worked. I'll tell, if I can use the `int*`. – VMAtm Aug 07 '11 at 15:12
  • 1
    ooh, .net 1.1. Not familiar with that relic! Your workaround is fine. Of course it would fail on a 64 bit target but that's not an issue on .net 1.1. – David Heffernan Aug 07 '11 at 15:16
5

A DWORD is a uint and WORD is a ushort.

[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
   public byte Level;
   public byte Associativity;
   public ushort LineSize;
   public uint Size;
   public PROCESSOR_CACHE_TYPE Type;
}

enum PROCESSOR_CACHE_TYPE
{
    Unified = 0,
    Instruction = 1,
    Data = 2,
    Trace = 3,
}

A union is a structure with a Explicit layout and FieldOffset.

[StructLayout(LayoutKind.Sequential)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
    public UIntPtr ProcessorMask;
    public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
    public ProcessorRelationUnion RelationUnion;
}

[StructLayout(LayoutKind.Explicit)]
struct ProcessorRelationUnion
{
    [FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
    [FieldOffset(0)] public uint NumaNodeNumber;
    [FieldOffset(0)] public byte ProcessorCoreFlags;
    [FieldOffset(0)] private UInt64 Reserved1;
    [FieldOffset(8)] private UInt64 Reserved2;
}

[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
    public byte Level;
    public byte Associativity;
    public ushort LineSize;
    public uint Size;
    public PROCESSOR_CACHE_TYPE Type;
}

enum LOGICAL_PROCESSOR_RELATIONSHIP : uint
{
    ProcessorCore = 0,
    NumaNode = 1,
    RelationCache = 2,
}

A ULONGLONG is a UInt64. It is being to align the structure to 8 byte boundary (24 bytes). As David pointed out in the comments, it is required and for some reason it was missing from the Microsoft Interop library.

Update: Added missing structures and link to the Windows Interop Library from Microsoft Research.

Source: WindowsInteropLib/Kernel32.cs

Dennis
  • 20,275
  • 4
  • 64
  • 80
  • 1
    Although this answer is attracting all the upvotes, it appears to me to be seriously deficient in two important ways. Ignoring the `ULONGLONG[2] Reserved` member means that the struct is incorrectly size, being 20 bytes rather than the correct 24 bytes. And more importantly there is no mention of actually calling the function which is rather tricky to get right. – David Heffernan Aug 07 '11 at 12:52
  • 2
    Thanks for pointing that out David - I was just reading your answer and realised the same thing. – Dennis Aug 07 '11 at 12:56