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.
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);