Windows OS がどのようにしてワープロソフトや表計算ソフトのアプリケーション(以後プログラムと呼ぶ)を処理するか簡単に説明します。まず最初に人間がマウスなどで実行ファイル(*.exe)をダブル・クリックします。この動作によって Windows OS は実行ファイル(*.exe)を起動させてプログラム内部にある特殊な名前の WinMain 関数を最初に呼び出します。この関数にはアプリケーションであるワープロソフトや表計算ソフト、またはゲームソフトのウインドウを表示するまでの一連の手順を記述します。記述方法はアプリケーションの種類によって微妙に異なりますが、基本的にはウインドウ・クラスを登録する処理、ウインドウを作成する処理、Windows OS から常に届く動作指示メッセージ(WM_xxxxx)を処理するためのループ構造を組みます。今日は、このスタートアップ・ルーチンの手順を解説します。また、ウインドウ型とダイアログ・アプリケーション型の雛型ソースを5種類紹介します。この雛型ソースを使って自作ソフトを製作してみて下さい。
Windows OS の最初の呼び出し
- 実行ファイル(*.exe)が起動されると Windows OS はプログラムの WinMain 関数を最初に呼び出します。
- WinMain 関数内でウインドウ・クラスを登録する funcWindowClass 関数を呼び出します。
- WinMain 関数内でウインドウを作成する funcCreateWindow 関数を呼び出します。
- WinMain 関数内で GetMessage 関数によるメッセージ・ループを処理します。
WinMain() ├─funcWindowClass() │ ├─LoadIcon() │ ├─LoadCursor() │ ├─GetStockObject() │ └─RegisterClassEx() │ ├─funcCreateWindow() │ ├─CreateWindowEx() │ ├─ShowWindow() │ └─UpdateWindow() │ └─GetMessage() ├─TranslateMessage() └─DispatchMessage()
具体的なソースは次のようになります。
//------------------------------------------------ // メイン関数 //------------------------------------------------ extern int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) { LPCTSTR lpClassName = TEXT("XXXX_WndClass"); LPCTSTR lpTitleName = TEXT("キャプション"); MSG Msg; if ( funcWindowClass(hInstance,lpClassName) == 0 ){ return -1; } if ( funcCreateWindow(hInstance,lpClassName,lpTitleName,nCmdShow) == NULL ){ return -2; } while ( GetMessage(&Msg,NULL,0,0) > 0 ){ TranslateMessage( &Msg ); DispatchMessage( &Msg ); } UNREFERENCED_PARAMETER( hPrevInstance ); UNREFERENCED_PARAMETER( lpCmdLine ); return Msg.wParam; }
- funcWindowClass 関数でウインドウ・クラスを登録します。
- funcCreateWindow 関数でウインドウを作成します。
- GetMessage 関数でメッセージ・ループを構成します。
//------------------------------------------------ // ウインドウ・クラスの登録 //------------------------------------------------ static ATOM funcWindowClass( HINSTANCE hInstance, LPCTSTR lpClassName ) { WNDCLASSEX wcex = { 0 }; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS); wcex.lpfnWndProc = mainWindowProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wcex.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH ); wcex.lpszMenuName = NULL; wcex.lpszClassName = lpClassName; return RegisterClassEx( &wcex ); }
- WNDCLASSEX 構造体に適切なパラメータをセットします。
- lpszMenuName メンバにメニュー・リソースの識別文字列をセットするとウインドウにメニュー・バーが表示されます。
- その後、RegisterClassEx 関数でウインドウ・クラスを登録します。
//------------------------------------------------ // ウインドウの作成 //------------------------------------------------ static HWND funcCreateWindow( HINSTANCE hInstance, LPCTSTR lpClassName, LPCTSTR lpTitleName, int nCmdShow ) { HWND hWnd = CreateWindowEx( 0, // 拡張ウインドウのスタイル lpClassName, // ウインドウのクラス名 lpTitleName, // ウインドウのタイトル名 WS_OVERLAPPEDWINDOW, // ウインドウのスタイル CW_USEDEFAULT, // ウインドウの横軸位置 CW_USEDEFAULT, // ウインドウの縦軸位置 CW_USEDEFAULT, // ウインドウの横サイズ CW_USEDEFAULT, // ウインドウの縦サイズ NULL, // 親ウインドウのハンドル NULL, // メニューのハンドル hInstance, // インスタンスのハンドル NULL ); // ウインドウ作成のデータ if ( hWnd != NULL ){ ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); } return hWnd; }
- CreateWindowEx 関数でウインドウを作成します。
- その後、ShowWindow 関数でウインドウを表示します。
- UpdateWindow 関数でウインドウの再描画を行います。
ウインドウ・プロシージャの処理
- WM_CREATE メッセージでウインドウの作成時処理を行います。
- WM_PAINT メッセージでウインドウの再描画処理を行います。
- WM_CLOSE メッセージでウインドウの終了処理を行います。
- WM_DESTROY メッセージでウインドウの破棄処理を行います。
WindowProc() ├─[WM_CREATE] │ ├─[WM_CLOSE] │ └─DestroyWindow() │ ├─[WM_DESTROY] │ └─PostQuitMessage() │ ├─[WM_PAINT] │ ├─BeginPaint() │ └─EndPaint() │ └─DefWindowProc()
具体的なソースは次のようになります。
//------------------------------------------------ // break付きのキーワード //------------------------------------------------ #define CASE break;case #define DEFAULT break;default //------------------------------------------------ // ウインドウ・プロシージャの関数 //------------------------------------------------ extern LRESULT CALLBACK mainWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch ( uMsg ){ CASE WM_CREATE: { HINSTANCE hInstance = ((LPCREATESTRUCT)lParam)->hInstance; LPVOID lpParam = ((LPCREATESTRUCT)lParam)->lpCreateParams; /* 初期化の処理 */ } CASE WM_CLOSE: /* 後始末の処理 */ DestroyWindow( hWnd ); CASE WM_DESTROY: PostQuitMessage( 0 ); CASE WM_PAINT: { PAINTSTRUCT ps; HDC hDC; hDC = BeginPaint( hWnd, &ps ); /* 描画の処理 */ EndPaint( hWnd, &ps ); } CASE WM_ERASEBKGND: return 1; /* 何も処理しない⇒塗り潰した */ DEFAULT:return DefWindowProc( hWnd, uMsg, wParam, lParam ); } return 0; }