簡體   English   中英

在沒有Marshal.Copy或Unsafe的情況下在C ++中更新C#數組

[英]Updating a C# array inside C++ without Marshal.Copy or Unsafe

我想更新在C#中創建的數組,然后將指向該數組的指針傳遞給C ++,然后讓C ++填充索引,以供在C#中使用。 現在,我正在使用Marshal.Copy()完成此任務,但我想避免可能不必要的復制,並返回c ++釋放數組。 這有可能嗎?

這些數組是浮點數和整數,用於幾何網格數據。

我當前的用法(正在工作,而不是我想使用的)C#

    IntPtr intptr=new IntPtr();
    int count = 0;
    PopulateArray(ref intptr, ref count);
    float[] resultVertices = new float[count];
    Marshal.Copy(intptr, resultVertices, 0, count);

C ++

extern "C" __declspec(dllexport) bool PopulateArray(float** resultVerts, int* resultVertLength){

    *resultVerts = new float[5]{0.123f, 3.141529f, 127.001f, 42.42f, 0};
    int myX = 5;
    *resultVertLength = myX;
    return true;
}

使C ++代碼更新托管C#數組的唯一安全方法是固定該數組。 否則,垃圾收集器有可能在本機代碼運行時嘗試移動數組。 您可以使用GCHandle對象執行此操作。

int count = 5; 
float[] resultVertices = new float[count];

GCHandle handle = GCHandle.Alloc(resultVertices, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();

PopulateArray(address, count);

handle.Free();

也可以使用不安全的代碼來完成,這樣閱讀和記憶起來會更直觀:

int count = 5; 
float[] resultVertices = new float[count];
unsafe 
{
    fixed(float* ptr = resultVertices)
    {
        PopulateArray(ptr, count);
    }
}

另一種選擇是讓C#分配一個非托管內存塊並將其傳遞給C ++方法。 這比您所做的要好,因為您沒有將分配/取消分配的責任放在C ++庫代碼中,而是將所有這些都保留在C#中。 我知道您想避免遇到麻煩,但是有時復制比固定對象更有效,但這取決於它們的大小。 我建議您進行性能測試以確定最適合您的情況。

int count = 5; 
float[] resultVertices = new float[count];
IntPtr unmanagedMemory = Marshal.AllocHGlobal(count * Marshal.SizeOf(typeof(float)));
PopulateArray(unmanagedMemory, count);
Marshal.Copy(unmanagedMemory, resultVertices, 0, count);

在所有這些情況下,您都應該將C ++代碼設置為如下所示:

extern "C" __declspec(dllexport) bool PopulateArray(float* resultVerts, int vertLength)
{
    resultVerts[0] = 0.123f;
    // fill out the rest of them any way you like.
    return true;
}

如果數組大小是可變的,那么我建議使用單獨的C ++方法來計算大小並返回大小,而不是讓C ++方法分配內存。

如果您願意允許C#分配數組(可能是一個更安全的選擇),則可以使用標准PInvoke屬性來執行此行為。

將您的C ++聲明更改為:

extern "C" __declspec(dllexport) bool PopulateArray(float resultVerts[], int resultVertLength)

和您的C#聲明:

[DllImport("Win32Library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool PopulateArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] resultVerts, int resultVertLength);

然后,您在C#方面的用法將更改為:

var resultVertices = new float[5];
PopulateArray(resultVertices, resultVertices.Length);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM