C# 在非托管内存上构造托管对象

问题描述 投票:0回答:1

我正在尝试在从 Marshal.AllocHGlobal() 分配的非托管内存上构造一个托管对象。目前,当我尝试从反射数据调用构造函数委托时,我收到一个异常,表明该对象的类型错误。我相信,如果我可以获得指向对象的 vftable 指针并将其复制到内存分配,我可以欺骗语言调用构造函数,但仅仅获取指向对象的静态实例的指针就已经很复杂了,而且我不知道 vftable 指针是否在前 8 个字节中,更不用说我是否可以复制它了。有没有其他方法可以做到这一点而不需要所有这些废话?

另外,我知道这不受支持;我不想看到这样的评论。

c# memory-management
1个回答
0
投票

对于仍在阅读本文的人来说,这个技巧对我来说太简单了,看不出来。基本上,还有另一个函数可以获取指针,该指针不会给出有关固定对象的d * mn(Unsafe.AsPointer(ref obj))。完成此操作后,只需复制方法表指针并通过反射调用构造函数即可。唯一的限制是所有类都必须使用顺序布局属性声明(否则我无法获得正确的 sizeof)

这是代码的粗略版本(所有静态字段都是为了避免创建新对象,因为这会破坏整个要点)

namespace UnmanagedMemory
{
    internal class HeapAllocatorTools
    {
        //gets first 8 (or 4) bytes of instantiated object as method table pointer
        protected unsafe static void* GetMethodTablePointer(object obj) => ((void**)Unsafe.AsPointer(ref obj))[0];
    }

    class HeapAllocator<Object> : HeapAllocatorTools
    {
        private static readonly Type objectType = typeof(Object);
        private static readonly ConstructorInfo objectConstrictor = objectType.GetConstructor(Type.EmptyTypes) ?? throw new ArgumentNullException();
        private static readonly MethodInfo objectDestructor = objectType.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentNullException();
        private static readonly Object obj = (Object) RuntimeHelpers.GetUninitializedObject(typeof(Object)) ?? throw new ArgumentNullException();
        public unsafe static Object* heapalloc()
        {
            Object* Memory = (Object*)Marshal.AllocHGlobal(Marshal.SizeOf<Object>());
            ((void**)Memory)[0] = (nint*)GetMethodTablePointer(obj);

            objectConstrictor.Invoke(*Memory, null);

            return Memory;
        }

        public unsafe static void heapfree(Object* ptr)
        {
            objectDestructor.Invoke(*ptr, null);
            Marshal.FreeHGlobal((nint)ptr);
        }
    }

    class HeapAllocator<Object, T1> : HeapAllocatorTools
    {
        private static readonly Type objectType = typeof(Object);
        private static readonly Type[] constructorParameterTypes = new Type[] { typeof(T1) };
        private static readonly ConstructorInfo objectConstrictor = objectType.GetConstructor(constructorParameterTypes) ?? throw new ArgumentNullException();
        private static readonly MethodInfo objectDestructor = objectType.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentNullException();
        private static readonly Object obj = (Object)RuntimeHelpers.GetUninitializedObject(typeof(Object));

        private static object?[] parameterPackValueFactory() { return new object?[] { null }; }
        private static ThreadLocal<object?[]?> parameterPack = new ThreadLocal<object?[]?>(parameterPackValueFactory);
        public unsafe static Object* heapalloc(T1 Param1)
        {
            Object* Memory = (Object*)Marshal.AllocHGlobal(Marshal.SizeOf<Object>());
            ((void**)Memory)[0] = (nint*)GetMethodTablePointer(obj);

            parameterPack.Value[0] = Param1;//avoids creation of object?[]
            objectConstrictor.Invoke(*Memory, parameterPack.Value);

            return Memory;
        }

        public unsafe static void heapfree(Object* ptr)
        {
            objectDestructor.Invoke(*ptr, null);
            Marshal.FreeHGlobal((nint)ptr);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.