简体   繁体   中英

c# - Trouble with a pointer of an array of structures

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM