2007 年 9 月 6 日 18 時 28 分

ホストの作成 #7: 描画コードを呼ぶ


このアーカイブは同期化されません。 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 だと?



Copyright (c) 1994-2007 Project Loafer. All rights reserved.