2

I want to get the infos about the perfomance of each processor by using NtQuerySystemInformation from ntdll. Now i have the problem that it just runs trough all 5 tries and then returns.

NtQuerySystemInformation returns a NtStatus which is always InfoLengthMismatch which usually means i have to make my buffer larger. If i try it with only one processor (no array) and a buffersize of 0x10000 it works kinda fine, it gives me InfoLengthMismatch in the first try but 2nd try always works.

I tried to increase the buffer by 100 times, also 1000 times but nothing worked.

private _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] GetPerformanceInfo()
    {
        try
        {
            //Getting count of processors
            SYSTEM_INFO sysInfo = new SYSTEM_INFO();
            GetSystemInfo(out sysInfo);
            int processors = (int)sysInfo.numberOfProcessors;

            int tries = 0;

            while (true)
            {

                //buffer size
                uint infoLength = (uint)(Marshal.SizeOf(typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) * processors);

                //buffer
                IntPtr infoPtr = Marshal.AllocHGlobal((int)infoLength);

                //Problem: result is always InfoLengthMismatch
                NtStatus result = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessorPerformanceInformation, infoPtr, infoLength, out infoLength);

                //if success, get the array and return it
                if (result == NtStatus.Success)
                {
                    int szStruct = Marshal.SizeOf(typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
                    _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] localStructs = new _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[processors];

                    for (uint i = 0; i < processors; i++)
                        localStructs[i] = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)Marshal.PtrToStructure(new IntPtr(infoPtr.ToInt64() + (szStruct * processors)), typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));

                    return localStructs;
                }

                //free ptr when fail
                Marshal.FreeHGlobal(infoPtr);

                if (++tries > 5)
                    return null;

                //set ptr again, new try
                infoPtr = Marshal.AllocHGlobal((int)infoLength);

            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Source + ": " + e.Message.ToString());
            return null;
        }
    }

}

//struct of a large int
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct LARGE_INTEGER
{
    [FieldOffset(0)] public Int64 QuadPart;
    [FieldOffset(0)] public UInt32 LowPart;
    [FieldOffset(4)] public Int32 HighPart;
}

//struct
public struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
{
    public LARGE_INTEGER IdleTime;
    public LARGE_INTEGER KernelTime;
    public LARGE_INTEGER UserTime;
    public LARGE_INTEGER Reserved1;
    public ulong Reserved2;
}

EDIT: Wrong thing was this line:

uint infoLength = (uint)(Marshal.SizeOf(typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) * processors);

Usually if the size is wrong, it will out the right size to the infoLength var, but in this case I'm setting it every time at the beginning of the loop.

1
  • Are you aware that you are allocating memory twice and freeing it once? Commented Sep 28, 2017 at 19:14

1 Answer 1

2

There are a couple things I've noticed. First off, you're allocating memory twice, and only freeing it once:

//buffer
IntPtr infoPtr = Marshal.AllocHGlobal((int)infoLength);

//...

//free ptr when fail
Marshal.FreeHGlobal(infoPtr);

//...

//set ptr again, new try
infoPtr = Marshal.AllocHGlobal((int)infoLength);

Secondly, When NtQuerySystemInformation returns an error, it will set the optional out parameter to the size required. Going off of what I see, the buffer size you're creating is 8 * 5 * processors. Or 40 * processors.

You can confirm that as well as the appropriate required size by outputting infoLength before and after the call.

// Should be 40 * processors
Console.WriteLine((int)infoLength);

//Problem: result is always InfoLengthMismatch
NtStatus result = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessorPerformanceInformation, 
                      infoPtr, infoLength, out infoLength);

// Will be bigger than 40 * processors
Console.WriteLine((int)infoLength);
Sign up to request clarification or add additional context in comments.

2 Comments

I have 4 processors but infoLength will be set to 40, not 40 * 4.
@Tom I duplicated your code and had no issue. The only things I added were the NtStatus and SYSTEM_INFORMATION_CLASS enums, the dllimport for NtQuerySystemInformation and I used Environment.ProcessorCount

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.