Version 18.02
計算機アプリを API だけで作る
「前回は計算機アプリ用プロジェクト、 NewCalc プロジェクトを作成して、
プログラムも書き込みました」
『でもまだ完成してないなんて……』
「そう、なぜかというと Version 8.06 ( No.148 ) で説明したダイアログの
【リソーススクリプト】を作ってないから」
『あ、そういえばそんなものが……』
「というわけで、まずはリソーススクリプトを作ります。メニューの
【ファイル】−【新規作成】で、下から2番目に【リソース スクリプト】が
あるからそれを選択してください。ファイル名は NewCalc.rc にしてくださ
い。 OK ボタンを押すと」
『リソースのフォルダが出たよ』
「この【NewCalc.rc】フォルダを右クリックして【挿入】選んで、表示され
た【リソースの挿入】ダイアログで【Dialog】を選んで【新規作成】ボタン
を押してください」
『ほい。ダイアログ作られたよ、いつもの OK ボタンと キャンセルボタン
があるやつ』
「まずこのダイアログの ID を IDD_MAIN に変更します。ダイアログを
ダブルクリックして、 ID を IDD_MAIN に変えてください」
『ほい』
「次に、このダイアログに以下のコントロールを追加してください」
・エディットボックス
・IDC_E_LEFT
・IDC_E_RIGHT
・IDC_E_ANSWER
・ボタン
・IDOK (OK)
・IDC_B_EQUAL (=)
・スタティック
・IDC_STATIC (+)
「(OK)(=)(+)は、ボタンに表示する文字列、つまりキャプションを
【OK】にしてください、っていう意味です」
『スタティック、ってただ文字列表示するだけのだよね』
「そう、だから ID は IDC_STATIC ね。で、計算機アプリだから、こんな感
じに並べるといいかな」
┌──────────────────────────────┐
│ │
│ ┌─────┐ ┌──────┐ ┌─┐ ┌──────┐ │
│ │IDC_E_LEFT│ + │IDC_E_RIGHT │ │= │ │IDC_E_ANSWER│ │
│ └─────┘ └──────┘ └─┘ └──────┘ │
│ │
│ ┌──┐ │
│ │OK │ │
│ └──┘ │
│ │
└──────────────────────────────┘
「【=】と【OK】はボタン、 + はスタティック、それ以外は
エディットボックスです」
『ほいほいほいと』
「これで完成。保存してビルドすれば実行できるよ」
『ビルドして実行っと』
「左のふたつのエディットボックスに整数値を入力して、【=】ボタンを押
せば右の IDC_B_EQUAL エディットボックスに計算結果が出力されます」
『お、ホントだ! これで Version 3.11 ( No.036 ) と同じのができた、
ってことなんだね』
「そういうこと。あのときは MFC を使ったけど、今回は API だけで
プログラムしています。今回はその解説をします」
『はーい』
「まずは WinMain() 関数から」
// WinMain() 。
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
「 Version 8.01 ( No.143 ) で説明した、一番最初に呼ばれる関数です」
『これは前の章でも使いまくってるからわかるよー』
「だね。で、この関数の中でダイアログを作成します」
int iRet
= DialogBox
( p_hInstance
, MAKEINTRESOURCE( IDD_MAIN )
, NULL
, DialogProc
);
「 Version 8.07 ( No.149 ) で説明した、 DialogBox() という API で
ダイアログを表示します。ちなみにこの API は、ダイアログが閉じるまで
は返らないから」
『それってつまり、ダイアログが閉じたらこの関数から返ってきて、
WinMain() 関数から抜けてアプリ終了、ね』
「そういうこと。さて次に、この API の第4引数で渡している DialogProc
が、 Version 8.08 ( No.150 ) で説明したダイアログプロシージャになり
ます」
『イベントの度に Windows から呼び出される関数になるわけね』
「そう、イベント、つまり〈ボタンを押した〉といった操作が行われるたび
に DialogProc() 関数が呼び出されます。そして、どういう操作が行われた
のか、という情報が引数で渡されてきます」
『それがメッセージってことだよね』
「そういうこと。で、今回はボタンを押す、っていうイベントしか処理しな
いので、 Version 8.09 ( No.151 ) で説明した WM_COMMAND メッセージだけ
を処理します」
// ダイアログプロシージャ。
BOOL CALLBACK DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
if( p_uiMessage == WM_COMMAND )
{
if( LOWORD( p_wParam ) == IDOK )
{
// OK ボタンが押されました。
EndDialog( p_hDlgWnd, IDOK );
return TRUE;
}
『メッセージが WM_COMMAND だと、ボタンを押したとか、メニューを選択し
たってことなんだよね』
「そう。で、 WPARAM の下位2バイトにボタンの ID が入っているので、そ
れを LOWORD マクロで取り出して比較すればいいわけです」
『で、 ID が IDOK のボタン、つまり OK ボタンが押されたらダイアログを
EndDialog() で閉じる、ってわけね』
「そういうこと。ここまではこれまでの復習。次からが応用になります」
『まず……最初のとこはさっきと同じだね』
else if( LOWORD( p_wParam ) == IDC_B_EQUAL )
{
『これは = ボタンを押した場合、ってことだね』
「そう、ここでは = ボタンを押した時の処理を行います。まずはみっつの
エディットボックスのウィンドウハンドルを取得します」
// 各エディットボックスのウィンドウハンドル
// を取得します。
HWND hLeftWnd
= GetDlgItem( p_hDlgWnd, IDC_E_LEFT ); ← 1
HWND hRightWnd
= GetDlgItem( p_hDlgWnd, IDC_E_RIGHT ); ← 2
HWND hAnswerWnd
= GetDlgItem( p_hDlgWnd, IDC_E_ANSWER ); ← 3
「それぞれ、以下のエディットボックスのウィンドウハンドルを取得してい
ます」
1 2 3
↓ ↓ ↓
│ ┌─────┐ ┌──────┐ ┌─┐ ┌──────┐ │
│ │IDC_E_LEFT│ + │IDC_E_RIGHT │ │= │ │IDC_E_ANSWER│ │
│ └─────┘ └──────┘ └─┘ └──────┘ │
「 GetDlgItem() は Version 5.26 ( No.091 ) で説明した、ダイアログ
コントロールのウィンドウハンドルを取得する API 」
『そうなんだよね、ボタンとかって全部ウィンドウなんだよね』
「そう、だからこうしてウィンドウハンドルを取得できるわけです。で、
ここで Version 5.26 ( No.091 ) と Version 5.27 ( No.092 ) の、
ボタンに書かれている文字列を変える例を思い出して」
『んー、確か SetWindowText() って API で好きな文字列をセットできるん
だよね』
「そうでした。で、その逆に GetWindowText() っていう API もあって、こ
れは逆にウィンドウの文字列を取得することができます」
『 Version 5.31 ( No.096 ) で使ったね。あれはメンバ関数の方だったけ
ど』
「で、その Version 5.31 ( No.096 ) でもちょっと触れたけど、
エディットボックスに入力された文字列は、この GetWindowText() で取得
できるんです。こんな感じに」
// 各エディットボックス用文字列を用意します。
char pchLeft[256];
char pchRight[256];
char pchAnswer[256];
// IDC_E_LEFT と IDC_E_RIGHT の文字列を取得します。
GetWindowText( hLeftWnd, pchLeft, 255 );
GetWindowText( hRightWnd, pchRight, 255 );
『あ、そっか、あのときはコンボボックスから取ってたけど、普通に
エディットボックスに書かれたのも取れるんだ』
「そういうこと。この GetWindowText() で、 IDC_E_LEFT と IDC_E_RIGHT
のふたつのエディットボックスに入力された文字列が取得できます」
『おお……って、 MFC 使わないとこれだけでもめんどいね……』
「確かに……さらに面倒なことに、計算機アプリなので、これを整数値に
変換する必要があります。これは Version 12.02 ( No.225 ) で説明した
atoi() というランタイム関数を使用します」
// それぞれ int 型に変換します。
int iLeft = atoi( pchLeft );
int iRight = atoi( pchRight );
『これで整数値に変換して、足すわけね』
「足したものを、 Version 5.07 ( No.072 ) で説明した sprintf() で
文字列にします」
// 足した結果を pchAnswer に文字列変換します。
sprintf( pchAnswer, "%d", iLeft + iRight );
『これもランタイムだね。これはいつも使ってるから慣れてるかも』
「最後に、この文字列を IDC_E_ANSWER エディットボックスにセットしま
す。セットということは、 API は?」
『 GetWindowText() の逆だから SetWindowText() !』
// それを IDC_E_ANSWER にセットします。
SetWindowText( hAnswerWnd, pchAnswer );
「と、これで足し算の結果が IDC_E_ANSWER エディットボックスに表示され
るわけです」
『ややこし!』
/*
Preview Next Story!
*/
『んー、これだけでも結構複雑かも』
「でもこれはこれまでの応用だから」
『確かにそうなんだけど』
「それにこれ以上は詳しく説明しないし」
『ってことは次は別の話?』
「というわけで次回」
< Version 18.03 ダイアログプロシージャを static メンバ関数にする >
『につづく!』
「残り回数が少ない分、ちゃっちゃと行きます!」
『げげ』