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

我有一些非托管的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.AccessViolationExceptionexception。 这个问题可能是什么原因?

  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 

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

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

wcsncpy是邪恶的, 邪恶的邪恶的function。 您通常会收到Microsoft C / C ++编译器的强烈异议,并警告您它不安全。 不要忽视这样的警告。 它通过做不可想象的问题解决了“字符串不适合”的问题,它不会对字符串进行零终止。 当发生这种情况时,呼叫者将始终发生故障,无法在字符串末尾浮躁。 AVE(几乎)是不可避免的,如果没有,则阅读垃圾。 你使用它的方式总会发生。

大概这是试图使function安全,它完成了相反的事情。 核心问题是调用者无法指定字符串缓冲区的大小。 因此,您应该重写它,以便它可以指定该大小。 例如:

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

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