Double *&的正确P / Invoke签名是什么?哪个指向一个非托管数组?

时间:2013-12-17 17:18:39

标签: c# c++ arrays pinvoke marshalling

我正在包装一个c ++ dll,它在c#中进行高质量的采样率转换,我不确定我应该使用什么类型的 op0 参数。 C ++包装器会像这样调用它:

int _cdecl process(double* const ip0, int l, double*& op0)

文档说明了参数:

“@ param [out] op0此变量接收指向重采样数据的指针。 该指针可以指向“ip0”输入缓冲区内的地址,或者指向 *此对象的内部缓冲区。建议在实时应用程序中使用 将此指针传递给下一个输出音频块并使用任何数据 在调用之前先从前一个输出音频块离开 process()函数再次出现。返回时“op0”指向的缓冲区可能 由重新采样器拥有,因此不应被调用者释放。“

我想做的是以下内容:

    [DllImport("r8bsrc.dll", EntryPoint="process", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Process([in] double[] ip0,
                                     int length,
                                     [out] double[] op0);

但我很确定这不起作用,因为编组人员无法知道 op1 背后的内存有多大,或者我错了?

所以我想我必须自己将op1背后的值复制回托管数组。也许:

    [DllImport("r8bsrc.dll", EntryPoint="process", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Process([in] double[] ip0,
                                     int length,
                                     out IntPtr op0); //or do i need out double* ?

然后用:

再次换行
    private IntPtr FOutBufferPtr; //reuse it as recommeded
    public int Process(double[] input, out double[] output)
    {
        var outSamples = R8BrainDLLWrapper.Process(input, input.Length, out FOutBufferPtr);         
        output = new double[outSamples];
        Marshal.Copy(FOutBufferPtr, output, 0, outSamples);
    }

涉及最少份数的最佳方式是什么?

EDIT2:

这是当前的代码,它完美运行:

    public int Process(double[] input, ref double[] output)
    {
        //pin the input during process
        var pinnedHandle = GCHandle.Alloc(input, GCHandleType.Pinned);

        //resample
        var outSamples = R8BrainDLLWrapper.Process(FUnmanagedInstance, pinnedHandle.AddrOfPinnedObject(), input.Length, out FOutBufferPtr);

        //copy to output array
        if(output.Length < outSamples)
            output = new double[outSamples];

        Marshal.Copy(FOutBufferPtr, output, 0, outSamples);

        //free pin
        pinnedHandle.Free();

        return outSamples;
    }

签名现在是:

    [DllImport("r8bsrc.dll", EntryPoint="r8b_process", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Process(IntPtr instance,
                                     IntPtr ip0,
                                     int length,
                                     out IntPtr op0);

1 个答案:

答案 0 :(得分:2)

  

@param [out] op0

     

此变量接收指向重采样数据的指针。该指针可以指向“ip0”输入缓冲区内的地址,或指向*此对象的内部缓冲区。在实时应用程序中,建议将此指针传递给下一个输出音频块,并在再次调用process()函数之前首先使用先前输出音频块留下的任何数据。返回时“op0”指向的缓冲区可能由重新采样器拥有,因此调用者不应释放它。

这会立即对ip0提出约束。您必须安排ip0指向的缓冲区在函数调用结束后保持稳定。这意味着你必须在调用函数之前将其固定。这反过来暗示必须将其声明为IntPtr

对于op0,这指向重采样器拥有的内存或ip0输入缓冲区内的位置。因此,您将不得不使用IntPtr,这次是out参数。

因此,声明必须是:

[DllImport("r8bsrc.dll", EntryPoint="process", 
    CallingConvention = CallingConvention.Cdecl)]
public static extern int Process(IntPtr ip0, int length, out IntPtr op0);

如上所述,您在ip0中传递的指针必须使用GCHandle类获取,以便您可以固定数组。