试图显示和转换一个 2 字节的 unicode 字符串

Trying to show and convert a 2 byte unicode string

我在 C# WPF 项目中调用了一个 C 接口方法。方法 returns 一个 2 字节的 Unicode(UTF-16,如果我没记错的话)字符串通过 StringBuilder。我试图在 WPF TextBox 控件中显示这个 2 字节的 Unicode 字符串,并将其写入 .txt 文件。

TextBox.txt 文件中的结果似乎不可读。

我试过将 Unicode (UTF-16) 字符串转换为 ANSI,但这也不起作用。

以下是 DllImport 和我尝试将字符串转换为可读内容的代码示例。

[DllImport("cdll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static int ChannelID(int uHandle, uint uChannel, StringBuilder szID);

for (uint i = 0; i <= numChannels - 1; i++)
{
     StringBuilder sbId = new StringBuilder(32);
     ChannelID(_handle, i, sbId);
     string val = "";
     UnicodeEncoding unicode = new UnicodeEncoding();
     val = Encoding.Default.GetString(Encoding.Convert(Encoding.Unicode, Encoding.Default, unicode.GetBytes(sbId.ToString())));
     File.AppendAllText(System.AppDomain.CurrentDomain.BaseDirectory + "dump.txt", sbId.ToString() + " - ", Encoding.Unicode);                
     textBox1.AppendText(val + " - ");
     textBox1.AppendText(sbId.ToString() + " - ");
}

正在从蓝牙连接的设备读取字符串。该设备用于测量温度、空气湿度、气压等...

所以输入是一个 Unicode 字符串,例如 °c,屏幕上和 txt 文件中的输出应该是这个的可读版本(例如 ansi)。

另一位可能很重要的信息,C 方法最初用于 excel 宏 VBA 项目,因此使用了这个 2 字节的 Unicode 编码。

解决方案

问题与 DllImport 中的 CharSet 无关,而与 CallingConvention 有关。在联系了制作 C 库的人后,他们告诉我他们给了我们一个错误的示例代码。正确的DllImport是这个:

[DllImport("cdll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]

添加这个之后数据就可以正确通过了,不需要做任何转换。

由于不清楚预期的输出是什么,您仍然可以尝试:

 byte[] bytes = Encoding.UTF8.GetBytes("°c");
 Console.WriteLine(Encoding.ASCII.GetString(bytes));

这给出了输出 ??c

我认为你是 "destroying" 这一行中的字符串:

val = Encoding.Default.GetString(Encoding.Convert(Encoding.Unicode, Encoding.Default, unicode.GetBytes(sbId.ToString())));

由于 .NET 完全能够处理双字节 unicode 字符,您需要的字符串应该已经在 sbId 中,因此理想情况下,以下内容应该有效:

val = sbId.ToString();

当您停在上述行并检查 sbId 的值时,调试器会显示什么?

 CharSet = CharSet.Unicode

这是你出错的地方,它不是 Unicode。您必须改用CharSet.Ansi。

Unicode 字符串需要两个 0 字节来终止字符串。本机代码只生成一个。正常的命运是一个 AccessViolationException,但你并不经常幸运地得到一个。在内存中找到两个相邻的二进制零的可能性有点大。所以你最终会得到一个很长的字符串,只是随机的垃圾。

只需声明它的真实情况,CharSet.Ansi。而且您也不再需要 Encoding.Convert() 代码。