diff --git a/PInvoke/Kernel32/SysInfoApi.cs b/PInvoke/Kernel32/SysInfoApi.cs index be0239fc..278c861f 100644 --- a/PInvoke/Kernel32/SysInfoApi.cs +++ b/PInvoke/Kernel32/SysInfoApi.cs @@ -1226,9 +1226,9 @@ namespace Vanara.PInvoke /// /// /// - /// The memory allocated for the results in . /// - /// An array of SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pointers. If the function fails, the contents of this buffer are undefined. + /// A safe handle that holds an array of SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pointers. If the function fails, the + /// contents of this buffer are undefined. /// /// /// @@ -1237,26 +1237,17 @@ namespace Vanara.PInvoke /// /// If the function fails, the return value has error information. /// - public static unsafe Win32Error GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType, out SafeCoTaskMemHandle mem, out RefEnumerator info) + public static unsafe Win32Error GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType, out SafeSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_List info) { info = default; - mem = default; uint sz = 0; var err = BoolToLastErr(GetLogicalProcessorInformationEx(RelationshipType, IntPtr.Zero, ref sz) || sz > 0); if (err.Failed && err != Win32Error.ERROR_INSUFFICIENT_BUFFER) return err; - mem = new SafeCoTaskMemHandle(sz); - err = BoolToLastErr(GetLogicalProcessorInformationEx(RelationshipType, mem, ref sz)); - if (err.Succeeded) - { - var ret = new List(); - for (IntPtr pCurrent = mem, pEnd = pCurrent.Offset(sz); pCurrent.ToInt64() < pEnd.ToInt64() && pCurrent != IntPtr.Zero;) - { - ret.Add(pCurrent); - pCurrent = pCurrent.Offset(pCurrent.ToStructure().Size); - } - mem.Write(ret, true, (int)sz); - info = new RefEnumerator((SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)mem.DangerousGetHandle().Offset(sz), ret.Count); - } + var iinfo = new SafeSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_List(sz); + if ((err = BoolToLastErr(GetLogicalProcessorInformationEx(RelationshipType, iinfo, ref sz))).Succeeded) + info = iinfo; + else + iinfo.Dispose(); return err; } @@ -2798,12 +2789,10 @@ namespace Vanara.PInvoke public PROCESSOR_CACHE_TYPE Type; } - /// - /// Describes cache attributes. This structure is used with the GetLogicalProcessorInformationEx function. - /// + /// Describes cache attributes. This structure is used with the GetLogicalProcessorInformationEx function. // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_cache_relationship typedef struct _CACHE_RELATIONSHIP { BYTE - // Level; BYTE Associativity; WORD LineSize; DWORD CacheSize; PROCESSOR_CACHE_TYPE Type; BYTE Reserved[20]; GROUP_AFFINITY GroupMask; - // } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP; + // Level; BYTE Associativity; WORD LineSize; DWORD CacheSize; PROCESSOR_CACHE_TYPE Type; BYTE Reserved[20]; GROUP_AFFINITY + // GroupMask; } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP; [PInvokeData("winnt.h", MSDNShortId = "f8fe521b-02d6-4c58-8ef8-653280add111")] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct CACHE_RELATIONSHIP @@ -2831,85 +2820,61 @@ namespace Vanara.PInvoke /// public byte Level; - /// - /// The cache associativity. If this member is CACHE_FULLY_ASSOCIATIVE (0xFF), the cache is fully associative. - /// + /// The cache associativity. If this member is CACHE_FULLY_ASSOCIATIVE (0xFF), the cache is fully associative. public byte Associativity; - /// - /// The cache line size, in bytes. - /// + /// The cache line size, in bytes. public ushort LineSize; - /// - /// The cache size, in bytes. - /// + /// The cache size, in bytes. public uint CacheSize; - /// - /// The cache type. This member is a PROCESSOR_CACHE_TYPE value. - /// + /// The cache type. This member is a PROCESSOR_CACHE_TYPE value. public PROCESSOR_CACHE_TYPE Type; - /// - /// This member is reserved. - /// + /// This member is reserved. private readonly uint Reserved1; - private readonly uint Reserved2; private readonly uint Reserved3; private readonly uint Reserved4; private readonly uint Reserved5; - /// - /// A GROUP_AFFINITY structure that specifies a group number and processor affinity within the group. - /// + /// A GROUP_AFFINITY structure that specifies a group number and processor affinity within the group. public GROUP_AFFINITY GroupMask; } /// - /// Represents information about processor groups. This structure is used with the GetLogicalProcessorInformationEx function. + /// Represents information about processor groups. This structure is used with the GetLogicalProcessorInformationEx function. /// // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_group_relationship typedef struct _GROUP_RELATIONSHIP { WORD // MaximumGroupCount; WORD ActiveGroupCount; BYTE Reserved[20]; PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY]; } GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP; [PInvokeData("winnt.h", MSDNShortId = "3529ddef-04c5-4573-877d-c225da684e38")] + [VanaraMarshaler(typeof(SafeAnysizeStructMarshaler), nameof(ActiveGroupCount))] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct GROUP_RELATIONSHIP { - /// - /// The maximum number of processor groups on the system. - /// + /// The maximum number of processor groups on the system. public ushort MaximumGroupCount; /// - /// /// The number of active groups on the system. This member indicates the number of PROCESSOR_GROUP_INFO structures in the /// GroupInfo array. - /// /// public ushort ActiveGroupCount; - /// - /// This member is reserved. - /// + /// This member is reserved. private readonly uint Reserved1; - private readonly uint Reserved2; private readonly uint Reserved3; private readonly uint Reserved4; private readonly uint Reserved5; - private PROCESSOR_GROUP_INFO _GroupInfo; /// - /// /// An array of PROCESSOR_GROUP_INFO structures. Each structure represents the number and affinity of processors in an active /// group on the system. - /// /// - public PROCESSOR_GROUP_INFO[] GroupInfo - { - get { unsafe { fixed (void* p = &_GroupInfo) { return ((IntPtr)p).ToArray(ActiveGroupCount); } } } - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public PROCESSOR_GROUP_INFO[] GroupInfo; } /// @@ -3038,34 +3003,25 @@ namespace Vanara.PInvoke } /// - /// /// Represents information about a NUMA node in a processor group. This structure is used with the GetLogicalProcessorInformationEx function. - /// /// - // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_numa_node_relationship typedef struct _NUMA_NODE_RELATIONSHIP - // { DWORD NodeNumber; BYTE Reserved[20]; GROUP_AFFINITY GroupMask; } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP; + // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_numa_node_relationship typedef struct + // _NUMA_NODE_RELATIONSHIP { DWORD NodeNumber; BYTE Reserved[20]; GROUP_AFFINITY GroupMask; } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP; [PInvokeData("winnt.h", MSDNShortId = "a4e4c994-c4af-4b4f-8684-6037bcba35a9")] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct NUMA_NODE_RELATIONSHIP { - /// - /// The number of the NUMA node. - /// + /// The number of the NUMA node. public uint NodeNumber; - /// - /// This member is reserved. - /// + /// This member is reserved. private readonly uint Reserved1; - private readonly uint Reserved2; private readonly uint Reserved3; private readonly uint Reserved4; private readonly uint Reserved5; - /// - /// A GROUP_AFFINITY structure that specifies a group number and processor affinity within the group. - /// + /// A GROUP_AFFINITY structure that specifies a group number and processor affinity within the group. public GROUP_AFFINITY GroupMask; } @@ -3243,30 +3199,21 @@ namespace Vanara.PInvoke public static readonly OSVERSIONINFOEX Default = new OSVERSIONINFOEX { dwOSVersionInfoSize = (uint)Marshal.SizeOf(typeof(OSVERSIONINFOEX)) }; } - /// - /// Represents the number and affinity of processors in a processor group. - /// + /// Represents the number and affinity of processors in a processor group. // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_processor_group_info typedef struct _PROCESSOR_GROUP_INFO { // BYTE MaximumProcessorCount; BYTE ActiveProcessorCount; BYTE Reserved[38]; KAFFINITY ActiveProcessorMask; } PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO; [PInvokeData("winnt.h", MSDNShortId = "6ff9cc3c-34e7-4dc4-94cd-6ed278dfaa03")] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct PROCESSOR_GROUP_INFO { - /// - /// The maximum number of processors in the group. - /// + /// The maximum number of processors in the group. public byte MaximumProcessorCount; - /// - /// The number of active processors in the group. - /// + /// The number of active processors in the group. public byte ActiveProcessorCount; - /// - /// This member is reserved. - /// + /// This member is reserved. private readonly ushort Reserved1; - private readonly uint Reserved2; private readonly uint Reserved3; private readonly uint Reserved4; @@ -3277,9 +3224,7 @@ namespace Vanara.PInvoke private readonly uint Reserved9; private readonly uint Reserved10; - /// - /// A bitmap that specifies the affinity for zero or more active processors within the group. - /// + /// A bitmap that specifies the affinity for zero or more active processors within the group. public UIntPtr ActiveProcessorMask; } @@ -3305,6 +3250,7 @@ namespace Vanara.PInvoke // { BYTE Flags; BYTE EfficiencyClass; BYTE Reserved[20]; WORD GroupCount; GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY]; } // PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP; [PInvokeData("winnt.h", MSDNShortId = "1efda80d-cf5b-4312-801a-ea3585b152ac")] + [VanaraMarshaler(typeof(SafeAnysizeStructMarshaler), nameof(GroupCount))] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct PROCESSOR_RELATIONSHIP { @@ -3335,32 +3281,23 @@ namespace Vanara.PInvoke /// public byte EfficiencyClass; - /// - /// This member is reserved. - /// + /// This member is reserved. private readonly ushort Reserved1; - private readonly uint Reserved2; private readonly uint Reserved3; private readonly uint Reserved4; private readonly uint Reserved5; private readonly ushort Reserved6; - /// - /// This member specifies the number of entries in the GroupMask array. For more information, see Remarks. - /// + /// This member specifies the number of entries in the GroupMask array. For more information, see Remarks. public ushort GroupCount; - private GROUP_AFFINITY _GroupMask; - /// /// An array of GROUP_AFFINITY structures. The GroupCount member specifies the number of structures in the array. Each /// structure in the array specifies a group number and processor affinity within the group. /// - public GROUP_AFFINITY[] GroupMask - { - get { unsafe { fixed (void* p = &_GroupMask) { return ((IntPtr)p).ToArray(GroupCount); } } } - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public GROUP_AFFINITY[] GroupMask; } /// @@ -3583,17 +3520,15 @@ namespace Vanara.PInvoke } /// - /// /// Contains information about the relationships of logical processors and related hardware. The GetLogicalProcessorInformationEx /// function uses this structure. - /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_system_logical_processor_information_ex typedef struct // _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { LOGICAL_PROCESSOR_RELATIONSHIP Relationship; DWORD Size; union { PROCESSOR_RELATIONSHIP // Processor; NUMA_NODE_RELATIONSHIP NumaNode; CACHE_RELATIONSHIP Cache; GROUP_RELATIONSHIP Group; } DUMMYUNIONNAME; } // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; [PInvokeData("winnt.h", MSDNShortId = "6ff16cda-c1dc-4d5c-ac60-756653cd6b07")] - [StructLayout(LayoutKind.Sequential), DebuggerDisplay("{DebugString}")] + [StructLayout(LayoutKind.Sequential, Size = 76)] public struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { /// @@ -3630,57 +3565,44 @@ namespace Vanara.PInvoke /// public LOGICAL_PROCESSOR_RELATIONSHIP Relationship; - /// - /// The size of the structure. - /// + /// The size of the structure. public uint Size; /// The relationship union. - public ProcessorRelationUnion RelationUnion; + private readonly ulong dummy; - /// Union tied to the relationship. - [StructLayout(LayoutKind.Explicit)] - public struct ProcessorRelationUnion + /// + /// A NUMA_NODE_RELATIONSHIP structure that describes a NUMA node. This structure contains valid data only if the + /// Relationship member is RelationNumaNode. + /// + public NUMA_NODE_RELATIONSHIP NumaNode => GetField(LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode); + + /// + /// A CACHE_RELATIONSHIP structure that describes cache attributes. This structure contains valid data only if the + /// Relationship member is RelationCache. + /// + public CACHE_RELATIONSHIP Cache => GetField(LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache); + + /// + /// A PROCESSOR_RELATIONSHIP structure that describes processor affinity. This structure contains valid data only if the + /// Relationship member is RelationProcessorCore or RelationProcessorPackage. + /// + public PROCESSOR_RELATIONSHIP Processor => GetField(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore, LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage); + + /// + /// A GROUP_RELATIONSHIP structure that contains information about the processor groups. This structure contains valid data + /// only if the Relationship member is RelationGroup. + /// + public GROUP_RELATIONSHIP Group => GetField(LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup); + + private T GetField(params LOGICAL_PROCESSOR_RELATIONSHIP[] r) { - /// - /// A PROCESSOR_RELATIONSHIP structure that describes processor affinity. This structure contains valid data only if the - /// Relationship member is RelationProcessorCore or RelationProcessorPackage. - /// - [FieldOffset(0)] public PROCESSOR_RELATIONSHIP Processor; - - /// - /// A NUMA_NODE_RELATIONSHIP structure that describes a NUMA node. This structure contains valid data only if the - /// Relationship member is RelationNumaNode. - /// - [FieldOffset(0)] public NUMA_NODE_RELATIONSHIP NumaNode; - - /// - /// A CACHE_RELATIONSHIP structure that describes cache attributes. This structure contains valid data only if the - /// Relationship member is RelationCache. - /// - [FieldOffset(0)] public CACHE_RELATIONSHIP Cache; - - /// - /// A GROUP_RELATIONSHIP structure that contains information about the processor groups. This structure contains valid data - /// only if the Relationship member is RelationGroup. - /// - [FieldOffset(0)] public GROUP_RELATIONSHIP Group; - } - - internal string DebugString - { - get + if (!r.Contains(Relationship)) + return default; + unsafe { - uint c = Relationship switch - { - LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore => RelationUnion.Processor.GroupCount, - LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode => RelationUnion.NumaNode.NodeNumber, - LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache => RelationUnion.Cache.CacheSize, - LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage => RelationUnion.Processor.GroupCount, - LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup => RelationUnion.Group.ActiveGroupCount, - _ => 0 - }; - return $"{Relationship}, Size={Size}, Count={c}"; + fixed (void* p = &dummy) + return ((IntPtr)p).ToStructure(); } } } @@ -3693,5 +3615,39 @@ namespace Vanara.PInvoke /// The cycle time for a processor. public ulong CycleTime; } + + /// Holds a list of structures retrived from . + public class SafeSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_List : SafeMemoryHandle + { + internal SafeSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_List(SizeT size) : base(size) + { + } + + /// Gets the number of elements available. + /// The number of elements available. + public int Count => Items.Count; + + /// Gets a reference to a at the specified index. + /// The index. + /// A reference to . + public unsafe SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* this[int index] => (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(void*)Items[index]; + + /// Move to next element. + private List Items + { + get + { + var ret = new List(); + for (IntPtr pCurrent = handle, pEnd = pCurrent.Offset(sz); pCurrent != IntPtr.Zero && pCurrent.ToInt64() < pEnd.ToInt64();) + { + var cSz = pCurrent.ToStructure().Size; + if (cSz == 0) break; + ret.Add(pCurrent); + pCurrent = pCurrent.Offset(cSz); + } + return ret; + } + } + } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Kernel32/SysInfoTests.cs b/UnitTests/PInvoke/Kernel32/SysInfoTests.cs index 155e6724..cba11a8d 100644 --- a/UnitTests/PInvoke/Kernel32/SysInfoTests.cs +++ b/UnitTests/PInvoke/Kernel32/SysInfoTests.cs @@ -57,26 +57,27 @@ namespace Vanara.PInvoke.Tests { unsafe { - Assert.That(GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup, out var mem, out var info), ResultIs.Successful); - using (mem) + Assert.That(GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup, out var info), ResultIs.Successful); + using (info) { Assert.That(info.Count, Is.GreaterThan(0)); for (int i = 0; i < info.Count; i++) { - switch (info[i].Relationship) + var pr = info[i]; + switch (pr->Relationship) { case LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode: - info[i].RelationUnion.NumaNode.WriteValues(); + pr->NumaNode.WriteValues(); break; case LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache: - info[i].RelationUnion.Cache.WriteValues(); + pr->Cache.WriteValues(); break; case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore: case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage: - info[i].RelationUnion.Processor.WriteValues(); + pr->Processor.WriteValues(); break; case LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup: - info[i].RelationUnion.Group.WriteValues(); + pr->Group.WriteValues(); break; default: break;