C DLL 中的 PInvoke char* 在 C# 中作为字符串处理。空字符问题

Posted

技术标签:

【中文标题】C DLL 中的 PInvoke char* 在 C# 中作为字符串处理。空字符问题【英文标题】:PInvoke char* in C DLL handled as String in C#. Issue with null characters 【发布时间】:2013-02-22 19:18:21 【问题描述】:

C DLL 中的函数如下所示:

int my_Funct(char* input, char* output);

我必须从 C# 应用程序调用它。我通过以下方式做到这一点:

...DllImport stuff...
public static extern int my_Funct(string input, string output);

输入字符串完美地传输到 DLL(我有明显的证据)。函数填写的输出虽然是错误的。我有六进制数据,比如:

3F-D9-00-01

但不幸的是,两个零之后的所有内容都被删除了,只有前两个字节进入我的 C# 应用程序。它发生了,因为(我猜)它被视为空字符并将其作为字符串的结尾。

知道如何摆脱它吗?我试图将它指定为 out IntPtr 而不是字符串,但我不知道之后该怎么处理它。 我试着做之后:

 byte[] b1 = new byte[2];
 Marshal.Copy(output,b1,0,2);

2 通常应该是字节数组的长度。但我得到了各种各样的错误:比如“请求的范围超出了数组的末尾。”或“试图读取或写入受保护的内存...”

感谢您的帮助。

【问题讨论】:

(1) C# 字符串比字符宽;它们是 2 个字符宽而不是 1 个字符。(2) 此函数返回的 char* 不会具有成为有效 C# 字符串所需的附加结构(即使它具有正确的宽度)。 您需要阅读 COM Interop 和 P/Invoke 以了解如何执行此操作。这样做,并在您对该材料有任何疑问时回电。 pinvoke marshaller 仅支持 C 字符串。当它在 0 之后有重要的字节时,它显然不是 C 字符串。然后它是一个字节 []。但是由于严重的挂断,没有人能弄清楚有多少 个字节是相关的。这个函数也很难从 C 代码中使用,当你 pinvoke 时也不会变得更好。你最好修一下。如果长度是可预测的,请使用 MarshalAs.SizeConst。 【参考方案1】:

您对输出字符串的编组不正确。在将数据从托管传递到本机时,在 p/invoke 声明中使用 string 是合适的。但是当数据流向另一个方向时,您不能使用它。相反,您需要使用StringBuilder。像这样:

[DllImport(...)]
public static extern int my_Funct(string input, StringBuilder output);

然后分配内存用于输出:

StringBuilder output = new StringBuilder(256);
//256 is the capacity in characters - only you know how large a buffer is needed

然后你就可以调用函数了。

int retval = my_Funct(inputStr, output);
string outputStr = output.ToString();

另一方面,如果这些参数中包含空字符,那么您不能将其编组为字符串。那是因为编组器不会编组任何超过空值的东西。相反,您需要将其编组为字节数组。

public static extern int my_Funct(
    [In] byte[] input, 
    [Out] byte[] output
);

这符合你的 C 声明。

然后假设您使用 ANSI 编码将输入字符串转换为这样的字节数组:

byte[] input = Encoding.Default.GetBytes(inputString);

如果您想使用不同的编码,很明显该怎么做。

对于输出,您确实需要分配数组。假设它与输入的长度相同,您可以这样做:

byte[] output = new byte[input.Length];

不知何故,您的 C 函数必须知道数组的长度。我会把这点留给你!

然后就可以调用函数了

int retval = my_Funct(input, output);

然后要将输出数组转换回 C# 字符串,您再次使用 Encoding 类。

string outputString = Encoding.Default.GetString(output);

【讨论】:

以上是关于C DLL 中的 PInvoke char* 在 C# 中作为字符串处理。空字符问题的主要内容,如果未能解决你的问题,请参考以下文章

C# PInvoke 传递出 unsigned char*

使用 PInvoke 连接 C/C++ DLL 的调试断言失败

反编译 c dll 以使用 pinvoke

处理来自 C++ DLL 的用户定义异常 - .NET PInvoke/Marshalling

PInvoke 使用 wchar16_t 参数调用函数

在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke