2016-11-14 16 views
0

私のC#コードでコールバック関数を定義し、それをネイティブC++コードに渡してから、後でC++コードを呼び出すようにします。コールバックは、構造体の可変長配列を受け取る必要があります。各構造体にはいくつかの配列フィールドが含まれています。C++からC#への配列フィールドを含む構造体の整列配列

配列フィールドを持つ単一の構造体と、スカラーフィールドを持つ構造体の可変長配列を渡しますが、配列フィールドを持つ構造体の可変長配列は渡すことはできませんでした。

私のC#コードです。私はそれが問題だとは思わないので、C#コードでC#コールバックメソッドを登録するコードを省略しています。問題のある特定の場合を除いて正常に動作します。

構造体:

[StructLayout(LayoutKind.Sequential)] 
public struct Foo 
{ 
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.R4, SizeConst = 2)] 
    public float[] a; 
} 

コールバック宣言

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    delegate void Callback(Int32 count, 
          [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Foo[] foos); 

コールバックメソッド自体

public void onFoo(Int32 count, Foo[] foos) { 
     Debug.Log("in onFoo callback, foo values=" + foo[0].a + ", " + foo[1].a); 
} 

そして、ここでは、C++のコードです:

まず構造体:

typedef struct { 
    float a[2]; 
} Foo; 

とコールバックの呼び出し:問題のあるケースでは

Foo* foos = new Foo[2]; 
foos[0].a[0] = 1.11; 
foos[0].a[1] = 2.22; 
foos[1].a[0] = 3.33; 
foos[1].a[1] = 4.44; 
onFoo(2, foos); 
delete[] foos; 

、私のコールバックメソッドは、(私は、ログ出力を取得していない)が呼び出されていません。私はかなりのグーグルを行ってきましたが、この特定のシナリオをカバーするものは何も見つかりませんでした。カスタムマーシャラが必要ですか?

+0

定義によるリストは可変長なので、 'List'コンテナを使用できますか? –

+0

C++のstd :: listを意味しますか?どのようにC#にマーシャリングされますか? –

+0

わかりませんでした。私はもはやC++で動作しません。 –

答えて

1

これは答えではありませんが、コールバック呼び出しの観察です。私がC++をやったので、何年もされていますが、次の

Foo* foos = new Foo[2]; 
foos[0].a = 1.11; 
foos[1].a = 2.22; 
onFoo(2, foos); 

は作品別のアプローチで、ここで自分の質問に答える、

Foo* foos = new Foo[2]; 
foos[0].a[0] = 1.11; 
foos[0].a[1] = 1.12; 
foos[1].a[0] = 2.22; 
foos[1].a[1] = 2.23; 
onFoo(2, foos); 
+0

はい、良いキャッチ、それは転写エラーでした。私は自分の質問を更新しました。 –

0

OKすべきではありません。誰か他の人がやって来て、最初に述べた問題を解決した場合には、私はそれを受け入れないでおきます。

回避策は、構造体の配列ではなく、コールバックに構造体ポインタの配列を渡すことです。

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
delegate void Callback(Int32 count, 
         [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] IntPtr[] fooPtrs); 

およびC++コードにコールバック署名の変更は、私は配列にそのポインタを格納し、その後、個別のFooを割り当て、初期化、フー*の配列を割り当てます。

Foo** foos = new Foo*[2]; 

Foo* foo1 = new Foo(); 
foo1->a[0] = 1.11; 
foo1->a[1] = 2.22; 
foos[0] = foo1; 

Foo* foo2 = new Foo(); 
foo2->a[0] = 3.33; 
foo2->a[1] = 4.44; 
foos[1] = foo2; 

onFoo(2, foos); 

delete foo1; 
delete foo2; 
delete[] foos; 

戻るC#の側に、コールバック方式で、Iがfooアレイ、着信のIntPtrアレイを通してループを割り当て、そうのような、Marshal.PtrToStructureを使用して各要素をマーシャリング:

public void onFoo(Int32 count, IntPtr[] FooPtrs) 
{ 
    Foo[] foos = new Foo[count]; 
    for (int i=0; i< count; i++) 
    { 
     foos[i] = (Foo)Marshal.PtrToStructure(ptrs[i], typeof(Foo)); 
    } 
} 

このアプローチの欠点は、C++で割り当てられたメモリにC#構造体の配列を直接マップするのではなく、より多くのメモリの割り当てとコピーが必要なことです。

関連する問題