C++から利用可能なC#によるクラスライブラリの作成
ちょっと詰まったのでメモ。
やりたいことは「ユーザであるC++のコードから、C#のクラスライブラリに任意型のデータを渡す。C#はそれをリストに溜め、適宜要求に応じてC++の人に返したりする」
とりあえずインタフェイスとして以下のようなものを考える。
実際にはもっといろんな操作が必要だろうな
- AddVariant() : データの追加
- GetVariantAt() : データの取り出し
これを備えたクラスライブラリを以下のように作る
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; namespace COMVariant { [ComVisible(true)] public interface IVariant { void AddVariant(Object obj); Object GetVariantAt(int num); } [ClassInterface(ClassInterfaceType.AutoDual)] public class Variant:IVariant { private List<Object> _list; public Variant() { _list = new List<object>(); } public void AddVariant(Object obj) { Debug.WriteLine("AddVariant "+ obj+": type="+obj.GetType()); _list.Add(obj); } public Object GetVariantAt(int num) { if(_list.Count > num) { Object obj = _list[num]; Debug.WriteLine("GetValueAt["+num+"] => " + obj); return obj; } //num is exceeding limit Debug.WriteLine("GetValueAt exceeded limit"); throw new Exception("Exceeded Limit"); } } }
各所、COMとして使うためにインタフェイス・クラスのそれぞれ適切な属性をつけている
[ComVisible(true)] // インタフェイス
[ClassInterface(ClassInterfaceType.AutoDual)] //クラス
またこれらに加えてプロジェクトのプロパティとして
さらにビルド後には
- 生成されたDLLにたいして
> regasm (生成されたDLL)
として登録しておく。
次に呼び出すC++の方
こちらでは基本的に以下の流れで先に作成したライブラリを呼び出す
- #importでtlbファイルの指定、タイプライブラリの読み込み
- COMの初期化
- インタフェイスポインタの宣言、インスタンス化
- ほげほげ
- COMの終了処理
まずは以下のように先に作成したtlbを指定する
#import "C:\\full\\path\\COMVariant.tlb" named_guids raw_interfaces_only
これによりプリプロセス時にDebug\bin以下にタイプライブラリヘッダ"callbacklib.tlh"なるファイルができる。
詳しくはここ*1にあるのを参照
それぞれの後続するオプションは
- named_guids: 後で使うCLSIDなどを自動生成
- raw_interfaces_only: つけとけって言ってる*2からとりあえずつけとく
他にもクラスライブラリの名前空間を省くno_namespaceなどがある
COMの初期化は基本的に以下の一行
CoInitialize(NULL);
これは後述する終了処理のCoUninitialize()と対になっている。
.NETのクラスをつかむためにインタフェイスポインタとやらを使う。
これに当たってスマートポインタというものがでてくる。
C#側で宣言したインタフェイスは"IVariant"だが、プリプロセッサで処理が終わった時点でインタフェイスポインタとして以下のような新しいものが定義される
スマートポインタ = インタフェイス名 + "Ptr" = IVariantPtr
このIVariantPtrをここでは使う。IVariantを使うと怒られるので注意。
このスマートポインタにたいしてCreateInstance()を呼び出すことでインスタンス化ができる。
IVariantPtr dotNetCOMPtr; // Smart Pointer Name = IVariant + Ptr
HRESULT hRes = dotNetCOMPtr.CreateInstance(COMVariant::CLSID_Variant); // Instancialize
このポインタを通してほげほげした後はCOMの終了処理を行う。
以下の一行挟めば終わり
CoUninitialize();
*1:「ひしだま's ホームページ」技術メモ>VC++>VC++2005>#import参照 http://www.ne.jp/asahi/hishidama/home/index.html
*2:http://msdn.microsoft.com/ja-jp/library/s5628ssw(VS.80).aspx:ここ