我正在尝试使用以下签名进行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行的第一项和第二项的段错误。我做错了什么,以及正确的方法来解决这个问题。
答案 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才会正确。