将 C# 字符串传递给 C++,并将 C++ 结果(字符串、char*.. 任何)传递给 C#

Posted

技术标签:

【中文标题】将 C# 字符串传递给 C++,并将 C++ 结果(字符串、char*.. 任何)传递给 C#【英文标题】:Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C# 【发布时间】:2010-02-01 18:58:37 【问题描述】:

我尝试了不同的方法,但我对 Interop 很生气。

(这里的字符串不是指变量类型,而是“字符的集合”): 我有一个在 dll 中定义的非托管 C++ 函数,我试图从 C# 访问该函数,该函数有一个字符串参数和一个字符串返回值,如下所示:

string myFunction(string inputString)


C++ 端的字符串应该是什么?和 C# 之一?以及为此需要 DllImport 哪些参数?

【问题讨论】:

【参考方案1】:

我发现最有效的方法是更明确地说明这里发生的事情。在这种情况下,可能不建议使用字符串作为返回类型。

一种常见的方法是让 C++ 端传递缓冲区和缓冲区大小。如果它对于 GetString 必须放入的内容不够大,则修改 bufferSize 变量以指示适当的大小。然后调用程序 (C#) 会将缓冲区的大小增加到适当的大小。

如果这是您导出的 dll 函数 (C++):

extern "C" __declspec void GetString( char* buffer, int* bufferSize );

匹配的 C# 如下:

void GetString( StringBuilder buffer, ref int bufferSize );

因此,要在 C# 中使用它,您需要执行以下操作:

int bufferSize = 512;
StringBuilder buffer = new StringBuilder( bufferSize );
GetString( buffer, ref bufferSize );

【讨论】:

这是正确的做法。请注意,您不必通过引用传递缓冲区大小。 C/C++ 应该终止字符串,buffer.ToString() 产生返回值。 谢谢大家的回答。我在某处读到 StringBuilder 用于输出字符串,但我还需要将输入字符串传递给 C++ 函数。我应该使用什么类型?在 C++ 端,我放置了 char* 和 C# 端字符串,但它不起作用。最后,我可以将输出“字符串”(C++ 端)定义为 const char* 吗?或者我必须有一个 char*? @Smjert:如果您不更改它,使用 const char* 是一个不错的方法。这应该很好地映射到 C# 端的字符串。您遇到什么类型的问题? @nobugz:我将检查 bufferSize 是否需要通过引用传递,并在找到时更新答案。如果它确实不需要作为参考,那将是一个很好的提示,但我不确定我是否理解为什么会这样。 @Scott - P/Invoke marshaller 明确支持以零结尾的字符串。它知道如何正确地将 char* 填充到 StringBuilder 中。如果 char* 不是像通常的 'C' 字符串那样以零结尾,就会有问题。【参考方案2】:

我知道这样做的唯一好方法是使用Managed C++ Extensions 编写一个.NET C++ 包装类,并在.NET C++ 对象中调用您的本机C++ 代码。托管扩展中有一些函数可以将 System.String 转换为 char* 或任何其他类型的非托管字符串。

基本上,您使用 C++ 创建一个 .NET 类并将其从程序集中公开,在该程序集内部,您可以调用您的本机 C++ 代码。另一种方法是使用 P/Invoke 将纯 C 函数添加到您的 C++ 代码中,然后从 C# 调用您的 C 代码并让您的 C 函数调用您的 C++ 代码。这会起作用,但我倾向于尝试尽可能多地使用托管代码。

【讨论】:

【参考方案3】:

将字符串从 C++ 传递回 C# 的最大问题是内存分配。 GC 应该能够知道如何清理为该字符串分配的内存。由于 C# 具有广泛的 COM 互操作支持,它确实知道 COM BSTR 以及如何分配和解除分配这些。因此,最简单的方法是在 C++ 端使用 BSTR,在 C# 端使用 string

注意,使用 BSTR 并不意味着您的函数必须通过 COM 公开。

【讨论】:

【参考方案4】:

“字符串”返回值是问题所在。 P/Invoke 编组器将在您返回的指针上调用 CoTaskMemFree()。除非您在 C/C++ 代码中使用 CoTaskMemAlloc() 来分配字符串缓冲区,否则这不会很好地工作。这是一件相当不寻常的事情。

最好的解决方案是让代码的调用者将指向缓冲区的指针和缓冲区长度作为参数传递给您。这样所有内存分配都发生在一侧。 Scott 向您展示了如何做到这一点。

【讨论】:

【参考方案5】:

我必须将 unicode C# 字符串转换为多字节表示,以便在 c++ 中转换为 char*(这是部分单向解决方案)

我发现以下非常有用

string st; 
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st); 
// Do your thing in C++ 

Marshal.FreeHGlobal(stPtr); 

这可能效率低下且不是 C# 方式,我是 C# 新手。

【讨论】:

以上是关于将 C# 字符串传递给 C++,并将 C++ 结果(字符串、char*.. 任何)传递给 C#的主要内容,如果未能解决你的问题,请参考以下文章

将 C++ 字符串传递给 C# 函数

将 C# 字符串数组传递给 C++

将字符串的引用从 C# 传递给 C++ 函数

将字符串数组作为缓冲区传递给 C++ 到 C#

使用 Dllimport 将一个非常大的字符串作为字节数组从 C++ 传递到 C#

将 c++ dll 的 char 指针数组传递给 c# 字符串