c# - 通过 P/Invoke 从 C# 将可写字符串数组传递给 C++

标签 c# c++ pinvoke

我正在尝试将可写(预分配)字符串数组从 C# 传递到 C++ dll。它因“访问冲突写入位置”而失败。

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# - 通过 P/Invoke 从 C# 将可写字符串数组传递给 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58661671/

相关文章:

c# - 为什么此互操作会导致 .NET 运行时崩溃?

c# - 如何调用 AnimateWindow 函数来为外部窗口/进程设置动画

c# - Windows 10 UWP AppService,package.appxmanifest 问题

.net - 如何在 Windows 中从非托管 C++ 代码调用托管 .NET 代码,反之亦然?

c# - 如何以 32/64 位兼容方式在 C# 中定义 MAPINAMEID 结构?

c++ - C Web 服务器和 Chrome 开发工具问题

c++ - 制作一个可滚动的小部件区域

c# - ASP.NET Identity 中使用的哈希方法

c# - 我应该如何解决以下错误消息? (111)连接被拒绝 : AH00957: HTTPS: attempt to connect to 127. 0.0.1 :5001 (127. 0.0.1) 失败

c# - 如何使用表达式将方法调用附加到类似构建器的调用链?