如何获得CLR blittable结构的对齐大小?

时间:2013-04-05 22:03:01

标签: .net interop sizeof

我正在创建一个类似于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有何不同?)。

所以问题是:

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

1 个答案:

答案 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]