将可写 StringBuilder 数组从 C# 传递给 C++

Posted

技术标签:

【中文标题】将可写 StringBuilder 数组从 C# 传递给 C++【英文标题】:Pass writeable StringBuilder array to C++ from C# 【发布时间】:2014-07-18 15:13:34 【问题描述】:

我有一些非托管 C++ 动态库和 C# GUI 应用程序,正在使用它。我需要将已知大小的字符串数组传递给填充它的 C++ 库。还有最大字符串长度值。我正在尝试传递 StringBuilder 的数组,它本身具有合适的大小和合适的内部元素容量(又名缓冲区数组)。

// C++ library part    
#define MAX_STRING_LENGTH 200
const wchar_t* stringToPopulate = L"Hello, World";

MYDLL_API uint8_t __stdcall getSomeStrings(wchar_t** strings, uint64_t count) 
    for(auto i =0; i < count; ++i)
        // trivial string population
        wcsncpy(*(providers + i), stringToPopulate,
                min(wcslen(stringToPopulate), MAX_STRING_LENGTH ));     
    
    return 0;



// C# application part
[DllImport("myDllName.dll", CallingConvention = CallingConvention.StdCall)]
static extern Byte getSomeStrings(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] StringBuilder[] strings,
    UInt64 size
);

// calling
// create suitable string array
StringBuilder[] stringArrayToPass= new StringBuilder[desirableStringCount];         
for(ulong i =0; i < desirableStringCount; ++i) 
    stringArrayToPass[i] = new StringBuilder(maxStringLength);


res = getSomeStrings(stringArrayToPass, desirableStringCount); // exception

我在库 API 调用中遇到了 System.AccessViolationException 异常。出现此类问题的原因可能是什么?

【问题讨论】:

【参考方案1】:
   [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]

废话,大小由参数1给出,而不是0。完全删除它,pinvoke marshaller不需要任何帮助来确定数组的长度。它已经知道了,它使用了数组的 Length 属性。

    wcsncpy(*(providers + i), stringToPopulate,
            min(wcslen(stringToPopulate), MAX_STRING_LENGTH ));

wcsncpy 是一个邪恶的,邪恶的邪恶的功能。你通常会从 Microsoft C/C++ 编译器那里得到一个强烈的反对意见,警告你它是不安全的。不要忽略这样的警告。它通过做不可思议的事情来解决“字符串不适合”的问题,它不会零终止字符串。当发生这种情况时,调用者将总是出现故障,无可救药地在字符串末尾之外犯错。 AVE(几乎)是不可避免的,如果没有,它会读取垃圾。你使用它的方式,这将总是发生。

大概这是为了使函数安全的尝试,结果恰恰相反。调用者无法指定字符串缓冲区有多大的核心问题。所以你应该重写它以便它可以指定那个大小。例如:

 MYDLL_API int __stdcall getOneString(int index, wchar_t* buffer, size_t bufferLength)

现在它很简单而且总是安全的,客户端代码只是重复调用它,增加 index 参数。它不必猜测它需要传递多大的数组。而且您的 C 代码不必猜测缓冲区有多大。如果索引超出范围,则返回错误代码,以便调用者知道停止调用。如果字符串不适合,则另一种,以便客户端可以恢复。

【讨论】:

以上是关于将可写 StringBuilder 数组从 C# 传递给 C++的主要内容,如果未能解决你的问题,请参考以下文章

C# String StringBuilder相关

RxSwift 将可观察对象数组与对象数组结合

将可插入通道实现到 C# 接口

C# stringbuilder为啥高效

C# string 和 stringbuilder

如何通过 C# 将可空整数列的空值插入 SQL 表