使用托管代码中的固定缓冲区调用非托管DLL(编码问题)

时间:2011-11-07 09:05:02

标签: .net unmanaged stringbuilder streamwriter

我有一个非托管DLL,可以从.NET调用预先分配的缓冲区来填充非托管DLL(根据Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C#)。

我的非托管函数有以下原型:

myFunc(char* a_inBuf,  int a_InLen, 
       char* a_outBuf, int* a_pOutLen, 
       char* a_errBuf, int* a_pErrLen);

因此,我在托管代码中声明了这样的方法:

public static extern int myFunc(
  [In, MarshalAs(UnmanagedType.LPStr)] string inputXml, int inputLen,
  [MarshalAs(UnmanagedType.LPStr)] StringBuilder outputXml, ref int outputLen,
  [MarshalAs(UnmanagedType.LPStr)] StringBuilder errorXml, ref int errorLen);

在调用myFunc之前,我创建了两个StringBuilders:

StringBuilder outputXml = new StringBuilder(100);
StringBuilder errorXml  = new StringBuilder(100);

在调用myFunc之后,我使用两个StringBuilders并使用

将它们写入XML文件(每个StringBuilder一个)
using (StreamWriter writer = new StreamWriter("OutputXmlFile.xml", false, Encoding.UTF8))
{
  writer.Write(outputXml.ToString());
  writer.Close();
}

输出应以UTF8写入,因为输入也是UTF8。但不幸的是,StringBuilder使用UTF16编码。 outputXmlerrorXml的内容也以UTF8编码填充在非托管DLL中。不应更改此行为。在编写文件时,StringBuilders中包含的特殊字符编写不正确。

如何告诉StringBuilder内容实际上是不是 UTF16但是UTF8?


编辑: the answer provided by Polynomial表示使用xmlWriter编写文件。但实际上,写入仅用于调试输出。在正常的应用程序运行中,outputXmlerrorXml的内容直接在程序中使用。因此,有关使用特殊XML处理类的任何提示都没有用。

实际问题是从StringBuilder中获取正确的字符串(或将它们转换为正确的字符串)。

3 个答案:

答案 0 :(得分:3)

你无法说服pinvoke marshaller从utf8转换。它将采用utf-16或系统默认代码页,并始终转换为utf-16。

不是问题,只需自己动手。声明类型为byte []的参数。调用之前使用适当的长度创建数组,调用后使用Encoding.UTF8.GetString()进行转换。

答案 1 :(得分:2)

有一篇关于这个主题的绝对精彩的文章帮助我解决了这个问题。这是:http://www.undermyhat.org/blog/2009/08/tip-force-utf8-or-other-encoding-for-xmlwriter-with-stringbuilder/

基本上,您必须使用xmlWriter.ForceEncoding(Encoding.UTF8)强制编码,但有一些警告。给这篇文章一个阅读,它应该有助于你理解发生了什么,为什么它首先是UTF-16以及如何绕过它。

答案 2 :(得分:1)

尝试做这样的事情(它提供了一种覆盖.NET的UTF-16默认特性的方法):

public class StringWriterWithEncoding : StringWriter
{
    Encoding encoding;

    public StringWriterWithEncoding (StringBuilder builder, Encoding encoding) :base(builder)
    {
        this.encoding = encoding;
    }

    public override Encoding Encoding
    {
        get { return encoding; }
    }

}

这背后的逻辑是它提供了一种方法来覆盖.NET的StringWriters的默认UTF-16编码。然后你可以像这样调用它:

修改

StringBuilder builder = new StringBuilder();
StringWriterWithEncoding stringWriter = new StringWriterWithEncoding(builder, Encoding.UTF8)
XmlWriter writer = new XmlTextWriter( stringWriter );
return stringWriter.ToString();