このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
GCHandle::Alloc でハンドルに ID を割り当てると、
数値として取り扱えるようになるので便利だが、
不要になった時にはちゃんと解放しなければならない。
これを怠ると、ハンドルへの参照が残ってしまうので、
オブジェクトがガベッジコレクションされなくなり、
メモリリークを引き起こす要因となる。
GCHandle::Alloc で参照したハンドルを解放するためには、
どこかで GCHandle::Free を呼ぶ必要がある。
では、Dispatcher::Destroy を作ってみよう。
void Dispatcher::Destroy(HWND hwnd) {
// ウィンドウのユーザデータから ID を取得
LONG_PTR value = GetWindowLongPtr(hwnd, GWLP_USERDATA);
void *pointer = reinterpret_cast<void *>(value);
// 参照は void * と相互変換可能
IntPtr id(pointer);
GCHandle holder = GCHandle::FromIntPtr(id);
// 参照が保持するハンドルを取得
ISaver ^saver = safe_cast<ISaver ^>(holder.Target);
// 後始末処理を呼び出す
saver->Destroy();
// 参照を破棄する
holder.Free();
}
まず、昨日の手順を使って、ISaver のハンドルを取得する。
プラグインにも後始末を通知しなければならないので、
ISaver::Destroy を呼び出しておく。
最後に GCHandle::Free を呼んでハンドルを解放する。
重要なのは、holder.Target を取得する前に、
holder.Free を呼んではいけないということだ。
holder.Free の時点でハンドルが解放されるので、
その瞬間にガベッジコレクタが割り込んだ場合、
ISaver が解放されてしまう可能性がある。
さて、上記ではウィンドウ属性に ID が残ったままだが、
悪影響はないのでそのままにしている。
0 を格納しておけば安全確実だろう。
今回のケースにおいては、WM_DESTROY のハンドラだ。
WM_DESTROY の後は、WM_NCDESTROY 位しか届かないし、
すぐにこの後ウィンドウが破棄されるわけなので、
省略してもほとんど問題ないと考えられる。