はじめに
Windowsで動作するGUIアプリケーションを自分で作りたい、そう思ったことはありませんか?
C++やC言語で本格的にWindowsプログラミングを始めようとすると、必ず最初に登場するのが WinMain
という関数です。
これは、Windowsアプリケーションにおける エントリーポイント(開始地点) です。
C言語の標準的なプログラムでは int main()
がエントリーポイントですが、WindowsのGUIアプリケーションでは WinMain
がその役割を担っています。
本記事では、この WinMainから始まるプログラムの流れを、教材風に体系的に学べるように 解説します。
初心者向けに一歩ずつ進み、最小限のプログラムから始めて、最終的に「ウィンドウを作成してイベントを処理できる」本格的なWindowsアプリを作れるようになります。
途中では以下の要素も盛り込みます:
- コードサンプル(最小プログラムからウィンドウ作成まで)
- APIのフィールド解説(表形式)
- 処理の流れを理解できるシーケンス図・フローチャート
- ダイアログの種類(モーダル/モードレス)
- 代表的なWindowsメッセージの一覧と解説
1. WinMainとは何か?
WindowsのGUIアプリケーションは、ユーザーが「アイコンをクリックして起動する」ことを前提にしています。
そのため、main
の代わりに WinMain がエントリーポイントとして呼び出されます。
典型的なシグネチャは以下の通りです。
int WINAPI WinMain(
HINSTANCE hInstance, // このアプリケーションインスタンスのハンドル
HINSTANCE hPrevInstance, // 16bit Windows時代の名残(常にNULL)
LPSTR lpCmdLine, // コマンドライン引数(ANSI文字列)
int nCmdShow // ウィンドウの表示状態
);
各引数の意味
- hInstance
アプリケーションインスタンスの識別子。リソース読み込みやウィンドウクラス登録などで使用します。 - hPrevInstance
16bit Windows(古い環境)との互換用。現在は常にNULL
です。 - lpCmdLine
コマンドライン引数(文字列)。main(int argc, char* argv[])
の代替に相当します。 - nCmdShow
起動時にウィンドウを「最大化」「最小化」「通常表示」などどう表示するかを指定する値。
2. 最小のWinMainプログラム
まずは「ウィンドウを作らず、すぐに終了するだけ」の最小プログラムを紹介します。
これは、C言語の int main(){ return 0; }
に相当します。
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
return 0;
}
解説
- このプログラムを実行しても、ウィンドウは表示されません。
- しかし、正しく「Windowsアプリケーション」として動作します。
- 実行すると、一瞬で終了しますが、WinMainがエントリーポイントになっていることが理解できます。
3. メッセージループが不要なダイアログベースアプリ
次に紹介するのは、メッセージループを自分で書かなくても動作するプログラムです。
Windowsでは「ダイアログボックス」をベースにしたアプリを作ることができ、これを利用すると最小限のコードでGUIを表示できます。
コード例(DialogBoxを利用)
#include <windows.h>
#include "resource.h"
BOOL CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hwnd, 0);
return TRUE;
}
break;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN_DIALOG), NULL, DialogProc);
return 0;
}
特徴
- DialogBox を呼び出すと、自動的にメッセージループを内部で処理してくれます。
- ユーザー操作は DialogProc(ダイアログプロシージャ) が受け取ります。
- OKやキャンセルを押すと、
EndDialog
で終了できます。
これは「ダイアログベースの簡単なツール」を作るときに便利です。
ポイント
なぜダイアログの場合はメッセージループが不要なのか?
通常のウィンドウアプリケーションでは、WinMain
内で自分で メッセージループ を書く必要があります。
これは、ユーザーからの入力(キー、マウス、ウィンドウ操作など)を逐次取り出して、WndProc
に振り分けるためです。
一方、DialogBox
などの ダイアログベースのAPI を使う場合は、メッセージループが内部に組み込まれている ため、開発者がループを自分で書く必要はありません。
処理の仕組み
DialogBox
を呼び出すと、Windows内部で専用のメッセージループが開始されます。- このループは、通常の
GetMessage → TranslateMessage → DispatchMessage
とほぼ同じ処理をしています。 - 開発者が実装するのは DialogProc(ダイアログプロシージャ) だけで、メッセージが自動的にそこへ送られます。
- そのため、
DialogBox
は呼び出し元に制御を返さず、モーダルダイアログが閉じられるまで処理がブロックされる 仕組みになっています。
メリット
- メッセージループを自前で書かなくてもよいので、コードが短くシンプルになる。
- モーダルダイアログの場合、ユーザーがダイアログを閉じるまで他の操作をブロックできる。
まとめ
つまり、ダイアログベースのアプリでは「OSが裏側でメッセージループを管理してくれる」ため、開発者はループを書く必要がないのです。
4. なぜウィンドウを表示する流れを学ぶのか?
ここから先は、いよいよ「自分でウィンドウを作る」本格的なWindowsアプリケーションです。
WindowsのGUIは イベント駆動型 であり、ユーザーの入力やシステムからの通知(メッセージ)を処理して動作します。
つまり、ウィンドウを作るには以下の流れが必要です。
- ウィンドウクラスを登録する(
RegisterClassEx
)
→ このアプリで使うウィンドウの「設計図」をOSに伝える。 - ウィンドウを作成する(
CreateWindowEx
)
→ 設計図に従って、実際にウィンドウを作る。 - メッセージループを回す(
GetMessage
/TranslateMessage
/DispatchMessage
)
→ ユーザー操作やシステム通知を取り出し、処理する。 - ウィンドウプロシージャを定義する(
WndProc
)
→ 受け取ったメッセージを解釈し、描画や入力処理を行う。
これらを理解することで、Windowsプログラミングの基本構造を完全に把握できます。
5. ウィンドウ生成の基本コード
#include <windows.h>
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
const char CLASS_NAME[] = "SampleWindowClass";
// ウィンドウクラスの登録
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = CLASS_NAME;
RegisterClassEx(&wc);
// ウィンドウ作成
HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Hello, Windows!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 400,
NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// メッセージループ
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
6. フローチャート(処理の流れ)
WinMain
↓
RegisterClassEx(ウィンドウの設計図を登録)
↓
CreateWindowEx(ウィンドウを作成)
↓
ShowWindow / UpdateWindow(表示)
↓
メッセージループ
├─ GetMessage(入力や通知を取得)
├─ TranslateMessage(キー入力を変換)
└─ DispatchMessage → WndProcへ
↓
WndProc(メッセージごとに処理)
├─ WM_PAINT → 描画
├─ WM_COMMAND → ボタン操作
├─ WM_CLOSE → 閉じる処理
└─ WM_DESTROY → PostQuitMessage
↓
アプリ終了
7. RegisterClassEx のフィールド解説(表形式)
フィールド | 意味 |
---|---|
cbSize | 構造体サイズ(sizeof(WNDCLASSEX) を必ず設定) |
style | 再描画やクラスの特性を指定(例: CS_HREDRAW , CS_VREDRAW ) |
lpfnWndProc | ウィンドウプロシージャ関数へのポインタ |
cbClsExtra | 追加メモリ(通常0) |
cbWndExtra | 追加メモリ(通常0) |
hInstance | アプリのインスタンスハンドル |
hIcon | アプリアイコン |
hCursor | カーソルハンドル |
hbrBackground | 背景ブラシ(例: COLOR_WINDOW+1 ) |
lpszMenuName | メニューリソース名 |
lpszClassName | ウィンドウクラス名 |
hIconSm | 小さいアイコン |
8. CreateWindowEx のフィールド解説(表形式)
パラメータ | 意味 |
---|---|
dwExStyle | 拡張スタイル(影や透明など) |
lpClassName | 登録したウィンドウクラス名 |
lpWindowName | ウィンドウのタイトルバー文字列 |
dwStyle | ウィンドウスタイル(例: WS_OVERLAPPEDWINDOW ) |
x, y | 初期座標 |
nWidth, nHeight | 幅・高さ |
hWndParent | 親ウィンドウ(通常NULL) |
hMenu | メニューやコントロールID |
hInstance | アプリインスタンス |
lpParam | 作成時に渡す追加データ |
9. モーダルダイアログとモードレスダイアログの違い
種類 | 特徴 | 例 |
---|---|---|
モーダルダイアログ | ユーザーが閉じるまで他の操作ができない | 「ファイルを保存しますか?」ダイアログ |
モードレスダイアログ | 他のウィンドウと並行して操作できる | 検索ボックス、ツールウィンドウ |
10. 代表的なWindowsメッセージ一覧
メッセージ | 意味 |
---|---|
WM_PAINT | ウィンドウの描画要求 |
WM_COMMAND | ボタンやメニュー操作 |
WM_KEYDOWN | キー押下 |
WM_KEYUP | キー解放 |
WM_LBUTTONDOWN | マウス左クリック押下 |
WM_LBUTTONUP | マウス左クリック解放 |
WM_MOUSEMOVE | マウス移動 |
WM_CLOSE | ウィンドウを閉じる要求 |
WM_DESTROY | ウィンドウ破棄、終了通知 |
まとめ
ここまでで学んだこと:
- WinMainがWindowsアプリのエントリーポイント
- 最小プログラムは
return 0;
だけ - ダイアログベースのアプリはメッセージループ不要
- 本格的なアプリは
RegisterClassEx
(設計図)CreateWindowEx
(ウィンドウ作成)- メッセージループ
WndProc
(処理)
の4つが必須
- 代表的なメッセージを理解するとアプリが作れる
- モーダル/モードレスで挙動が変わる
この流れを理解すると、GUIアプリの土台を自分で書けるようになります。
次のステップとしては、メニューやコントロール(ボタン、テキストボックスなど)を追加し、実際に便利なアプリを作ってみるとよいでしょう。