Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

ATL/WTLプログラミング 1:ATL/WTLの概要と基本

ATL/WTLを利用したVisual C++のWindowsプログラミング

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/10/05 12:00

Windowsアプリケーションを作成するためのC++クラスライブラリといえば、Microsoftが提供するMFCが有名ですが、同社が提供するライブラリATLを利用して作成することもできます。本稿では、ATLおよびATLの拡張ライブラリとも言えるWTLを使用したWindowsプログラミングについて説明します。

目次

はじめに

 Windowsアプリケーションを作成するためのC++クラスライブラリといえば、Microsoftが提供するMFCが有名ですが、同じくMicrosoftが提供するライブラリATLを利用することでもWindowsアプリケーションを作成することができます。

 本稿では、ATLおよびATLの拡張ライブラリとも言えるWTLを使用したWindowsプログラミングについて説明します。本稿を通してWTLに興味を持っていただけると幸いです。

対象読者

 ATL/WTLによるWindowsプログラミングに興味があり、C++やWin32APIによるWindowsプログラミングの基本的な知識がある方。

必要な環境

 サンプルはVisual C++ 6.0で作成し、Windows 2000で動作確認しています。

ATLとWTL

 ATLとWTLの概要を示します。

ATLとは

 「ATL(Active Template Library)」とは、Visual C++ に付属するC++テンプレートライブラリです。主にCOMをサポートするライブラリとして知られていますが、Windowsプログラミングを簡略化するクラスも備えています。そのようなクラスを使用する場合は必ずしもCOMの知識は必要ありません。

WTLとは

 「WTL(Windows Template Library)」とは、主にWindowsのGUI部分(コントロール、コモンダイアログ、コマンドバー、ペインコンテナなど)をサポートするC++テンプレートライブラリで、ATLの拡張ライブラリと言えます。元々Microsoftが無料で提供していましたが、2004年5月にオープンソース化されました。ATLと同様、Win32APIをラッパクラスで覆うことにより、Windowsプログラミングを簡略化します。

コマンドバー
コマンドバー
ペインコンテナ
ペインコンテナ

WTLのインストール

 以下にWTLのインストール方法を示します。

WTLの入手

 WTLは、オープンソース化される前のバージョン(WTL 7.1)とオープンソース化された後のバージョン(WTL 7.5以降)で、入手場所が異なります。

ATL/WTL AppWizardのインストール

 WTLにはAppWizardが付属しています。WTL入手先でダウンロードしたファイルを展開すると「AppWiz」フォルダの中に「setupXX.js」(XXはVisualC++のバージョンを表す数字)ファイルが入っています。このファイルをダブルクリックするとAppWizardファイルがVisual C++の既定のフォルダにコピーされ、プロジェクトの新規作成時にATL/WTL AppWizardのアイコンが表示されるようになります。

Visual C++ 6.0用のATL/WTL AppWizard
 最新のWTLにはVisual C++ 6.0用のATL/WTL AppWizardが含まれていません。
 Visual C++ 6.0にATL/WTL AppWizardを追加するためには、WTL 7.1の中に入っている「AppWiz60」フォルダの「setup.js」ファイルを実行します。「setup.js」ファイルを実行すると、AppWizardファイルがVisual C++の既定のフォルダにコピーされ、プロジェクトの新規作成時にATL/WTL AppWizardのアイコンが表示されるようになります。
Visual C++ 6.0用のATL/WTL AppWizard
Visual C++ 6.0用のATL/WTL AppWizard

インクルードファイルパスの設定

 WTLはC++ヘッダファイルのみで構成されています。どのバージョンのWTLを使う場合でも、Visual C++のインクルードファイルパスにWTLの「include」フォルダパスを追加するだけでインストールは完了です。

インクルードファイルパス設定
インクルードファイルパス設定

ATLウィンドウ

 まずはATLのみでウィンドウを作成するサンプルを示します。

 ATLにおける最も基本的なウィンドウ関連クラスはCWindowです。CWindowクラスはウィンドウハンドルをカプセル化し、ウィンドウに関するWin32APIをラップする多くのメンバ関数を持ちます。

 ただし、CWindowクラスはメッセージへの反応を定義することはできません。ATLではCWindowImplクラスから派生クラスを作り、そのクラス内でメッセージへの応答を定義します。以下の示すのは、ウィンドウ中央に「Hello, ATL/WTL」と表示するだけの簡単なプログラムのソースコードです。このプログラムでは、CWindowImplクラスから派生クラスCMyWindow(これがメインウィンドウとなります)を作り、 CMyWindowクラス内でWM_PAINTWM_DESTROYメッセージへの応答を定義しています。なお、このプログラムはATL/WTL AppWizardを使用せずWin32Applicationプロジェクトでビルドします。

Hello, ATL/WTLプログラム
Hello, ATL/WTLプログラム
「stdafx.h」
#include <atlbase.h>
extern CComModule _Module;
#include <atlwin.h>
「MainWindow.h」
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    // ウィンドウクラス名を登録
    DECLARE_WND_CLASS(_T("Hello"));

private:
    // メッセージマップ
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_PAINT, OnPaint)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    END_MSG_MAP()

    LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL&){
        PAINTSTRUCT ps;
        HDC hDC = BeginPaint(&ps);
        RECT rect;
        GetClientRect(&rect);
        DrawText(hDC, _T("Hello, ATL/WTL"),
            -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(&ps);
        return 0;
    }

    LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&){
        PostQuitMessage(0);
        return 0;
    }
};
「hello.cpp」
#include "stdafx.h"
#include "MainWindow.h"

CComModule _Module;

int WINAPI _tWinMain(HINSTANCE hInstance,
    HINSTANCE, LPTSTR lpCmdLine, int nCmdShow)
{
    _Module.Init(NULL, hInstance);

    // ウィンドウを作成
    CMyWindow wnd;
    wnd.Create(NULL, CWindow::rcDefault,
        _T("Hello, ATL/WTL"), WS_OVERLAPPEDWINDOW | WS_VISIBLE);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0) > 0){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    _Module.Term();

    return msg.wParam;
}

 まず、ATLを使用するためのヘッダをインクルードしますが、CComModuleクラスのインスタンスである _ModuleはATLの各ヘッダから参照されるのでグローバルに宣言しておきます。この_Moduleは、_tWinMain()の最初と最後で初期化と後始末をしています。

 CMyWindowクラスはCWindowImplクラスから派生していますが、CWindowImplの第1テンプレート引数にもCMyWindowという名前を渡します(第2、3テンプレート引数は省略可能です。ここでは省略しています)。CMyWindowクラス内では、ウィンドウクラス名をDECLARE_WND_CLASSマクロによって登録し、メッセージマップによってメッセージとそれに対するハンドラを結びつけています。今回の例では、ハンドラ内の処理はSDKスタイルとほぼ同等です。

ウィンドウクラス情報

 SDKスタイルのWindowsプログラミングでは、ウィンドウクラス名や背景色、カーソルなどの属性を指定するためにWNDCLASSEX構造体を埋めていきますが、 ATLにはその作業を単純化するマクロや構造体が用意されています。

  • DECLARE_WND_CLASS(ウィンドウクラス名)
  • DECLARE_WND_CLASS_EX(ウィンドウクラス名, スタイル, 背景色)
  • CWndClassInfo構造体

 CWndClassInfo構造体はウィンドウクラス名マクロよりもウィンドウの属性を細かく指定できます。CWndClassInfoを使用して新しい属性を指定するためには、 CWindowImpl::GetWndClassInfo()をオーバーライドして、カスタマイズした静的なCWndClassInfoインスタンスの参照を返します。

CWndClassInfo構造体の使用例
class CMyWindow: public CWindowImpl<CMyWindow>
{
public:
    static CWndClassInfo& GetWndClassInfo()
    {
        static CWndClassInfo wc =
        {
            {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW,
            StartWindowProc, 0, 0, NULL, NULL, NULL,
            (HBRUSH)(COLOR_WINDOW + 1), NULL, 
            "MyWindow", NULL},  // WNDCLASSEX構造体
            NULL,               // 既存のウィンドウクラス名
            NULL,               // 既存のウィンドウプロシージャ
            MAKEINTRESOURCE(IDC_CURSOR1), // カーソルリソース名
            // システムカーソルならばTRUE、それ以外はFALSE
            FALSE,
            0,                  // 登録済みウィンドウクラスの識別子
            _T("")              // ATLが自動生成したウィンドウクラス名
        };
        return wc;
    }
...
};

ウィンドウ特性

 ATLでは、ウィンドウのスタイルは、CWinTraitsクラスによる「ウィンドウ特性」として表現できます。CWinTraitsクラスは、ウィンドウスタイルと拡張ウィンドウスタイルをサポートします。

 「Hello, ATL/WTL」プログラムでは、ウィンドウ作成時にCWindowImpl::Create()の第4引数でウィンドウスタイルを指定しました。この第4引数は省略可能です。省略した場合は、次のように宣言されているCControlWinTraitsクラスがデフォルト値として使用されます。

CControlWinTraits
typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
    WS_CLIPSIBLINGS, 0> CControlWinTraits;

 CControlWinTraitsクラスは、CWindowImplクラスの定義でデフォルトのテンプレート引数として次のように使用されています。

CWindowImplのデフォルトテンプレート引数
template <class T,
          class TBase = CWindow,
          class TWinTraits = CControlWinTraits>
class CWindowImpl : public ...

 このため、CWindowImplクラスの第3テンプレート引数に独自のウィンドウ特性を指定することによって、独自のウィンドウスタイルを指定することも可能です。

独自のウィンドウ特性を持つウィンドウ
typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_VISIBLE, WS_EX_CLIENTEDGE>
CMyTraits;

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyTraits>
{...};

 なお、これらのテンプレート引数によるウィンドウ特性は、CWindowImpl::Create()の引数によって上書きされます。つまり、CWindowImpl::Create()で指定したウィンドウスタイルの方が優先されます。

ATLのメッセージマップ

 ATLでは、メッセージとそれに対するハンドラ関数をメッセージマップによって結び付けます。

ATLのメッセージマップとメッセージハンドラ関数
class CMyWindow : public CWindowImpl<CMyWindow>
{
    // メッセージマップ
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_PAINT, OnPaint)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    END_MSG_MAP()

    LRESULT OnPaint(UINT uMsg, WPARAM wParam,
        LPARAM lParam, BOOL& bHandled){
        // WM_PAINTメッセージへの応答処理
    }

    LRESULT OnDestroy(UINT uMsg, WPARAM wParam,
        LPARAM lParam, BOOL& bHandled){
        // WM_DESTROYメッセージへの応答処理
    }
};

 ウィンドウがメッセージを受け取ると、メッセージマップの先頭から順に検索されるので、頻繁に使用されるメッセージを最初の方に記述しておくと良いでしょう。メッセージマップ中に対応するメッセージマクロが見つからなければ、メッセージはデフォルトウィンドウプロシージャに渡されます。

 ATLが用意するメッセージマクロは大きく分けて3種類あります。それは汎用メッセージハンドラマクロ、コマンドメッセージハンドラマクロ、通知メッセージハンドラマクロの3つです。

汎用メッセージハンドラマクロ

 すべてのメッセージを対象とします。汎用メッセージハンドラマクロには次の2つがあります。

  • MESSAGE_HANDLER(メッセージ名, ハンドラ名)
  • MESSAGE_RANGE_HANDLER(開始位置のメッセージ名, 終了位置のメッセージ名, ハンドラ名)
汎用メッセージハンドラ関数のプロトタイプ
// ハンドラ関数名は任意
LRESULT MessageHandler(UINT uMsg, WPARAM wParam,
    LPARAM lParam, BOOL& bHandled);

 uMsgはメッセージを識別し、wParamlParamはメッセージパラメータです。メッセージパラメータの内容はメッセージの種類によって変わります。 bHandledはメッセージの処理を終えたかどうかを示すフラグです。 bHandledがハンドラ関数の中でFALSEに設定されていると、メッセージマップの残りの部分で、そのメッセージのためのハンドラが別にないかどうかが検索されます。 bHandledはハンドラ関数の呼び出し前にTRUEに設定されるので、ハンドラ関数がbHandledを明示的にFALSEに設定しない限り、それ以上のハンドラ検索処理は行われません。

コマンドメッセージハンドラマクロ

 コマンドメッセージ(WM_COMMAND)を対象とします。コマンドメッセージハンドラマクロには次の4つがあります。

  • COMMAND_HANDLER(コントロールID, 通知コード, ハンドラ名)
  • COMMAND_ID_HANDLER(コントロールID, ハンドラ名)
  • COMMAND_CODE_HANDLER(通知コード, ハンドラ名)
  • COMMAND_RANGE_HANDLER(開始位置のメッセージ名, 終了位置のメッセージ名, ハンドラ名)
コマンドメッセージハンドラ関数のプロトタイプ
// ハンドラ関数名は任意
LRESULT CommandHandler(WORD wNotifyCode, WORD wID,
    HWND hWndCtl, BOOL& bHandled);

 wNotifyCodeは通知コード、wIDはコマンドを送信しているコントロールの識別子、hWndCtlはコマンドを送信しているコントロールのハンドル、bHandledは前述のフラグです。

通知メッセージハンドラマクロ

 通知メッセージ(WM_NOTIFY)を対象とします。通知メッセージハンドラマクロには次の4つがあります。

  • NOTIFY_HANDLER(コントロールID, 通知コード, ハンドラ名)
  • NOTIFY_ID_HANDLER(コントロールID, ハンドラ名)
  • NOTIFY_CODE_HANDLER(通知コード, ハンドラ名)
  • NOTIFY_RANGE_HANDLER(開始位置のメッセージ名, 終了位置のメッセージ名, ハンドラ名)
通知メッセージハンドラ関数のプロトタイプ
// ハンドラ関数名は任意
LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);

 idCtrlは通知を送信しているコントロールの識別子、pnmhNMHDR構造体へのポインタ、 bHandledは前述のフラグです。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

バックナンバー

連載:ATL/WTLを利用したVisual C++のWindowsプログラミング
All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5