ハンドル型の値は演算も変換もできないため、
そのままではスコープを超えて、
オブジェクトを保持することが困難である。

グローバル領域に変数を確保し、
ハンドル型の値を保持する手はあるが、
変数の可視性を高めてしまうのは、
保守性に劣り、見通しも悪くなるのでやりたくない。

ではどうすれば良いか。

.NET Framework には、マネージオブジェクトを、
アンマネージメモリに保持するために、
専用の仕組みを用意している。
それが、GCHandle 構造体である。

GCHandle 構造体を使うと、
任意のマネージオブジェクトへのハンドルを、
一意の ID に変換することができるようになる。
逆に、この ID からハンドルを得ることもできるので、
ハンドルと ID の相互変換が可能となる。

得られる ID は、プラットフォーム固有の整数値なので、
アンマネージメモリに格納することができる。

では、Dispatcher::Create を書きなおしてみよう。

    void Dispatcher::Create(HWND hwnd) {

        // ウィンドウのサイズを測る
        RECT rc;
        GetClientRect(hwnd, &rc);

        // スクリーンセーバーを作成
        ISaver ^saver = gcnew HelloSaver();

        // HWND のラッパーを作成
        HwndWrapper window(hwnd);

        // スクリーンセーバーを初期化する
        saver->Create(%window, rc.right, rc.bottom);

        // マネージオブジェクトである saver が
        // 知らぬ間に GC されないように参照を作成
        GCHandle holder = GCHandle::Alloc(saver);

        // 参照は void * と相互変換可能
        IntPtr id = GCHandle::ToIntPtr(holder);
        void *pointer = id.ToPointer();

        // ID をウィンドウのユーザデータ領域に記憶する
        LONG_PTR value = reinterpret_cast<LONG_PTR>(pointer);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, value);

    }

GCHandle::Alloc を呼び出すと、
アンマネージオブジェクトへのハンドルを示す、
ID を割り当てることができる。

GCHandle::Alloc で ID が割り当てられると、
そのハンドルは、内部的に強参照されるようになり、
ガベージコレクションの対象にならない。

イメージとしては、どこか見えないところに、
ハンドル型の変数があり、そこに値を代入している感じか。
オブジェクトへの有効な参照が存在すると見なされるのだ。

GCHandle::Alloc の戻り値は、GCHandle 構造体である。
GCHandle 構造体は単に IntPtr の ID をカプセル化し、
便利なメソッドを提供しているラッパーに過ぎないため、
この構造体が破棄されたとしても ID は有効のままだ。

GCHandle 構造体を GCHandle::ToIntPtr に渡すことで、
ID を表す IntPtr 型の整数値を得ることができる。
これは ToPointer を呼ぶことで void ポインタに変換できる。
SetWindowLongPtr の引数は LONG_PTR 型なので、
得られた void ポインタをキャストすれば格納可能となる。

このようにすれば、マネージオブジェクトを、
アンマネージメモリ上に保持することができるのだ。