在C#PInvoke中引用对象数组

问题描述:

我正在构建一个使用C#GUI和本机C ++逻辑dll的光谱应用程序.我正在尝试使dll填充从C#端通过引用传递的简单C ++结构的数组.但是,当我尝试打印[应该填充的]数组元素时,我得到了System.NullReferenceExceptions且该数组元素在内存中被标记为null.

I'm building a spectrometry application which uses a C# GUI and a native C++ logical dll. I'm trying to make the dll fill an array of simple C++ structs passed by reference from the C# side. However, when I try to print the [supposed to be filled] array elements, I'm getting System.NullReferenceExceptions and the array elements are marked as null in memory.

这是C ++结构定义和方法实现:

Here is the C++ struct definition and method implementation:

typedef struct intensitytype {
    unsigned short usEchelleOrder;      // echelle order
    unsigned short usPixel;             // horizontal camera pixel index (unbinned !!)
    double dIntensity;                  // intensity
    double dWaveLen;                    // wave length [nm]
} intensitytype;


void CameraControl::getResults(intensitytype* graphData)
{
    graphData = _spectroData; // _spectroData is a pointer to a dynamic intensitytype array.
}

这是C#类的定义和签名

Here are the C# class definition and signature

[StructLayout(LayoutKind.Sequential)]
    public class intensitytype
{
    public ushort usEchelleOrder = 0;      // echelle order
    public ushort usPixel = 0;             // horizontal camera pixel index (unbinned !!)
    public double dIntensity = 0;                  // intensity
    public double dWaveLen = 0;                    // wave length [nm]
}

 [DllImport(@"Elemission.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void getResults(bool freeData, out intensitytype[] graphData);

我不确定在这种情况下是否需要什么类型的C#参考标识符,甚至不确定是否需要手动编组指针.如果你们中的一个可以指出我正确的方向,我将永远感激不已.

I'm not sure what type of C# reference identifier is needed in this instance, or even if manual pointer marshalling is required. If one of you guys can point me in the right direction, I'd be forever grateful.

我终于找到了解决问题的方法,并将其发布给需要澄清的人:

I finally found the solution to my problems, and am posting this for anyone who would need some clarification:

首先,正如David指出的那样,简单地将引用的参数视为指针并为其分配数组的地址是行不通的.您必须将整个数组内容复制到引用的数组中.轻松修复.

First, as David pointed out, simply treating the referenced argument as a pointer and assigning it the array's address does not work. You have to copy the entire array contents into the referenced array. Easily fixed.

第二个错误是C#方法签名;此处需要的描述是带有括号的"[Out]",而不是简单的"out"(再次感谢David).

The second mistake was in the C# method signature; the descripton that was needed here was "[Out]", with the brackets, compared to simply "out" (Once again, thanks David).

因此,最终结果:

结构不会更改,但是C#中的函数签名会更改:

The structs do not change, but the function signature in C# does:

[DllImport(@"Elemission.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void getResults([Out] intensityType[] graphData);

这是函数调用的代码段:

and here is a snippet of the function call:

int arraySize;
DllMethods.executeAcquisition(out arraySize); // arraySize is the Struct array length
intensityType[] resultData = new intensityType[arraySize];
DllMethods.getResults(resultData);

与此同时,在C ++中,C#调用被包装程序接收并传递给成员函数...

Meanwhile in C++, the C# call is received by the wrapper and passed to the member function...

__declspec(dllexport) void getResults(intensitytype* graphData)
{
    MainControl::getInstance()->getCamera()->getResults(graphData);
}

...然后瞧瞧,结构数组已填充.

...and lo and behold, the struct array is filled.

void CameraControl::getResults(intensitytype* graphData)
{
    for (int i = 0; i < _spectroDataLength; i++)
        graphData[i] = _spectroData[i];
}