ホーム < ゲームつくろー! < IKD備忘録

COM
ATLチュートリアルを噛み砕いてみる その2

(2009. 7. 18)


 ケツカッチンだ。やらなくちゃ(大汗)。前章でプログラムを1行も書かずにATLコントロールがIE上に出ました。チュートリアルを続けます。

○ 手順3: コントロールへのプロパティの追加

 前章で作成したATLコントロールは四角い枠の中にテキストが表示されているものでした。なぜそう出るのかは置いておき、これにプロパティ(変数)を追加するのがこの手順の目的です。

 前章の手順でクラスビューには沢山のクラスが自動生成されています。その中に「IPolyCtl」というクラス(インターフェイス)があります。ここで右クリックして[追加]→[プロパティの追加]でウィザードが開きます。プロパティの種類にSHORT型、プロパティ名に指示通り「Sides」と記入し完了します。Sidesはどうやら多角形の辺の数のようです。この追加によりIPolyCtlインターフェイスには次の2行が追加されます:

Polygon.idlファイル
[propget, id(1), helpstring("プロパティ Sides")] HRESULT Sides([out, retval] SHORT* pVal);
[propput, id(1), helpstring("プロパティ Sides")] HRESULT Sides([in] SHORT newVal);

idlファイル(インターフェイス定義言語ファイル)は、その名の通りDLLに含まれるインターフェイス(メソッド)を定義する特殊なファイルです。上の場合、SidesというSHORT型の変数に対するゲッターとセッターが定義されている事がわかります。

 これはあくまでもインターフェイスを教えるファイルです。実体は「CPolyCtlクラス」にあります。ただ、ここにはSidesに該当する変数は追加されていません。ここは自前で入れるようです。なるべくVisual Studioの力で入れた方が間違いが無いかなと思いますので、クラスの上で右クリックして[追加]→[変数の追加]で追加してみました。すると、ヘッダーの一番したに追加してくれたようです:

private:
    short m_nSides;

 この変数の直ぐ上にある次の記述、

STDMETHOD(get_Sides)(SHORT* pVal);
STDMETHOD(put_Sides)(SHORT newVal);

これがメソッドの宣言です。マクロなので微妙に分かりにくさはありますが、空気は読めますよね。このメソッドの実装部分はCPolyCtl.cppにあります。ご丁寧に「ここに書いてね」とコメントがあります。その通りにします:

STDMETHODIMP CPolyCtl::get_Sides(SHORT* pVal)
{
    *pVal = m_nSides;
    return S_OK;
}

STDMETHODIMP CPolyCtl::put_Sides(SHORT newVal)
{
    if ( m_nSides > 2 && m_nSides < 101 )
        m_nSides = newVal;
    else
        return Error(_T("Shape must have between 3 and 100 sides"));

    return S_OK;
}

セッターはともかくゲッターがポイントですね。3角形以上100角形以下がOKで、それ以外は駄目。この時returnで上のようにしてエラーを返せるというのは覚えておきたいところですね。


○ 手順4: 描画コードの変更

 手順3で辺の数を設定できるようになったので、実際にその辺の数で多角形を描画できるようにプログラムするのが手順4の目的です。プログラムらしくなってきました。チュートリアルによると、描画は「CPolyCtl::OnDrawメソッド」の中に書くようです。OnDrawという名前から、これが「ハンドラ」である事がわかります。誰かがきっと呼んでくれる・・・んでしょう。

 どう書くかはチュートリアルにあります。こう書くようです:

HRESULT CPolyCtl::OnDraw(ATL_DRAWINFO& di)
{
    RECT& rc = *(RECT*)di.prcBounds;
    HDC hdc = di.hdcDraw;

    COLORREF colFore;
    HBRUSH hOldBrush, hBrush;
    HPEN hOldPen, hPen;

    // Translate m_colFore into a COLORREF type
    OleTranslateColor(m_clrFillColor, NULL, &colFore);

    // Create and select the colors to draw the circle
    hPen = (HPEN)GetStockObject(BLACK_PEN);
    hOldPen = (HPEN)SelectObject(hdc, hPen);
    hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

    Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);

    // Create and select the brush that will be used to fill the polygon
    hBrush = CreateSolidBrush(colFore);
    SelectObject(hdc, hBrush);

    CalcPoints(rc);
    Polygon(hdc, &m_arrPoint[0], m_nSides);

    // Select back the old pen and brush and delete the brush we created
    SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);
    DeleteObject(hBrush);

    return S_OK;
}

え〜と、まんまWindowsプログラムですが、ATL系な記述も見え隠れしています。まず引数のATL_DRAWINFO構造体。調べると描画に必要な情報が色々と入っているようです。prcBoundsは描画可能範囲かな。OleTranslateColorはコメントにあるようにm_clrFillColor(OLE_COLOR型)をCOLORREF型に変換してくれるようです。後はペンやブラシをデバイスコンテキストに設定して、Ellipse関数で楕円を描画、さらに「CalcPoints関数」という多角形の頂点座標を計算する関数で頂点座標を貰い、Polygon関数でそれを描画しています。通り一遍で難しいところはありません。まぁ、コピペで次にいこう。

 これをビルドするとエラーが2つ。CalcPoints関数なんて知らない、m_arrPointなんて知らない。この多角形の頂点座標を算出する関数を実装です。まぁ・・・チュートリアルにあるのを利用するだけですが。

 ヘッダー(PolyCtl.h)に関数を次のように定義します:

private:
    void CalcPoints(const RECT& rc);

これ、先ほどのゲッターやセッターと違って普通の関数宣言です。もちろん外部非公開。実装はチュートリアルにあるので省略。・・・とそのままコピペしたら「RECTなんて知らない」と言われました。windows.hをPolyCtl.cppにインクルードしておきましょう。これでエラー一つ解消。

 もう1つ「m_arrPointなんて知らない」というエラーの対処は簡単です。m_arrPointは頂点座標の配列でPOINT型です。

private:
    short m_nSides;
    POINT m_arrPoint[100];

これでビルドすると、「PolyCtl.cpp内のsin、cosなんて知らない」と起こられます。数学関数がインクルードされていないためです。PolyCtl.cppにmath.hをインクルードです。これで再度ビルドすると、ATLコントロールが無事できました。ソルーションエクスプローラにあるPolyCtl.htmをブラウザで開くとこうなります:

ん?多角形が見えません。あ、これは描画時に多角形に色を付けていないからです。多角形の色は確かm_clrFillColorです。これをCPolyCtl.hにあるPolyCtlクラスのコンストラクタで適当に設定します:

PolyCtl.h
CPolyCtl() : m_nSides(3), m_clrFillColor(RGB(0, 0xFF, 0))
{
}

多角形の辺の初期値も設定しないといけないですね。これでビルドすると〜:

はい出ました〜。

 出たのは良いのですが、せっかく多角形の辺の数を変更するセッターがあるのにこのままですと何もできません。宝の持ち腐れです。これを何とかテストしたいわけです。これについては次で。