0

我正在制作一个类似于SafeBuffer针对 .NET 2.0 的类。其中一个功能是void ReadArray<T>(long position, T[] array, int offset, int count)(or WriteArray),它将一系列 (blittable) 结构读入/写入数组。

我的第一个猜测是简单地使用Marshal.PtrToStructure/StructureToPtr和推进 by Marshal.SizeOf。然而,查看 IL 以获取SafeBuffer.ReadArrayMarshal.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 有何不同?)。

所以问题是:

  1. 讨论:为什么这个对齐大小与正常大小不同?在 C/C++ 中 sizeof() 是完全对齐的值。(sizeof(arr)/sizeof(arr[0])总是有效)
  2. 是否有一种托管方式(有或没有不安全代码)来获得 blittable 通用结构的对齐大小?
  3. 我应该只使用SizeOf()而不关心这个额外的对齐吗?在那种情况下,我可能只是在进行块转移......
4

1 回答 1

1

您必须选择选项 3,您也无权访问 StructureToPtrNative()。您可以使用 Marshal.StructureToPtr() 来复制值类型值,并且还要求您使用 Marshal.SizeOf() 来测量它。

这就是降压停止的地方。至于其他问题,CLR 用于内部存储的确切布局规则非常不寻常,并且很难进行逆向工程。存储在数组中的示例结构的大小确实为 8,添加了两个额外的填充字节。只需要 6 个即可使所有字段正确对齐。不知道为什么会这样,太埋没在 CLR 内部。如果您想尝试使用调试器在实践中看到这一点,那么这个答案将向您展示如何做到这一点。

    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

并在调试器内存窗口的地址框中输入“&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]
于 2013-04-06T00:19:22.847 回答