如何在C#中使用结构体和联合体

3

你好,我正在用C#编写一个包装器,但遇到了一些问题。我有一个在C++中的结构体。

typedef struct pjmedia_format
{
    pj_uint32_t id;
    pjmedia_type type;
    pjmedia_format_detail_type detail_type;
    union
    {
    pjmedia_audio_format_detail aud;
    pjmedia_video_format_detail vid;
    char user[PJMEDIA_FORMAT_DETAIL_USER_SIZE];
    } det;
} pjmedia_format;

这是指向结构体pjmedia_format的链接。

在C#中,我有以下代码:

[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_format
{
    public uint id;
    public pjmedia_type type;
    public pjmedia_format_detail_type detail_type;
    public det_t det;
}

[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.Struct)]
    public pjmedia_audio_format_detail aud;
    [FieldOffset(36)]
    [MarshalAs(UnmanagedType.Struct)]
    public pjmedia_video_format_detail vid;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    [FieldOffset(60)]
    public char[] user;
}

[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_audio_format_detail
{
    public uint clock_rate;
    public uint channel_count;
    public uint frame_time_usec;
    public uint bits_per_sample;
    public int avg_bps;
    public int max_bps;
}

[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_video_format_detail
{
    public pjmedia_rect_size size;
    public pjmedia_ratio fps;
    public int avg_bps;
    public int max_bps;
}

当我想使用此结构时,我遇到了以下错误:

System.Runtime.InteropServices.MarshalDirectiveException未经处理。 Message="方法签名与元素不兼容。"

我尝试使用一些属性,例如size或pack,但没有帮助(可能我使用不正确)。我测试了其他结构,如pjmedia_video_format_detail,它们运行良好。有什么建议吗?

最好的问候 Andrzej


为什么不制作“接口”? - Raptor
@Shivan,这怎么可能在任何情况下都有助于具有特定布局的PInvoke结构体呢? - Marc Gravell
2个回答

2
作为一个联合体,那么不应该是这样的吗:

[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.Struct)]
    public pjmedia_audio_format_detail aud;
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.Struct)]
    public pjmedia_video_format_detail vid;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    [FieldOffset(0)]
    public char[] user;
}

即重叠?此外,你可能需要将user作为一个固定缓冲区而不是数组。

实际上,ByValArray 是可以的。 - David Heffernan

1
在C++代码中,det是一个union。这意味着所有字段都具有零偏移量,它们被覆盖。只需通过在det_t中的所有字段上使用[FieldOffset(0)]来更改您的C#声明即可匹配。

如果我改成[FieldOffset(0)],我会得到另一个错误 >System.ExecutionEngineException - Andrzej
我们很难为您调试这个问题。我们不知道接口的另一端是什么,也看不到您的调用代码。 - David Heffernan
很抱歉我的上一条回复,如果我在_pjmedia_vid_dev_info_中设置了一些[FieldOffset(..)],那么_fmt_中也有一些值,但是当我尝试在pjmedia_vid_codec_param中使用pjmedia_format时会出现错误。 - Andrzej
调用转换C++代码就像你之前写的cdecl一样,如果我改成FieldOffset(0)并在DllImport属性中添加CallingConvention = CallingConvention.Cdecl,我会得到System.TypeLoadException was unhandled Message = "Could not load type 'pjsipdll.det_t' from assembly 'pjsipdll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' because it contains a field object to postpone '0 'desktop applications is incorrectly aligned or partially cover the field of non-object field." - Andrzej
我们不能在评论中完成这个。我的回答和Marc的回答完全回答了你问题中的所有内容。对于这个问题,没有更多需要补充的了。如果你有后续问题,应该提出一个新的问题。请确保在未来的问题中包含更多细节。 - David Heffernan
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接