-1

ほぼ同じコードの2つの例があります。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;
}

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

期待どおりに動作しない

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

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

ご覧のとおり、オブジェクトのポインタが必要です。しかし、2番目の例では無効なポインタが表示されます。どうしてか分かりません。これは私が.netラッパーを作成しようとしている大きなライブラリであり、この関数を除いてすべてが正常に機能します。

注:Array <>は、サイズと配列の2つのフィールドと、いくつかのメソッドを含むテンプレートクラスです。


*編集:それがローカル変数であることは知っていますが(VSでも警告が表示されます)、なぜ最初の変数が機能するのですか?!


*編集2:

@JefferyThomasの回答を使用して2番目の問題を解決しました。今はこのように見え、問題はありません: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;
}

現在、@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;
}

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

そして問題:

  1. destroy関数で「delete[]dP」を保持できません。アレイを削除するだけでなく、データも削除します。
  2. 正しく最初のポインタ(配列の最初の項目)しか持てません。他のポインタはすべて正しくありません。どうしてか分かりません。DeviceInfoをオブジェクトの配列ではなくポインタの配列に直接変更しようとしましたが(2番目の関数のように)、それでも成功しませんでした。

現在、すべてを破棄して破棄するArray<>クラスが原因だと思います。


編集3:私は最初の問題の解決策を自分で見つけました。私を正しい方向に導いてくれたジェフに感謝します。

4

1 に答える 1

5

ここでの問題は、スタック変数を返すことです。これは悪い考えです。

VideoMode dP[255];関数が戻ると、 との両方DeviceInfo dP[255];が破棄されます。最初のケースでメモリが上書きされていないのはたまたまですが、2番目のケースではそれほど幸運ではありません.

過去に、ヒープから割り当てて、そのメモリを解放するための 2 番目の 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;
}

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;
    }
于 2013-02-13T20:33:13.037 に答える