C# <-> C++:封送包含 Char[] 的 [In,Out] 结构

C# <-> C++ : Marshaling an [In,Out] struct containing Char[]

我有一个 C++ .dll(无法更改代码),我试图从中调用一个函数,该函数采用 Struct 类型(也在 .dll 中定义)的引用参数。

该函数要求 Struct 填充 'paramName' 和 'groupName' 字段,并且根据这些字段,它将 return Struct 填充其余字段。

我没有收到任何编组错误,但是,库调用 returns 是一个错误代码和调试日志显示它正在接收两个字符串字段的空字符串(请参阅下面的字段如何已设置)。我的假设是此结构的大小未在托管和非托管表示之间对齐,因此该结构不可 blittable。

这是 C++ 方法签名:

int GetConfigs(int contextHandle, Configs* configs);

C++ 配置结构:

struct Configs {    
    int myInt;
    float myFloat;
    bool flag;
    char name[64];
    char group[64];
}  

C# 函数包装器:

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int GetConfigs(int contextHandle, [MarshalAs(UnmanagedType.Struct), In, Out] ref Configs configs);

C# 结构定义:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
    public int myInt;
    public float myFloat;
    [MarshalAs(UnmanagedType.U1)]
    public bool flag;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string name;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string group;
}

根据 Microsoft documentation,我已声明 C++ char[] 在 C# 中由字符串表示,并编组为具有设定大小的 ByValTStr。 同样来自微软 documentation:

最后,C#调用代码:

var configs = new Library.Configs
{
    name = "testName",
    group = "testGroup"
};

var returnCode = Library.GetConfigs(GetContextId(), ref configs);

return 代码作为失败代码返回,库调试输出文件显示结构参数具有:

name = []

(当我通过 C# 代码进行调试时,名称明确设置为我在调用时期望的名称)

而不是将字符串字段声明为

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;

我试过了:

[MarshalAs(UnmanagedType.ByValArray,  SizeConst = 64)]
public byte[] name;

但这也没什么区别。

float/double 是什么放弃了结构字节大小——库期望一个 4 字节的浮点数,但默认情况下数据编组器将它们保留为 8 字节。修复上面评论中提到的布尔值解决了这个问题:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
    public int myInt;
    [MarshalAs(UnmanagedType.R4)]
    public float myFloat;
    [MarshalAs(UnmanagedType.U1)]
    public bool flag;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string name;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string group;
}