C#调用C++ DLL时嵌套结构与联合体的字符串传递问题
解决C#调用C++ DLL时嵌套联合体结构体的字符串传递问题
问题根源
- 联合体内存偏移错误:C++的
union所有成员共享同一块起始内存,但你在C#的pdll_test_data中给sensor字段设置了FieldOffset(4),导致结构体内存布局完全错位,后续的字符串成员自然无法正确映射。 - bool类型封送不匹配:C#默认会把
bool封送为4字节的UnmanagedType.Bool,但C++中的bool是1字节,类型大小不匹配会导致后续成员的内存偏移全部错乱。 - 结构体对齐差异:C#和C++编译器的默认内存对齐规则可能不同,未显式指定会引发内存布局偏差。
修正后的C#代码
using System; using System.Runtime.InteropServices; namespace CSharpApp { public class Program { public enum pdll_test_id { dut_com_init = 0, cont_pkt_tx = 1, }; // 显式指定对齐方式为1,匹配C++默认对齐(或根据实际编译器调整) [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct pdll_sensor_test { public pdll_test_id test_id; // 显式指定封送为1字节的bool,匹配C++的bool类型 [MarshalAs(UnmanagedType.U1)] public bool en; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)] public string name; public int reg_addr; }; // 联合体所有成员共享偏移0的内存 [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct pdll_test_data { [FieldOffset(0)] public pdll_test_id id; [FieldOffset(0)] public pdll_sensor_test sensor; }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct pdll_device { [MarshalAs(UnmanagedType.U1)] public bool is_active; public pdll_test_data test; }; [DllImport("CPPDynamicDLL.dll")] public static extern int PdllDeviceMethod(ref pdll_device pdlldevice); public static void Main(string[] args) { Console.WriteLine("This is C# program"); pdll_device pdllDevice = new pdll_device(); pdllDevice.is_active = true; // 注意:联合体成员互斥,设置sensor后id的值会被覆盖,按需调整赋值逻辑 pdllDevice.test.sensor.test_id = pdll_test_id.cont_pkt_tx; pdllDevice.test.sensor.en = true; pdllDevice.test.sensor.name = "wsa"; pdllDevice.test.sensor.reg_addr = 1024; var result = PdllDeviceMethod(ref pdllDevice); } } }
关键修改说明
- 联合体FieldOffset修正:将
pdll_test_data中sensor的FieldOffset改为0,和id共享同一块内存,符合C++ union的内存布局规则。 - bool类型封送指定:给所有
bool字段添加[MarshalAs(UnmanagedType.U1)],确保封送为1字节,和C++的bool大小一致。 - 结构体对齐设置:给所有结构体添加
Pack=1,强制按1字节对齐,避免因编译器默认对齐规则不同导致的内存偏移问题。 - 联合体成员赋值注意:C++ union的成员互斥,赋值一个成员会覆盖另一个,代码中移除了对
id的冗余赋值(会被sensor覆盖),可根据实际业务逻辑调整。
测试效果
修改后重新编译C#程序和C++ DLL,调用时C++端的printf会正确输出pdlldevice->test.sensor.name=wsa,其他字段也能正常映射。
内容的提问来源于stack exchange,提问作者Rajanbabu
相关产品推荐
相关产品推荐

