プログラマの隠れ里 〜プログラミング初心者のサイト〜
[Contents]

- トップページ
- 自己紹介
- ダウンロード
- プログラマ度診断
- リンク

- ゲストブック
- 質問BBS

[Programming]

- VB 2005
- C 言語
- アセンブラ
- Windows
- Win2K Apache
- Java3D
- ヒント
- 書籍紹介

Last modified : 2006.06.20

WinMain 関数

CUI (Character User Interface) アプリケーションでは、C言語のプログラムは main 関数から出発しました。では Windows の GUI (Graphical User Interface) アプリケーションでは、どこからプログラムが出発するのでしょうか?

Windows のプログラムは "WinMain" 関数から出発します。VC++ の場合、WinMain 関数は "winbase.h" にプロトタイプ宣言されています。C/C++ 言語の Windows のプログラムは、ここからすべて出発します。WinMain のプロトタイプ宣言は下のように宣言されています。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);

正確に言うと、通常 WinMain 関数が実行される前に、スタートアップコードというのが実行され、その中から WinMain 関数が実行されます。スタートアップコードは開発環境によって異なります。VC++ の場合、一番最初に呼び出される関数は "WinMainCRTStartup" 関数で、そこからスタートアップコードが呼び出され WinMain 関数が呼び出されます。スタートアップコードの中では、C ランタイムライブラリの初期化等が行われます。

下のプログラムは何もしないプログラムです。Windows プログラムにはかならず "windows.h" ファイルをインクルードします。

/***********************************************************
 * 一番最初の Windows プログラム
 ***********************************************************/

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nShowCmd)
{
    // 何もしないプログラムです。
    // ちゃんと実行できます。
    
    return 0;
}

このプログラムを実行してみましょう。何もせずにプログラムが終了するはずです。タスクマネージャで観察すると一瞬でプログラムが終了するはずです。

さて新しいキーワード "HINSTANCE" / "LPSTR" / "WINAPI" が出てきました。Windows プログラミングでは、たくさんの変数や構造体がよく出てきます。出会うごとにひとつひとつ MSDN (Microsoft Developer Network) ライブラリで調べていかなければなりませんが、法則があるので慣れてしまえばそんなに難しいものではありません。詳しくは、データ型で解説するつもりです。

HINSTANCE / LPSTR / WINAPI については、少し解説しておきましょう。

WINAPI は、"API (Application Programming Interface)" 関数を呼び出すときの呼び出し規約です。これについては特に気にする必要はありません。次のように定義されています。

typedef __stdcall WINAPI;

HINSTANCE は、アプリケーションの "インスタンスハンドル" です。アプリケーションごとにOSから渡されるハンドルです。接頭語に [H] がつくものはすべてハンドルと呼ばれます。Windows 内部では色々なオブジェクトが管理されていますが、そのオブジェクトを操作する時のキーみたいなものを "ハンドル" といいます。

LPSTR は文字列へのポインタです。接頭に "P" or "LP" がつくデータ型はポインタとして定義されています。LPSTR は "Long Pointer of STRing" の略です。Windows がまだ 16 ビットだったころ、32 ビットのポインタを "Long Pointer"、16 ビットのポインタを "Short Pointer" と呼んでいました。今はすべてのポインタは 32 ビットなのですが、"LP" はそのころの名残として残っています。

ちなみに LPSTR は、次のように定義されていると考えてよいでしょう。

typedef char *LPSTR;

LPSTR のほかの文字列型として UNICODE を扱える LPTSTR というのがあります。普通の文字列と UNICODE 文字列の違いは、前者が 8 ビットで後者が 16 ビットであることです。UNICODE は、ここではあまり詳しく触れません。

目次へ


他のアプリケーションを呼び出してみる

それでは WinMain 関数からほかのアプリケーションを呼び出してみましょう。CUI アプリケーションの場合、ほかの実行ファイルを呼び出すには、system (stdlib.h) 関数がありました。Windows にもそれに似た関数の WinExec 関数があります。次のプログラムは、WinExec 関数を用いてマインスイーパ (Windows 付属のゲーム) を呼び出しています。

/***********************************************************
 * マインスイーパを呼び出してみよう!
 ***********************************************************/

// Windows プログラミングには必ずインクルード
#include <windows.h>

// WinMain 関数 〜すべてはここから始まる〜
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstace, 
                   LPSTR lpCmdLine, int nShowCmd)
{
    // マインスイーパは Winmine.exe
    char lpFileName[] = "winmine.exe";

    WinExec(lpFileName, SW_SHOWNORMAL);

    return 0;
}

ファイル名を変えれば、ほかのアプリケーションも呼び出すことができます。WinExec は、

UINT WinExec(
    LPCSTR lpCmdLine, //コマンドラインへのポインタ
    UINT nCmdShow     // ウィンドウの表示状態
);

と定義されています。第 1 引数には実行ファイル名の文字列を、第 2 引数にはウィンドウの表示状態を指定します。通常は、SW_SHOWNORMAL 定数を指定しておけばいいでしょう。この定数を指定しておけば、普通にウィンドウを表示します。その他の定数の指定には、次のようなものがあります。

定数名 説明
SW_HIDE ウィンドウを非表示にし、他のウィンドウをアクティブにします。
SW_MAXMIZE ウィンドウを最大化します。
SW_MINIMIZE ウィンドウを最小化し、Z 順位が次のトップレベルウィンドウをアクティブにします。
SW_RESTORE ウィンドウをアクティブにし、表示します。ウィンドウが最小化されていたり最大化されていたりすると、元の位置とサイズに戻ります。
SW_SHOW ウィンドウをアクティブにして、現在の位置とサイズで表示します。
SW_SHOWDEFAULT アプリケーションを起動させたプログラムが CreateProcess 関数に渡す STARTUPINFO 構造体の wShowWindow メンバで指定された SW_ フラグを基にして、表示状態を設定します。
SW_SHOWMAXIMIZE ウィンドウをアクティブにして、最大化します。
SW_SHOWMINIMIZE ウィンドウをアクティブにして、最小化します。
SW_SHOWMINNOACTIVE ウィンドウを最小化します。アクティブなウィンドウは、アクティブな状態を維持します。非アクティブなウィンドウは、非アクティブなままです。
SW_SHOWNA ウィンドウを現在の状態で表示します。アクティブなウィンドウはアクティブな状態を維持します。
SW_SHOWNOACTIVE ウィンドウを直前の位置とサイズで表示します。アクティブなウィンドウはアクティブな状態を維持します。
SW_SHOWNORMAL ウィンドウをアクティブにして、表示します。ウィンドウが最小化または最大化されているときは、位置とサイズを元に戻します。

たとえば最大化して表示したいときは SW_MAXMIZE、ウィンドウを隠したいときは SW_HIDE を指定します。詳しい説明は MSDN ライブラリを見ておいてください。

用語 解説
(発展1)WinExec … 簡単なプロセスを作成するのに用いる。完全に制御したい場合には CreateProcess 関数を用いてプロセスを作成する。
(発展2)SW_定数 … もともと ShowWindow 関数用の定数。

目次へ


メッセージボックスを表示してみる

こんどはメッセージボックスを表示してみましょう。メッセージボックスを表示するには、MessageBox 関数を使用します。次の例は、簡単なメッセージボックスを表示するプログラムです。

/***********************************************************
 * メッセージボックスを表示してみよう!!例題1−3
 ***********************************************************/

#include <windows.h>

#define    MESSAGE  "メッセージボックスのテスト"
#define    TITLE    "タイトルを記入〜ここがタイトル〜"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    // メッセージの表示
    MessageBox(NULL, MESSAGE, TITLE, MB_OK);

    return 0;
}

メッセージボックスを表示する関数は、

int MessageBox(
    HWND hWnd,         // ウインドウのハンドル
    LPCTSTR lpText,    // 表示するテキスト
    LPCTSTR lpCaption, // タイトル
    UINT uType         // メッセージボックスのスタイル
);

と定義されています。第 1 引数には、関連するウィンドウハンドルを渡します。ここではまだウィンドウを作成していないので、NULL を渡しておきます。第 2 引数は、メッセージボックスに表示する文字列を指定します。引数のタイプが、LPCTSTR となっていますが、これについてちょっと説明しておきます。LPSTR / LPCSTR / LPCTSTR の間に違いはあるのでしょうか。ほとんど同じと考えて差し支えありません。LPSTR と LPCSTR の違いは、

typedef  char       *LPSTR;
typedef  const char *LPCSTR;

となっています。つまり LPCSTR は文字列定数です。LPCTSTR は LPCSTR とほぼ同じです。"UNICODE" というマクロが定義されている時には、LPCTSTR は UNICODE として扱われます。詳しくは次の定義を見てください。

#ifdef UNICODE
    typedef unsigned int TCHAR;
#else
    typedef unsigned char TCHAR;
#endif

typedef TCHAR *LPTSTR, *LPTCH;
typedef const TCHAR * LPCTSTR;

第 3 引数には、メッセージボックスのタイトルの文字列を指定します。第 4 引数にはメッセージボックスのスタイルを指定します。OK ボタンをもったメッセージボックスを作る場合には、MB_OK 定数を指定します。YES/NO ボタンをもったメッセージボックスを作成する場合には、ここに MB_YESNO という定数を指定します。詳しい指定方法については、MSDN ライブラリ の MessageBox について調べてみればいろいろと見つかると思います。UINT は、

typedef  unsigned int    UINT;

と "windef.h" に定義されています。

目次へ


ウィンドウを作成してみよう

次は、ウィンドウを作成してみましょう。ウィンドウを作成するには結構厄介な手順を踏みます。ちょっとプログラムをみてみましょう。

/***********************************************************
 * ウィンドウを表示するプログラム
 ***********************************************************/

#define WIN32_LEAN_AND_MEAN // これは何??後ろを参照
#include <windows.h>

#define APP_NAME "FIRSTAPP"
#define APP_TITLE "Window Test Application"

// ウィンドウプロシージャ関数のプロトタイプ(メッセージの処理)
LRESULT CALLBACK WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

/***********************************************************
 * メイン関数
 ***********************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nCmdShow)
{
    HWND        hWnd = NULL;
    WNDCLASSEX  wcex;
    MSG         msg;

    wcex.cbSize         = sizeof(wcex);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WinProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = APP_NAME;
    wcex.hIconSm        = NULL;

    if(!RegisterClassEx(&wcex))
    {
        return 0;
    }

    hWnd = CreateWindowEx( WS_EX_APPWINDOW, 
            APP_NAME, 
            APP_TITLE , 
            WS_OVERLAPPEDWINDOW, 
            CW_USEDEFAULT, 
            CW_USEDEFAULT, 
            400, 
            400, 
            NULL, 
            NULL, 
            hInstance, 
            NULL );

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    while(GetMessage(&msg, NULL, 0, 0) != 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}
/***********************************************************
 * ウィンドウプロシージャ関数の実体(メッセージの処理)
 ***********************************************************/
LRESULT CALLBACK WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }

    return 0L;
}

ウィンドウを作るだけで、こんなにも手順が必要なのです。このあたりが CUI アプリケーションとちがうところですね。こんなにもコードの量が多いのは、根本的に MS-DOS や UNIX のCUIなどと全く違うプログラム構造をしているからです。CUI のような逐次実行していくタイプのアプリケーションを一般的に "手続き型プログラム" といいます。しかし Windows アプリケーションの場合は、"メッセージ通信型プログラム" といいます。Windows アプリケーションは、OS とメッセージを交換してプログラムを実行していきます。例えば、ウィンドウの一部分をクリックすると、OS はそのクリックされたというメッセージをクリックされたウィンドウに渡します。ウィンドウは、そのメッセージにしたがって処理をします。

GUI アプリケーションは、一般的に「メッセージ通信型プログラム」です。Java の Applet プログラミングもそうだし、UNIX の GUI アプリケーションにしてもそうです。この考え方は "サーバークライアントアーキテクチャ (Server/Client Architecture)" に基づいたものです。Windows アプリケーション場合は OS が "Server" でウィンドウが "Client" となります。

目次へ


メッセージを処理する

ウィンドウの作成方法の解説は後まわしにして、先にメッセージの解説をしましょう。(ウィンドウ作成は決まったやり方なので特に難しくないので・・・)

メッセージの処理は、前述のプログラムでは、"ウィンドウプロシージャ関数"( "コールバック関数" とも呼ばれる)にやらせます。あらかじめOS にウィンドウプロシージャ関数を登録しておきます。OS からメッセージが送信されると、登録しておいたウィンドウプロシージャ関数が呼び出されます。メッセージ内容はウィンドウプロシージャ関数の第 2 引数にセットされます。この値は整数型で "windows.h" の中に定数が定義されています。例えば、上述プログラム中の WM_DESTROY メッセージは、ウィンドウが消滅するときに呼び出されるメッセージです。

ウィンドウプロシージャ関数には、かならず CALLBACK という修飾子をつけます。これをつけないと、コンパイルが通りません。CALLBACK は WINAPI と同様に __stdcall と定義されています。ウィンドウプロシージャ関数は次のように定義されています。

LRESULT CALLBACK WindowProc( // ファイル名は何でもよい
    HWND hWnd,               // ウィンドウハンドル
    UINT Msg,                // メッセージをあらわす符号なし整数
    WPARAM wParam,           // メッセージに付属する1つめのパラメータ
    LPARAM lParam            // メッセージに付属する2つめのパラメータ
);

第 1 引数は HWND 型です。この型は何回も出てきましたが、まだ説明してませんでした。これはウィンドウハンドルと呼ばれ、メッセージが送信されたウィンドウを識別するためのハンドルです。ウィンドウに処理を施したい場合、このウィンドウハンドルを使います。

第 2 引数はメッセージです。第 3 引数と第 4 引数は、そのメッセージに付属するパラメータです。たとえば、左のマウスボタンが押されたとします。そうすると WM_LBUTTONDOWN メッセージが送られてきます。その時、lParam には押されたときの X-Y 座標が指定され、上位ワードには Y 座標が、下位ワードには X 座標が入ります。マクロを用いて、次のように取り出せます。LPARAM / WPARAM がどのような用途で使われているかについては、MSDN ライブラリのメッセージ説明の項目に詳しく載っています。

// ここから
int x, y;

x = LOWORD(lParam);
y = HIWORD(lParam);
// ここまで

代表的なメッセージを載せておきましょう。この他にもたくさんあります。

アプリケーション作成関係  
WM_CREATE ウィンドウを作成すると呼び出されるメッセージ。実際には、CreateWindow / CreateWindowEx 関数を使ってウィンドウを作成するときに発行されるメッセージです
WM_CLOSE アプリケーションが終了するときに呼び出されるメッセージ。ウィンドウの右隅にある終了ボタン(Xボタン)を押すと発行されます。呼び出されたときに、明示的に DestroyWindow 関数でウィンドウを終了するか、DefWindowProc 関数を呼び出さない限り、ウィンドウを閉じることができなくなってしまいます。
WM_DESTROY ウィンドウを閉じたときに呼び出されるメッセージ。ウィンドウが閉じるときに発生します。ウィンドウが閉じるときに呼び出されるもので、アプリケーションが終了するときに呼び出されるメッセージでないことに注意。
WM_QUIT アプリケーションを終了するときに呼び出されるメッセージ。実際には PostQuitMessage 関数によって発行される。
ウィンドウ関係  
WM_ACTIVATE ウィンドウがアクティブになったとき呼び出されるメッセージ。
WM_MOVE ウィンドウを移動したときに呼び出されるメッセージ。
WM_SIZE ウィンドウのサイズを変更したときに呼び出されるメッセージ。
WM_MOVING ウィンドウを移動しているときに呼び出されるメッセージ。
WM_SIZING ウィンドウのサイズ変更をしているときに呼び出されるメッセージ。
WM_SETFOCUS キーボードのフォーカスを取得したときに呼び出されるメッセージ。
WM_KILLFOCUS キーボードのフォーカスを失ったときに呼び出されるメッセージ
WM_PAINT ウィンドウを再描画するときに呼び出されるメッセージ。
キーボード関係  
WM_KEYDOWN キーを押したときに呼び出されるメッセージ。
WM_KEYUP キーをあげたときに呼び出されるメッセージ。
WM_CHAR アプリケーションにキーコードを渡すときに呼び出されるメッセージ。
マウス関係  
WM_LBUTTONDOWN マウスの左ボタンをクリックしたときに呼び出されるメッセージ。
WM_LBUTTONUP マウスの左ボタンをあげたときに呼び出されるメッセージ。
WM_LBUTTONDBLCLK マウスの左ボタンをダブルクリックしたときに呼び出されるメッセージ。
WM_MBUTTONDOWN マウスの中央のボタンを押したときに呼び出されるメッセージ。
WM_MBUTTONUP マウスの中央のボタンをあげたときに呼び出されるメッセージ。
WM_MBUTTONDBLCLK マウスの中央のボタンをダブルクリックしたときに呼び出されるメッセージ。
WM_RBUTTONDOWN マウスの右ボタンをクリックしたときに呼び出されるメッセージ。
WM_RBUTTONUP マウスの右ボタンをあげたときに呼び出されるメッセージ。
WM_RBUTTONDBLCLK マウスの右ボタンをダブルクリックしたときに呼び出されるメッセージ。
WM_MOUSEMOVE マウスを動かしたときに呼び出されるメッセージ。
WM_MOUSEWHEEL マウスのホイールを動かしたときに呼び出されるメッセージ。

基本的なメッセージなので、覚えたほうがいいでしょう。

目次へ


ウィンドウの作成

ウィンドウの作成方法について説明していきましょう。ウィンドウとはなんでしょうか。ウィンドウとは、今さっき作った一般的なウィンドウから、ボタン、リスト、コンボボックスなどあらゆるものが Windows の世界ではウィンドウです。それぞれひとつひとつを OS は管理しています。

ウィンドウを作成する関数は、CreateWindow もしくは、 CreateWindowEx 関数です。今回は CreateWindowEx 関数を用いましたが、CreateWindow 関数を用いても作成することが出来ます。どちらの関数もウィンドウハンドル (HWND 型) を戻り値として返します。関数の実行が失敗した場合は NULL が返ってきます。

HWND CreateWindow(
    LPCTSTR lpClassName,  // 登録されているクラス名文字列へのポインタ
    LPCTSTR lpWindowName, // ウィンドウタイトルの文字列へのポインタ
    DWORD dwStyle,        // ウィンドウスタイル
    int x,                // X 座標
    int y,                // Y 座標
    int nWidth,           // ウィンドウの幅
    int nHeight,          // ウィンドウの高さ
    HWND hWndParent,      // 親ウィンドウのウィンドウハンドル
    HMENU hMenu,          // メニューハンドル
    HANDLE hInstance,     // アプリケーションハンドル
    LPVOID lpParam        // WM_CREATE メッセージに送るパラメータ
);

第 1 引数には、OS に登録してあるウィンドウクラス名の入った文字列を指定します。ウィンドウを作成する前に、作成したいウィンドウの雛形となる情報が、あらかじめ登録されていなければなりません。ウィンドウの作成は、その雛形を用いて行います。ウィンドウクラスとはウィンドウの性質を記述したクラスで、作成するウィンドウの雛形となるものです。ウィンドウクラスは、"クラス" という名前こそついていますが C++ 言語の "class" とは別物ですので勘違いしないでくださいね。ウィンドウクラスの登録は、RegisterClass もしくは、 RegisterClassEx 関数を用いて行います。この関数については後ほど説明します。(ちなみに関数名の接尾語 Ex は、Extended の略です)

第 2 引数には、タイトル文字列を指定します。第 3 引数には、ウィンドウスタイルを指定します。代表的なものを挙げると、ウィンドウスタイルには次のようなものがあります。

説明
WS_BORDER 境界線を持つウィンドウを作成します。
WS_HSCROLL 水平スクロールバーを持つウィンドウを作成します。
WS_MAXIMIZE ウィンドウを最大化の状態で作成します。
WS_MAXIMIZEBOX 最大化ボタンを持つウィンドウを作成します。WS_SYSMENU スタイルも指定する必要があります。WS_EX_CONTEXTHELP スタイルと組み合わせることはできません。
WS_MINIMIZE ウィンドウを最小化の状態で作成します。WS_ICONIC スタイルと同じです。
WS_MINIMIZEBOX 最小化ボタンを持つウィンドウを作成します。WS_SYSMENU スタイルも指定する必要があります。WS_EX_CONTEXTHELP スタイルと組み合わせることはできません。
WS_OVERLAPPEDWINDOW WS_OVERLAPPED スタイル、WS_CAPTION スタイル、WS_SYSMENU スタイル、WS_THICKFRAME スタイル、WS_MINIMIZEBOX スタイル、WS_MAXIMIZEBOX スタイルを持つオーバーラップウィンドウを作成します。
WS_POPUP ポップアップウィンドウを作成します。このスタイルは、WS_CHILD スタイルと一緒には使えません。
WS_SYSMENU タイトルバー上にウィンドウメニューボックスを持つウィンドウを作成します。WS_CAPTION スタイルも指定する必要があります。
WS_THICKFRAME サイズ変更境界を持つウィンドウを作成します。WS_SIZEBOX スタイルと同じです。
WS_VISIBLE 可視状態のウィンドウを作成します。
WS_VSCROLL 垂直スクロールバーを持つウィンドウを作成します。

今回は "WS_OVERLAPPEDWINDOW" スタイルを使いました。このスタイルはタイトルバー、システムメニュー、サイズ変更枠のついたウィンドウです。

第 4, 5, 6, 7 番目の引数は、ウィンドウの大きさや位置を指定します。

第 8 番目の引数には、親ウィンドウのウィンドウハンドルを指定します。MDI (Multi Document Interface)アプリケーションの場合などに指定します。MDI について今回は詳しくやらず、ここではSDI (Single Document Interface) のみに言及します。MDI については MSDN ライブラリを参照してください。今回は、親ウィンドウがないので NULL を指定します。

第 9 番目の引数は、メニューハンドルを渡します。メニューについては "メニュー作成" の項目を参照してください。メニューを使わない場合は、NULL を指定します。

第 10 番目の引数には、アプリケーションハンドルを渡します。WinMain の 1 番目の引数です。

第 11 番目の引数には、WM_CREATE メッセージへのパラメータを渡します。CreateWindow もしくは、CreateWindowEx 関数でウィンドウが作成された直後に、WM_CREATE メッセージが OS から送られます。そのときに渡したいパラメータがあればここに指定します。使わないときは、NULL でよいです。

次に CreateWindowEx について説明します。接尾語の Ex からわかるように、CreateWindow が拡張されただけで、第 2 〜 12 引数については、CreateWindow と同じです。拡張として、新しいスタイルがいくつか使えるようになってます。

HWND CreateWindowEx(
    DWORD dwExStyle, // 拡張スタイル
    LPCTSTR lpClassName, 
    LPCTSTR lpWindowName, 
    DWORD dwStyle, 
    int x, 
    int y, 
    int nWidth, 
    int nHeight, 
    HWND hWndParent, 
    HMENU hMenu, 
    HANDLE hInstance, 
    LPVOID lpParam
);

拡張スタイルは、特に指定するものがなかったら、WS_EX_APPWINDOW を指定しておけばいいでしょう。代表的なものを載せておきます。

説明
WS_EX_ACCEPTFILES ドラッグアンドドロップで、ファイルを受け入れます。
WS_EX_APPWINDOW ウィンドウが最小化されると、トップレベルウィンドウがタスクバー上に置かれます。
WS_EX_CONTEXTHELP ダイアログボックスのタイトルバーに[?]ボタンを追加します。 ユーザーがこの[?]ボタンをクリックすると、マウスポインタに疑問符が付きます。その後、ユーザーがダイアログボックス内のコントロールをクリックすると、コントロールは WM_HELP メッセージを受け取ります。コントロールはダイアログの処理にそのメッセージを送ります。このダイアログの処理は、HELP_WM_HELP コマンドを使った WinHelp 関数です。ヘルプアプリケーションは、通常、コントロールのヘルプを持つポップアップウィンドウを表示します。WS_EX_CONTEXTHELP は WS_MAXIMIZEBOX スタイルや WS_MINIMIZEBOX スタイルとは一緒に使用できません。
WS_EX_TOOLWINDOW ツールウィンドウを作成します。これは、フローティングツールバー用のウィンドウです。ツールウィンドウは通常のタイトルバーより小さいタイトルバーを持ちます。タスクバーには表示されません。ユーザーが[Alt]キーと[Tab]キーを同時に押すと現れるダイアログ内にも、表示されません。
WS_EX_TOPMOST 最前面ウィンドウを作成します。ウィンドウが非アクティブな状態でも、ほかのウィンドウの前面に表示されます。SetWindowPos 関数を使用すると、非最前面ウィンドウに変更できます。
WS_EX_TRANSPARENT 透過ウィンドウを作成します。このウィンドウの下にあるどのようなウィンドウも、遮られることなく表示されます。このスタイルで作成されたウィンドウは、その下にある兄弟ウィンドウがすべて更新されたときにだけ、WM_PAINT メッセージを受け取ります。

その他については、MSDN ライブラリを参照してください。

ここで、新しい型として DWORD / LPVOID が出てきましたが、次のように定義されています。

typedef unsigned long   DWORD;
typedef void            *LPVOID;

次に、作成したウィンドウの表示について解説します。

ウィンドウの表示には、ShowWindow 関数と UpdateWindow 関数を使います。ShowWindow はウィンドウを表示します。

BOOL ShowWindow(
    HWND hWnd,    // ウィンドウのハンドルを指定
    int nCmdShow  // ウィンドウの表示状態を指定
);

第 1 引数には、表示するウィンドウのハンドルを指定します。第 2 引数には、ウィンドウの表示状態を指定します。ここには、WinExec 関数の第 2 引数に指定する値と同じです。

UpdateWindow はウィンドウのクライアント領域を描画します。クライアント領域というのは、ウィンドウ内部の描画領域のことです。UpdateWindow 関数が呼び出されると、OS はウィンドウに WM_PAINT メッセージを送ります。対象となるウィンドウは WM_PAINT メッセージを拾って、クライアント領域の書き換え処理をします。

BOOL UpdateWindow(
    HWND hWnd
);

引数には、WM_PAINT メッセージを送るウィンドウハンドルを渡します。ShowWindow / UpdateWindow 関数は、成功すると TRUE を失敗すると FALSE を返します。

BOOL という型が出てきましたが、この値は真偽値を表します。定義は下のように "windef.h" で定義されています。

typedef int BOOL;

#define TRUE 1
#define FALSE 0

目次へ


ウィンドウクラスの登録

ウィンドウを作成する前に、必要ならば、ウィンドウクラスをOSに登録しなければなりません。ウィンドウクラスを登録するには、RegisterClass もしくは、RegisterClassEx 関数を用います。クラスの記述には、WNDCLASS / WNDCLASSEX 構造体を使います。前者は RegisterClass 後者は RegisterClassEx 関数で登録を行います。例題では後者を用いていますね。

まず WNDCLASS 構造体を説明していきましょう。

typedef struct _WNDCLASS {
    UINT style,           // ウィンドウクラスのスタイル
    WNDPROC lpfnWndProc,  // ウィンドウプロシージャのコールバック関数
    int cbClsExtra,       // 余分のウィンドウクラスを指定する場合指定。
    int cbWndExtra,       // 余分のウィンドウを作成する場合に指定。
    HANDLE hInstance,     // アプリケーションハンドルを指定。
    HICON hIcon,          // アイコンハンドルを指定。
    HCURSOR hCursor,      // カーソルハンドルを指定。
    HBRUSH hbrBackground, // 背景を塗りつぶすブラシを指定。
    LPCTSTR lpszMenuName, // メニューリソース名を指定
    LPCTSTR lpszClassName // クラス名を指定。
} WNDCLASS;

次にそれぞれの説明です。

メンバ 解説
style ウィンドウクラスのスタイルを指定します。CS_VREDRAW と CS_HREDRAW を指定しておけばよいでしょう。
lpfnWndProc ウィンドウプロシージャの関数ポインタを指定します。
cbClsExtra 次に指定するウィンドウクラスのため割り当てる余分のバイト数を指定します。使用しない場合は、0 を指定します。
cbWndExtra 次に作成するウィンドウのために割り当てる余分のバイト数を指定します。使用しない場合は、0 を指定します。
hInstance アプリケーションハンドルを指定します。WinMain の一番目の引数を渡せばいいでしょう。
hIcon アイコンのハンドルを指定します。
hCursor カーソルのハンドルを指定します。
hbrBackground 背景を塗りつぶすブラシハンドルを指定します。例題は、GetStockObject 関数を使っています。この関数を使うことによって、あらかじめ OS にストックしてある GDI (Graphics Device Interface) オブジェクトを使うことが出来ます。GDI については "グラフィックス" のところで説明します。
lpszMenuName メニューリソースの名前を指定します。
lpszClassName ウィンドウクラス名の文字列を指定します。

これらを指定して RegisterClass 関数でウィンドウクラスを登録します。引数には、WNDCLASS 構造体のポインタを指定します。

次に WNDCLASSEX 構造体について説明します。WNDCLASSEX は、WNDCLASS を拡張したものです。次のように定義されています。

typedef struct _WNDCLASSEX {
    UINT cbSize, 
    UINT style, 
    WNDPROC lpfnWndProc, 
    int cbClsExtra, 
    int cbWndExtra, 
    HANDLE hInstance, 
    HICON hIcon, 
    HCURSOR hCursor, 
    HBRUSH hbrBackground, 
    LPCTSTR lpszMenuName, 
    LPCTSTR lpszClassName, 
    HICON hIconSm
} WNDCLASSEX;

この構造体は、cbSize メンバと hIconSm メンバが新しく加わっています。cbSize には、この構造体自身のバイト数を指定します。 hIconSm には小さいアイコン (16 x 16 ビット) を指定します。このメンバが NULL の場合は、hIcon メンバで指定されたアイコンが使われます。WNDCLASSEX は、RegisterClassEx によって登録します。

また RegisterClass / RegisterClassEx 関数は失敗すると 0 を返します。成功すると 0 以外の値を返します。

目次へ


メッセージループ

ウィンドウを表示するプログラムの部分で、下の部分を "メッセージループ" といいます。

while( GetMessage(&msg, NULL, 0L, 0L) != 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

ウィンドウのメッセージはメッセージキューに蓄えられます。GetMessage 関数によって、メッセージキューからメッセージを取り出します。DispatchMessage 関数が適切なウィンドウプロシージャにメッセージを送ります。メッセージの内容は、MSG 構造体の中に格納されます。TranslateMessage 関数は、仮想キーメッセージの内容をキャラクターメッセージに変換します。たとえば、WM_KEYDOWN / WM_KEYUP メッセージから、WM_CHAR メッセージを作成します。GetMessage 関数は WM_QUIT メッセージが送られてくると 0を返してループを抜け出します。

メッセージが送られてこない状態をアイドル状態といいますが、このアイドル状態の間に処理をさせたい場合があります。このアイドル時間内に処理をさせるには次のようにメッセージループを記述します。

while(true)
{
    if(PeekMessage(&msg, NULL, 0L, 0L, PM_NOREMOVE))
    {
        if(GetMessage(&msg, NULL, 0L, 0L) == 0)
            break;

        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        // アイドルプロセス
        // ここにアイドル状態のときの処理を書き込む
    
    }
}

アイドル状態のときの処理を "アイドルプロセス" といいます。PeekMessage 関数はメッセージキューにメッセージがあるかどうかチェックします。メッセージキューにメッセージがある場合は、メッセージを処理します。メッセージキューにメッセージがない場合は、アイドルプロセスを行います。

メッセージは最初メッセージキューに蓄えられます。そして順番にメッセージが取り出されていきます。"キュー (Queue)"  とはご存知のFIFO (First In - First Out; 先入れ先出し) データ構造のことです。

メッセージ処理を行った場合は、WinMain 関数の戻り値として MSG 構造体の wParam メンバを返しましょう。メッセージ処理を行わない場合は、WinMain 関数は 0 を返しましょう。

目次へ


ウィンドウプロシージャ

メッセージはウィンドウプロシージャで処理します。ウィンドウプロシージャは次のように記述します。

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{

    switch(Msg)
    {
    case WM_CREATE:
        // ウィンドウが作成されたときの処理
        break;

    case WM_PAINT:
        // クライアント領域の描画
        break;

    case WM_DESTROY:
        // ウィンドウが消滅するときの処理

    ...


    default:
        // デフォルトの処理
        DefWindowProc(hWnd, Msg, wParam, lParam);
        break;
    }

    return 0;
}

DefWindowProc 関数は、デフォルトのメッセージの処理を行います。デフォルト処理は必ず行うようにしましょう。

WPARAM / LPARAM / LRESULT は次のように定義されています。

typedef unsigned long WPARAM;
typedef unsigned long LPARAM;

typedef long          LRESULT;

ソースコードに定数を指定することで、コンパイル時にオプションを指定することが出来ます。例題では、WIN32_LEAN_AND_MEAN というオプションを指定しています。このオプションを指定することで、あまり使わない関数群をヘッダーファイルから省くことが出来ます。これによってコンパイル時間が縮小できます。そのほか代表的なものを上げておきましょう。

定数 説明
WIN32_LEAN_AND_MEAN ヘッダーからあまり使われない関数を省く
STRICT 型チェックを厳密に行う
UNICODE 文字列を Unicode として扱う。

次のように指定します。

#define STRICT
#define WIN32_LEAN_AND_MEAN

#include <windows.h>

...

オプションはこの他にもいろいろあります。

目次へ

Copyright 1999-2005 けんいち All Right Reserved. V6PJ.