pinvoke to函数返回字符串数组

时间:2014-03-02 09:59:50

标签: c# .net c pinvoke

我正在尝试使用以下签名进行pinvoke:

const char ** SWDLLEXPORT org_crosswire_sword_SWModule_parseKeyList(SWHANDLE hSWModule, const char *keyText);

返回一个字符串数组。

我从c

中有这个例子
const char **results = org_crosswire_sword_SWModule_parseKeyList(module, argv[2]);
while (results && *results) {
    printf("%s\n", *results);
    ++results;
}

我尝试过的pinvoke如下:

[DllImport(DLLNAME)]
public static extern IntPtr org_crosswire_sword_SWModule_parseKeyList(IntPtr hSWModule, string keyText);

使用它的代码:

public IEnumerable<string> ParseKeyList(string keyText)
{
    IntPtr keyListPtrs = NativeMethods.org_crosswire_sword_SWModule_parseKeyList(_handle, keyText);
    return NativeMethods.MarshalStringArray(keyListPtrs);
}

public static IEnumerable<string> MarshalStringArray(IntPtr arrayPtr)
{
    IntPtr ptr = Marshal.ReadIntPtr(arrayPtr);
    while(arrayPtr != IntPtr.Zero && ptr != IntPtr.Zero)
    {
        ptr = Marshal.ReadIntPtr(arrayPtr);
        string key = Marshal.PtrToStringAnsi(ptr);
        yield return key;
        arrayPtr = new IntPtr(arrayPtr.ToInt64() + 1);
    }
}

这适用于PtrToStringAnsi行的第一项和第二项的段错误。我做错了什么,以及正确的方法来解决这个问题。

1 个答案:

答案 0 :(得分:4)

C ++代码按如下方式递增指针:

++results;

将地址递增sizeof(*results),因为这就是C ++指针算法的工作原理。因此,假设sizeof(*results)等于4,就像32位机器上的情况一样。然后++results会将地址增加4。

现在,您的C#代码不同。指针是无类型的,编译器对底层数组元素类型一无所知。所以你的代码

arrayPtr = new IntPtr(arrayPtr.ToInt64() + 1);

将地址增加1.相反,您需要提供缺少的类型信息。像这样:

arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size);

最重要的是,你的循环实现不正确。您无法在正确的时间更新ptr。它应该是:

public static IEnumerable<string> MarshalStringArray(IntPtr arrayPtr)
{
    if (arrayPtr != IntPtr.Zero)
    {
        IntPtr ptr = Marshal.ReadIntPtr(arrayPtr);
        while (ptr != IntPtr.Zero)
        {
            string key = Marshal.PtrToStringAnsi(ptr);
            yield return key;
            arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size);
            ptr = Marshal.ReadIntPtr(arrayPtr);
        }
    }
}

您可能更愿意重新投射该方法,以便它只包含对Marshal.ReadIntPtr的一次调用的代码。

最后一点。 C ++函数看起来可能正在使用cdecl调用约定。您应该检查SWDLLEXPORT的定义是什么。只有在SWDLLEXPORT指定__stdcall时,您的p / invoke才会正确。