Xamarin Android:C#向C++封送Unicode字符串仅显首字符的解决方法
这个问题的核心原因是Android NDK中wchar_t的编码与C#封送的Unicode字符串不匹配,咱们一步步拆解解决:
问题根源
在Android的NDK环境中,wchar_t是4字节的UTF-32编码;而C#的CharSet.Unicode和[MarshalAs(UnmanagedType.LPWStr)]会将.NET的UTF-16字符串(每个字符2字节)转换为2字节的宽字符数组传递给原生库。
当你在C中用__android_log_print的%ls格式符打印wchar_t*时,它期望每个字符占4字节,但收到的是2字节的UTF-16数据——比如字符'1'的UTF-16表示是0x31 0x00,第二个字节的0x00会被C当成字符串的终止符,所以只显示第一个字符。
解决方案(推荐方案):C++端使用char16_t匹配C#的UTF-16
char16_t是C++11标准定义的、专门用于UTF-16编码的字符类型,正好和C#封送的LPWStr(UTF-16)匹配。修改代码如下:
1. 修改C++原生库代码
#include <android/log.h> #include <string> #include <codecvt> // 用于UTF-16到UTF-8的转换 extern "C" void logANSI(const char* data) { __android_log_print(ANDROID_LOG_INFO, "StringMarshaling", "ANSI data: %s", data); } extern "C" void logUnicode(const char16_t* data) { // 将UTF-16字符串转换为UTF-8(Android日志默认支持UTF-8) std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter; std::string utf8_str = converter.to_bytes(data); __android_log_print(ANDROID_LOG_INFO, "StringMarshaling", "Unicode data: %s", utf8_str.c_str()); }
注意:如果你的NDK工具链不支持
std::codecvt,可以改用Android原生的utf16_to_utf8函数(需包含android/utf.h头文件),或者手动实现简单的UTF-16到UTF-8转换。
2. 保持C#端的DllImport声明不变
原有的C#声明已经是正确的UTF-16封送配置,无需修改:
[DllImport("StringMarshaling", EntryPoint = "logANSI", SetLastError = true, CharSet = CharSet.Ansi, ThrowOnUnmappableChar = true)] public static extern void LogANSI([MarshalAs(UnmanagedType.LPStr)] string data); [DllImport("StringMarshaling", EntryPoint = "logUnicode", SetLastError = true, CharSet = CharSet.Unicode)] public static extern void LogUnicode([MarshalAs(UnmanagedType.LPWStr)] string data);
备选方案:C#端封送为UTF-32匹配C++的wchar_t
如果不想修改C代码,可以让C#将字符串转换为UTF-32(4字节宽字符)后传递,对应C的wchar_t*:
修改C#的DllImport声明
[DllImport("StringMarshaling", EntryPoint = "logUnicode", SetLastError = true)] public static extern void LogUnicode([MarshalAs(UnmanagedType.LPUTF32Str)] string data);
此方案需要.NET Framework 4.6+(Xamarin Android通常支持),但因为要做UTF-16到UTF-32的转换,效率略低于第一种方案,仅推荐在无法修改C++代码时使用。
验证结果
修改后再次调用StringMarshaling.LogUnicode("12345"),日志应该会完整输出Unicode data: 12345。
内容的提问来源于stack exchange,提问作者TheSned

