Search code examples
c#reflectionmarshallingunmanaged-memorytypedreference

Marshal.StructureToPtr without boxing


Is there a way to marshal a structure (possibly stored in a TypedReference) to unmanaged memory without actually boxing it? The type of the structure isn't known at runtime, so I can't use the generic overload of StructureToPtr (.NET 4.5.1). I can get a MethodInfo of the StructureToPtr overload, but there doesn't seem to be a way to invoke it passing a generic reference or a TypedReference.

Edit: The generic StructureToPtr still boxes the structure, so trying to invoke it is useless.


Solution

  • I've finally found the answer, the SafeBuffer class. It contains exactly what I wanted - structure with marshalling methods using both TypedReference and generic parameters. So, it's really simple then to make a wrapper:

    public static unsafe class InteropTools
    {
        private static readonly Type SafeBufferType = typeof(SafeBuffer);
        public delegate void PtrToStructureNativeDelegate(byte* ptr, TypedReference structure, uint sizeofT);
        public delegate void StructureToPtrNativeDelegate(TypedReference structure, byte* ptr, uint sizeofT);
        const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Static;
        private static readonly MethodInfo PtrToStructureNativeMethod = SafeBufferType.GetMethod("PtrToStructureNative", flags);
        private static readonly MethodInfo StructureToPtrNativeMethod = SafeBufferType.GetMethod("StructureToPtrNative", flags);
        public static readonly PtrToStructureNativeDelegate PtrToStructureNative = (PtrToStructureNativeDelegate)Delegate.CreateDelegate(typeof(PtrToStructureNativeDelegate), PtrToStructureNativeMethod);
        public static readonly StructureToPtrNativeDelegate StructureToPtrNative = (StructureToPtrNativeDelegate)Delegate.CreateDelegate(typeof(StructureToPtrNativeDelegate), StructureToPtrNativeMethod);
    
        private static readonly Func<Type,bool,int> SizeOfHelper_f = (Func<Type,bool,int>)Delegate.CreateDelegate(typeof(Func<Type,bool,int>), typeof(Marshal).GetMethod("SizeOfHelper", flags));
    
        public static void StructureToPtrDirect(TypedReference structure, IntPtr ptr, int size)
        {
            StructureToPtrNative(structure, (byte*)ptr, unchecked((uint)size));
        }
    
        public static void StructureToPtrDirect(TypedReference structure, IntPtr ptr)
        {
            StructureToPtrDirect(structure, ptr, SizeOf(__reftype(structure)));
        }
    
        public static void PtrToStructureDirect(IntPtr ptr, TypedReference structure, int size)
        {
            PtrToStructureNative((byte*)ptr, structure, unchecked((uint)size));
        }
    
        public static void PtrToStructureDirect(IntPtr ptr, TypedReference structure)
        {
            PtrToStructureDirect(ptr, structure, SizeOf(__reftype(structure)));
        }
    
        public static void StructureToPtr<T>(ref T structure, IntPtr ptr)
        {
            StructureToPtrDirect(__makeref(structure), ptr);
        }
    
        public static void PtrToStructure<T>(IntPtr ptr, out T structure)
        {
            structure = default(T);
            PtrToStructureDirect(ptr, __makeref(structure));
        }
    
        public static T PtrToStructure<T>(IntPtr ptr)
        {
            T obj;
            PtrToStructure(ptr, out obj);
            return obj;
        }
    
        public static int SizeOf<T>(T structure)
        {
            return SizeOf<T>();
        }
    
        public static int SizeOf<T>()
        {
            return SizeOf(typeof(T));
        }
    
        public static int SizeOf(Type t)
        {
            return SizeOfHelper_f(t, true);
        }
    }
    

    Usage

    Guid g = Guid.NewGuid();
    int size = InteropTools.SizeOf(g);
    IntPtr mem = Marshal.AllocHGlobal(size);
    InteropTools.StructureToPtr(ref g, mem);
    
    Guid g2 = InteropTools.PtrToStructure<Guid>(mem);
    

    Now, does it actually have any advantages over the non-generic object-taking methods in the Marshal class? It seems the StructureToPtr takes about 80 % less time, and PtrToStructure can take almost 95 % less time. Also, these methods can handle nullable types correctly.