通过 P/Invoke 从 C# 将可写字符串数组传递给 C++
Pass writable string array to C++ from C# via P/Invoke
我正在尝试将可写(预分配)字符串数组从 C# 传递到 C++ dll。它因 "Access violation writing location".
而失败
C++:
int StringArrayTest(size_t numberOfStrings, char **valueOut, size_t maxStringLength) {
for (unsigned int i = 0; i < numberOfStrings; i++) {
auto str = std::to_string(i); //Create simple string
strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
}
return 0;
}
C#:
[DllImport("MyDLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int StringArrayTest(ulong arraySize, [MarshalAs(UnmanagedType.LPArray)]StringBuilder[] valuesOut, ulong maxStringLength);
public string[] GetTestStrings(ulong arraySize, ulong maxStringLength) {
var stringBuilder = new StringBuilder[(int)arraySize];
for (var i = 0; i < (int)arraySize; i++) {
stringBuilder[i] = new StringBuilder((int)maxStringLength);
}
var result = StringArrayTest(arraySize, stringBuilder, maxStringLength);
var returnValues = new string[arraySize];
for (var i = 0; i < (int)arraySize; i++) {
returnValues[i] = result.ToString();
}
return returnValues;
}
请注意,使用单个字符串(char * 签名并传递单个 StringBuilder)可以按预期工作。
C++
extern "C" {
__declspec(dllexport) int StringArrayTest(size_t numberOfStrings, char*** ptrValueOut, size_t maxStringLength)
{
char** valueOut = *ptrValueOut;
for (unsigned int i = 0; i < numberOfStrings; i++) {
auto str = std::to_string(i); //Create simple string
strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
}
return 0;
}
}
将声明从 ... char** valueOut, ...
更改为 ... char*** ptrValueOut, ...
C#:
[DllImport("StringArrayTest", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int StringArrayTest(int arraySize, ref IntPtr valuesOut, int maxStringLength);
public static string[] GetTestStrings(int arraySize, int maxStringLength)
{
var buffer = Marshal.AllocCoTaskMem(IntPtr.Size * arraySize);
var strings = new IntPtr[arraySize];
for (int i = 0; i < strings.Length; i++)
strings[i] = Marshal.AllocCoTaskMem(maxStringLength);
Marshal.Copy(strings, 0, buffer, strings.Length);
StringArrayTest(arraySize, ref buffer, maxStringLength);
var returnValues = new string[arraySize];
for (var i = 0; i < arraySize; i++)
{
returnValues[i] = Marshal.PtrToStringAnsi(strings[i]);
Marshal.FreeCoTaskMem(strings[i]);
}
Marshal.FreeCoTaskMem(buffer);
return returnValues;
}
该代码旨在说明该过程,可能并非没有错误。内存通过 Marshal
方法分配。指向内存的指针随后作为对 StringArrayTest
函数的引用传递。
我正在尝试将可写(预分配)字符串数组从 C# 传递到 C++ dll。它因 "Access violation writing location".
而失败C++:
int StringArrayTest(size_t numberOfStrings, char **valueOut, size_t maxStringLength) {
for (unsigned int i = 0; i < numberOfStrings; i++) {
auto str = std::to_string(i); //Create simple string
strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
}
return 0;
}
C#:
[DllImport("MyDLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int StringArrayTest(ulong arraySize, [MarshalAs(UnmanagedType.LPArray)]StringBuilder[] valuesOut, ulong maxStringLength);
public string[] GetTestStrings(ulong arraySize, ulong maxStringLength) {
var stringBuilder = new StringBuilder[(int)arraySize];
for (var i = 0; i < (int)arraySize; i++) {
stringBuilder[i] = new StringBuilder((int)maxStringLength);
}
var result = StringArrayTest(arraySize, stringBuilder, maxStringLength);
var returnValues = new string[arraySize];
for (var i = 0; i < (int)arraySize; i++) {
returnValues[i] = result.ToString();
}
return returnValues;
}
请注意,使用单个字符串(char * 签名并传递单个 StringBuilder)可以按预期工作。
C++
extern "C" {
__declspec(dllexport) int StringArrayTest(size_t numberOfStrings, char*** ptrValueOut, size_t maxStringLength)
{
char** valueOut = *ptrValueOut;
for (unsigned int i = 0; i < numberOfStrings; i++) {
auto str = std::to_string(i); //Create simple string
strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
}
return 0;
}
}
将声明从 ... char** valueOut, ...
更改为 ... char*** ptrValueOut, ...
C#:
[DllImport("StringArrayTest", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int StringArrayTest(int arraySize, ref IntPtr valuesOut, int maxStringLength);
public static string[] GetTestStrings(int arraySize, int maxStringLength)
{
var buffer = Marshal.AllocCoTaskMem(IntPtr.Size * arraySize);
var strings = new IntPtr[arraySize];
for (int i = 0; i < strings.Length; i++)
strings[i] = Marshal.AllocCoTaskMem(maxStringLength);
Marshal.Copy(strings, 0, buffer, strings.Length);
StringArrayTest(arraySize, ref buffer, maxStringLength);
var returnValues = new string[arraySize];
for (var i = 0; i < arraySize; i++)
{
returnValues[i] = Marshal.PtrToStringAnsi(strings[i]);
Marshal.FreeCoTaskMem(strings[i]);
}
Marshal.FreeCoTaskMem(buffer);
return returnValues;
}
该代码旨在说明该过程,可能并非没有错误。内存通过 Marshal
方法分配。指向内存的指针随后作为对 StringArrayTest
函数的引用传递。