通常、メニューバーやツールバーからのWM_COMMANDメッセージは、
メインウィンドウのメッセージマップが処理します。
しかし、特定のクラスやビューウィンドウのメッセージマップで処理したい場合もあります。
そのような時は、WTLのコマンドチェインマクロを使用します。
ATLもいくつかのチェインマクロを用意していますが、
コマンドチェインマクロはWM_COMMANDメッセージ専用のチェインマクロです。
WTLは次のようなコマンドチェインマクロを用意しています。
CHAIN_COMMANDS(基底クラス名)
WM_COMMANDメッセージを、指定された基底クラスのメッセージマップにチェインします。
CHAIN_COMMANDS_MEMBER(メンバ変数名)
WM_COMMANDメッセージを、指定されたメンバ変数のメッセージマップにチェインします。
CHAIN_COMMANDS_ALT(基底クラス名, 代替メッセージマップ番号)
WM_COMMANDメッセージを、指定された基底クラスの代替メッセージマップにチェインします。
CHAIN_COMMANDS_ALT_MEMBER(メンバ変数名, 代替メッセージマップ番号)
WM_COMMANDメッセージを、指定されたメンバ変数の代替メッセージマップにチェインします。
CHAIN_CLIENT_COMMANDS()
WM_COMMANDメッセージを、m_hWndClientで識別されるビューウィンドウのメッセージマップにチェインします。
次に示すのは、前回のビューウィンドウを追加したプログラムにコマンドチェインを追加し、
ビューウィンドウ側でリソースIDがID_RMENU_VERSIONのWM_COMMANDメッセージを処理する例です。
// 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> // CFrameWindowImplを使用するため
|
// 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:
// ウィンドウクラス名、共通リソースIDを登録
DECLARE_FRAME_WND_CLASS(_T("SampleProject"), IDR_MAINFRAME)
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;
}
// 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_CLIENT_COMMANDS() // ビューウィンドウへチェイン
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
// メニューバー作成
CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
// ツールバー作成
HWND hWndToolBar = CreateSimpleCEToolBar();
UIAddToolBar(hWndToolBar);
// ステータスバー作成
CreateSimpleStatusBar();
// ビューウィンドウを作成
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
// [ステータスバー表示]メニューのチェックマーク設定
UISetCheck(ID_RMENU_STATUSBAR, true);
// メッセージループにメッセージフィルタとアイドルハンドラを追加
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)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
TCHAR szAppName[] = _T("SampleProject");
HWND hWnd = FindWindow(szAppName, szAppName);
if(hWnd){
SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
return 0;
}
_Module.Init(NULL, hInstance);
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainWindow wnd;
wnd.CreateEx();
wnd.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
_Module.Term();
return nRet;
}
|
まず、リソースIDがID_RMENU_VERSIONのWM_COMMANDメッセージ用メッセージマップエントリおよびメッセージハンドラを、
CMainWindowクラスからCViewクラスへ移動します。
そして、CMainWindowクラスのメッセージマップにCHAIN_CLIENT_COMMANDS()
コマンドチェインマクロを追加します。
これにより、リソースIDがID_RMENU_VERSIONのWM_COMMANDメッセージは、
ビューウィンドウであるCViewクラスに送られ、
CViewクラスのOnRMenuVersion()が呼び出されるようになります。
なお、今回の例ではCHAIN_CLIENT_COMMANDS()マクロを使用しましたが、
CHAIN_COMMANDS_MEMBER()マクロを使用することもできます。
しかし、CHAIN_COMMANDS_MEMBER()マクロを使用すると、
チェイン先のビューウィンドウがWTLメッセージクラッカーを使用している場合にアサートが発生します。
// MainWindow.hのメッセージマップ
// このままではチェイン先のビューウィンドウでアサートが発生
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_COMMANDS_MEMBER(m_view) // ビューウィンドウへチェイン
END_MSG_MAP()
|
これを解消するためには、次のようにビューウィンドウのBEGIN_MSG_MAPマクロをBEGIN_MSG_MAP_EXマクロに変更します。
// View.hのメッセージマップ
BEGIN_MSG_MAP_EX(CView)
MSG_WM_PAINT(OnPaint)
COMMAND_ID_HANDLER_EX(ID_RMENU_VERSION, OnRMenuVersion)
END_MSG_MAP()
|
または、ビューウィンドウでWTLメッセージクラッカーを使用しないように変更します。
// View.hのメッセージマップ
BEGIN_MSG_MAP(CView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_ID_HANDLER(ID_RMENU_VERSION, OnRMenuVersion)
END_MSG_MAP()
|
|