C到C#数组编组中的矛盾行为

Sor*_*ati -1 c# c++ pinvoke marshalling

我有两个几乎相同代码的例子.我可以从C#中检索一个数据,但我没有为另一个人提供正确的数据.他们来了:

效果很好

C++部分:

__declspec(dllexport) void** enumerateDevices(int *dIsize){
    Array<DeviceInfo> dIArray;
    Framewoek::enumerateDevices(&dIArray);
    *dIsize = dIArray.getSize();
    DeviceInfo dP[255];
    for (int i = 0; i < dIArray.getSize(); i++)
        dP[i] = dIArray[i];
    void* p = dP;
    return &p;
}
Run Code Online (Sandbox Code Playgroud)

C#部分:

    [DllImport("Wrapper.dll")]
    static extern IntPtr enumerateDevices(out int devicesSize);
    public static DeviceInfo[] EnumerateDevices()
    {
        int devicesSize;
        IntPtr arrayPointer = enumerateDevices(out devicesSize);
        IntPtr[] array = new IntPtr[devicesSize];
        Marshal.Copy(arrayPointer, array, 0, devicesSize);
        DeviceInfo[] arrayObjects = new DeviceInfo[devicesSize];
        for (int i = 0; i < devicesSize; i++)
            arrayObjects[i] = new DeviceInfo(array[i]);
        return arrayObjects;
    }
Run Code Online (Sandbox Code Playgroud)

不要按预期工作

C++部分:

__declspec(dllexport) void** SensorInfo_getSupportedVideoModes(SensorInfo* si, int *dIsize){
    const Array<VideoMode>& dIArray = si->getSupportedVideoModes();
    *dIsize = dIArray.getSize();
    VideoMode dP[255];
    for (int i = 0; i < dIArray.getSize(); i++)
        dP[i] = dIArray[i];
    void* p = dP;
    return &p;
}
Run Code Online (Sandbox Code Playgroud)

C#部分:

    [DllImport("Wrapper.dll")]
    static extern IntPtr SensorInfo_getSupportedVideoModes(IntPtr objectHandler, out int arraySize);
    public VideoMode[] getSupportedVideoModes()
    {
        int arraySize;
        IntPtr arrayPointer = SensorInfo_getSupportedVideoModes(this.Handle, out arraySize);
        IntPtr[] array = new IntPtr[arraySize];
        Marshal.Copy(arrayPointer, array, 0, arraySize);
        VideoMode[] arrayObjects = new VideoMode[arraySize];
        for (int i = 0; i < arraySize; i++)
            arrayObjects[i] = new VideoMode(array[i]);
        return arrayObjects;
    }
Run Code Online (Sandbox Code Playgroud)

如你所见,我只想要对象的指针.但第二个例子给了我无效的指针.我不知道为什么.这是一个很大的库,我试图创建一个.net包装器,除了这个功能,一切都很好.

注意:Array <>是一个模板类,包含两个字段,大小和数组以及几个方法.


*编辑:我知道这是一个局部变量(甚至VS给我警告),但为什么第一个工作呢?!


*编辑2:

我使用@JefferyThomas答案解决了第二个问题.它现在看起来像这样并没有问题:C++方面:

__declspec(dllexport) void* SensorInfo_getSupportedVideoModes(SensorInfo* si, int *dIsize){
    const Array<VideoMode>& dIArray = si->getSupportedVideoModes();
    *dIsize = dIArray.getSize();
    VideoMode** dP = new VideoMode*[255];
    for (int i = 0; i < dIArray.getSize(); i++)
        dP[i] = const_cast<VideoMode*>(&(dIArray[i]));
    return dP;
}
__declspec(dllexport) void SensorInfo_destroyVideoModesArray(VideoMode** dP){
    delete[] dP;
}
Run Code Online (Sandbox Code Playgroud)

目前我在改变我的第一个功能时遇到了问题,就像@JefferyThomas建议的那样.我们来看看代码然后我会告诉你问题在哪里.C++方面:

__declspec(dllexport) void** Framework_enumerateDevices(int *dIsize){
    Array<DeviceInfo> dIArray;
    Framework::enumerateDevices(&dIArray);
    *dIsize = dIArray.getSize();
    DeviceInfo* dP = new DeviceInfo[255];
    for (int i = 0; i < dIArray.getSize(); i++)
        dP[i] = dIArray[i];
    void** p = new void*;
    *p = dP;
    return p;
}
__declspec(dllexport) void Framework_destroyDevicesArray(void **p){
    DeviceInfo *dP = (DeviceInfo *)*p;
    //delete [] dP;
    delete p;
}
Run Code Online (Sandbox Code Playgroud)

C#侧:

    [DllImport("Wrapper.dll")]
    static extern IntPtr Framework_enumerateDevices(out int devicesSize);
    [DllImport("Wrapper.dll")]
    static extern IntPtr Framework_destroyDevicesArray(IntPtr arrayPointer);
    public static DeviceInfo[] EnumerateDevices()
    {
        int devicesSize;
        IntPtr arrayPointer = Framework_enumerateDevices(out devicesSize);
        IntPtr[] array = new IntPtr[devicesSize];
        Marshal.Copy(arrayPointer, array, 0, devicesSize);
        DeviceInfo[] arrayObjects = new DeviceInfo[devicesSize];
        for (int i = 0; i < devicesSize; i++)
            arrayObjects[i] = new DeviceInfo(array[i]);
        Framework_destroyDevicesArray(arrayPointer);
        return arrayObjects;
    }
Run Code Online (Sandbox Code Playgroud)

和问题:

  1. 我不能在destroy函数中保留"delete [] dP".它不仅删除数组,还删除数据.
  2. 我只能正确地拥有第一个指针(数组的第一项).其他指针都不正确.我不知道为什么.尝试将DeviceInfo更改为指针数组而不是直接更改对象数组(如第二个函数),但仍然没有成功.

目前我认为这是因为Array <>类处理和销毁所有东西.


编辑3:我找到了第一个问题的解决方案.谢谢杰夫让我正确的方向.

Jef*_*mas 5

这里的问题是你返回一个堆栈变量.这是一个坏主意.

双方VideoMode dP[255];DeviceInfo dP[255];当函数返回被摧毁.事实上,在第一种情况下,内存没有被覆盖,在第二种情况下,你不是那么幸运.

在过去,我已经从堆中分配并提供了第二个releaseXXXX方法来释放该内存.

__declspec(dllexport) void** enumerateDevices(int *dIsize){
    Array<DeviceInfo> dIArray;
    Framewoek::enumerateDevices(&dIArray);
    *dIsize = dIArray.getSize();
    DeviceInfo *dP = new DeviceInfo[255];
    for (int i = 0; i < dIArray.getSize(); i++)
        dP[i] = dIArray[i];
    void** p = new void*;
    *p = dP;
    return p;
}

__declspec(dllexport) void releaseEnumerateDevices(void **p){
    DeviceInfo *dP = (DeviceInfo *)*p;
    delete [] dP;
    delete p;
}
Run Code Online (Sandbox Code Playgroud)

C#

    [DllImport("Wrapper.dll")]
    static extern IntPtr enumerateDevices(out int devicesSize);
    static extern void releaseEnumerateDevices(IntPtr arrayPointer);
    public static DeviceInfo[] EnumerateDevices()
    {
        int devicesSize;
        IntPtr arrayPointer = enumerateDevices(out devicesSize);
        IntPtr[] array = new IntPtr[devicesSize];
        Marshal.Copy(arrayPointer, array, 0, devicesSize);
        DeviceInfo[] arrayObjects = new DeviceInfo[devicesSize];
        for (int i = 0; i < devicesSize; i++)
            arrayObjects[i] = new DeviceInfo(array[i]);
        releaseEnumerateDevices(arrayPointer);
        return arrayObjects;
    }
Run Code Online (Sandbox Code Playgroud)