在非托管内存中托管类的标头的布局和大小是什么?

DeF*_*zer 6 c# reflection

最近,在这个问题中,我已经问过如何在C#中获取类的原始内存地址(这是一个粗略的不可靠的黑客和一个不好的做法,除非你真的需要它,否则不要使用它).我成功了,但后来出现了一个问题:根据这篇文章,类原始内存表示中的前2个字应该是指向SyncBlock和RTTI结构的指针,因此第一个字段的地址必须偏移2个字 [ 8个字节 in 从一开始就是32位系统,64 位系统中的16个字节.但是,当我从对象位置的内存中转储第一个字节时,第一个字段与对象地址的原始偏移量只有1个32位字(4个字节),这对两种类型的系统都没有任何意义.从我链接的问题:

class Program
{
    // Here is the function.
    // I suggest looking at the original question's solution, as it is 
    // more reliable.
    static IntPtr getPointerToObject(Object unmanagedObject)
    {
        GCHandle gcHandle = GCHandle.Alloc(unmanagedObject, GCHandleType.WeakTrackResurrection);
        IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle));
        gcHandle.Free();
        return thePointer;
    }
    class TestClass
    {
        uint a = 0xDEADBEEF;
    }
    static void Main(string[] args)
    {
        byte[] cls = new byte[16];

        var test = new TestClass();

        var thePointer = getPointerToObject(test);
        Marshal.Copy(thePointer, cls, 0, 16); //Dump first 16 bytes...
        Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(thePointer.ToInt32())));
        Console.WriteLine(BitConverter.ToString(cls));

        Console.ReadLine();

        gcHandle.Free();
    }
}
/* Example output (yours should be different):
40-23-CA-02
4C-38-04-01-EF-BE-AD-DE-00-00-00-80-B4-21-50-73

That field's value is "EF-BE-AD-DE", 0xDEADBEEF as it is stored in memory. Yay, we found it!
*/
Run Code Online (Sandbox Code Playgroud)

为什么会这样?也许我的地址错了,但是如何以及为什么?如果我没有,反正什么可能是错的?也许,如果那篇文章错了,我只是误解了托管类标题的样子?或者它可能没有锁定指针 - 但为什么以及如何可能?

(显然,这些只是一些可能的选择,而且,虽然我仍然会仔细检查我能预测的每一个,但是疯狂的猜测无法在时间和准确性上与正确答案进行比较.)

DeF*_*zer 2

@HansPassant 出色地指出,所讨论的对象的指针指向第二个结构,即方法表。现在,出于性能原因,这完全有意义,因为方法表(RTTI 结构)的使用频率远高于 SyncRoot 结构,因此,后者仍然位于其之前的负索引 -1 处。他明确表示他不想发布这个答案,所以我自己发布它,但功劳仍然归于他。

但我想提醒一下,这是一个肮脏的、不可靠的黑客行为,可能会使系统不稳定:

除了固定问题之外,其他令人讨厌的问题是不知道对象有多长以及字段如何排列。

您应该使用调试器,除非您了解所有后果,准确了解您正在尝试执行的操作以及真正需要执行的操作 - 使用这种肮脏且不可靠的方式。