2014-03-02 79 views
1

我想的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

你正在增加'arrayPtr' 1,它將它移動1個字節。但是你想讓它指向下一個數組元素,所以你應該添加'IntPtr.Size'。 – Dirk

回答

3

的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纔是正確的。

+0

我每次增加它後都不需要檢查IntPtr.Zero的arrayPtr嗎? – trampster

+0

@tramp不需要。只需一個檢查就可以了。 –

+0

我希望我可以多次upvote。一次用於IntPtr.Size,一次for循環不正確,一次用於調用約定。你們哪裏對三個都是正確的。 – trampster