C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: C# COMとのデータ受け渡し


(過去ログ 46 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■24679 / inTopicNo.1)  C# COMとのデータ受け渡し
  
□投稿者/ ぽち (1回)-(2008/09/08(Mon) 09:31:58)

分類:[C#] 

2008/09/08(Mon) 09:52:57 編集(投稿者)
おはようございます。

C#でコーディングしており、C++で作成したフィルターを使うことになりました。
インターフェイスの取得などはエラーもなくうまくいっているのですが、
データの受け渡しがうまくいきません。

  [StructLayout(LayoutKind.Sequential)]
  public struct DataInf {
    public int     dataA;
    public int     dataB;
  }

 [ComVisible(true), ComImport,
 Guid("*****"),
 InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
 public interface ITEST 
  {
        int GetInfo( 
            [Out, MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.LPStruct)] out DataInf[] fi );
 }

上記インターフェイスのGetInfoで、DataInf構造体ポインタ配列のポインタを取得しなくてはいけないのですが
GetInfoのfiの戻り値が配列数0になってしまいます(本当は配列数2が取れるはずなのですが・・・)。
「ネットなどを検索しているのですが、構造体ポインタ配列のポインタを取得する例が載っていない」+「C#経験少ない」
で解決策が見つかりません。

ご教授のほどよろしくお願いいたします。

ちなみにC++の場合は、

typedef struct DataInf_t {
	int     dataA;
	int     dataB;
} DataInf, *PDataInf;

ITEST : public IUnknown
{
  virtual HRESULT STDMETHODCALLTYPE GetInfo(
    /* [out] */ PDataInf **pppfi) = 0;
}

になっています。

開発環境は、
VS2005 C#
です。


引用返信 編集キー/
■24682 / inTopicNo.2)  Re[1]: C# COMとのデータ受け渡し
□投稿者/ Hongliang (292回)-(2008/09/08(Mon) 11:00:39)
C++ 側が要素数を返す手段を持ってない以上、C# としてもいくつの要素数を持った配列を作ればいいのか分からないかと。
out IntPtr で受けて、Marshal クラス使うなりして読み込むようにすればいいんじゃないですか?
引用返信 編集キー/
■24689 / inTopicNo.3)  Re[2]: C# COMとのデータ受け渡し
□投稿者/ ぽち (3回)-(2008/09/08(Mon) 12:57:25)
No24682 (Hongliang さん) に返信
> C++ 側が要素数を返す手段を持ってない以上、C# としてもいくつの要素数を持った配列を作ればいいのか分からないかと。
> out IntPtr で受けて、Marshal クラス使うなりして読み込むようにすればいいんじゃないですか?
返信ありがとうございます。

メソッドの仕様が、構造体ポインタ配列の値がNULLまでデータが入っているという仕様でしたので、
このメソッドを呼ぶタイミングでは個数がわからないのです(データ数が2個の場合は、fi[2]=NULL)

このメソッドの仕様では、Hongliang さんのご指摘のように取得が無理っぽいので、
引数に個数を追加してみました。

(C++側)
ITEST : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE GetInfo(
  /* [in] */ int num,
/* [out] */ PDataInf **pppfi) = 0;
}

(C#側)
int GetInfo(
[In]int num,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] IntPtr[] fi //
);

このようなインターフェイスを作ってみて試してみました
GetInfoで取得したfiには、C++側で設定したポインタが入っており、取得に成功しました。
(MarshalAsのUnmanagedType.LPArrayが間違ってる気もしますが)

ただ、その後の変換でうまくいきません
(DataInf構造体のメンバdataA、dataBにptrで入っているアドレス付近らしい値が入ってしまっている)
Hongliang さんのご指摘頂いている内容は下記のような感じだと思うのですが・・・

ご教授いただけたら幸いです。

ITEST ifTest;

IntPtr[] ptr = new IntPtr[2];
ifTest.GetInfo(2,ptr);

DataInf[] test2 = new DataInf[2];

test2[0] = (DataInf)Marshal.PtrToStructure(ptr[0],typeof(DataInf));
test2[1] = (DataInf)Marshal.PtrToStructure(ptr[1],typeof(DataInf));




引用返信 編集キー/
■24693 / inTopicNo.4)  Re[3]: C# COMとのデータ受け渡し
□投稿者/ Hongliang (293回)-(2008/09/08(Mon) 13:36:56)
MSDN の『配列に対する既定のマーシャリング』の記述から、MarshalAs 属性の SizeParamIndex フィールドで要素数を示すパラメータのインデックスを指定できるように読み取れますが、詳しくないのでパスします。
一般的に COM に公開する配列は SafeArray を使用します。これなら SafeArray 自身が要素数を保持しており、.NET のマーシャラも SafeArray による配列のやり取りに対応しているため、UnmanagedType.LPArray な配列のパラメータの要素数を決定できます。要素に独自型を使う場合についてはやっぱり詳しくないのでパス。
先ほど提示した out IntPtr なら、まず「配列の先頭ポインタ」を IntPtr で受け取ります。そして Marshal.ReadIntPtr で先頭要素のポインタを取得します。あとは Marshal.PtrToStructure 辺りで要素のポインタから構造体にコピーします。
配列が NULL 終端されてるなら Marshal.ReadIntPtr で IntPtr.Zero まで読み進めればいい話です。

他の問題として、ポインタを返す場合、そのポインタが指すメモリは誰が解放するのかって点が重要になります。
配列で受けた場合、.NET のマーシャラは渡されたポインタに対し CoTaskMemFree で解放を試みます。勝手に解放を試みられたら困るなら、IntPtr で受けるようにすべきです。
引用返信 編集キー/
■24702 / inTopicNo.5)  Re[4]: C# COMとのデータ受け渡し
□投稿者/ ぽち (4回)-(2008/09/08(Mon) 14:38:38)
No24693 (Hongliang さん) に返信
> MSDN の『配列に対する既定のマーシャリング』の記述から、MarshalAs 属性の SizeParamIndex フィールドで要素数を示すパラメータのインデックスを指定できるように読み取れますが、詳しくないのでパスします。
> 一般的に COM に公開する配列は SafeArray を使用します。これなら SafeArray 自身が要素数を保持しており、.NET のマーシャラも SafeArray による配列のやり取りに対応しているため、UnmanagedType.LPArray な配列のパラメータの要素数を決定できます。要素に独自型を使う場合についてはやっぱり詳しくないのでパス。
> 先ほど提示した out IntPtr なら、まず「配列の先頭ポインタ」を IntPtr で受け取ります。そして Marshal.ReadIntPtr で先頭要素のポインタを取得します。あとは Marshal.PtrToStructure 辺りで要素のポインタから構造体にコピーします。
> 配列が NULL 終端されてるなら Marshal.ReadIntPtr で IntPtr.Zero まで読み進めればいい話です。
>
> 他の問題として、ポインタを返す場合、そのポインタが指すメモリは誰が解放するのかって点が重要になります。
> 配列で受けた場合、.NET のマーシャラは渡されたポインタに対し CoTaskMemFree で解放を試みます。勝手に解放を試みられたら困るなら、IntPtr で受けるようにすべきです。
Hongliang さん、ありがとうございました。

Hongliang さんのご指摘の方法で思うような値を取得することができました。

本当にありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -