c# - 结构数组指针的问题
c# - Trouble with a pointer of an array of structures
我想通过使用 ntdll 中的 NtQuerySystemInformation 获取有关每个处理器性能的信息。现在我遇到的问题是它只运行了所有 5 次尝试然后 returns.
NtQuerySystemInformation returns 始终为 InfoLengthMismatch 的 NtStatus,这通常意味着我必须增大缓冲区。如果我只使用一个处理器(无数组)和 0x10000 的缓冲区大小来尝试它,它工作得很好,它在第一次尝试时给了我 InfoLengthMismatch 但第二次尝试总是有效。
我尝试将缓冲区增加 100 倍,也尝试增加 1000 倍,但没有任何效果。
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;
}
编辑:这一行是错误的:
uint infoLength = (uint)(Marshal.SizeOf(typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) * processors);
通常如果大小错误,它会将正确的大小输出到 infoLength var,但在这种情况下,我每次都在循环开始时设置它。
我注意到了几件事。首先,您分配了两次内存,并且只释放了一次:
//buffer
IntPtr infoPtr = Marshal.AllocHGlobal((int)infoLength);
//...
//free ptr when fail
Marshal.FreeHGlobal(infoPtr);
//...
//set ptr again, new try
infoPtr = Marshal.AllocHGlobal((int)infoLength);
其次,当NtQuerySystemInformation
returns出错时,它会将可选的输出参数设置为所需的大小。与我看到的不同,您正在创建的缓冲区大小是 8 * 5 * processors
。或者 40 * processors
.
您可以通过在调用前后输出 infoLength
来确认以及所需的适当大小。
// 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);
我想通过使用 ntdll 中的 NtQuerySystemInformation 获取有关每个处理器性能的信息。现在我遇到的问题是它只运行了所有 5 次尝试然后 returns.
NtQuerySystemInformation returns 始终为 InfoLengthMismatch 的 NtStatus,这通常意味着我必须增大缓冲区。如果我只使用一个处理器(无数组)和 0x10000 的缓冲区大小来尝试它,它工作得很好,它在第一次尝试时给了我 InfoLengthMismatch 但第二次尝试总是有效。
我尝试将缓冲区增加 100 倍,也尝试增加 1000 倍,但没有任何效果。
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;
}
编辑:这一行是错误的:
uint infoLength = (uint)(Marshal.SizeOf(typeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) * processors);
通常如果大小错误,它会将正确的大小输出到 infoLength var,但在这种情况下,我每次都在循环开始时设置它。
我注意到了几件事。首先,您分配了两次内存,并且只释放了一次:
//buffer
IntPtr infoPtr = Marshal.AllocHGlobal((int)infoLength);
//...
//free ptr when fail
Marshal.FreeHGlobal(infoPtr);
//...
//set ptr again, new try
infoPtr = Marshal.AllocHGlobal((int)infoLength);
其次,当NtQuerySystemInformation
returns出错时,它会将可选的输出参数设置为所需的大小。与我看到的不同,您正在创建的缓冲区大小是 8 * 5 * processors
。或者 40 * processors
.
您可以通过在调用前后输出 infoLength
来确认以及所需的适当大小。
// 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);