【问题标题】:Returing a string whose length is unknown a priori from C++ called from C#从 C# 调用的 C++ 返回一个长度未知的字符串
【发布时间】:2013-08-21 08:23:57
【问题描述】:

我已经广泛搜索了我的问题的答案,但所有解决方案都不可接受、不适用和/或令人困惑。

我需要将C++ 中实现的函数中的字符串返回给C# 中的调用代码。

返回的字符串需要作为参数而不是返回值返回,因为我需要为某些函数传递/返回多个字符串。字符串的长度各不相同,所以我不能只分配一个缓冲区等。

任何帮助将不胜感激。

注意:贾斯汀和/或其他人发布和提到的解决方案不是我用例的解决方案。正如我在问题中所述,在调用 C++ 代码之前我不知道字符串的大小。我无法预先分配一个 StringBuffer 并将其传递给 C++ 代码。

【问题讨论】:

  • 请添加函数声明。
  • 托管 C++ 还是原生 C++?
  • 字符串分配的内存在哪里?由调用者还是被调用者?
  • 贾斯汀,接受的答案涉及预分配缓冲区,我说这是不可能的,因为在调用 C++ 缓冲区之前我不知道所需的大小。

标签: c# c++ .net string parameter-passing


【解决方案1】:

使用平台提供的可移植(多编译器多语言)字符串,用于在不同语言实现的组件之间传递字符串——BSTR

在 C++ 中,您使用 SysAllocStringSysAllocStringLen。只要您使用正确的签名,P/invoke 已经知道如何处理这些(转换为 .NET 字符串并调用 SysFreeString)。

extern static void DoSomething([MarshalAs(UnmanagedType.BStr)] out String returnedString);

然后简单地调用它:

string theString;
DoSomething(out theString);

就是这样,不需要特殊的转换或清理,因为 p/invoke 已经处理好了。

欲了解更多信息,请阅读this MSDN page on string handling in p/invoke

注意:我想链接中的示例都不是完全这种情况,所以这里是 C++ 原型

void DoSomething(__out BSTR *s);

【讨论】:

  • C++ 原型是什么样的?是不是: void DoSomething(char** returnedString); ? C++ 代码是原生的。
  • @Stephenosella: SysAllocStringSysFreeString 是当你 #include <windows.h> 时可用的本机函数。您可以使用 Unicode,也可以使用 SysAllocStringByteLen[MarshalAs(UnmanagedType.AnsiBStr)]
【解决方案2】:

除了 Jim Michel 的回答,我会创建一个辅助函数,例如

String FromCppString(IntPtr a_Pointer)
{
    String result = Marshal.PtrToStringAnsi(a_Pointer);
    FreeCppString(a_Pointer);
    return result;
}

FreeCppString 是另一个从 C++ 导出的函数,可以正确释放字符串。原始的 c++ 函数将根据需要分配尽可能多的字符串并将它们放入参数中。 C# 函数将使用 FromCppString() 来提取它们。

【讨论】:

    【解决方案3】:

    一种方法是将参数声明为ref IntPtr。所以:

    static extern void DoSomething(ref IntPtr returnedString);
    

    所以你调用它并得到一个字符串:

    IntPtr pstr;
    DoSomething(ref pstr);
    string theString = Marshal.PtrToStringAnsi(pstr);
    

    但是,请务必记住,返回的指针是由您的 C++ 代码分配的。如果你想释放它,你需要调用 C++ 代码来完成它。

    您可能还想查看Marshal.PtrToStringAuto 和其他类似功能。

    还要注意,这会将数据从指针复制到字符串。如果您想在适当的位置引用字符串,则必须使用 IntPtrMarshal 类,或者深入研究不安全代码和指针的奇妙世界。

    【讨论】:

    • 如果您必须与二进制 blob 通信,Marshal.PtrToStringAnsi 很好。如果你能控制双方,这绝不是一个好的选择。
    • @BenVoigt:明白。从 Q 中,我认为他没有控制权。
    • C++ 原型是什么样的?是不是: void DoSomething(int* returnedString); ? C++ 代码是原生的,不是托管的。
    • @Stephenosella:C++ 原型将是 void DoSomething(char* returnedString); 或者,如果您要返回 Unicode 字符串:TCHAR * returnedString);
    • 类似:std::string str = "快速棕色狐狸跳过懒狗";返回字符串 = 新字符[str.length() + 1]; str.copy(returnedString, str.length() - 1, 0);
    猜你喜欢
    • 2019-04-18
    • 1970-01-01
    • 2011-03-12
    • 2022-01-04
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    相关资源
    最近更新 更多