WTLのウィザードを使ってダイアログを作ってみましょう
新規のプロジェクトを作成
[ ファイル ] - [ 新規作成 ] - [ プロジェクト ] で以下の画面が表示されます。

"プロジェクト名"に適当な名前(ここでは、例として"dlgtest"とします。)を設定し、"OK"ボタンをクリックします。
プロジェクト詳細を設定

"次へ"ボタンをクリックします。

ここで、
画面左の"アプリケーションの種類"で
"ダイアログベース"を選択します。
"モーダルダイアログ"にチェック入れます。
画面右の"プロジェクトのオプション"で
".CPPファイル"にチェック入れます。(MFCのウィザードで吐き出されるCPPと比較するためにCPPファイルを作成します)
"完了"ボタンをクリックします。
例として"dlgtest"のプロジェクト名であれば、以下のファイルが作成されたはずです。
WTL | MFC |
res/dlgtest.ico
dlgtest.cpp
dlgtest.h
dlgtest.rc
MainDlg.cpp
MainDlg.h
resource.h
stdafx.cpp
stdafx.h
|
res/dlgtest.ico
res/dlgtest.manifest
res/dlgtest.rc2
dlgtest.cpp
dlgtest.h
dlgtest.rc
dlgtestDlg.cpp
dlgtestDlg.h
Resource.h
stdafx.cpp
stdafx.h
|
MainDlg(WTL)とdlgtestDlg(MFC)は、名前は異なりますが、ほぼ同じ機能をもつクラスです。
多少のリソース用のファイルの持ち方は異なりますが、ほぼ、1対1の対応したファイルが作成されます。
ウィザードの罠(1)
(罠というほどのものでもありませんが、とりあえずタイトルに従いまして・・・)
ここに至る前に、ウィザードの最後に、"完了"ボタンをクリックした時、以下のエラーが出力され、
自動的にプロジェクトを読み込めないことがあります。
クラスはオートメーションをサポートしていません。
これは、このウィザードスクリプトが、Expressでのウィザードであることを正しく認識していないために
発生します。
WTLのウィザードセットアップのスクリプトファイル(setupxx.js)を以下のように1行追加します。
※スクリプトファイルは、
%WTLインストールディレクトリ%\AppWiz\setupxx.js
の名前であります。
setupxx.js の xx には、
70:VC++70用
71:VC++71用
80:VC++80用
があります。
90 : VC++90用 は、正式には、まだないようです。個別には、色々なサイトで提供されています。
このサイトでも提供しています。
setup90.jsの124行目あたりの以下の行に1行追加します。
1
2
3
| fileDest.WriteLine("Param=\"NO_RESOURCE_EDITOR = 1\"");
// 以下の行を追加します。
fileDest.WriteLine("Param=\"VC_EXPRESS = 1\"");
|
上記を変更し、保存した後、ダブルクリックで、再度、セットアップします。
その後のウィザードでは、上記のエラーはでないと思います。
これをコンパイルして実行してみると、以下のような単純な画面が表示されます。
新しいボタンを追加して、メッセージボックスを表示してみましょう
新しいボタンを追加する
ResEditを使って、ボタンを追加します。
ここでは、例として
Caption:ありがとう
ID:IDC_BUTTON1
とします。
スライダーコントロールやピクチャーコントロールなどを貼り付けて、(以下のメッセージのような)リソースのコンパイルエラーが発生する場合があります。
これは、COMMCTRLのヘッダを正しく読み込めていない場合などに発生することが多いようです。
(単純に環境設定に問題があります。)
error RC2104 : undefined keyword or key name: WC_STATIC
[ ツール ] - [ オプション ]
[ プロジェクトおよびソリューション ](画面左) - [ インクルードファイル ](画面右)

で、Microsoft Platform SDKのincludeディレクトリを先頭まで、順位を上げてあげます。
%Platform SDKインストールディレクトリ%\include
例) C:\Program Files\Microsoft Platform SDK\Include
その後、リビルドを実施してみてください。先のエラーはなくなると思います。
新しいボタンのハンドラを追加する
VC++側で、OKボタンのハンドラにならって、追加していきます。
[MainDlg.hに追加]
1
2
3
4
5
6
7
8
9
10
11
12
| BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
:
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
// 以下の1行を追加!!
COMMAND_ID_HANDLER(IDC_BUTTON1, OnThankYou)
END_MSG_MAP()
:
:
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
// 以下の1行を追加!!
LRESULT OnThankYou(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
|
[MainDlg.cppの最終行に追加]
1
2
3
4
5
| LRESULT CMainDlg::OnThankYou(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
MessageBox("ThankYou");
return 0;
}
|
ビルドして実行してみる
ビルドして、実行してみると簡単にメッセージボックスが表示できたと思います。
ここまでは、非常に簡単でした。MFCをお使いの方であれば、このハンドラのパラメータの多さに少々びっくりされているでしょう。
COMMAND_ID_HANDLERのマクロの定義を見ると、更に、?があります。
1
2
3
4
5
6
7
8
| #define COMMAND_ID_HANDLER(id, func) \
if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}
|
確かに動作的に同じになるのですが、wParamのlowしか見ていないのです。
実際にMFCでは、以下のように展開されるのですから、同じようにするには、wParamのhighを"BN_CLICKED"で確認した後、ハンドラのコールを行うべきですね。
ただ、これは、これでも良いのだとは思いますが、ここでは、正確にいきますので、次に、MFCに近づけていきたいと思います。
(WTLでもそれに相当するハンドラの定義があります。)
[dlgtestDlg.hの最後に追加]
1
2
3
4
| :
public:
afx_msg void OnThankYou();
:
|
[dlgtestDlg.cppに追加]
1
2
3
4
5
6
7
8
9
10
11
12
13
| BEGIN_MESSAGE_MAP(CdlgtestDlg, CDialog)
:
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON1, OnThankYou)
END_MESSAGE_MAP()
:
:
:
void CdlgtestDlg::OnThankYou()
{
// TODO : ここにコントロール通知ハンドラ コードを追加します。
MessageBox("ThankYou");
}
|
MFCのハンドラに近づける
メッセージのチェックをMFCに近づける
MFCに近づけるために、WTLで定義されている"COMMAND_HANDLER_EX"を使います。
(atlclack.hにほとんどのメッセージマップマクロ定義があります。)
これを使って、以下のように変更を行います。
[MainDlg.hの変更]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
:
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
// 以下の1行を変更!!
//COMMAND_ID_HANDLER(IDC_BUTTON1, OnThankYou)
COMMAND_HANDLER_EX(IDC_BUTTON1, BN_CLICKED,OnThankYou)
END_MSG_MAP()
:
:
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
// 以下の1行を変更!!
// LRESULT OnThankYou(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
void OnThankYou(UINT uNotifyCode, int nID, CWindow wndCtl);
|
[MainDlg.cppの変更]
1
2
3
4
5
6
| //LRESULT CMainDlg::OnThankYou(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
void CMainDlg::OnThankYou(UINT uNotifyCode, int nID, CWindow wndCtl)
{
MessageBox("ThankYou");
// return 0;
}
|
ウィザードの罠(2)
このままコンパイルすると以下のエラーが出ます。
error C3867: 'CMainDlg::OnThankYou': 関数呼び出しには引数リストがありません。メンバへのポインタを作成するために '&CMainDlg::OnThankYou' を使用してください
error C2143: 構文エラー : ';' が 'break' の前にありません。
error C3861: 'COMMAND_HANDLER_EX': 識別子が見つかりませんでした
ウィザードで作成したものは、デフォルトで、WTLのメッセージマップマクロのヘッダと取り込んでいないのです。
stdafx.hに以下のように追加してやります。
1
2
3
4
5
| :
#include <atldlgs.h>
// 追加
#include <atlcrack.h> // WTL enhanced msg map macros
:
|
これで大丈夫かとおもいきや、コンパイルを実行すると以下のようなエラーが出力されます。
error C3861: 'SetMsgHandled': 識別子が見つかりませんでした
error C3861: 'IsMsgHandled': 識別子が見つかりませんでした
これは、WTL80からの問題か?不明ですが、マップ開始定義に、"BEGIN_MSG_MAP"でなく
"BEGIN_MSG_MAP_EX"を使用しなければならなくなっています。
ドキュメントを見る限りでは、WTL7.0以降は、"BEGIN_MSG_MAP"でも"BEGIN_MSG_MAP_EX"でも良いように記述がありますが、実際には、そのドキュメントの注意に従い
"BEGIN_MSG_MAP_EX"を使わないといけないようです。
ここまでやると、ビルドが完了できるはずです。
罠?というほどのことはなくて、単純に、ウィザードをそのまま鵜呑みにしていると、カスタマイズするときに、
結構、面倒ですよという意味合いでくみとっていただけると助かります。
実際に、WTLを使うためのウィザードではなく、ATLで画面を簡単に作成するためのウィザードと捕らえることが
混乱を招かないと思います。
MFCのウィザードでは、ウィザードを使った後は、MFCを使ってたいていのことができますが、
WTLのウィザードでは、ウィザードを使った後は、
ATLを使ってたいていのことができる環境が整うと思うべきなのでしょうね。

結局のところ、WTLは、WindowsAPI+ATLとテンプレートの組み合わせです。
もっと、そのあたりについて詳しく知りたい方は、以下の本なども良いと思います。
はじめて間もない方は、2,3冊読まれると、ネットの記事を読んでも、おおよその理解ができるようになると思います。
本は、経験者でも、ネットだけでは判らない様々な事に気づかされることがあります。
コメントをどうぞ