在 .Net 4.7 中将非托管字节数组分区为 "typed fields"
Partition an unmanaged byte array into "typed fields" in .Net 4.7
在 .Net 中,假设我有一个表示存储页面的非托管字节数组。每个数组都有一个 IntPtr
。现在我想将数组划分为 "fields".
下面的代码说明了我的意思,但是它抛出了一个异常,我不知道为什么。
[StructLayout(LayoutKind.Explicit, Size = 1024 * 4)]
internal unsafe struct Page
{
// the actual byte array
[FieldOffset(0)]
public fixed byte buffer[1024 * 4];
[FieldOffset(1024 * 0)]
public fixed byte header[1024];
[FieldOffset(1024 * 1)]
public fixed byte block1[1024];
[FieldOffset(1024 * 2)]
public fixed byte block2[1024];
[FieldOffset(1024 * 3)]
public fixed byte block3[1024];
}
private unsafe void test_Click(object sender, EventArgs e)
{
IntPtr p = GCHandle.ToIntPtr(GCHandle.Alloc(new Page(), GCHandleType.Pinned));
Page* page = (Page*)p;
page->block1[0] = 1; // << works
page->block2[0] = 2; // << works
page->block3[0] = 3; // << System.AccessViolationException
if (page->buffer[1024] == 1
&& page->buffer[1024*2] == 2
// && page->buffer[1024*3] == 3
)
{
MessageBox.Show("OK");
}
GCHandle.FromIntPtr(p).Free();
}
正确的做法是什么?我想要实现的是将 block1 等偏移到正确的指针。所以看看我的例子,如果 p
指向 4,096 字节数组,那么 page->header
应该 = p
,page->block1
应该 = p + 1024
,等等
GCHandle.ToIntPtr
不是指向 Page
对象的指针。它只是表示为整数的 GC 句柄(例如,允许您轻松地将 "pseudo-reference" 传递给未处理代码的句柄,然后再返回)。您正在随机写入内存:)
您需要使用 GCHandle.AddrOfPinnedObject
来实际获取 Page
对象的地址。
此外,对于大多数情况,GCHandle
是一种矫枉过正。如果您可以包含对范围的固定,最好改用 fixed
块。
最后,只是为了避免在评论中出现:fixed byte
只是一个字节指针,而不是一个托管数组。重叠字段不会给您带来太多好处;它等同于只获取指向数组中元素的指针(例如 block2 == &buffer[1024*2]
)。在大字节缓冲区的情况下,可能也没有太多额外的危险,不过 - 我需要通过规范来检查它是否合法 :)
在 .Net 中,假设我有一个表示存储页面的非托管字节数组。每个数组都有一个 IntPtr
。现在我想将数组划分为 "fields".
下面的代码说明了我的意思,但是它抛出了一个异常,我不知道为什么。
[StructLayout(LayoutKind.Explicit, Size = 1024 * 4)]
internal unsafe struct Page
{
// the actual byte array
[FieldOffset(0)]
public fixed byte buffer[1024 * 4];
[FieldOffset(1024 * 0)]
public fixed byte header[1024];
[FieldOffset(1024 * 1)]
public fixed byte block1[1024];
[FieldOffset(1024 * 2)]
public fixed byte block2[1024];
[FieldOffset(1024 * 3)]
public fixed byte block3[1024];
}
private unsafe void test_Click(object sender, EventArgs e)
{
IntPtr p = GCHandle.ToIntPtr(GCHandle.Alloc(new Page(), GCHandleType.Pinned));
Page* page = (Page*)p;
page->block1[0] = 1; // << works
page->block2[0] = 2; // << works
page->block3[0] = 3; // << System.AccessViolationException
if (page->buffer[1024] == 1
&& page->buffer[1024*2] == 2
// && page->buffer[1024*3] == 3
)
{
MessageBox.Show("OK");
}
GCHandle.FromIntPtr(p).Free();
}
正确的做法是什么?我想要实现的是将 block1 等偏移到正确的指针。所以看看我的例子,如果 p
指向 4,096 字节数组,那么 page->header
应该 = p
,page->block1
应该 = p + 1024
,等等
GCHandle.ToIntPtr
不是指向 Page
对象的指针。它只是表示为整数的 GC 句柄(例如,允许您轻松地将 "pseudo-reference" 传递给未处理代码的句柄,然后再返回)。您正在随机写入内存:)
您需要使用 GCHandle.AddrOfPinnedObject
来实际获取 Page
对象的地址。
此外,对于大多数情况,GCHandle
是一种矫枉过正。如果您可以包含对范围的固定,最好改用 fixed
块。
最后,只是为了避免在评论中出现:fixed byte
只是一个字节指针,而不是一个托管数组。重叠字段不会给您带来太多好处;它等同于只获取指向数组中元素的指针(例如 block2 == &buffer[1024*2]
)。在大字节缓冲区的情况下,可能也没有太多额外的危险,不过 - 我需要通过规范来检查它是否合法 :)