2007 年 8 月 30 日 23 時 56 分

C++/CLI #1: HwndWrapper マネージクラス


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


最初に、Dispatcher::Configure を作ってみよう。

    void Dispatcher::Configure(HWND hdlg) {

        // スクリーンセーバーを作成
        HelloSaver saver;

        // 設定画面を呼び出す
        saver.Configure(hdlg); // ダメ

    }

HelloSaver はマネージ (CLI) クラスであるが、
C++/CLI では、C++ のクラスと同じように扱うことができる。
なので、「HelloSaver saver;」という構文で、
既定のコンストラクタを呼び出す HelloSaver クラスの
インスタンスを作成することができる。

ただ、ISaver::Configure の呼び出しは、
引数に IWin32Window 型の値を取るのだが、
hdlg は HWND 型なので、そのまま渡すことができない。

そのため、HWND を IWin32Window に変換するための、
ラッパークラスを用意する必要がある。

========== HwndWrapper.hpp ==========

#ifndef z_loafer_screensaver_host_hwndwrapper
#define z_loafer_screensaver_host_hwndwrapper

#using <System.dll>
#using <System.Windows.Forms.dll>

namespace Loafer {
namespace ScreenSaver {
namespace Host {

private ref class HwndWrapper
        : public System::Windows::Forms::IWin32Window {
public:
    HwndWrapper(HWND handle) {
        this->handle = static_cast<System::IntPtr>(handle);
    }

    virtual property System::IntPtr Handle {
        System::IntPtr get() {
            return handle;
        }
    }

private:
    System::IntPtr handle;

};

}}} // Loafer::ScreenSaver::Host

#endif // !z_loafer_screensaver_host_hwndwrapper

========== end of HwndWrapper.hpp ==========

徐々に C++/CLI の世界が見えてきた。

IWin32Window がマネージインタフェースなので、
それを実装する HwndWrapper もマネージクラスとなる。

    private ref class HwndWrapper
            : public System::Windows::Forms::IWin32Window;

C++/CLI でマネージクラスを定義する場合、
「ref class」という構文を使う。
この場合、クラスにもアクセス修飾子を付けることができる。
public は公開、private はアセンブリ内のスコープとなる。
それぞれ、C# の public, internal に相当する。

C++ の構文に従い、基底クラスやインタフェースの継承にも
アクセス修飾子が必要となるが、
protected や private 継承、virtual 継承は使えず、
純粋な public 継承しか指定することができない。
これは、CLI の仕様に合わせるためだ。

    this->handle = static_cast<System::IntPtr>(handle);

コンストラクタでは、HWND から IntPtr に変換している。
HWND は内部的にはポインタ型であるため、
void * と互換性がある IntPtr にキャスト可能である。
キャスト構文も、C++ のものを利用できる。

    virtual property System::IntPtr Handle {
        System::IntPtr get() {
            return handle;
        }
    }

IWin32Window は、Handle プロパティを定義している。
C++ にはプロパティ構文が存在しないため、
プロパティ用に新しい構文が導入されている。

プロパティを作成するためには、
まず property ブロックを記述する。
property キーワードの前に virtual を書けば、
プロパティ全体が仮想メンバとなる。

次に、プロパティのアクセサを作成するため、
ブロックの中に get と set メソッドを入れる。

get は引数なしで戻り値がプロパティの型、
set はプロパティの型の引数ひとつで戻り値はなし。
メソッドは片方だけ用意しても構わない。

Handle プロパティは読み取り専用なので、
get メソッドだけ用意することになる。

これで、HwndWrapper マネージクラスができた。

今回は、ヘッダファイルにすべて定義したが、
C++ で従来から行われているように、
定義(実装)のみを cpp ファイルで行い、
宣言のみを hpp ファイルに残す事も可能だ。

余談になるが、マネージクラスの定義を、
宣言と定義に分離する場合、宣言は以下のような形になる。

    private ref class HwndWrapper
            : public System::Windows::Forms::IWin32Window {
    public:
        HwndWrapper(HWND handle);

        virtual property System::IntPtr Handle {
            System::IntPtr get();
        }

    private:
        System::IntPtr handle;

    };

そして、定義は以下のようになる。

    HwndWrapper::HwndWrapper(HWND handle) {
        this->handle = static_cast<System::IntPtr>(handle);
    }

    System::IntPtr HwndWrapper::Handle::get() {
        return handle;
    }

get() アクセサはプロパティの名前空間の下に定義するのだ。

では、本題に戻る。

HwndWrapper ができたので、これを使って
Dispatcher::Configure を再度作ってみよう。
HwndWrapper.hpp を #include することを忘れずに。

    void Dispatcher::Configure(HWND hdlg) {

        // スクリーンセーバーを作成
        HelloSaver saver;

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

        // 設定画面を呼び出す
        saver.Configure(window); // ダメ

    }

うまく行ったように見えるのだが、
実はこれだけではダメなのだ。正しくはこうなる。

    void Dispatcher::Configure(HWND hdlg) {

        // スクリーンセーバーを作成
        HelloSaver saver;

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

        // 設定画面を呼び出す
        saver.Configure(%window); // OK

    }

ん? % って何だ?



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