Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
对于结构内的固定字符数组,将C头转换为C#-ByValArray与ByValTStr_C#_C_Pinvoke_Marshalling - Fatal编程技术网

对于结构内的固定字符数组,将C头转换为C#-ByValArray与ByValTStr

对于结构内的固定字符数组,将C头转换为C#-ByValArray与ByValTStr,c#,c,pinvoke,marshalling,C#,C,Pinvoke,Marshalling,我的结构在C中定义为: typedef struct { char struct_id[4]; int struct_version; int keepAliveInterval; …… } MQTTClient_connectOptions 我在C#中创建了一个相应的结构,如下所示: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct MQTTClient_co

我的结构在C中定义为:

typedef struct {
    char struct_id[4];
    int struct_version;
    int keepAliveInterval;
    ……
} MQTTClient_connectOptions
我在C#中创建了一个相应的结构,如下所示:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MQTTClient_connectOptions {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string struct_id;
    public int struct_version;
    public int keepAliveInterval;
    ……
}
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] struct_id;
struct_id = Encoding.ASCII.GetBytes("MQTC");
这个C#结构是由p/Invoke Interop Assistant生成的,是我的google搜索中多篇文章推荐的C#代码段

定义C结构的DLL/源代码也定义了函数:

DLLExport  int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options);
我在我的C#代码中定义为:

在我的C代码中,我可以设置

MQTTClient_connectOptions.struct_id = "MQTC"
当我在调试时检查对象时,我可以在该字段中看到这4个字符。但是,当我使用此结构调用MQTTClient_connect()时,“MQTC”被截断为“MQT”

当我单步执行代码时,只要进入MQTTClient_connect,C#object inspector中的struct_id字段就会从“MQTC”更改为“MQT\0”,并且MQTTClient_connect会失败,因为struct_id不是预期的

如果我改为用C#定义结构,如下所示:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MQTTClient_connectOptions {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string struct_id;
    public int struct_version;
    public int keepAliveInterval;
    ……
}
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] struct_id;
struct_id = Encoding.ASCII.GetBytes("MQTC");
并按如下方式设置其值:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MQTTClient_connectOptions {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string struct_id;
    public int struct_version;
    public int keepAliveInterval;
    ……
}
 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] struct_id;
struct_id = Encoding.ASCII.GetBytes("MQTC");
那么一切正常吗

我的目标是理解封送处理、p/Invoke以及将C/C++头转换为C#代码,因此我很想知道:

1-当我进入MQTTClient_connect()例程时,为什么使用“byte[]”而使用“string”会导致struct_id的值发生变化

2-有没有一种方法可以使用“string”来定义C#结构,从而使我的C#代码的其余部分更简单


谢谢

原因是p/invoke封送处理程序必须决定
char[4]
字段是文本还是二进制(即字节)。这里的惯例是将
字符串
编组为
非托管类型。ByValTStr
假定为文本。在这种情况下,它可以具有可变长度,由必须存在的空终止符确定。这是与固定长度数组的
char
的典型用法相匹配的约定,用于保存C字符串。C字符串以null结尾

但实际上,我怀疑你的数据不是真正的文本。我怀疑该字段最好在C中声明为
unsigned char struct_id[4]
,以指示它包含4个字节。不过,所有这些都有点主观,当然,我们并不赞同图书馆设计背后的所有理由。也许有一些很好的理由将其声明为我看不到的
char
数组


不管怎样,您的C#代码都不能在此字段中使用
string
。带有
SizeConst=4的
byte[]
是封送此数据的正确方法。记录类型上的一些帮助器方法可以帮助在字节数组和字符串之间进行转换。但是,我想知道您将遇到多少不同的ID。可能您只需要声明少量字节数组常量,就可以使用这些常量而不是字符串文本。

C#字符串不一定以“\0”结尾。格式正确的C/C++字符串必须以“\0”结尾。因此,将c#字符串编组到c/c++可能需要编组程序NUL终止c#字符串。但是,由于您已将长度限制为4,封送拆收器可能必须截断c#字符串才能附加该'\0'。只是好奇:如果使用SizeConst=5会发生什么?编辑:@David的回答似乎证实了我的猜测。它不是字符串,在本机代码中非常常见。您的意思是,在C中,char[4]实际上并不意味着“一个4个字符的数组”,而是被C编译器解释为“一个长度为4的数组,其中包含一个以零结尾的字符串”。从这个角度来看,我看到的一切都是有意义的,我同意DLL开发人员不应该在C头文件中使用char[4]来定义这个字段。谢谢你的解释!