このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
最初に、Dispatcher::Configure を作ってみよう。
void Dispatcher::Configure(HWND hdlg) {
// スクリーンセーバーを作成
HelloSaver saver;
// 設定画面を呼び出す
saver.Configure(hdlg); // ダメ
}
HelloSaver はマネージ (CLI) クラスであるが、
C++/CLI では、C++ のクラスと同じように扱うことができる。
なので、「HelloSaver saver;」という構文で、
既定のコンストラクタを呼び出す HelloSaver クラスの
インスタンスを作成することができる。
ただ、ISaver::Configure の呼び出しは、
引数に IWin32Window 型の値を取るのだが、
hdlg は HWND 型なので、そのまま渡すことができない。
そのため、HWND を IWin32Window に変換するための、
ラッパークラスを用意する必要がある。
========== HwndWrapper.hpp ==========
#ifndef z_loafer_screensaver_host_hwndwrapper
#define z_loafer_screensaver_host_hwndwrapper
#using <System.dll>
#using <System.Windows.Forms.dll>
namespace Loafer {
namespace ScreenSaver {
namespace Host {
private ref class HwndWrapper
: public System::Windows::Forms::IWin32Window {
public:
HwndWrapper(HWND handle) {
this->handle = static_cast<System::IntPtr>(handle);
}
virtual property System::IntPtr Handle {
System::IntPtr get() {
return handle;
}
}
private:
System::IntPtr handle;
};
}}} // Loafer::ScreenSaver::Host
#endif // !z_loafer_screensaver_host_hwndwrapper
========== end of HwndWrapper.hpp ==========
徐々に C++/CLI の世界が見えてきた。
IWin32Window がマネージインタフェースなので、
それを実装する HwndWrapper もマネージクラスとなる。
private ref class HwndWrapper
: public System::Windows::Forms::IWin32Window;
C++/CLI でマネージクラスを定義する場合、
「ref class」という構文を使う。
この場合、クラスにもアクセス修飾子を付けることができる。
public は公開、private はアセンブリ内のスコープとなる。
それぞれ、C# の public, internal に相当する。
C++ の構文に従い、基底クラスやインタフェースの継承にも
アクセス修飾子が必要となるが、
protected や private 継承、virtual 継承は使えず、
純粋な public 継承しか指定することができない。
これは、CLI の仕様に合わせるためだ。
this->handle = static_cast<System::IntPtr>(handle);
コンストラクタでは、HWND から IntPtr に変換している。
HWND は内部的にはポインタ型であるため、
void * と互換性がある IntPtr にキャスト可能である。
キャスト構文も、C++ のものを利用できる。
virtual property System::IntPtr Handle {
System::IntPtr get() {
return handle;
}
}
IWin32Window は、Handle プロパティを定義している。
C++ にはプロパティ構文が存在しないため、
プロパティ用に新しい構文が導入されている。
プロパティを作成するためには、
まず property ブロックを記述する。
property キーワードの前に virtual を書けば、
プロパティ全体が仮想メンバとなる。
次に、プロパティのアクセサを作成するため、
ブロックの中に get と set メソッドを入れる。
get は引数なしで戻り値がプロパティの型、
set はプロパティの型の引数ひとつで戻り値はなし。
メソッドは片方だけ用意しても構わない。
Handle プロパティは読み取り専用なので、
get メソッドだけ用意することになる。
これで、HwndWrapper マネージクラスができた。
今回は、ヘッダファイルにすべて定義したが、
C++ で従来から行われているように、
定義(実装)のみを cpp ファイルで行い、
宣言のみを hpp ファイルに残す事も可能だ。
余談になるが、マネージクラスの定義を、
宣言と定義に分離する場合、宣言は以下のような形になる。
private ref class HwndWrapper
: public System::Windows::Forms::IWin32Window {
public:
HwndWrapper(HWND handle);
virtual property System::IntPtr Handle {
System::IntPtr get();
}
private:
System::IntPtr handle;
};
そして、定義は以下のようになる。
HwndWrapper::HwndWrapper(HWND handle) {
this->handle = static_cast<System::IntPtr>(handle);
}
System::IntPtr HwndWrapper::Handle::get() {
return handle;
}
get() アクセサはプロパティの名前空間の下に定義するのだ。
では、本題に戻る。
HwndWrapper ができたので、これを使って
Dispatcher::Configure を再度作ってみよう。
HwndWrapper.hpp を #include することを忘れずに。
void Dispatcher::Configure(HWND hdlg) {
// スクリーンセーバーを作成
HelloSaver saver;
// HWND のラッパーを作成
HwndWrapper window(hdlg);
// 設定画面を呼び出す
saver.Configure(window); // ダメ
}
うまく行ったように見えるのだが、
実はこれだけではダメなのだ。正しくはこうなる。
void Dispatcher::Configure(HWND hdlg) {
// スクリーンセーバーを作成
HelloSaver saver;
// HWND のラッパーを作成
HwndWrapper window(hdlg);
// 設定画面を呼び出す
saver.Configure(%window); // OK
}
ん? % って何だ?