How to pinvoke a variable-length array of structs from GetTokenInformation() safely for 32-bit and 64-bit? C#
.net, c#, marshalling, pinvoke
Solution
I think it looks okay -- as okay as any poking about in unmanaged land is, anyway.
However, I wonder why the start is `tokenInformation.ToInt64() + IntPtr.Size` and not `tokenInformation.ToInt64() + 4` (as the GroupCount field type is an int and not IntPtr). Is this for packing/alignment of the structure or just something fishy? I do not know here.
Using `tokenInformation.ToInt64()` is important because on a 64-bit machine will explode (OverflowException) if the IntPtr value is larger than what an int can store. However, the CLR will handle a long just fine on both architectures and it doesn't change the actual value extracted from the IntPtr (and thus put back into the `new IntPtr(...)`).
Imagine this (untested) function as a convenience wrapper:
// unpacks an array of structures from unmanaged memory
// arr.Length is the number of items to unpack. don't overrun.
void PtrToStructureArray<T>(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);
Happy coding.
Problem
I'm following the pinvoke code provided here but am slightly scared by the marshalling of the variable-length array as size=1 and then stepping through it by calculating an offset instead of indexing into an array. Isn't there a better way? And if not, how should I do this to make it safe for 32-bit and 64-bit? ``` [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)); // ... } ``` I see here another approach of declaring the length of the array as much bigger than it's likely to be, but that seemed to have its own problems. As a side question: When I step through the above code in the debugger I'm not able to evaluate `tokenInformation.ToInt64()` or `ToInt32()`. I get an ArgumentOutOfRangeException. But the line of code executes just fine!? What's going on here?