我正在创建一个类似于SafeBuffer
的类,目标是.NET 2.0。其中一个函数是void ReadArray<T>(long position, T[] array, int offset, int count)
(或WriteArray
),它从一个数组中读取/写入一系列(blittable)结构。
我的第一个猜测是简单地使用Marshal.PtrToStructure
/ StructureToPtr
以及Marshal.SizeOf
推进。然而,查看IL SafeBuffer.ReadArray
显示它使用Marshal.AlignedSizeOf<T>()
进行推进(内部方法)。该功能定义为:
uint s = Marshal.SizeOf<T>();
if (s == 1u || s == 2u || IntPtr.Size == 8 && s == 4u) { return s; }
return Marshal.AlignedSizeOfType(typeof(T)); // an internalcall
该方法仅在.NET 4.0中定义,因此对我来说不可用(也不在Rotor中)。
我的想法是在数组中的相邻元素上使用Marshal.UnsafeAddrOfPinnedArrayElement
,但这不起作用。这是我的测试代码:
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
struct A
{
byte a;
short x;
byte b;
}
private static MethodInfo MarshalAlignedSizeOf;
static int MAlignedSizeOf(Type t)
{
if (MarshalAlignedSizeOf == null) { MarshalAlignedSizeOf = typeof(Marshal).GetMethod("AlignedSizeOf", BindingFlags.NonPublic | BindingFlags.Static); }
return (int)(uint)MarshalAlignedSizeOf.MakeGenericMethod(t).Invoke(null, null);
}
static int AlignedSizeOf(Type t)
{
Array a = Array.CreateInstance(t, 0);
GCHandle pin = GCHandle.Alloc(a, GCHandleType.Pinned);
try
{
return (int)(Marshal.UnsafeAddrOfPinnedArrayElement(a, 1).ToInt64() - Marshal.UnsafeAddrOfPinnedArrayElement(a, 0).ToInt64());
}
finally { pin.Free(); }
}
unsafe static void Main(string[] args)
{
Console.WriteLine("sizeof: " + sizeof(A));
Console.WriteLine("SizeOf: " + Marshal.SizeOf(typeof(A)));
Console.WriteLine("aligned size: " + AlignedSizeOf(typeof(A)));
Console.WriteLine("mars algn sz: " + MAlignedSizeOf(typeof(A)));
}
}
}
在x86或x64上输出6, 6, 6,
8
(请注意原生AlignedSizeOf有何不同?)。
所以问题是:
sizeof(arr)/sizeof(arr[0])
始终有效)SizeOf()
而不关心这个额外的对齐方式吗?在那种情况下,我可能正在进行块转移...... 答案 0 :(得分:1)
您必须选择选项3,您也无权访问StructureToPtrNative()。您可以使用Marshal.StructureToPtr()来复制值类型值,并且还要求您使用Marshal.SizeOf()来测量它。
这就是降压停止的地方。至于其他问题,CLR用于内部存储的确切布局规则非常不寻常,并且很难进行逆向工程。存储在数组中的示例结构的大小确实为8,添加了两个额外的填充字节。只需要6个就可以正确对齐所有字段。不知道为什么会这样做,太过于埋没在CLR内部。如果您想在实践中尝试使用调试器,那么this answer会向您展示如何执行此操作。
static void Main(string[] args) {
var arr = new A[] { new A() { a = 1, x = 2, b = 3}, new A { a = 0x11, x = 0x12, b = 0x13 }};
} // <== set breakpoint here
并将“&amp; arr”放入调试器内存窗口的地址框中以获取
0x000000001D67DE70 98 73 14 03 00 00 00 00 01 00 02 00 03 00 00 00 11 00 12 00 13 00 00 00
^ arr[0] ^ arr[1]