2006-03-03

C++/CLI - ネイティブクラスメンバのマネージクラス  [by miyachi]

相変わらずC++/CLIとの格闘の日々(笑) 細かな点はおいといてだいぶ使えるようになってきたので、少し本格的に古いGUIから .NET Framework を使おうとしてみた。古いGUIとはMFCのCDialogをベースとしたクラスだ。つまり

// マネージクラス
ref class ManageClass {
public:
 bool dotnet();
};

// ネイティブクラス
class NativeClass {
public:
 NativeClass()
 {
  m_manage = gcnew ManageClass();
 }
 bool test()
 {
  return m_manage->dotnet();
 }
private:
 ManageClass^ m_manage;
};


としてみたら見事にエラー…

1>.\test.cpp(nn) : error C3265: マネージ 'm_manage' をアンマネージ 'NativeClass' で宣言できません。
1> グローバル変数、静的変数、または gc ヒープのオブジェクトを参照しているネイティブ型のメンバを宣言することはできません


う~む。なるほど理屈から言えばネイティブなクラスにマネージ管理されているgcヒープのオブジェクトは管理出来ないな… でもそれが出来なきゃ非常に使いにくい。MSから出ているC++/CLIの資料を見るも解決策が見えない。でもあのMSのことだ。どこかに卑怯な回避方法があるに違い無い!古いC++/CLI以前のマネージ拡張をチェックしてみた。…やっぱりあった(笑)

先に書いたように普通はマネージクラスへのポインタは、ネイティブ(アンマネージ)クラスのメンバ変数やグローバル変数には出来ない。でも gcroot テンプレートを使えば可能だとある。これはC++/CLIの規格外のようでC++/CLI関連の資料には載っていない。これを使って先のプログラムを書くと。

// マネージクラス
ref class ManageClass {
public:
 bool dotnet();
};

// ネイティブクラス
class NativeClass {
public:
 NativeClass()
 {
  m_manage = gcnew ManageClass();
 }
 bool test()
 {
  return m_manage->dotnet();
 }
private:
 gcroot<ManageClass^> m_manage;
};


そう。ネイティブクラスのメンバ変数として使う場合にgcrootでマネージクラスへのポインタを囲うだけで良いのだ。なんて安易(笑) でもこのおかげで例えばMFCのCDialogクラスで .NET Framework のマネージクラスをメンバ変数に出来る。gcrootを使えば STL の vector でマネージクラスを扱えたりもする。更にマネージコードのポインタをネイティブコードから扱う為にアドレスをピン止めする pin_ptr と言う物も用意されている。こちらはC++/CLIでも説明がある(マネージ拡張では __pin だった)。いやさすがに何でもありのMSだ。卑怯ではあるが非常に便利。

よお~し!これで(だいたい)C++/CLIは使えるようになった!本来のコーディングを進めねば(^^;
2006-03-03 09:51:11 - miyachi - - [プログラミング] -