【问题标题】:Referencing an object array in C# PInvoke在 C# PInvoke 中引用对象数组
【发布时间】:2016-11-02 20:03:44
【问题描述】:

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

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

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# 类的定义和签名

[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# 引用标识符,或者即使需要手动指针编组。如果你们中的某个人能指出我正确的方向,我将永远感激不尽。

【问题讨论】:

  • 您需要至少一种方法让 .NET 封送处理程序确定您传递的数组的大小。查看ManageAsUnmanagedType。顺便说一句,out intensitytype[] 在 C++ 中会转换为 intensitytype**
  • 这里有很多问题。 getResults 是静态的吗?看起来不像。不能 p/invoke 实例方法。我认为 C++ struct 应该映射到 C# 中的结构。您不希望在 C# 参数中出现 out。你想要[Out] intensitytype[] graphData。您需要将已分配缓冲区的长度从 C# 传递给 C++。您的 C++ 代码将数组的地址复制到一个值参数,因此在 C# 端看不到任何内容。当然,您需要复制数组的内容。很多问题需要解决。
  • 真相是,C# getResults 调用转到一个 c++ 接口,然后调用 CameraControl 类的 getResults 方法: __declspec(dllexport) void getResults(intensitytype* graphData) { MainControl::getInstance()-> getCamera()->getResults(graphData); }
  • 事实是,我现在不知道问题是什么。祝你好运。

标签: c# c++ arrays pinvoke pass-by-reference


【解决方案1】:

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

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

第二个错误是在 C# 方法签名中;这里需要的描述是“[Out]”,带括号,而不是简单的“out”(再次感谢 David)。

所以,最终结果:

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

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

这是函数调用的sn-p:

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

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

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

...你瞧,结构体数组被填满了。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-05
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多