Tips of VC++ >
ウィンドウ >
ウィンドウベースのアプリケーションを作る
★ 前へ戻る ★ 次へ進む ★
このドキュメントにはサンプルプログラムが含まれています。
ワークスペース
ソースファイル(テキスト)
ここで言うウィンドウベースのアプリケーションというのは MFCで言うところのSDIのことです。 厳密に言うと、ダイアログもウィンドウベースに含まれるのですが、 まあ、目を瞑ってください。(^^;
リソースが使えないので、全てをコーディングで行います。 って、そんなに難しくないですよ。 ウィンドウクラスを作って、メッセージループを作って、 プロシージャをがりがり書けば終わりです。
メインとなるAPIはCreateWindowというものです。 動的にコントロールを配置する場合にも使われるので よく覚えてください。
HWND CreateWindow( LPCTSTR lpClassName, // 登録されているクラス名 LPCTSTR lpWindowName, // ウィンドウ名 DWORD dwStyle, // ウィンドウスタイル int x, // ウィンドウの横方向の位置 int y, // ウィンドウの縦方向の位置 int nWidth, // ウィンドウの幅 int nHeight, // ウィンドウの高さ HWND hWndParent, // 親ウィンドウまたはオーナーウィンドウのハンドル HMENU hMenu, // メニューハンドルまたは子ウィンドウ ID HINSTANCE hInstance, // アプリケーションインスタンスのハンドル LPVOID lpParam // ウィンドウ作成データ );
lpClassNameのウィンドウクラスについては
こちらを読んでください。
ウィンドウクラスというのは、そのウィンドウが使うプロシージャや
スタイルなどが定義されたグループみたいなものです。
エディットボックスは"EDIT"、リストボックスは"LISTBOX"などのように
文字列で識別されます。
lpWindowNameは、ウィンドウのタイトルバーになる部分です。
コントロールだと、表示されるテキストだったりします。
dwStyleはウィンドウスタイルを示す定数値(WS_XXX)を
演算子"|"で組み合わせて指定します。
他にも、コントロール特有の定数(ES_XXX,LBS_XXX,etc...)もあります。
x,y,nWidth,nHeightは定番ですね。
hWndParentも分かると思います。メインウィンドウだったらNULLでいいのです。
hMenuも使わないならNULLでいいです。
hInstanceはWinMain関数のパラメータを使用してください。
lpParamはWM_CREATEメッセージが送られたときのlParamになります。
滅多に使いません。
戻り値がNULLかどうかはチェックしたほうがよいでしょう。
x,y,nWidth,nHeightにはCW_USEDEFAULTという定数を使用できます。 これを使う場合、座標や幅などはOSが勝手に決めてくれます。 私は使わないことをオススメしますが。
基本となるウィンドウスタイルは次のように分けられます。 表を作るまでもありませんが…。
サイズ変更可 | WS_OVERLAPPEDWINDOW |
サイズ変更不可 | WS_OVERLAPPED|WS_SYSMENU |
また、WS_OVERLAPPEDWINDOWを使いつつも 最大化ボタンを使えないようにしたいのであれば、 WS_OVERLAPPEDWINDOW&(~WS_MAXIMIZEBOX)というようにもできます。 いろいろ組み合わせてください。
この関数が成功すると、制御が戻る前に ウィンドウにWM_CREATEメッセージが送られます。 このとき、まだウィンドウが表示されていないことに注意してください。 WM_CREATEメッセージを処理するときによく間違えます。(^^;A
ウィンドウを表示する関数は、次の関数です。
BOOL ShowWindow( HWND hWnd, // ウィンドウのハンドル int nCmdShow // 表示状態 );
nCmdShowにSW_SHOWを渡すだけです。 SW_HIDEを渡すと表示されなくなるのがミソ。便利です。 これで表示されなくなっても、ウィンドウが破棄されたわけじゃないので 再び表示させるたときも、コントロールの状態などはそのままなのです。 ああ便利。って、けっこう定番ですけど。とにかく重宝します。
次にメッセージループを書きます。
これを書かないと、メッセージが受理できないばかりか、
ウィンドウが表示された瞬間、スレッドが終了してしまいます。
といっても、次のを丸写ししてください。
MSG msg; while ( ::GetMessage(&msg,NULL,0,0) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); }
これが基本で、たまにちょっと追加することがあります。
まず、GetMessageで、キューにたまったメッセージを構造体に取得します。
続くTranslateMessageで、そのメッセージに付加する新たなメッセージを
キューに入れ(WM_CHARとか)ます。
DispatchMessageさんの仕事は構造体のメッセージを
プロシージャに送出することです。
キーボードアクセラレータを使うときは、以下のように変更します。
MSG msg; HACCEL hAccel= ::LoadAccelerators( hAppModule,MAKEINTRESOURCE(IDR_ACCEL1)); while ( ::GetMessage(&msg,NULL,0,0) ) { if ( !::TranslateAccelerator(hWnd,hAccel,&msg) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } }
TranslateAccelerator関数は、キーボードからのメッセージを リソースのアクセラレータテーブルに従って WM_COMMANDなどのメッセージに変換して プロシージャの送出までしてくれる関数です。 したがって、これでメッセージが受理されたときは TranslateMessageやDispatchMessageなどは呼ばなくてもいいのです。
ウィンドウベースの基本はこの二通りぐらいです。 モードレスダイアログベースのときは、 IsDialogMessageとかいうのも出てきますが、それはまた別の話。
メッセージループはあまり複雑にしないほうがいいです。 スレッドの動作中は常にループしているわけですから、 ここの処理速度はすぐに反映されます。
次にプロシージャの作成となります。 ここがVCプログラミングのコアとなる部分ですね。 枠組みだけ書けばいいでしょう。これは丸写しで。
LRESULT CALLBACK WndProc(HWND in_hWnd, UINT in_Message, WPARAM in_wParam, LPARAM in_lParam) { switch ( in_Message ) { case WM_DESTROY: ::PostQuitMessage(0); break; default: return ::DefWindowProc(in_hWnd, in_Message,in_wParam,in_lParam); } return 0; }
WM_DESTROYはウィンドウの×ボタンを 押したときにも呼び出されるので、必須です。 デフォルトの処理も必須です。
これまでのをまとめると次のような感じになります。 プロシージャだけでなく、ウィンドウクラスの作成から メッセージループまでの部分は、ほとんどのアプリケーションを 作る際に共通なので、定型文として保存しておくのが便利です。 それをサンプルにもしておきます。
const char szClassName[]="アプリケーション名"; LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { // ウィンドウクラスの登録 WNDCLASSEX wc; wc.cbClsExtra = 0; wc.cbSize = sizeof(WNDCLASSEX); wc.cbWndExtra = 0; wc.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH); wc.hCursor = ::LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW)); wc.hIcon = NULL; wc.hIconSm = NULL; wc.hInstance = hInstance; wc.lpfnWndProc = (WNDPROC)WndProc; wc.lpszClassName = szClassName; wc.lpszMenuName = NULL; wc.style = CS_HREDRAW|CS_VREDRAW; ::RegisterClassEx(&wc); // ウィンドウの表示 HWND hWnd= ::CreateWindow(szClassName,szClassName, WS_OVERLAPPEDWINDOW&~WS_MAXIMIZEBOX, 100,100,400,300, NULL,NULL,hInstance,0); if ( hWnd==NULL ) return 0; ::ShowWindow(hWnd,nCmdShow); // メッセージループ MSG msg; while ( ::GetMessage(&msg,NULL,0,0) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } return 0; } // ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND in_hWnd, UINT in_Message, WPARAM in_wParam, LPARAM in_lParam) { switch ( in_Message ) { case WM_DESTROY: ::PostQuitMessage(0); break; default: return ::DefWindowProc(in_hWnd, in_Message,in_wParam,in_lParam); } return 0; }
Oct.11, 2002