使用PInvoke从C#读取带有"union"类型的C结构

Zé *_*los 7 .net pinvoke

我正试图将管理端(C#)带入一个用C语言构建的结构.

我们假设这个结构(C代码):

typedef struct S{
    int i;
    union{
        TypeA a;
        TypeB b;
        TypeC c;
    }uni;
 } S;  
Run Code Online (Sandbox Code Playgroud)

现在,我创建了C#包装类:

[StructLayout(LayoutKind.Explicit)]
public class S
{
    [FieldOffset(0)] 
    public int i;
    [FieldOffset(4)] 
    public TypeA a;
    [FieldOffset(4)]
    public TypeB b;
    [FieldOffset(4)]
    public TypeC c;
}
Run Code Online (Sandbox Code Playgroud)

我有一个PInvoke方法来获取S对象:
(C的实现在union字段中创建并返回一个带有TypeA的S结构)

[DllImport("Library.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.S)]
public static extern S getUnionStruct();
Run Code Online (Sandbox Code Playgroud)

在主要功能的某处,我做:

S s = getUnionStruct();
Console.WriteLine("unions type: {0}",(S.a).GetType());
Run Code Online (Sandbox Code Playgroud)

结果是"AssembleName.TypeC"(???)

.net Framework假设TypeC,因为那是最后声明的.我还注意到,如果TypeC的大小小于TypeA,我将无法读取所有TypeA字段.

这是来自.net的错误还是我应该做些不同的事情?

Zé *_*los 7

问题是使用引用类型来包装非托管类型.当CLR执行"GetType"方法时,它使用一个虚拟表,该表只能包含一个在声明中被连续覆盖的类型.最后声明的字段获胜(在这种情况下为TypeC)
将"class"切换为"struct"可以解决问题.

[StructLayout(LayoutKind.Explicit)]
public struct S
{
    [FieldOffset(0)] 
    public int i;
    [FieldOffset(4)] 
    public TypeA a;
    [FieldOffset(4)]
    public TypeB b;
    [FieldOffset(4)]
    public TypeC c;
}

[StructLayout(LayoutKind.Sequencial)]
public struct TypeA
{
    //...
}

[StructLayout(LayoutKind.Sequencial)]
public struct TypeB
{
    //...
}

[StructLayout(LayoutKind.Sequencial)]
public struct TypeC
{
    //...
}
Run Code Online (Sandbox Code Playgroud)