访问库函数时 char** 的编组问题

Marshaling issue with char** while accessing a library function

我正在将用 C++/VisualStudio 编写的现有 library/DLL 移植到 codeblocks/GCC。 Windows中的DLL已在C#、C、C++、Python、Delphi、Java、VB.NET、LabVIEW等环境中测试,运行良好稳定. 但是,将它移植到 Linux 时,我在从 Mono/C# 测试它时遇到问题,而它在 FreePascal 和 Python.

上运行良好

问题的根源是检测某些设备的函数和 returns 检测到的设备数量的整数,以及设备所在路径的列表(ASCII 字符串数组)定位,通过参数:

int DetectDevices(char ** DevicePaths);

我在库中复制结果的方式是:

i=0;
for (vector<string>::iterator it=lstDetected.begin(); it!=lstDetected.end(); ++it)
    strcpy(DevicePaths[i++], (*it).c_str());

在 C# 中,我使用以下代码声明外部函数:

[DllImport(LIBRARY_PATH)]
public static extern int DetectDevices([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] DevicePaths);

我想指出,在调用函数并获取返回值之前,我实际上在 C# 中保留了一些内存 space:

string[] DevicePaths = new string[50];
for (int i=0; i<DevicePaths.Length; i++)
    DevicePaths[i] = new string('[=13=]', 255);

这在 Windows/VisualStudio 中运行良好,但在 Linux/Mono 中运行良好。

用 LPWStr 替换 LPStr 并执行调试,表明字符应该到达,但接收到的等效 ASCII 代码对于 LPStr 中的所有字符为 0,在 LPWStr 中为 63。

我认为这可能与字符编码相关的问题有关,但我可能错了。

有没有人知道这里可能出了什么问题? 非常感谢您的帮助!

尝试使用 LPTStr 将字符串转换为平台的默认字符串编码。对于 Mono,这是 UTF-8

  • UnmanagedType.LPStr => 安西
  • UnmanagedType.LPWStr => unicode
  • UnmanagedType.LPTStr => 平台默认值

还有其他 UnmanagedType 也可以帮助... BStr 也许...?

如果这没有帮助,请考虑使用自定义封送处理或手动封送处理。

这个documentation还不错

我终于设法找到了编组问题的解决方案。

而在 Windows (.NET framework) & Visual Studio 中,允许通过以下方式返回 C 字符串数组(char 数组的数组)参数:

[DllImport(LIBRARY_PATH)]
public static extern int DetectDevices([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] DevicePaths);

出于某种原因,这在 Linux / Mono 中不起作用,我不得不使用以下方法:

public static extern int DetectDevices(IntPtr[] pDevicePaths);

然后,在代码中使用以下方法检索每个字符串:

const int VCOUNT = 50;
const int MAXSTRINGSIZE = 255;
string[] MyValues = new string[VCOUNT];

IntPtr[] ptr = new IntPtr[VCOUNT];
for (int i = 0; i < ptr.Length; i++) ptr[i] = Marshal.AllocCoTaskMem(MAXSTRINGSIZE);

int n = DetectDevices(ptr);
if (n > 0) {
    for (int i = 0; i < n; i++) {
        StringBuilder sb = new StringBuilder(Marshal.PtrToStringAnsi(ptr[i]));
        MyValues[i] = sb.ToString();
    }
}

这是一种更 C/C++ 的风格,它增加了复杂性但也很有意义。 所以我认为要么 Mono 没有完全实现,要么某处存在错误。

如果有人有更好的解决方案,我将不胜感激。