C# 封送处理 - 获取 C char** 的内容并转换为 byte[]

C# Marshaling - Get contents of C char** and convert to byte[]

我有一个 C DLL,其功能是 returns "char**"。我需要将它传递给 C# 下的 MemoryStream。

如何编组这个?

我已经试过了

[DllImport("ctf.dll")]
    public static extern long tts_sintetizaTexto_mm(long canal, string text, char* BuffVoice);

当我尝试在 MemoryStream 上使用 BuffVoice 时它产生了编译错误...

TIA, 尼洛 - 巴西

关于互操作映射的最佳新人文章:http://www.mono-project.com/docs/advanced/pinvoke/(虽然它是关于 Mono 的,但同时也是关于 .NET 的,而它们是兼容的)。 最常见的一点是使用 MarshallAs 属性直接从 System.String 编组 "in" 字符串以进行精确映射,对于 "out" 字符串使用带有 MarshallAs 和预定义大小的 StringBuilder。

我自己在使用 PInvoke 时遇到了一些问题,这与您的问题很接近:

但是你的样本不清楚:tts_sintetizaTexto_mm(long canal, string text, char* BuffVoice);

这里的 C 是什么,C# 是什么 - 字符串是 System.String 或 std::string char* 是什么意思?不安全的 C# char(宽字符!)指针或只是 C char*(有符号!)字节缓冲区???? - 如果 std::string - 它与 PInvoke 不兼容 - 使用 char* 或 wchar_t* 实现自己的 C 重载,如果它是 char* - 请记住,它不是 byte[] 而 byte[] 是 uchar*。 .. 如果它是 C# char* - 请记住 C# char 是 16 位,因此有效的 C 类型将是 wchar_t 或 ushort...我们可以将缓冲区大小发送到哪里以防止溢出?那么您刚才问的更多问题在哪里...

根据我的经验,虽然它不是来自属性,但更可控:使用 Marshall.GlobalHAlloc 和 IntPtr 作为互操作类型(对于任何 xxxx*)

[DllImport("ctf.dll")]
public static extern long tts_sintetizaTexto_mm(long canal, string text, IntPtr BuffVoice); //you should provide valid pointer to global memory IntPtr is compatible with any pointer type

 ...
 _ptr = Marshal.AllocHGlobal(REQUIRED_BUFFER_SIZE_IN_BYTES);
 try{
      /* initialize it if required, for ex write ZERO char to start buffer  to allow strlen and other work well */
     tts_sintetizaTexto_mm ( /*other params*/, _ptr); //it will be valid pointer in C context
     /* do required work with _ptr - you can work with Read method and so on */
 }finally{
      Marshall.FreeHGlobal(_ptr); // we was beyond GC - remembere to clear memory
 }

此处:How can I pass MemoryStream data to unmanaged C++ DLL using P/Invoke您可以看到带有几乎相同的 GCHandle 的接近样本

记住C中的char实际上是一个无符号字节。这意味着它实际上是 byte** - 它本身可以是两件事之一:指向数组的指针 (byte[]*) 或数组的数组 (byte[][]).

指向数组的指针

这种情况更有可能。

[DllImport("ctf.dll")]
public static extern long tts_sintetizaTexto_mm(long canal, string text, ref byte[] BuffVoice);

如果这是 Microsoft API,则很有可能是这样工作的:

[DllImport("ctf.dll")]
private static extern long tts_sintetizaTexto_mm(long canal, string text, ref byte[] BuffVoice);

public byte[] SintetizaText(long canal, string text)
{
    // The first call requests the size of the memory that we need to allocate.
    byte[] buffer = null;
    var size = tts_sintetizaTexto_mm(canal, text, ref buffer);
    if (size <= 0) throw new Exception();

    // The second call actually does the work.
    buffer = new byte[(int)size];
    size = tts_sintetizaTexto_mm(canal, text, ref buffer);

    // size either holds a new size, or the result code.
    // size: Array.Resize(ref buffer, size);
    // result code: if (size != 0) throw new Exception();

    return buffer;
}

数组的数组

[DllImport("ctf.dll")]
public static extern long tts_sintetizaTexto_mm(long canal, string text, ref byte[][] BuffVoice);