如何从GetTokenInformation()安全地为32位和64位拼接可变长度的结构数组? C#

我正在遵循这里提供的pinvoke代码,但稍微有点害怕可变长度数组的编组为size = 1,然后通过计算偏移量而不是索引到数组中来踩过它。 有没有更好的方法? 如果没有,我该怎么做才能使它对32位和64位安全?

[StructLayout(LayoutKind.Sequential)] public struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } [StructLayout(LayoutKind.Sequential)] public struct TOKEN_GROUPS { public int GroupCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public SID_AND_ATTRIBUTES[] Groups; }; public void SomeMethod() { IntPtr tokenInformation; // ... string retVal = string.Empty; TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_GROUPS)); int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES()); for (int i = 0; i < groups.GroupCount; i++) { // *** Scary line here: SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure( new IntPtr(tokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), typeof(SID_AND_ATTRIBUTES)); // ... } 

我在这里看到另一种声明数组长度比它可能大得多的方法,但这似乎有其自身的问题。

作为一个附带问题:当我在调试器中单步调试上面的代码时,我无法评估tokenInformation.ToInt64()ToInt32() 。 我得到一个ArgumentOutOfRangeException。 但是代码行执行得很好!? 这里发生了什么?

我觉得它看起来没问题 – 无论如何,无论如何在无人管理的土地上进行任何探索都是好的。

但是,我想知道为什么start是tokenInformation.ToInt64() + IntPtr.Size而不是tokenInformation.ToInt64() + 4 (因为GroupCount字段类型是int而不是IntPtr)。 这是用于包装/对齐结构还是只是一些可疑的东西? 我不知道在这里。

使用tokenInformation.ToInt64()很重要,因为如果IntPtr值大于int可以存储的值,64位机器将爆炸 (OverflowException)。 但是,CLR将在两种体系结构上处理很长时间,并且它不会更改从IntPtr中提取的实际值(因此将其放回到new IntPtr(...) )。

想象一下这个(未经测试的)函数作为一个方便的包装器:

 // unpacks an array of structures from unmanaged memory // arr.Length is the number of items to unpack. don't overrun. void PtrToStructureArray(T[] arr, IntPtr start, int stride) { long ptr = start.ToInt64(); for (int i = 0; i < arr.Length; i++, ptr += stride) { arr[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T)); } } var attributes = new SID_AND_ATTRIBUTES[groups.GroupCount]; PtrToStructureArray(attributes, new IntPtr(tokenInformation.ToInt64() + IntPtr.Size), sidAndAttrSize); 

快乐的编码。

通常更好的方法是使用Marshal.OffsetOf(typeof(TOKEN_GROUPS), "Groups")来获得到数组开头的正确偏移量,而不是猜测偏移量。