C++如何将std::string正确转换为C#调用所需的byte数组
问题原因
你之前的实现存在三个核心错误,导致C#侧无法拿到正确字符串:
- C++函数参数中写的
std::byte buf[256]本质是std::byte* buf,是存储在函数栈上的局部指针变量。你执行buf = ptr只是修改了这个局部指针的指向,完全没有把数据写入C#侧传入的托管数组对应的内存块,函数执行完局部变量直接销毁,对调用方没有任何影响。 - 你写的C++函数返回值是
void,和C#声明的string返回值不匹配。C#的P/Invoke封送逻辑会从函数返回值的指针位置读取空终止字符串生成托管string,你没有返回有效指针时,返回值寄存器存的是随机无效值,大概率读到空内存就返回空字符串,甚至可能触发内存访问崩溃。 - 就算你做了内存拷贝,也不能直接返回局部
std::string的内部指针:局部string对象在函数返回时会自动析构,对应内存会被释放,属于野指针,CLR读取时会出现乱码或崩溃。
正确实现
你不需要修改C#侧的现有声明,C++端按照如下逻辑实现即可:
#include <cstring> #include <string> // 按C接口导出,注意返回值为char*,匹配C#侧string的封送规则 extern "C" __declspec(dllexport) char* __cdecl Name(std::byte* buf) { const std::string user_name = "test"; // 这里替换成你实际要返回的用户名 // 将字符串内容连同末尾的空终止符一起拷贝到传入的缓冲区 // 注意:C#侧传入的byte[]长度必须 >= 字符串长度 + 1,预留空终止符的位置 std::memcpy(buf, user_name.c_str(), user_name.length() + 1); // 返回缓冲区首地址,供CLR封送为托管string return reinterpret_cast<char*>(buf); }
注意事项
- 你当前C#的P/Invoke声明默认使用
CharSet.Ansi封送字符串,和上述实现的单字节/多字节ANSI字符串逻辑完全匹配,不需要调整现有接口定义。如果后续需要支持UTF-8编码,可以在不修改方法签名的前提下,给P/Invoke声明增加CharSet = CharSet.UTF8(.NET 5+支持)的配置即可。 - C#侧调用时传入的
byte[]长度要足够容纳你要返回的字符串+末尾空终止符,比如你最长返回255个字符的用户名,就传入长度为256的byte数组即可,避免C++端内存拷贝越界。 - 不要尝试在C++端给传入的buf指针重新赋值指向其他内存,必须直接操作buf指向的托管缓冲区内存,否则既无法写入数据,返回非CLR持有的堆内存指针还可能导致内存泄漏或崩溃。
内容的提问来源于stack exchange,提问作者Man Ling
相关产品推荐
相关产品推荐

