簡體   English   中英

P / Invoke包含C#和C ++之間的雙編組數據數組

[英]P/Invoke with arrays of double - marshalling data between C# and C++

我已經在這里這里閱讀了C ++ Interop與P / Invoke的各種MSDN頁面,但我仍然感到困惑。

我有一些大型的雙打數組,我需要進入本機代碼,以及一些需要返回的結果數組。 我事先並不知道輸出數組的大小。 為簡單起見,我將在示例中僅使用單個數組。 該平台是x64; 我讀到在32位和64位環境之間編組內部是完全不同的,所以這可能很重要。

C#

    [DllImport("NativeLib.dll")]
    public static extern void ComputeSomething(double[] inputs, int inlen, 
        [Out] out IntPtr outputs, [Out] out int outlen);

    [DllImport("NativeLib.dll")]
    public static extern void FreeArray(IntPtr outputs);

    public void Compute(double[] inputs, out double[] outputs)
    {           
        IntPtr output_ptr;
        int outlen;
        ComputeSomething(inputs, inputs.Length, out output_ptr, out outlen);

        outputs = new double[outlen];
        Marshal.Copy(output_ptr, outputs, 0, outlen);

        FreeArray(output_ptr);
    }

C ++

extern "C" 
{
    void ComputeSomething(double* inputs, int input_length, 
        double** outputs, int* output_length)
    {
    //...
    *output_length = ...;
    *outputs = new double[output_length];
    //...
    }

    void FreeArray(double* outputs)
    {
        delete[] outputs;
    }
}

它有效,也就是說,我可以讀出我在C ++端寫入數組的雙打。 不過,我想知道:

  • 這真的是使用P / Invoke的正確方法嗎?
  • 我的簽名不是很復雜嗎?
  • 可以更有效地使用P / Invoke來解決這個問題嗎?
  • 我相信我讀過可以避免對內置類型的單維數組進行編組。 Marshal.Copy有辦法嗎?

請注意,我們有一個有效的C ++ / Cli版本,但是在第三方庫代碼中存在與導致崩潰的本地靜態有關的一些問題。 微軟將此問題標記為WONTFIX ,這就是我尋找替代品的原因。

沒關系。 完全沒有返回錯誤代碼的方法非常糟糕,當數組很大並且程序內存耗盡時會受到傷害。 你得到的嚴重崩潰是非常不可知的。

復制數組並明確釋放它們的需要當然不會贏得任何獎項。 您可以通過讓調用者將指針傳遞給自己的數組來解決這個問題,然后編寫元素。 但是,您需要一個協議讓調用者確定數組需要多大的數量,這需要調用該方法兩次。 第一個調用返回所需的大小,第二個調用完成工作。

樣板示例如下:

[DllImport("foo.dll")]
private static int ReturnData(double[] data, ref int dataLength);

以及樣本用法:

int len = 0;
double[] data = null;
int err = ReturnData(data, ref len);
if (err == ERROR_MORE_DATA) {    // NOTE: expected
    data = new double[len];
    err = ReturnData(data, len);
}

無需復制,無需釋放內存,好事。 如果本地代碼沒有注意傳遞的len ,那么本機代碼可能會損壞GC堆,這不是一件好事。 但當然容易避免。

如果將確定輸出長度的代碼與填充輸出的代碼分開是可行的,那么您可以:

  • 導出返回輸出長度的函數。
  • 從C#代碼調用它,然后分配輸出緩沖區。
  • 再次調用非托管代碼,這次要求它填充輸出緩沖區。

但我假設你拒絕了這個選項,因為它是不切實際的。 在這種情況下,您的代碼是解決問題的完美合理方式。 事實上,我會說你做得很好。

修復調用約定不匹配后,代碼在x86中的工作方式相同。 在C ++方面,調用約定是cdecl ,但在C#方面它是stdcall 這與x64無關,因為只有一個調用約定。 但是在x86下這將是一個問題。

一些評論:

  • 你並不需要使用[Out]以及out 后者意味着前者。
  • 您可以通過分配共享堆來避免導出deallocator。 例如,C ++端的CoTaskMemAlloc ,然后在C#端釋放Mashal.FreeCoTaskMem

如果您事先知道數組大小,則可以編寫一個C ++ / CLI DLL,它將托管數組作為參數,將其固定,並在它獲得的固定指針上調用本機C ++ DLL。

但如果只是輸出,我沒有看到沒有副本的任何版本。 您可以使用SAFEARRAY,因此P / Invoke會復制而不是您,但這就是全部。

暫無
暫無

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

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