このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
Create/Destroy の準備ができたので、
本題となる Dispatcher::Paint の実装を行う。
void Dispatcher::Paint(HWND hwnd) {
// 描画開始
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
try {
// ウィンドウのユーザデータから 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);
// Win32 の RECT と .NET の Rectangle は構造が異なる
// そのため、メモリレイアウトのままコピーできない
Drawing::Rectangle rc = Rectangle::FromLTRB(
ps.rcPaint.left , ps.rcPaint.top,
ps.rcPaint.right, ps.rcPaint.bottom);
// HDC から Graphics を作る
Graphics ^g = Graphics::FromHdc(static_cast<IntPtr>(ps.hdc));
try {
// 描画処理を呼び出す
saver->Paint(g, rc);
} finally {
// Graphics は必ず Dispose
g->Dispose(); // ダメ
}
} finally {
// BeginPaint と EndPaint は必ず対
EndPaint(hwnd, &ps);
}
}
このメソッドは、WM_PAINT のハンドラなので、
BeginPaint/EndPaint の呼び出しが必要となる。
EndPaint が確実に呼び出せるように、
try ~ finally 構文の中に入れておくことにした。
C++ には finally が存在しないのだが、
C++/CLI の拡張構文として使うことができる。
まあ、今まで例外処理放りっぱなしなのに、
ここだけ処理するってのもなんだが。
C++/CLI では、常にマネージ例外が飛んでくるため、
そんなもの知らんネイティブ C++ に渡さないように。
ちゃんと処理してやる必要がある。本来は。
さて、BeginPaint を呼び出した後は、
昨日の手順を使って、ISaver のハンドルを取得する。
プラグインの描画メソッドは、
Graphics と Rectangle を引数に取るため、
それらインスタンスを PAINTSTRUCT から作成する。
Rectangle には、RECT 構造体から変換するのに便利な、
FromLTRB という静的メソッドが存在するのでこれを使う。
Rectangle は値型(構造体)なので、
FromLTRB の戻り値はハンドルではない。
なお、Drawing::Rectangle とクラス名を装飾しているのは、
Rectangle では API 関数の Rectangle と区別できないから。
これくらい機転利かせてくれてもいいんだが、仕方ないか。
Graphics にも、HDC から変換することができる、
FromHdc という静的メソッドがある。
ただ、Graphics は HDC のラッパとして振舞うため、
必ず使用後に Dispose する必要がある。
なので、これも finally に入れておいた。
これで、描画メソッドが呼び出せるようになった。
が、このコードはコンパイル時にエラーとなる。
問題が起きているのは g->Dispose(); の箇所だ。
error C2039: 'Dispose' : is not a member of 'System::Drawing::Graphics'
いや、Graphics は Dispose メソッドを持つはずだ。
Graphics は IDisposable を実装しているし、
メソッドの名前を変えるようなこともしていない。
間違いはないように見えるのだが。
……これ、実はこう書かなければならないのだ。
// Graphics は必ず Dispose
delete g;
ん? delete だと?