2013-06-13 49 views
2

我開始爲這個問題而努力。我搜索並尋找幫助,而我嘗試過的一切似乎都不起作用。我顯然做錯了什麼。將包含未知大小的int數組的結構從c#傳遞給c並返回

反正 - 我有一個C#結構定義爲:

public struct TestStruct 
[StructLayout(LayoutKind.Sequential)] 
{ 
    public int num; 
    public IntPtr intArrayPtr; 
} 

,並在我的C#代碼的主體,我有:

public class Testing 
{ 
    [DllImport("testing.dll")] 
    static extern void Dll_TestArray(out IntPtr intArrayPtr); 

    public GetArray() 
    { 
     IntPtr structPtr = IntPtr.Zero; 
     TestStruct testStruct; 
     structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(testStruct)); 
     Marshal.StructureToPtr(testStruct, structPtr, false); 

     Dll_TestArray(structPtr); 

     testStruct = (TestStruct) Marshal.PtrToStructure(structPtr, typeof(TestStruct)); 
    } 
} 

所以現在對C++的一部分。具有結構開始:

struct TestStruct 
{ 
    public: 
    int num; 
    int* intArray; 
} 

現在的功能:

extern "C" __declspec(dllexport) void Dll_TestArray(TestStruct *&testStruct) 
{ 
    int num = 15; 
    testStruct->num = num; 
    testStruct->intArray = new int[num]; 

    for (int i = 0; i < num; i++) 
    testStruct->intArray[i] = i+1; 
} 

所以 - 我遇到的問題是,當我得到的結構返回到C#,我的結構是不是如何應該。我可以看到num字段已被正確填寫:它顯示了15.但是,它仍然被設置爲零的IntPtr。在C++中完成的數組創建沒有傳遞給c#。

如果我嘗試退後一步,回到dll函數,我可以看到該數組已創建好,仍然保留該信息。

因此,結構中的c#intptr沒有被設置爲在C++中創建的指針(如果有意義的話)。

所以我的問題真的是 - 如何讓這個工作正確?

我希望能夠從dll中獲得一個包含我需要的所有信息的結構。就是說,在這個例子中,元素的數量和指向數組的指針。這樣,我就可以在intptr上做一個Marshal.Copy來獲取數組。

如果還有另一種方法可以做到這一點,我很樂意做到這一點。我已經嘗試了幾種方法,但無濟於事。這包括試圖在c#下述結構(其中包含int數組,而不是IntPtr的):

public struct TestStruct 
{ 
    public int num; 

    // i have tried various methods to marshal this- eg: 
    // [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I4] 
    public int[] intArray; 
} 

,我也已經嘗試通過引用本身傳遞結構,而不是一個IntPtr。 對這個問題的任何幫助將大量讚賞。我無法更改C++代碼,但可以更改c#代碼。

+0

這是你的真實密碼?什麼是'structPtr。假'和'TestStruct *&testStruct'?另外,如果'intArrayPtr'被定義爲'out',爲什麼在調用'Dll_TestArray'之前先填充結構?請發佈實際的代碼(剪切和粘貼是一個好主意)。 –

+0

這是非常真實的代碼,是的。這不是特別有用,我只是想獲得一些非常基本的工作,所以我可以在更先進的東西上工作。我編輯了一些錯別字。 我之前沒有使用out參數,但是當我讀到某個地方時,我開始這樣做,這將是一件好事。因此,如果我們使用out參數,那就是爲什麼我使用TestStruct *&testStruct 我已經重命名了一些變量,以便它們對您更有意義。 – user2477533

+0

我在c#中沒有經驗,因此對任何愚蠢的錯誤表示歉意。 – user2477533

回答

1

首先改變C++代碼只使用一個級別的間接:

extern "C" __declspec(dllexport) void Dll_TestArray(TestStruct &testStruct) 
{ 
    const int num = 15; 
    testStruct.num = num; 
    testStruct.intArray = new int[num]; 
    for (int i=0; i<num; i++) 
     testStruct.intArray[i] = i+1; 
} 

在你想要這個C#的一面:

public struct TestStruct 
{ 
    public int num; 
    public IntPtr intArray; 
} 

[DllImport("testing.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern void Dll_TestArray(out TestStruct testStruct); 

public GetArray() 
{ 
    TestStruct testStruct; 
    Dll_TestArray(out testStruct); 
    int[] arr = new int[testStruct.num]; 
    Marshal.Copy(testStruct.intArray, arr, 0, arr.Length); 
    // need to call back to DLL to ask it to deallocate testStruct.intArray 
} 

您還需要導出的函數這將釋放您使用C++堆分配的數組。否則,你會泄漏它。

也許更簡單的方法是改變設計讓調用者分配緩衝區。

+0

非常感謝。至於釋放記憶等,我很高興照顧這一點。我只是在努力取回我想要的數據。 – user2477533

相關問題