none
[C++/CLI]配列を含む構造体のpin_ptrによるメモリ受け渡し RRS feed

  • 質問

  • C#(GUI部) + C++/CLI(ロジック)という構成でアプリを組んでおりますが、マネージド->アンマネージドの
    情報のやり取りに、構造体のpin_ptr渡しを考えております。

    マーシャリングのコードを書かなくても良いことと、マーシャリング時のメモリコピーによる効率低下を懸念して
    このような構成にしております。

    マネージド構造体のpin_ptrによる受け渡し自体は問題なくできていますが、構造体中に配列がある場合は
    うまくいきません。.NETの配列は参照型でありBlittable型ではないので当然なのですが、このようなケース
    でうまく構造体を渡す方法はないのでしょうか?構造体中の配列は当然Blittable型の配列が前提です。

    要するにマネージド構造体中に配列として固定長の連続メモリエリアが確保できれば良いのですが、いろいろ
    調べてみましたが見つけることができませんでした。

    よろしくお願いします。


    2010年3月4日 14:39

回答

  • C#でfixedを用いるとそのような配列を埋め込んだ構造体を表現できます。これと同等のものをC++/CLIで作成することは不可能のようです。そのため、この構造体を宣言するクラスライブラリをC#で作り、それをC++/CLIロジックのプロジェクトから参照設定するのがよいと思います。C++/CLIでは、同等のネイティブ構造体を作成し、そのポインタへキャストすることでアクセス可能でした。

    public unsafe struct ManageStruct
    {
        public fixed int Array[4];
    }

    確認のため、次のようなプログラムを作りました(簡単にするため、C++/CLI側にmain関数を置いています)。

    まず、次のようなC#ライブラリを作りました(fixed指定したメンバはC++/CLIからアクセスできそうになかったため、こっち側でテスト用のオブジェクトを作るメソッドGetも用意しました)。

    namespace Hoge
    {
        public unsafe struct ManageStruct
        {
            public fixed int Array[4];
        }
    
        public static class Test
        {
            public unsafe static ManageStruct Get()
            {
                ManageStruct h;
                h.Array[0] = 1;
                h.Array[1] = 10;
                h.Array[2] = 100;
                h.Array[3] = 1000;
                return h;
            }
        }
    }
    

    そして、C++/CLI側で次の内容のEXEを作りました。

    #include <iostream>
    
    struct NativeStruct
    {
        int Array[4];
    };
    
    static void Test(Hoge::ManageStruct% m)
    {
        pin_ptr<Hoge::ManageStruct> pm = &m;
        NativeStruct* p = reinterpret_cast<NativeStruct*>(pm);
        for each (int x in p->Array)
        {
            std::cout << x << std::endl;
        }
    }
    
    int main()
    {
        Hoge::ManageStruct hoge = Hoge::Test::Get();
        Test(hoge);
    }

    実際には、C#側からTest関数を呼び出すことになるでしょう。実行すると次のように出力され、うまくいっているようです。

    1
    10
    100
    1000
    
    2010年3月7日 12:51

すべての返信

  • C#でfixedを用いるとそのような配列を埋め込んだ構造体を表現できます。これと同等のものをC++/CLIで作成することは不可能のようです。そのため、この構造体を宣言するクラスライブラリをC#で作り、それをC++/CLIロジックのプロジェクトから参照設定するのがよいと思います。C++/CLIでは、同等のネイティブ構造体を作成し、そのポインタへキャストすることでアクセス可能でした。

    public unsafe struct ManageStruct
    {
        public fixed int Array[4];
    }

    確認のため、次のようなプログラムを作りました(簡単にするため、C++/CLI側にmain関数を置いています)。

    まず、次のようなC#ライブラリを作りました(fixed指定したメンバはC++/CLIからアクセスできそうになかったため、こっち側でテスト用のオブジェクトを作るメソッドGetも用意しました)。

    namespace Hoge
    {
        public unsafe struct ManageStruct
        {
            public fixed int Array[4];
        }
    
        public static class Test
        {
            public unsafe static ManageStruct Get()
            {
                ManageStruct h;
                h.Array[0] = 1;
                h.Array[1] = 10;
                h.Array[2] = 100;
                h.Array[3] = 1000;
                return h;
            }
        }
    }
    

    そして、C++/CLI側で次の内容のEXEを作りました。

    #include <iostream>
    
    struct NativeStruct
    {
        int Array[4];
    };
    
    static void Test(Hoge::ManageStruct% m)
    {
        pin_ptr<Hoge::ManageStruct> pm = &m;
        NativeStruct* p = reinterpret_cast<NativeStruct*>(pm);
        for each (int x in p->Array)
        {
            std::cout << x << std::endl;
        }
    }
    
    int main()
    {
        Hoge::ManageStruct hoge = Hoge::Test::Get();
        Test(hoge);
    }

    実際には、C#側からTest関数を呼び出すことになるでしょう。実行すると次のように出力され、うまくいっているようです。

    1
    10
    100
    1000
    
    2010年3月7日 12:51
  • Egtraさん

    実装例まで示していただきありがとうございました。

    こちらでも同様の手法で試してみたところ動作を確認できました。

    C++/CLI側で配列にアクセスしようとするとエラーになるのは残念ですが、Native側でアクセス
    すればよいので致命的な問題ではありません。
    いずれC++/CLIでも固定の配列メンバを確保できるようになるのでしょうか・・・。

    とても参考になりました。
    ありがとうございました。
    2010年3月9日 13:22