我正在尝试在从 Marshal.AllocHGlobal() 分配的非托管内存上构造一个托管对象。目前,当我尝试从反射数据调用构造函数委托时,我收到一个异常,表明该对象的类型错误。我相信,如果我可以获得指向对象的 vftable 指针并将其复制到内存分配,我可以欺骗语言调用构造函数,但仅仅获取指向对象的静态实例的指针就已经很复杂了,而且我不知道 vftable 指针是否在前 8 个字节中,更不用说我是否可以复制它了。有没有其他方法可以做到这一点而不需要所有这些废话?
另外,我知道这不受支持;我不想看到这样的评论。
对于仍在阅读本文的人来说,这个技巧对我来说太简单了,看不出来。基本上,还有另一个函数可以获取指针,该指针不会给出有关固定对象的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);
}
}
}