2013-06-29 29 views
0

我被C#代碼中的字符串數據結構傳遞給C++ dll。如何使用PINVOKE中的字符串數據傳遞結構?

C++代碼

typedef struct 
{ 
    LPCSTR lpLibFileName; 
    LPCSTR lpProcName; 
    LPVOID pPointer1; 
    LPVOID pPointer2; 
} ENTITY, *PENTITY, *LPENTITY; 
extern "C" __declspec(dllexport) int Test(LPENTITY entryList, int size); 

int Test(LPENTITY entryList, int size) 
{ 
    for (int i = 0; i < size; i++) 
    { 
     ENTITY e = entryList[i]; 
     // the char* value doesn't get passed correctly. 
     cout << e.lpLibFileName << e.lpProcName << endl; 
    } 
    return 0; 
} 

C#代碼

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
private class Entity 
{ 
    public string lpLibFileName; 
    public string lpProcName; 
    public IntPtr pPointer1; 
    public IntPtr pPointer2; 
} 

[DllImport("cpp.dll")] 
private static extern int Test(
    [In, Out, MarshalAs(UnmanagedType.LPArray)]Entity[] entities, 
    int size); 

static void Main(string[] args) 
{ 
    var entries = new[] 
     { 
      new Entity 
       { 
        lpLibFileName = "comdlg32", 
        lpProcName = "PrintDlgExW", 
        pPointer1 = Marshal.GetFunctionPointerForDelegate(new PrintDlgEx(PrintDlgExCallback)), 
        pPointer2 = IntPtr.Zero, 
       }, 
      new Entity 
       { 
        lpLibFileName = "shell32", 
        lpProcName = "ShellAboutW", 
        pPointer1 = Marshal.GetFunctionPointerForDelegate(new ShellAbout(ShellAboutCallback)), 
        pPointer2 = IntPtr.Zero, 
       }, 
     }; 
    var ret = Test(entries, entries.Length); 
} 

的PInvoke的被觸發,但字符*像lpLibFileName和lpProcName數據不能正確地傳遞。我錯過了什麼?如何糾正?

謝謝。

+0

您在[DllImport]屬性中缺少必需的CallingConvention屬性。默認是Stdcall,但你的函數是Cdecl。 –

+0

是的,你是對的,我應該在C++代碼中使用stdcall而不是cdecl。但是實際上阻止了我的是,我在c#代碼中將結構定義爲'class',而不是'struct'。將它更改回結構後,一切正常。謝謝。 –

回答

0

您的代碼將C#類映射到本機結構。因爲C#類是一個引用類型,所以它將被編組爲一個引用。所以你的代碼傳遞一個引用數組,將其編組到本機端的指針數組中。

但本機代碼需要一個指向值類型的結構數組的指針。所以最簡單的解決方案是將Entity的聲明更改爲struct而不是class

,我可以看到的其他問題:出現

  1. 本機代碼中使用cdecl調用約定。您需要更改C#代碼以匹配。
  2. 您正在裝飾數組參數Out。您無法將對string字段的修改編組回到託管代碼。
  3. 您需要確保您保持活動,您將傳遞給GetFunctionPointerForDelegate的代表停止收集。
0

當傳遞像自定義結構數組這樣的參數時,在定義自己的數據結構時使用'struct'而不是'class'。將它更改回struct後,一切正常。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
private struct Entity 
{ 
    public string lpLibFileName; 
    public string lpProcName; 
    public IntPtr pPointer1; 
    public IntPtr pPointer2; 
}