如何针对 32 位和 64 位安全地从 GetTokenInformation() 调用可变长度结构数组? 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。但是这行代码执行得很好!?这是怎么回事?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通常最好使用 Marshal.OffsetOf(typeof(TOKEN_GROUPS), "Groups") 来获取到数组开头的正确偏移量,而不是猜测偏移量。
Instead of guessing what the offset, is its generally better to use
Marshal.OffsetOf(typeof(TOKEN_GROUPS), "Groups")
to get the correct offset to the start of the array.我认为这看起来还不错——无论如何,就像在无人管理的土地上闲逛一样。
但是,我想知道为什么开始是
tokenInformation.ToInt64() + IntPtr.Size
而不是tokenInformation.ToInt64() + 4
(因为 GroupCount 字段类型是 int 并且不是 IntPtr)。这是为了结构的包装/对齐还是只是一些可疑的东西?我不知道这里。使用 tokenInformation.ToInt64() 非常重要,因为在 64 位计算机上,如果 IntPtr 值大于 int 可以存储的值,将会爆炸(OverflowException)。但是,CLR 在两种体系结构上都可以很好地处理 long,并且不会更改从 IntPtr 中提取的实际值(从而放回到 new IntPtr(...) 中)。
想象一下这个(未经测试的)函数作为一个方便的包装器:
快乐编码。
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 nottokenInformation.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 thenew IntPtr(...)
).Imagine this (untested) function as a convenience wrapper:
Happy coding.