C#marshal非托管指针返回类型

Jay*_*mon 9 .net c# interop unmanaged marshalling

我有一个非托管库,它有这样的功能:

type* foo();
Run Code Online (Sandbox Code Playgroud)

foo基本上通过分配托管type堆上的非托管实例Marshal.AllocHGlobal.

我有一个托管版本type.它不是blittable但我MarshalAs在成员上设置属性,所以我可以使用它Marshal.PtrToStructure来获得它的托管版本.但不得不foo用额外的簿记打电话来打电话Marshal.PtrToStructure有点烦人.

我希望能够在C#方面做这样的事情:

[DllImport("mylib", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStruct)]
type* foo();
Run Code Online (Sandbox Code Playgroud)

让C#的marshaller处理幕后转换,就像它对函数参数一样.我以为我应该能够这样做因为type在托管堆上分配了.但也许我不能?有没有办法让C#的内置编组器为我处理返回类型的非托管到托管转换而无需手动调用Marshal.PtrToStructure

Sim*_*ier 6

如果在.NET端type声明为类而不是结构,则自定义封送器工作正常.这在UnmanagedType枚举中有明确说明:

与MarshalAsAttribute.MarshalType或MarshalAsAttribute.MarshalTypeRef字段一起使用时,指定自定义封送器类.MarshalAsAttribute.MarshalCookie字段可用于将其他信息传递给自定义封送程序.您可以在任何引用类型上使用此成员.

这是一些应该正常工作的示例代码

[[DllImport("mylib", CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef= typeof(typeMarshaler))]
private static extern type Foo();

private class typeMarshaler : ICustomMarshaler
{
    public static readonly typeMarshaler Instance = new typeMarshaler();

    public static ICustomMarshaler GetInstance(string cookie) => Instance;

    public int GetNativeDataSize() => -1;

    public object MarshalNativeToManaged(IntPtr nativeData) => Marshal.PtrToStructure<type>(nativeData);

    // in this sample I suppose the native side uses GlobalAlloc (or LocalAlloc)
    // but you can use any allocation library provided you use the same on both sides
    public void CleanUpNativeData(IntPtr nativeData) => Marshal.FreeHGlobal(nativeData);

    public IntPtr MarshalManagedToNative(object managedObj) => throw new NotImplementedException();
    public void CleanUpManagedData(object managedObj) => throw new NotImplementedException();
}

[StructLayout(LayoutKind.Sequential)]
class type
{
    /* declare fields */
};
Run Code Online (Sandbox Code Playgroud)

当然,将非托管结构声明更改为类可能会产生深刻的影响(可能并不总是会引发编译时错误),尤其是在您有大量现有代码的情况下.

另一种解决方案是使用Roslyn来解析代码,提取所有类似Foo的方法,并为每个方法生成一个额外的.NET方法.我会这样做的.