c# - 将可写 StringBuilder 数组从 C# 传递到 C++

标签 c# c++ marshalling

我有一些非托管的 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 异常。出现此类问题的原因可能是什么?

最佳答案

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

那是胡说八道,大小由参数 1 而不是 0 给出。只需将其完全删除,pinvoke 编码器不需要任何帮助来计算数组的长度。它已经知道,它使用数组的 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 代码不必猜测缓冲区有多大。如果索引超出范围,则返回错误代码,以便调用者知道停止调用。如果字符串不合适,则使用另一个,以便客户端可以恢复。

关于c# - 将可写 StringBuilder 数组从 C# 传递到 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24828391/

相关文章:

c++ - 为什么我会收到此代码的 -fpermissive 错误?

java - GSON:有没有办法在编码时返回两种不同的日期格式

c# - 防止使用默认构造函数

c# - 是否可以为整个 MVC 应用程序设置全局 RoutePrefix?

c# - 使用 FileSystemWatcher 监视文件创建并在删除前复制它

c++ - 为什么不包括 guard 或编译指示一旦工作?

c# - 在Unity中,如何使用脚本访问预制件的子组件?

c++ - 这个函数调用会被g++优化吗?

c# - 编码 IDispatch 时出现 COM Interop 灾难性故障 (0x8000FFFF)

c# - 编码指向结构数组的指针