none
C++関数で unsigned char のポインターのポインターを引き数に持つ関数のラッパーの書き方は? RRS feed

  • 質問

  • C++の関数で、 BufPointerGet( unsigned char ** pBuf) 
    のようなポインターのポインターを引き数に持つ関数があります。
    バッファー自体は、C++の方で確保するプログラムもあり、開放するプログラムもあります。
    事前に確保された状態で利用するものです。

    C#の方は利用するだけですが、ラッパーの書き方とその利用の仕方をお願いします。

    2011年6月21日 8:05

回答

  • byte[]はmanaged heap上に存在します。
    一方C++側のBufferはheapなりstackなりunmangedなmemory空間上に存在します。
    お互い相容れませんから、copyが必要になります。

    直接C++のbufferにaccessしたければ、unsafe codeで記述することになります。

    >C++のmallocで確保された領域をC#の配列と同じ場所にできないかと考えましたが、ダメですかね?
    ダメですね。

    >unsafe で、関数を作って行くという対応になりますか。
    そうなりますね。

    • 回答としてマーク クサキ 2011年6月24日 2:34
    2011年6月23日 11:45

すべての返信

  • IntPtr で扱えばよいかと。この場合 unsigned char* が IntPtr に当たりますね。

    読み取り書き込みは Marshal クラスの各メソッドで。

    2011年6月21日 8:42
  • P/Invokeの書き方は、以下のK. Takaokaさんの回答と同じノリでよいです。

    [C++でポインターのポインターを引き数に持つ関数のラッパーはどう書けば良いでしょうか?]
      http://social.msdn.microsoft.com/Forums/ja-JP/csharpgeneralja/thread/c9cc04f6-584b-48c7-bfae-d23dd2f7c7e2

    以下も参考にしてください。
    [プラットフォーム呼び出しチュートリアル]
      http://msdn.microsoft.com/ja-jp/library/aa288468(v=vs.71).aspx

    >その利用の仕方
    どう利用したいのですか?

    2011年6月21日 10:01
  • unsigned char* も IntPtr で扱うんですね。

     利用の仕方としては、C#で普通に使いたいということで、下記のようです。

    byte[] aBuf;

    BufPointerGet( aBuf );

    for ( int i = 0 ; i < 100 ; i++ )
          aBuf[i] = aBuf[i] * i;


    C++のバッファーから、C#の配列にわざわざ転送したくありません.
    Marshal クラスのメソッドは何を使えば良いのでしょうか?

    ちなみに、転送しても良い場合は、Marshal.ReadByteなどを使うのですか?

     

    2011年6月22日 1:28
  • 別スレッドでも指摘しましたが、こういった問題の場合、必ず考えないといけないのはメモリ確保はC++ / C#どちらで行うのか、です。

    見た感じ、C++側でメモリ確保しているかのようなコードになっていますが、C++でC#のbyte[]型のメモリ確保ができません。それを踏まえて、どちらでメモリ確保を行い、相手方はそれを参照する、ということをまず考えてください。

    2011年6月22日 1:50
  •  「unsigned char のポインターのポインター」なのに、なぜ Byte が出てくるのでしょうか。BYTE が typedef unsigned char BYTE だから?

     尋ねたいのですが、「ポインターのポインターを引数に持つ関数のラッパー」の書き方と、「unsigned char のポインターのポインターを引数に持つ関数のラッパー」の書き方が、なぜ違うとお考えなのでしょうか。前回は文字列定数へのポインターを設定する関数でしたが、今回は「文字列配列を関数引数で返す」関数なのでしょうか。タイトルだけ見ていると、「どうして同じ質問を繰り返すの?」と思ってしまいます。

     「C++のバッファーから、C#の配列にわざわざ転送したくありません.」というのも、よくわかりません。C++ でどの様に定義されているものを扱いたいのか、C# でどの様に扱いたいのか。その辺も明確にしましょう。

     それから、「プラットフォーム呼び出しによるデータのマーシャリング」は、参考になります。それから、Win32 API が対象なら、pinvoke.net で探すとよいでしょう。


    Jitta@わんくま同盟
    2011年6月22日 14:04
  • >「unsigned char のポインターのポインター」なのに、なぜ Byte が出てくるのでしょうか。
    > BYTE が typedef unsigned char BYTE だから?
    C++のunsigned char も C#のbyteも符号なしの8bitで同じものと、
    C#を始めた時からから思っていまして、unsigned charは全て、byteとして
    やってきて問題なく動いていますが、違うのですか?何が正解なのでしょうか?

    C++のDLLで、mallocでデータ領域を確保しています。
    最大で、unsigned charの場合は16Mbyte、floatの複素数の場合は128Mbyteになります。
    高速で動かしたいので、C++のバッファーから、C#の配列にわざわざ転送したくありません。

    また、C++のDLLで初期化処理、終了処理なども行っていてひとつの体系ができあがっています。
    現在、実績もあり安定しています。さらに、VB6.0のアプリからも利用していまして、
    こちらの仕様を変える気にはなりません。というわけで、C++側でメモリの確保は行います。

    マーシャリングのところを少し調べましたら、
       C++の関数 
       int TestRefArrayOfInts(int** ppArray, int* pSize);
       ラッパー
       [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
       public static extern int TestRefArrayOfInts( ref IntPtr array, ref int size );
      利用側
          int[] array2 = new int[ 10 ];
          IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( size ) * array2.Length );
          Marshal.Copy( array2, 0, buffer, array2.Length );    
          int sum2 = LibWrap.TestRefArrayOfInts( ref buffer, ref size );
    というのがありました。この例題はC#側で配列を確保していて私の場合に当てはまりません。
    また、Marshal.Copyなどがあり、コピーをしているようで、私がやりたいこととは違います。

    文字列定数へのポインターの場合はファイル名を渡すぐらいで速度的な問題がなく、
    どんな内部的な処理がなされていてもよく、C#から簡単に利用できれば良かったのですが、
    こちらのは速度的な問題と大きな配列をC#側で、また確保するのを避けたいということです。

    unsafe な関数を作り、fixed (byte* pBuf = aBuf) のような形で
    C++のmallocで確保された領域をC#の配列と同じ場所にできないかと考えましたが、ダメですかね?

    ダメでしたら、unsafe で、関数を作って行くという対応になりますか。

     

     

    2011年6月23日 6:56
  • byte[]はmanaged heap上に存在します。
    一方C++側のBufferはheapなりstackなりunmangedなmemory空間上に存在します。
    お互い相容れませんから、copyが必要になります。

    直接C++のbufferにaccessしたければ、unsafe codeで記述することになります。

    >C++のmallocで確保された領域をC#の配列と同じ場所にできないかと考えましたが、ダメですかね?
    ダメですね。

    >unsafe で、関数を作って行くという対応になりますか。
    そうなりますね。

    • 回答としてマーク クサキ 2011年6月24日 2:34
    2011年6月23日 11:45
  • ダメでしたら、unsafe で、関数を作って行くという対応になりますか。

    少し気になったのは、「unsafe で関数を作っていく」という表現です。
    普段は IntPtr でやりとりし、実際に unmanaged な配列を参照する必要があるところだけ、unsafe コンテキストにすればよいのですが、そんなにいっぱい参照する関数が存在するということでしょうか。

    IntPtr をメンバーに持ち、unmanaged 配列へのアクセスを実現させるラッパークラスを作ってもいいかもしれませんね。
    ただ、処理の書き方次第では、アクセス効率が落ちるやもしれませんが。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年6月23日 14:04
    モデレータ
  • ポインターのポインターを引数にする関数はそう多くありません。
    多くの場合は、ポインターを引数にする関数です。
    IntPtr を使ったやりかたはあまり慣れていませんで、ほとんどは、
    fixed (byte* pBuf = aBuf) のようなやりかたで、managed heap
    上にデータを読み込む、safeなC#のラッパーが作れています.
    普段はそれを利用しています。

    話が整理できました。また、これで私のC++のDLLの関数が全て利用できるようになりました。
    皆さんどうもありがとうございました。

    2011年6月24日 2:32