Version 8.07
ダイアログを作ろう!
「前回はリソーススクリプトを作ってみました」
『作ったってほどじゃないけどねー』
「で、そのリソースを作った理由は?」
『ダイアログを作る!』
「そう、ダイアログを表示するためには、まずリソースにダイアログを作ら
なきゃいけないからね」
『……なんかそのふたつダイアログって出てくるとわかんない……』
「んー、そうだね、〈ウィンドウとしてのダイアログ〉を作るためには、ま
ず〈リソースとしてのダイアログ〉を作る必要がある、ってとこかな」
『リソースのダイアログが元になって、普通のダイアログができるってこと
ね』
「そゆこと。じゃ、まずはコードを見てもらおうか」
#include <Windows.h>
#include <stdio.h>
// リソースをインクルードします。
#include "resource.h"
// ダイアログプロシージャ。
BOOL CALLBACK DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
return FALSE;
}
// WinMain() 。
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
int iRet
= DialogBox
( p_hInstance
, MAKEINTRESOURCE( IDD_MAIN )
, NULL
, DialogProc
);
return 0;
}
『うお、ちょっと長い! 関数がふたつになったね。ひとつは前と同じ
WinMain() だけど』
「関数ひとつひとつも、改行多くしてあるだけで」
『中身はかなりシンプルね……』
「あ、とりあえず実行してみて」
『ほい実行。お、さっき作ったダイアログが表示された!』
「とういうわけで、こうすればダイアログは表示できます。……表示はね」
『む、その思わせぶりな口調は何?』
「とりあえず閉じて」
『ほい。……閉じねぇ!』
「そう、閉じないんです」
『 OK もキャンセルも、右上のペケボタンもタスクバーで閉じるもダメ!』
「 VC で【デバッグの中止】させるしかないかな」
『永久ループしたわけでもないのにこのボタン押すハメになるなんて……』
「そういうわけで、今の段階では表示するだけってこと」
『ってゆーか、ボタン押したら閉じるとかっていうのはデフォルトの機能
じゃないの??』
「じゃないんです」
『 MFC 使わないとかなり面倒そうね……』
「じゃ、その面倒なのを見ていきましょう。まずは WinMain() から見てい
こうか」
『 DialogBox() って関数呼んでるね。まさにダイアログを表示する!
って感じ』
「だね。抜き出してみると」
int iRet
= DialogBox
( p_hInstance
, MAKEINTRESOURCE( IDD_MAIN )
, NULL
, DialogProc
);
「この DialogBox() って API 、正確にはマクロがダイアログを表示しま
す」
『マクロなの?』
「うん、ほとんど同じ DialogBoxParam() って API を呼ぶだけだけどね。
英文の方のリファレンスを見ると書いてあるよ」
『うん、色々書いてある。ダイアログ閉じるには EndDialog() を使うと
か。これ使わないとダイアログは閉じないのねー』
「ただ呼ぶだけじゃダメだけどね」
『そなの?』
「そうなの。それはあとでね。まずは引数をひとつずつ見ていきましょう」
『第1引数はただのインスタンスハンドルね』
「これはいつものだね」
『第2引数は……マクロっぽいのと、ダイアログの ID 、ってことは、ここ
でどのダイアログリソースを使うかって指定してるんだ』
「そゆこと。 MAKEINTRESOURCE は昔と今との違いを吸収するためのマク
ロってとこかな」
『リファレンス見ると第2引数は LPCTSTR 、ってことはホントは文字列渡
すの?』
「なんだけど、今はホントに文字列を渡すことはないかな。この例みたいに
MAKEINTRESOURCE 使ってリソース ID 渡すのが普通」
『第3引数は NULL だね』
「これは〈親ウィンドウ〉を指定するところ。元々のウィンドウがあって、
そのダイアログとして表示するときには」
『ここにその元々の方のウィンドウハンドルを渡せばいいわけね。でもこれ
はそういうのないから』
「 NULL にしておけば大丈夫」
『第4引数が……??? DialogProc って……上にある関数だよね』
「そう、これ」
// ダイアログプロシージャ。
BOOL CALLBACK DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
return FALSE;
}
『つまり、この関数の戻り値を渡してるんだよね。でもそれならわざわざ関
数作る必要ないか』
「そう、それなら必要ないでしょ。そうじゃなくて、この第4引数は〈関数
そのもの〉を渡してるんです」
『???』
「関数そのものってのはおかしいか。どう進めようかな……そうだね、まず
MFC の時を思い出してみようか。 MFC の時、ボタンを押した時に呼ばれる
関数とかがあったでしょ」
『うん、イベントハンドラとかだよね』
「そうそう。 MFC を使わない場合、それと同じ機能を持っているのが
〈ウィンドウプロシージャ〉っていうものなんです」
『ウィンドウプロシージャ? あ、上の関数に〈ダイアログプロシージャ〉
って書いてあるね』
「そうそう、ダイアログプロシージャはウィンドウプロシージャのダイアロ
グ版。ほとんど同じって思って」
『ってことは、この DialogProc() が、ボタンを押したときに呼ばれる?』
「そう、呼ばれます。それだけじゃなく、ウィンドウプロシージャはどーん
なイベントが起きたときでも呼ばれます」
『どんなイベント?』
「マウスがクリックされた、移動した、キー入力があった、ウィンドウが隠
れた」
『再描画の指示とかもあるよねー。む、 MFC はそれ全部別々の関数だった
けど、このウィンドウプロシージャはぜーんぶ送られてきちゃうの!?』
「そゆこと。もう少し説明すれば、 MFC もちゃんとウィンドウプロシー
ジャを持っていて、イベントに合わせてそれぞれの関数を呼び出すってこと
をしてるんです」
『だからイベント毎の関数、イベントハンドラがあったわけね』
「その辺はこれからに取っておくとして、第4引数の話」
『そうそう』
「今言ったように、ダイアログでイベントが起きた時にダイアログプロシー
ジャが呼ばれるわけだけど、そうしてもらうようにダイアログを作るときに
ウィンドウズに登録する必要があるんです」
『それがあの第4引数?』
「そゆこと。あの第4引数で、何かイベントが起きたら DialogProc() を呼
び出してくださいって登録してるんです」
『なるほどねー、第4引数で関数を渡してるっていうのはそういうことなの
ね』
「で、最後にどうやって登録してるかって話。ちょっとだけコード追加し
て」
// WinMain() 。
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// 以下3行追加。
char pchFuncPointer[1024];
sprintf( pchFuncPointer, "DialogProc : %x", DialogProc );
MessageBox( NULL, pchFuncPointer, "WinMain()", MB_OK );
// あとはそのまま。
『むむむ? 何やってんの?』
「実は関数は、 () を付けないと〈関数のアドレス〉になるんです」
『関数のアドレス?』
「 Version 8.02 ( No.144 ) で言ったように、プログラムはメモリに置か
れてるわけでしょ」
『そっか、なら関数も置かれてる、関数のアドレスもある!』
「 DialogBox() の第4引数にもこれを渡してるでしょ。これを実際に見て
みようって話」
『ほいほい。まず実行。 DialogProc : 411005 って出た』
「次に DialogProc() の return 0; にブレークポイント」
『ほい』
「そしたら【混合モード】表示させて」
『あれ? メモリじゃないの?』
「うん、今回は混合モードで。表示させたらダイアログ閉じて」
『ほい。ブレークポイントで止まった』
「混合モードで上の方へ遡っていって、アドレスが 00411005 のとこを見て
みて」
『ほい〜ってあれ? DialogProc() っぽいとこがあるけどそれはいい
の?』
「うん、それは無視してもっと上へ」
『ほいほい。あったよ。結構それらしい感じ』
00411005 E9 16 00 00 00 jmp DialogProc (00411020)
「 jmp ってあるでしょ」
『ジャンプ?』
「そう! これは右端の 00411020 ってアドレスに飛びなさいって命令」
『今度は下の方……あ、これがさっきの DialogProc() っぽいとこなん
だ!』
「整理すると、 DialogBox() の第4引数に DialogProc() のアドレス、
00411005 を渡すと、ウィンドウズは 00411005 を関数って見なして呼び出
します」
『イベントが起きたときにね』
「そうそう。で、このジャンプを経由して」
『実際の DialogProc() が呼ばれる!』
「そういうこと。ま、これだけじゃ分かんないとこもあるかもしれないけ
ど、とりあえずこんなところで」
/*
Preview Next Story!
*/
『ってゆーか、これじゃあ引数渡すのとか理由付かないし』
「その辺はおいおいね」
『げ、そーゆーのも勉強しなきゃいけないの?』
「 C++ 使いはその辺も知ってなきゃいけないんだよねー」
『こーゆーメモリの話から』
「メッセージの処理の仕方まで」
『というわけで次回』
< Version 8.08 ダイアログプロシージャ >
「につづく!」
『なんてゆーか、憶えること多すぎ!』
「そうかなぁ」
『……このプログラミングバカは……』