Swig与C#:无法为结构体数组成员赋值的问题求助
问题分析与解决方案
你的核心问题在于C#侧构造的内存布局和C++结构体中二维char数组的内存布局不匹配,导致SWIG生成的setter无法正确解析数据,以下是具体原因和修复方案:
原方法失败原因
第一种方法错误
C++中char routing_key[100][56]是连续的内存块(共100*56=5600字节),每个元素是56字节的ANSI字符串;而你用IntPtr[]构造的是指针数组(每个元素是一个字符串的内存地址),两者内存结构完全不同,SWIG生成的SWIGTYPE_p_a_56__char对应的是char(*)[56](指向56字节char数组的指针),而非char**(指针数组),因此无法匹配。
第二种方法错误
C#的char[,]是UTF-16编码的双字节字符数组,每个char占2字节;而C++的char是单字节ANSI字符,内存布局错位,直接pin传递会导致数据乱码,类型也不匹配。
正确解决方案
方案1:直接构造匹配C++内存布局的字节数组
模拟C++连续单字节内存块的结构,用byte[]存储数据后传递给SWIG:
// 分配与C++数组完全匹配的连续内存:100*56字节 byte[] routingKeyBuffer = new byte[100 * 56]; // 填充第一个字符串"abcd"为ANSI编码,复制到对应位置 byte[] firstStrBytes = Encoding.ASCII.GetBytes("abcd"); Array.Copy(firstStrBytes, 0, routingKeyBuffer, 0, firstStrBytes.Length); // 剩余字节默认是0,正好对应C++的字符串终止符'\0' // pin字节数组,获取首地址 GCHandle gch = GCHandle.Alloc(routingKeyBuffer, GCHandleType.Pinned); IntPtr bufferPtr = gch.AddrOfPinnedObject(); // 构造SWIG类型并赋值 ReqSubscribeField_t subscribeField = new ReqSubscribeField_t(); subscribeField.routing_key = new SWIGTYPE_p_a_56__char(bufferPtr, true); // 注意:若SWIG构造函数第二个参数为true,会接管内存管理,无需手动释放GCHandle;否则使用完需调用gch.Free()
方案2:修改SWIG接口,生成更易用的C#访问器
通过SWIG自定义typemap,让二维char数组直接映射为C#的string[],避免手动操作指针:
在你的SWIG接口文件(.i)中添加以下代码:
%module YourModuleName // 为routing_key生成C#字符串数组的getter %typemap(csgetter) char ReqSubscribeField::routing_key[100][56] { string[] result = new string[100]; IntPtr basePtr = $imcall; for (int i = 0; i < 100; i++) { result[i] = Marshal.PtrToStringAnsi(basePtr + i * 56); } return result; } // 为routing_key生成C#字符串数组的setter %typemap(cssetter) char ReqSubscribeField::routing_key[100][56] { IntPtr basePtr = $imcall; for (int i = 0; i < value.Length && i < 100; i++) { string str = value[i] ?? string.Empty; // 将字符串转为ANSI并填充到56字节,不足补'\0' byte[] bytes = Encoding.ASCII.GetBytes(str.PadRight(56, '\0')); Marshal.Copy(bytes, 0, basePtr + i * 56, 56); } } // 导入你的C++头文件 %include "your_header_file.h"
重新生成C#代码后,即可直接用字符串数组赋值:
ReqSubscribeField_t subscribeField = new ReqSubscribeField_t(); subscribeField.routing_key = new string[100]; subscribeField.routing_key[0] = "abcd"; // 其余元素默认空字符串,会自动填充'\0'
内容的提问来源于stack exchange,提问作者SongXiangGong
相关产品推荐
相关产品推荐

