2013-06-28 62 views
1

我有一個DLL導出函數返回一個float *,我想在我的C#代碼中使用它。我不知道如何編組我的float *,以便我可以在C#中安全地使用它。 所以,在我的C++ DLL,我宣佈:元帥float *到C#

static float* GetSamples(int identifier, int dataSize); 

在我的C#腳本,我有:

[DllImport ("__Internal")] 
public static extern float[] GetSamples (int identifier, int dataSize); 

的C++ GetSamples(INT,INT)分配內存,並返回一個指針t時的浮點數組。我如何聲明C#GetSamples來編組我的float數組,以及如何訪問數據(通過迭代或Marshal.Copy)? 另外,我可以從C#中刪除float *還是必須調用另一個C++函數來刪除分配的內存?

編輯: 所以這就是我到現在爲止所嘗試的。 首先,在C#的一面:

聲明:

[DllImport ("__Internal")] 
public static extern int GetSamples ([In, Out]IntPtr buffer,int length, [Out] out IntPtr written); 

試圖把它叫做:

IntPtr dataPointer = new IntPtr(); 
IntPtr outPtr; 
GetSamples(dataPointer, data.Length, out outPtr); 
for (var i = 0; i < data.Length; i++){ 
    copiedData[i] = Marshal.ReadByte(dataPointer, i); 
} 

然後在我的C++的lib:

int AudioReader::RetrieveSamples(float * sampleBuffer, size_t dataLength, size_t * /* out */ written) 
{ 
    float* mydata = new float[dataLength]; 

    //This is where I copy the actual data into mydata 

    memcpy(sampleBuffer, mydata, dataLength*sizeof(float)); 

    delete data; 
    return dataLength; 
} 

我不真的知道outPtr是什麼...我知道我有一些額外的複製步驟,我可以刪除,我只是想讓它現在工作。

+0

您是否控制該DLL的代碼? – usr

+0

是的,我確實控制了DLL的代碼 –

回答

5

所以這是一個有點複雜回答...

.NET不知道如何處理C++內存分配,所以無論返回float *充其量這是很危險的。此外,.NET內存模型基於COM,所以它基於CoTaskMemAlloc,而不是它真的幫助你。因此,這裏是我的建議:

int AudioReader::RetrieveSamples(
    float * sampleBuffer, 
    int dataLength, 
    int * /* out */ written) 
{ 
    // assuming mydata is already defined 
    if(sampleBuffer == NULL || dataLength == 0) 
    { 
     *written = sizeof(mydata); 
     return -1; 
    } 
    ZeroMemory(sampleBuffer, dataLength); 
    int toCopy = min(dataLength, sizeof(myData)); 
    //This is where I copy the actual data into mydata 

    memcpy(sampleBuffer, mydata, toCopy); 
    *written = toCopy; 
    return 0; 
} 
[DLLImport("__internal")] 
private static extern int GetSamples(
    [In, Out]IntPtr buffer, 
    [In] int length, 
    [Out] out int written); 

float[] RetrieveFloats() 
{ 
    int bytesToAllocate = 0; 
    GetSamples(IntPtr.Zero, 0, out bytesToAllocate); 
    if(bytesToAllocate == 0) 
     return null; 
    int floatCount = bytesToAllocate/ sizeof(float); 
    float[] toReturn = new float[floatCount]; 
    IntPtr allocatedMemory = Marshal.AllocHGlobal(bytesToAllocate); 

    int written = 0; 
    if(GetSamples(allocatedMemory, bytesToAllocate, out written) != -1) 
    { 
     floatCount = written/sizeof(float); 
     Marshal.Copy(allocatedMemory, toReturn, 0, floatCount); 
    } 
    Marshal.FreeHGlobal(allocatedMemory); 
    return toReturn; 
} 

傳遞零BufferLength中會返回緩衝區所需要的空間,然後可將其進行分配,並通過

您將需要。在C#中爲緩衝區分配內存,不能在C++中分配內存

+0

寫什麼?我嘗試了這個,我仍然遇到麻煩。我聲明一個IntPtr,我作爲sampleBuffer傳遞,並使用C++中的memcpy爲其分配一些數據 - > memcpy(sampleBuffer,mydata,bufferLength * sizeof(float));然後在C#端,我嘗試使用它讀取它Marshal.ReadByte(dataPointer,i);,但我得到一個錯誤的訪問錯誤。當談到這個時,我是一個新手,任何提示?我會更詳細地更新我的問題。 –

+1

IntPtr擁有一個對你將分配的C#數組的引用,它本身就是一個指針保持器。您將調用pinvoke兩次,一次獲取要分配的數組大小(如果您永遠不會分配那麼多內存,則可以寫入int而不是intptr,但如果使用C#編寫,則將其更改爲int C++也一樣),分配的大小將以書面形式返回。第二次調用將分配給緩衝區intptr的數組,長度分配給長度intptr – Mgetz

+1

@DavidMenard我已經編輯我的示例以更易於使用,但提醒您需要在C#中執行內存分配而不是C++ – Mgetz