什么是双*&;其指向非托管数组

本文关键字:数组 amp 什么 | 更新日期: 2024-10-18 12:39:44

我正在包装一个在c#中进行高质量采样率转换的c++dll,我不确定我应该为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);
    }

涉及最少副本数量的最佳方式是什么?

第2版:

这是当前的代码,它工作得很好:

    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);

什么是双*&;其指向非托管数组

@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类获得,这样才能固定数组。