Win32API

WinMainから始めるWindowsアプリケーション開発入門

はじめに

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は イベント駆動型 であり、ユーザーの入力やシステムからの通知(メッセージ)を処理して動作します。
つまり、ウィンドウを作るには以下の流れが必要です。

  1. ウィンドウクラスを登録するRegisterClassEx
    → このアプリで使うウィンドウの「設計図」をOSに伝える。
  2. ウィンドウを作成するCreateWindowEx
    → 設計図に従って、実際にウィンドウを作る。
  3. メッセージループを回すGetMessage / TranslateMessage / DispatchMessage
    → ユーザー操作やシステム通知を取り出し、処理する。
  4. ウィンドウプロシージャを定義する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アプリの土台を自分で書けるようになります。
次のステップとしては、メニューやコントロール(ボタン、テキストボックスなど)を追加し、実際に便利なアプリを作ってみるとよいでしょう。

-Win32API