Unity向C++原生插件传递字节数组:字符串出现空字节间隔问题
解决Unity传递字符串到C++出现
A\0B\0C\0的问题 这个问题本质是编码不匹配导致的:Unity中的char是16位的UTF-16编码,而C里的char是8位的ASCII/UTF-8编码。当你直接把Unity的char[]传给C时,每个UTF-16字符的高字节会被填充为\0,就出现了字符间夹空字节的情况。下面给你三种常用的解决思路:
方法一:Unity侧转UTF-8字节数组传递
这是跨平台最稳妥的方案,UTF-8在C++里可以直接用char*处理,兼容性拉满。
Unity 代码:
using System.Text; using System.Runtime.InteropServices; public class NativeBridge : MonoBehaviour { [DllImport("myDLL")] public static extern bool myFunc( byte[] strA, int lengthA, byte[] strB, int lengthB, byte[] strC, int lengthC, byte[] strD, int lengthD, byte[] strE, int lengthE ); public void CallMyFunc() { string inputA = "ABC"; // 把字符串转成UTF-8字节数组 byte[] byteStrA = Encoding.UTF8.GetBytes(inputA); byte[] byteStrB = Encoding.UTF8.GetBytes("DEF"); // 其他字符串同理处理 // 调用C++函数,传递字节数组和对应长度 bool result = myFunc( byteStrA, byteStrA.Length, byteStrB, byteStrB.Length, new byte[0], 0, new byte[0], 0, new byte[0], 0 ); } }
C++ 代码:
#include <cstring> extern "C" __declspec(dllexport) bool myFunc( char* strA, int lengthA, char* strB, int lengthB, char* strC, int lengthC, char* strD, int lengthD, char* strE, int lengthE ) { // 现在strA就是正常的UTF-8字符串"ABC",可以直接处理 // 用lengthA做边界检查,避免越界访问 if (strncmp(strA, "ABC", lengthA) == 0) { // 你的业务逻辑 } return true; }
方法二:直接传递宽字符串(Windows平台友好)
如果你的项目只针对Windows平台,直接用宽字符匹配Unity的UTF-16编码会更简单:
Unity 代码:
using System.Runtime.InteropServices; public class NativeBridge : MonoBehaviour { [DllImport("myDLL")] public static extern bool myFunc( [MarshalAs(UnmanagedType.LPWStr)] string strA, [MarshalAs(UnmanagedType.LPWStr)] string strB, [MarshalAs(UnmanagedType.LPWStr)] string strC, [MarshalAs(UnmanagedType.LPWStr)] string strD, [MarshalAs(UnmanagedType.LPWStr)] string strE // 宽字符串自带null结尾,可以去掉长度参数 ); public void CallMyFunc() { bool result = myFunc("ABC", "DEF", "", "", ""); } }
C++ 代码:
#include <cwchar> extern "C" __declspec(dllexport) bool myFunc( wchar_t* strA, wchar_t* strB, wchar_t* strC, wchar_t* strD, wchar_t* strE ) { // strA现在是L"ABC",用宽字符函数处理即可 if (wcscmp(strA, L"ABC") == 0) { // 你的业务逻辑 } return true; }
方法三:用Marshal类手动转换(适合老项目兼容)
如果不想修改太多C++代码,可以用Unity的Marshal类把字符串转成ANSI编码的非托管内存:
Unity 代码:
using System; using System.Runtime.InteropServices; public class NativeBridge : MonoBehaviour { [DllImport("myDLL")] public static extern bool myFunc( IntPtr strA, int lengthA, IntPtr strB, int lengthB, IntPtr strC, int lengthC, IntPtr strD, int lengthD, IntPtr strE, int lengthE ); public void CallMyFunc() { string inputA = "ABC"; // 把字符串转成ANSI编码的非托管内存 IntPtr ptrA = Marshal.StringToHGlobalAnsi(inputA); IntPtr ptrB = Marshal.StringToHGlobalAnsi("DEF"); try { bool result = myFunc( ptrA, inputA.Length, ptrB, inputA.Length, IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero, 0 ); } finally { // 必须手动释放非托管内存,避免泄漏 Marshal.FreeHGlobal(ptrA); Marshal.FreeHGlobal(ptrB); } } }
C++ 代码:
和你原来的函数签名完全一致即可,因为StringToHGlobalAnsi生成的就是标准8位ANSI字符串:
extern "C" __declspec(dllexport) bool myFunc( char* strA, int lengthA, char* strB, int lengthB, char* strC, int lengthC, char* strD, int lengthD, char* strE, int lengthE ) { // 正常处理strA即可,就是"ABC" return true; }
注意事项
- 无论用哪种方法,都要保证编码一致性:要么全用UTF-8,要么全用UTF-16,避免乱码。
- 如果C++需要返回字符串给Unity,也要按对应的编码转换后再传递。
- 用Marshal手动分配内存时,一定要在
finally块里释放,防止内存泄漏。
内容的提问来源于stack exchange,提问作者user8469759
相关产品推荐
相关产品推荐

