※ 修正版をこちらのページにアップしています。将来的にはこのページは削除される可能性があります。
さて、ウインドウは「何もしない」ウインドウはできましたが、見栄えがいまいちぱっとしません。
まずは、メニューをつけて、それらしいウインドウにしようと思います。
メニューの実装方法はいくつかあります(Win32プログラムでは、ひとつのことをやるのに大体複数のやり方が用意されています。
そのことがよいか悪いかは、考え方次第ですが…)が、今回はリソースを使います。
リソースとは、ウインドウプログラムが使う部品のようなものです。
このため、コンパイルする前に用意しておかなくてはなりません。
さて、通常リソースはリソースエディタで作りますが、今回は使いません。
つまり、ツールの手を借りず自分でシコシコ記述するということです。ちょっと前時代的です。
以下のようにリソースを記述してください。
そして入力後 「FTPManager.rc」 と名前を付けて保存してください。
/* ========================================================================== */ /* FILE FTPManager.rc */ /* CREATE 2006/10/21 */ /* MODIFY ----/--/-- */ /* REMARK */ /* COPYRIGHT 2000-2007, Latin. All Rights Reserved */ /* ========================================================================== */ #include "Resource.h" /* -------------------------------------------------------------------------- */ /* メニュー宣言部 */ /* -------------------------------------------------------------------------- */ IDM_MENUMAIN MENU DISCARDABLE { POPUP "ファイル(&F)" { MENUITEM "終了(&X)", IDM_EXIT } }
IDM_MENUMAIN とか IDM_EXIT は、メニュー自体とメニュー項目の名前です。
「IDM〜」はメニュー のリソースにつけられるプリフィックスで単なる慣例です。別に何でもかまいません。
プログラムでメニュー自体や各項目を使用するときは、この名前を使いますが、実は単なる数値です。
リソースID という定数ですが、これは、自分で宣言しなければなりません。
そこで、定数値を宣言します。 定数値は慣例として、100 〜 32768 の間でとってください。
以下のように定数値を宣言します。そして入力後 「Resource.h」 と名前を付けて保存してください。
/* ========================================================================== */ /* FILE Resource.h */ /* CREATE 2006/10/21 */ /* MODIFY ----/--/-- */ /* REMARK */ /* COPYRIGHT 2000-2007, Latin. All Rights Reserved */ /* ========================================================================== */ /* -------------------------------------------------------------------------- */ /* メニューアイテム宣言部 */ /* -------------------------------------------------------------------------- */ #define IDM_MENUMAIN 20000 #define IDM_EXIT 20001
IDM_MENUMAIN とか IDM_EXIT それぞれに、定数値を割り振りました。
さて、リソースを作りましたが、「何もしない」ウインドウで使えるようにする必要があります。
定数値をわからせるために、先ほど作成した、Resource.h をインクルードします。
また、作成した Transit.rc をプロジェクトに参加させるのも忘れないでください。
そして以下のようにソースを修正してください。
// ウインドウクラス作成 WndClass.cbSize = sizeof(WndClass); WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); WndClass.lpszMenuName = NULL; ↓ WndClass.lpszMenuName = MAKEINTRESOURCE(IDM_MENUMAIN); WndClass.lpszClassName = "FTPManager";
ちょっと解説すると、lpszMenuName には、文字列へのポインタを指定しなければなりません。
ですが、IDM_MENUMAIN は、何度もいっているように定数値です。
そこで、文字列へのポインタへの変換を行うのが MAKEINTRESOURCE マクロ です。
そうして、実行してみてください。
次のようなメニューつき画面が表示されると思います。
うそだー、エラーが表示されたという方、ごめんなさい。
「Resource.h」 をインクルードしてますか?インクルードの仕方は、「WinMain.cpp」 のあたまの、<Windows.h> の下に "Resource.h" と書くだけです。
自分で作成したヘッダファイルは、<> ではなく、"" で囲みます。

しかし、終了を押しても反応がありません。
当たり前といえば、当たり前です。押したときの処理は、いちいち記述していくのです。
これがウィンドウズプログラミングの本質なのですから。
さて、メニューの項目をクリックすると、説明したメッセージキューにメッセージが送出されます。
このときのメッセージとは、WM_COMMAND メッセージ です。
ですから、ウインドウプロシージャの中で、WM_COMMAND メッセージを処理するようにします。
WM_COMMAND wNotifyCode = HIWORD(wParam); // 通知コード wID = LOWORD(wParam); // 項目ID、 コントロールID、 またはアクセラレータID hwndCtl = (HWND) lParam; // コントロールのハンドル
WM_COMMAND メッセージは、wParam の下位ワードに項目ID(メニューの「IDM〜」)がセットされますのでこれを捕捉します。
今回は、IDM_EXIT ですね。
そこで、ウインドウプロシージャを以下のように修正します。
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* 関数本体部 */
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
SendMessage(hWnd, WM_CLOSE, 0, 0L);
break;
default:
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
return 0L;
}
IDM_EXIT だった場合に、ウィンドウに終了の合図を送出してやる必要があります。
終了の合図が WM_CLOSE メッセージ で、送出するには、
SendMessage()
を使います。
LRESULT SendMessage( HWND hWnd, // 送信先ウィンドウのハンドル UINT Msg, // メッセージ WPARAM wParam, // メッセージの最初のパラメータ LPARAM lParam // メッセージの 2 番目のパラメータ );
第2引数はメッセージですので、WM_CLOSE を指定しますが、第3引数と第4引数はありません。
つまり0を指定します。なぜかというと、WM_CLOSE メッセージ には、パラメータがないからです。
WM_CLOSE 0 = wParam; // 未使用 0L = lParam; // 未使用
WM_CLOSE メッセージがメッセージキューにたまるといつものように、WndProc() に入ってきますね。
ですが、WndProc() 内部では WM_CLOSE を捕捉していません。
この場合は、最後の DefWindowProc() が補足してウィンドウ メッセージのデフォルト処理を行います。
WM_CLOSE メッセージの場合のメッセージのデフォルト処理がおこなわれると、WM_DESTROY メッセージがメッセージキューにたまります。
すると説明したように、プログラムが終了します。
ところで、lParam の0についている L にはどんな意味があるのでしょうか?
WndProc() の最後のリターンコードにもありますね。ふつうの 0 と何が違うのでしょう。
実は、0L も 0 もまったく同じ 0 です。ですが、C言語では、0 は通常 int型 の定数として扱われます。
WPARAM は unsigned int型 なので問題ないですが、LPARAM は long型 なので long型 の定数として扱うために L をつけているのです。
さて、今回作成したメニューをよく見ると、ファイル(F) とありますが、この (F) って何者なのでしょうか?
これは、アクセスキーといって、Alt キーと一緒に特定の文字キーを押すことで、メニュー や ボタンを選択したことになります。
今回でいえば、[ファイル] メニューを開くには、Alt キーを押しながら F キーを押します。
すると、このファイルメニューを選択したことになります。
リソースに記述するときは アクセスキーの前に & (アンバサンド)記号をつけます。
このようにすると、実行時にアンダーラインがつきます。
前後の 小カッコ はなくてもいいのですが、慣例でつけたほうが分かり易いですね。
私はアクセスキーと呼んでいますが、ここらへんはいい加減で、人によってショートカットキーと呼んだりもします。
そもそもこのアクセスキーを使用していらっしゃる方は、どのくらいいるのでしょうか?
マウスを使わない人が使用する機能なのでしょうが、どのプログラムにもついていますのでつけていたほうが無難な機能でしょう。
さて、最後に今回の「WinMain.cpp」の全文を載せておきます。
/* ========================================================================== */ /* FILE WinMain.cpp */ /* CREATE 2006/10/01 */ /* MODIFY 2006/10/21 */ /* REMARK */ /* COPYRIGHT 2000-2007, Latin. All Rights Reserved */ /* ========================================================================== */ #include <Windows.h> #include "Resource.h" /* -------------------------------------------------------------------------- */ /* CALLBACK WndProc */ /* PARAM HWND hWnd : ウィンドウハンドル */ /* UINT uMsg : メッセージ */ /* WPARAM wParam : メッセージの追加情報 */ /* LPARAM lParam : メッセージの追加情報 */ /* RETURN LRESULT */ /* メッセージ処理の結果 */ /* REMARK */ /* -------------------------------------------------------------------------- */ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* 関数本体部 */ switch (uMsg) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_EXIT: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; default: return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } return 0L; } /* -------------------------------------------------------------------------- */ /* WINAPI WinMain */ /* PARAM HINSTANCE hInstance : 現在のインスタンス */ /* HINSTANCE hPrevInst : 直前のインスタンス */ /* LPSTR lpCmdLine : コマンドライン引数 */ /* int nCmdShow : ウインドウの表示形式 */ /* RETURN int : */ /* メッセージの wParam パラメータ */ /* REMARK Win32 アプリケーションの初期エントリポイント */ /* Windows システムが呼び出す。 */ /* -------------------------------------------------------------------------- */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { /* 変数宣言部 */ HWND hWnd; WNDCLASSEX WndClass; MSG Msg; /* 関数本体部 */ // ウインドウクラス作成 WndClass.cbSize = sizeof(WndClass); WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); WndClass.lpszMenuName = MAKEINTRESOURCE(IDM_MENUMAIN); WndClass.lpszClassName = "FTPManager"; // ウィンドウクラス登録 if (! RegisterClassEx(&WndClass)) { return false; } // ウィンドウ作成 hWnd = CreateWindow(WndClass.lpszClassName, // 登録されているクラス名 "FTPManager", // ウィンドウ名 WS_OVERLAPPEDWINDOW, // ウィンドウスタイル CW_USEDEFAULT, // ウィンドウの横方向の位置 CW_USEDEFAULT, // ウィンドウの縦方向の位置 CW_USEDEFAULT, // ウィンドウの幅 CW_USEDEFAULT, // ウィンドウの高さ NULL, // 親ウィンドウまたはオーナーウィンドウのハンドル NULL, // メニューハンドルまたは子ウィンドウ ID hInstance, // アプリケーションインスタンスのハンドル NULL); // ウィンドウ作成データ if (! hWnd) { return false; } // ウィンドウの表示 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // メッセージループ while (GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return (int) Msg.wParam; }
本日のソース: FTPManager102(VC8).zip FTPManager102(BCC).zip
Visual C++ 2005 の Express バージョンや BCC Developer (Borland C++ 5.5.1) には、リソースエディタがありません。
Visual C++ 2005 の Express バージョンがなぜリソースエディタをケチっているのか理解に苦しみます。
このため、このサイトでは特に断りがない場合は、リソース作成に ResEdit を使用しています。