このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
GCHandle を使って割り当てた ID はただの数値なので、
利用するときにはハンドルに戻さなければならない。
ID からハンドルに戻すのは、
ハンドルから ID に変換した時と逆の手順を踏めば良い。
// ウィンドウのユーザデータから ID を取得
LONG_PTR value = GetWindowLongPtr(hwnd, GWLP_USERDATA);
void *pointer = reinterpret_cast<void *>(value);
// 参照は void * と相互変換可能
IntPtr id(pointer);
GCHandle holder = GCHandle::FromIntPtr(id);
昨日の例では数値をウィンドウ属性に格納しておいたので、
GetWindowLongPtr で取得し、void * に戻す。
IntPtr は void * を受け取るコンストラクタを持つので、
IntPtr に変換して CLI の世界に突入する。
IntPtr から GCHandle 構造体に戻すためには、
GCHandle::ToIntPtr と対称となる、
GCHandle::FromIntPtr を使えば良い。
GCHandle に戻すことができたら、
この Target プロパティの値を取得することで、
参照されているハンドルを取得することができる。
GCHandle はマネージハンドル全般を保持できるように
汎用的に作られているので、このハンドルは Object 型だ。
必要に応じて、もとの型にキャストする必要がある。
// 参照が保持するハンドルを取得
ISaver ^saver = safe_cast<ISaver ^>(holder.Target);
さて、Object ^ から ISaver ^ への変換は、
ダウンキャストと呼ばれ、一般的には危険な操作である。
C++/CLI において、マネージオブジェクトのハンドルを
ダウンキャストする構文には、以下の 3 つがある。
・static_cast
・dynamic_cast
・safe_cast (C++/CLI 新構文)
static_cast は必ず成功するため、例外は発生しない。
必ず成功というのは、優れているということではなく、
型の互換性は何も検証されず、強制的に変換され、
そのまま新しい型として扱われるということである。
万が一、実行時に型の互換性のないハンドルが渡されたら、
何が起きるか分からない。下手するとクラッシュするだろう。
このようなキャストは、C# では不可能だ。
dynamic_cast は C# の as 構文に相当する。
型の互換性がない場合は nullptr を返すため、
dynamic_cast でも例外は発生しない。
なお、nullptr は C# の null に相当し、
NULL ポインタのハンドル版である。
safe_cast は C# のキャスト構文 (T) に相当し。
型の互換性がない場合は InvalidCastException が発生する。
これが最も一般的なキャストだろうか。
余談だが、dynamic_cast が例外を発生させないのは、
ハンドルやポインタ等、間接型を使った場合に限られる。
C++/CLI は C++ の構文規則に基づくので、
参照型のキャストに失敗した場合は
マネージ・アンマネージ問わず、例外が発生するので注意。