ホーム WTL Mobile
行儀の良いウィンドウ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2009/07/18
PR
 WTLは行儀の良い(well-behaved)Windows Mobile 用ウィンドウを作成するためにCAppWindowクラステンプレートを用意しています。 CAppWindowクラステンプレートを使用すると、Windows Mobile アプリケーションにレジストリを操作する機能を追加したり、 _tWinMain()の定型的な処理を簡略化することができます。

 次に示すのは、前回のコマンドチェインを追加したプログラムにCAppWindowクラステンプレートを追加する例です。

プロジェクトファイル ダウンロード
// stdafx.h
#pragma once

#define WINVER _WIN32_WCE
#define _SECURE_ATL 1

#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>

#include <aygshell.h>
#pragma comment(lib, "aygshell.lib")

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlframe.h>

#define _WTL_CE_NO_ZOOMSCROLL
#define _WTL_CE_NO_CONTROLS
#include <atlwince.h>
			

// View.h
#pragma once

class CView : public CWindowImpl<CView>
{
public:
    DECLARE_WND_CLASS(NULL)

    // メッセージフィルタ処理
    BOOL PreTranslateMessage(MSG* pMsg){
        return FALSE;
    }

    // メッセージマップ
    BEGIN_MSG_MAP(CView)
        MSG_WM_PAINT(OnPaint)
        COMMAND_ID_HANDLER_EX(ID_RMENU_VERSION, OnRMenuVersion)
    END_MSG_MAP()

    void OnPaint(CDCHandle /*dc*/){
        CPaintDC dc(m_hWnd);
        CRect rect;
        GetClientRect(rect);
        dc.DrawText(_T("Hello, ATL/WTL"), -1,
            rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    }

    void OnRMenuVersion(UINT uNotifyCode, int nID, CWindow wndCtl){
        MessageBox(_T("SampleProject"));
    }
};
			

// MainWindow.h
#pragma once

class CMainWindow : public CFrameWindowImpl<CMainWindow>, 
    public CMessageFilter, public CIdleHandler, 
    public CUpdateUI<CMainWindow>, public CAppWindow<CMainWindow>
{
public:
    // ウィンドウクラス名、共通リソースID、レジストリキーを登録
    DECLARE_APP_FRAME_CLASS(_T("SampleProject"), IDR_MAINFRAME, _T("Software\\WTL"))

    CView m_view;

    virtual BOOL PreTranslateMessage(MSG* pMsg){
        // 基底クラスのPreTranslateMessageを呼び出す
        if(CFrameWindowImpl<CMainWindow>::PreTranslateMessage(pMsg))
            return TRUE;

        // ビューウィンドウクラスのPreTranslateMessageを呼び出す
        return m_view.IsWindow() ? m_view.PreTranslateMessage(pMsg) : FALSE;
    }

    virtual BOOL OnIdle(){
        UIUpdateToolBar();
        return FALSE;
    }

    void AppSave(){
        // ステータスバーの状態をレジストリに保存
        CAppInfo info;
        bool bVisible = ::IsWindowVisible(m_hWndStatusBar) ? true : false;
        info.Save(bVisible, _T("Status"));
    }

    // UI更新ハンドラマップ
    BEGIN_UPDATE_UI_MAP(CMainWindow)
        UPDATE_ELEMENT(ID_RMENU_STATUSBAR, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
    END_UPDATE_UI_MAP()

    BEGIN_MSG_MAP(CMainWindow)
        MSG_WM_CREATE(OnCreate)
        MSG_WM_DESTROY(OnDestroy)
        COMMAND_ID_HANDLER_EX(ID_RMENU_STATUSBAR, OnRMenuStatusbar)
        COMMAND_ID_HANDLER_EX(ID_LMENU_CLOSE, OnLMenuClose)
        CHAIN_MSG_MAP(CUpdateUI<CMainWindow>)         // CUpdateUIへチェイン
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainWindow>)  // CFrameWindowImplへチェイン
        CHAIN_MSG_MAP(CAppWindow<CMainWindow>)        // CAppWindowへチェイン
        CHAIN_CLIENT_COMMANDS()                       // ビューウィンドウへチェイン
    END_MSG_MAP()

    int OnCreate(LPCREATESTRUCT lpCreateStruct){
        // ステータスバーの状態をレジストリから取得
        CAppInfo info;
        bool bVisible = true;
        info.Restore(bVisible, _T("Status"));
        DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
        if(bVisible){
            dwStyle |= WS_VISIBLE;
        }

        // メニューバー作成
        CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);

        // ツールバー作成
        HWND hWndToolBar = CreateSimpleCEToolBar();
        UIAddToolBar(hWndToolBar);

        // ステータスバー作成
        CreateSimpleStatusBar(ATL_IDS_IDLEMESSAGE, dwStyle);

        // ビューウィンドウを作成
        m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, 
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

        // [ステータスバー表示]メニューのチェックマーク設定
        UISetCheck(ID_RMENU_STATUSBAR, bVisible);

        // メッセージループにメッセージフィルタとアイドルハンドラを追加
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        pLoop->AddMessageFilter(this);
        pLoop->AddIdleHandler(this);
        return 0;
    }

    void OnDestroy(){
        // メッセージループからメッセージフィルタとアイドルハンドラを削除
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        pLoop->RemoveMessageFilter(this);
        pLoop->RemoveIdleHandler(this);
        SetMsgHandled(false);
    }

    void OnRMenuStatusbar(UINT uNotifyCode, int nID, CWindow wndCtl){
        bool bVisible = !::IsWindowVisible(m_hWndStatusBar);
        ::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
        UpdateLayout();
        UISetCheck(ID_RMENU_STATUSBAR, bVisible);
    }

    void OnLMenuClose(UINT uNotifyCode, int nID, CWindow wndCtl){
        PostMessage(WM_CLOSE);
    }
};
			

// SampleProject.cpp
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include "MainWindow.h"

CAppModule _Module;

int APIENTRY _tWinMain(HINSTANCE hInstance, 
                       HINSTANCE hPrevInstance, 
                       LPTSTR    lpCmdLine, 
                       int       nCmdShow)
{
    HRESULT hRes = CMainWindow::ActivatePreviousInstance(hInstance, lpCmdLine);
    if(FAILED(hRes) || S_FALSE == hRes){
        return hRes;
    }

    _Module.Init(NULL, hInstance);

    int nRet = CMainWindow::AppRun(lpCmdLine, nCmdShow);

    _Module.Term();

    return nRet;
}
			

 まず、stdafx.hヘッダではCAppWindowクラステンプレートを使用するためにatlwince.hヘッダをインクルードします。 なお、今回の例ではズームスクロールウィンドウやモバイル用コントロールを使用しないため、 _WTL_CE_NO_ZOOMSCROLL_WTL_CE_NO_CONTROLSを定義します。

 CAppWindowクラステンプレートは、メインウィンドウであるCMainWindowクラスの基底クラスとして使用します。 CMainWindowクラスでは、まず、DECLARE_APP_FRAME_CLASSマクロを使用してウィンドウクラス名、 共通リソースID、アプリケーションが使用するレジストリキーを登録します。 CAppWindowクラステンプレートを使用すると、 DECLARE_APP_FRAME_CLASSマクロを含め次のようなマクロが使用できます。

  • DECLARE_APP_FRAME_CLASS(ウィンドウクラス名, 共通リソースID, レジストリキー)
    ウィンドウクラス名、共通リソースID、レジストリキーを定義します。

  • DECLARE_APP_FRAME_CLASS_EX(ウィンドウクラス名, 共通リソースID, スタイル, 背景色, レジストリキー)
    ウィンドウクラス名、共通リソースID、スタイル、背景色、レジストリキーを定義します。

これらのマクロはpublic宣言で使用しなければなりません。 これらのマクロを使ってウィンドウクラス名を明示的に登録しない、 またはウィンドウクラス名としてNULLを指定した場合、WTLは "ATL:00406060" のような名前を自動的に登録します。

 次に、メッセージマップにCAppWindowへのチェインを追加します。 CAppWindowの基底クラスであるCAppWindowBaseはメッセージマップを用意しており、 メッセージによって次のようなメンバ関数を呼び出します。

メッセージ メンバ関数名 説明
WM_COPYDATA AppNewInstance アプリケーションの新しいインスタンスが生成された
WM_CLOSE AppSave アプリケーションを閉じる
WM_HIBERNATE または WM_ACTIVATE AppHibernate アプリケーションのリソースが不足している
WM_HOTKEY AppBackKey バックキーが押された

CAppWindowの派生クラスは、これらのメンバ関数をオーバーライドすることによって、 Windows Mobile アプリケーションの動作をカスタマイズすることができます。 今回の例ではAppSave()をオーバーライドし、 CAppInfo::Save()を呼び出してステータスバーの表示状態をレジストリに保存します。 CAppInfoはアプリケーション情報をDECLARE_APP_FRAME_CLASSマクロで設定したレジストリキーに対して保存/取得するクラスで、 CAppWindowの派生クラスで使用できます。

 次に、WM_CREATEメッセージハンドラでは、 CAppInfo::Restore()を呼び出してステータスバーの表示状態をレジストリから取得し、 ステータスバーのウィンドウスタイルとメニューのチェックマークを設定します。

 最後に_tWinMain()では、ActivatePreviousInstance()を呼び出して多重起動を防止し、 AppRun()を呼び出してアプリケーションを実行します。 なお、ActivatePreviousInstance()を使用すると、 WTLは共通リソースIDの文字列リソースを固定値のウィンドウクラス名として登録します。 (つまり、DECLARE_APP_FRAME_CLASSマクロ等で登録したウィンドウクラス名を上書きします。) 言い換えると、ActivatePreviousInstance()を使用する場合は共通リソースID(今回の例ではIDR_MAINFRAME)の文字列リソースを用意する必要があります。