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);
我有一个 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);