> ## Content Index
> Fetch the complete content index at: https://www.geekjp.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# WinAPIのWNDCLASSA・WNDCLASSW・WNDCLASSEXA・WNDCLASSEXWの違いってなんだ？
- URL: https://www.geekjp.com/aroundwindowsclass/
- Published: 2026-07-25T15:41:58.000Z
- Updated: 2026-08-01T12:18:22.000Z
- Description: Win32 APIで登場するWNDCLASSA・WNDCLASSW・WNDCLASSEXA・WNDCLASSEXWの違いを、EXの有無とA/Wの文字列方式の2軸で整理します。Visual Studioの文字セット設定、UNICODEマクロ、TEXTマクロ、UTF-8保存設定との違いまで解説します。
- Author: 工学じじい
- Tags: ゲームプログラミング, DirectX11

Win32 APIでウィンドウを作ろうとすると、`RegisterClass`や`RegisterClassEx`という関数を使って、ウィンドウの属性や動作をあらかじめWindowsへ登録する場面が出てきます。

このとき設定するのが、**ウィンドウクラス**です。

ここでいうウィンドウクラスは、C++の`class`とは別物です。ウィンドウのアイコン、マウスカーソル、背景色、ウィンドウプロシージャなどをまとめた、いわば**ウィンドウの設計情報**です。

ところが、このウィンドウクラスを設定しようとすると、いきなり次の4種類が登場します。

```cpp
WNDCLASSA
WNDCLASSW
WNDCLASSEXA
WNDCLASSEXW

```

名前がほとんど同じなので、初めて見ると次のような疑問が出てきます。

- `WNDCLASS`と`WNDCLASSEX`は何が違うのか
- 末尾の`A`と`W`には何の意味があるのか
- なぜ文字列に`L`を付けるコードがあるのか
- `WNDCLASSEX`とだけ書かれているサンプルは何なのか
- Visual Studioの「Unicode文字セットを使用する」と関係があるのか

この記事では、この4種類の違いを順番に整理します。

結論から言うと、違いは次の2つだけです。

```text
WNDCLASS / WNDCLASSEX
    基本版か拡張版か

A / W
    扱う文字列の種類

```

4種類を個別に暗記する必要はありません。`EX`の意味と、`A`・`W`の意味を理解すれば整理できます。

## 4種類の違いを先に整理する

| 構造体         | 基本・拡張 | 使用する文字列   | クラス名の型  |
| ----------- | ----- | --------- | ------- |
| WNDCLASSA   | 基本版   | char系     | LPCSTR  |
| WNDCLASSW   | 基本版   | wchar\_t系 | LPCWSTR |
| WNDCLASSEXA | 拡張版   | char系     | LPCSTR  |
| WNDCLASSEXW | 拡張版   | wchar\_t系 | LPCWSTR |

名前を分解すると、次のようになります。

```text
WNDCLASS + A
WNDCLASS + W
WNDCLASSEX + A
WNDCLASSEX + W

```

つまり、覚えることは次の2点です。

```text
EXが付く
    → 拡張版

AまたはWが付く
    → 文字列の種類が変わる

```

まずは、`WNDCLASS`と`WNDCLASSEX`の違いから確認します。

---

## 1．WNDCLASSとWNDCLASSEXの違い

最初に、末尾の`A`と`W`を無視して考えます。

```text
WNDCLASS
WNDCLASSEX

```

`WNDCLASSEX`は、`WNDCLASS`を拡張した構造体です。

### WNDCLASSの構造

Unicode版の`WNDCLASSW`は、概略として次のメンバを持っています。

```cpp
typedef struct tagWNDCLASSW
{
    UINT      style;
    WNDPROC   lpfnWndProc;
    int       cbClsExtra;
    int       cbWndExtra;
    HINSTANCE hInstance;
    HICON     hIcon;
    HCURSOR   hCursor;
    HBRUSH    hbrBackground;
    LPCWSTR   lpszMenuName;
    LPCWSTR   lpszClassName;
} WNDCLASSW;

```

ウィンドウクラスには、次のような設定を登録します。

- ウィンドウの動作を表すスタイル
- メッセージを処理するウィンドウプロシージャ
- アプリケーションのインスタンス
- アイコン
- マウスカーソル
- 背景ブラシ
- メニュー名
- ウィンドウクラス名

ここでいう「ウィンドウクラス」は、C++の`class`とは別物です。

Win32 APIにおけるウィンドウクラスは、これから生成するウィンドウの共通設定をまとめたものです。

```text
ウィンドウクラス
    ウィンドウの設計情報

ウィンドウ
    その設計情報を使って実際に生成されたもの

```

### WNDCLASSEXの構造

拡張版の`WNDCLASSEXW`は、概略として次のメンバを持っています。

```cpp
typedef struct tagWNDCLASSEXW
{
    UINT      cbSize;
    UINT      style;
    WNDPROC   lpfnWndProc;
    int       cbClsExtra;
    int       cbWndExtra;
    HINSTANCE hInstance;
    HICON     hIcon;
    HCURSOR   hCursor;
    HBRUSH    hbrBackground;
    LPCWSTR   lpszMenuName;
    LPCWSTR   lpszClassName;
    HICON     hIconSm;
} WNDCLASSEXW;

```

`WNDCLASSW`と比較すると、次の2つが追加されています。

```cpp
UINT  cbSize;
HICON hIconSm;

```

### cbSize

`cbSize`には、使用する構造体全体のサイズを指定します。

```cpp
WNDCLASSEXW windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEXW);

```

`WNDCLASSEXW`を使用しているのに、別の構造体のサイズを指定してはいけません。

```cpp
WNDCLASSEXW windowClass{};

// 間違い
windowClass.cbSize =
    sizeof(WNDCLASSW);

```

使用している構造体自身のサイズを指定します。

```cpp
windowClass.cbSize =
    sizeof(WNDCLASSEXW);

```

### hIconSm

`hIconSm`には、ウィンドウクラスで使用する小さいアイコンを指定します。

```cpp
windowClass.hIconSm =
    LoadIconW(
        nullptr,
        IDI_APPLICATION
    );

```

従来からある`hIcon`には、通常サイズのアイコンを指定します。

```cpp
windowClass.hIcon =
    LoadIconW(
        nullptr,
        IDI_APPLICATION
    );

```

`WNDCLASSEX`と`WNDCLASS`の構造上の違いは、基本的に次の2つです。

| 追加メンバ   | 役割             |
| ------- | -------------- |
| cbSize  | 構造体全体のサイズを指定する |
| hIconSm | 小さいアイコンを指定する   |

### 登録関数も異なる

`WNDCLASS`と`WNDCLASSEX`では、対応する登録関数も異なります。

| 構造体         | 登録関数             |
| ----------- | ---------------- |
| WNDCLASSA   | RegisterClassA   |
| WNDCLASSW   | RegisterClassW   |
| WNDCLASSEXA | RegisterClassExA |
| WNDCLASSEXW | RegisterClassExW |

例えば、`WNDCLASSEXW`を使う場合は`RegisterClassExW`で登録します。

```cpp
WNDCLASSEXW windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEXW);

RegisterClassExW(
    &windowClass
);

```

次の組み合わせは間違いです。

```cpp
WNDCLASSEXW windowClass{};

// WNDCLASSW用の関数なので型が合わない
RegisterClassW(
    &windowClass
);

```

### WNDCLASSとWNDCLASSEXの比較

| 項目             | WNDCLASS      | WNDCLASSEX      |
| -------------- | ------------- | --------------- |
| 基本的なウィンドウクラス設定 | できる           | できる             |
| 構造体サイズの指定      | なし            | cbSize          |
| 小さいアイコンの指定     | なし            | hIconSm         |
| 対応する登録関数       | RegisterClass | RegisterClassEx |
| 新規コードでの選択      | 使用可能          | 基本的にこちらでよい      |

したがって、新しくコードを書く場合は、拡張版の`WNDCLASSEX`を選べばよいでしょう。

---

## 2．末尾のAとWの違い

次に、末尾の`A`と`W`を見ていきます。

```text
A
    Windowsコードページ版

W
    Unicode版・Wide Character版

```

一般には、次のように説明されることが多いです。

```text
A
    ANSI版

W
    Wide版

```

ただし、A版の「ANSI」という呼び方は歴史的なものです。

実際には、A版APIはWindowsのコードページに基づく`char`文字列を扱います。必ずしも特定のANSI規格そのものを意味するわけではありません。

### A版で使う文字列

A版では、`char`系の文字列を使用します。

```cpp
WNDCLASSEXA windowClass{};

windowClass.lpszClassName =
    "SampleWindowClass";

```

`WNDCLASSEXA::lpszClassName`の型は`LPCSTR`です。

`LPCSTR`は、実質的には次の型です。

```cpp
const char*

```

したがって、通常の文字列リテラルを代入できます。

```cpp
"SampleWindowClass"

```

登録にはA版の関数を使用します。

```cpp
RegisterClassExA(
    &windowClass
);

```

ウィンドウ生成にもA版を使用します。

```cpp
CreateWindowExA(
    0,
    "SampleWindowClass",
    "Sample Window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    1280,
    720,
    nullptr,
    nullptr,
    hInstance,
    nullptr
);

```

### W版で使う文字列

W版では、`wchar_t`系のワイド文字列を使用します。

```cpp
WNDCLASSEXW windowClass{};

windowClass.lpszClassName =
    L"SampleWindowClass";

```

`WNDCLASSEXW::lpszClassName`の型は`LPCWSTR`です。

`LPCWSTR`は、実質的には次の型です。

```cpp
const wchar_t*

```

そのため、文字列リテラルの先頭に`L`を付けます。

```cpp
L"SampleWindowClass"

```

登録にはW版の関数を使用します。

```cpp
RegisterClassExW(
    &windowClass
);

```

ウィンドウ生成にもW版を使用します。

```cpp
CreateWindowExW(
    0,
    L"SampleWindowClass",
    L"サンプルウィンドウ",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    1280,
    720,
    nullptr,
    nullptr,
    hInstance,
    nullptr
);

```

### 普通の文字列とL付き文字列は型が違う

次の2つは、見た目が似ていますが、C++上では異なる型です。

```cpp
"SampleWindowClass"

```

```cpp
L"SampleWindowClass"

```

対応を整理すると次のようになります。

| 書き方    | 文字型      | ポインター型           | 対応するWin32 API |
| ------ | -------- | ---------------- | ------------- |
| "ABC"  | char     | const char\*     | A版            |
| L"ABC" | wchar\_t | const wchar\_t\* | W版            |

文字列の先頭に付いている`L`は、表示される文字ではありません。

コンパイラに対して、文字列を`wchar_t`の配列として作るように指定する接頭辞です。

### A版とW版の対応

| 項目      | A版               | W版               |
| ------- | ---------------- | ---------------- |
| 構造体     | WNDCLASSEXA      | WNDCLASSEXW      |
| クラス名の型  | LPCSTR           | LPCWSTR          |
| 実質的な型   | const char\*     | const wchar\_t\* |
| 文字列リテラル | "ABC"            | L"ABC"           |
| 登録関数    | RegisterClassExA | RegisterClassExW |
| ウィンドウ生成 | CreateWindowExA  | CreateWindowExW  |
| 文字の扱い   | Windowsコードページ    | UTF-16           |

構造体だけをW版にしても、登録関数や文字列がA版のままでは型が合いません。

```text
WNDCLASSEXW
    ↓
RegisterClassExW
    ↓
CreateWindowExW
    ↓
L"文字列"

```

このように、構造体、関数、文字列を同じ系統へそろえる必要があります。

---

## 3．マルチバイト文字列とは

A版の説明では、`char`系の文字列とWindowsコードページが登場しました。

では、A版でよく使われる「マルチバイト文字列」とは何でしょうか。

マルチバイト文字列は、`char`を使いながら、1文字を1バイトまたは複数バイトで表現する方式です。

日本語Windowsで古くから使われてきたコードページでは、英数字などを1バイト、日本語の多くを2バイトで表現します。

```text
A
    1バイト

あ
    2バイトになることがある

```

このため、見た目の文字数とバイト数が一致しない場合があります。

```cpp
const char* text = "日本語";

```

この文字列を`char`単位で調べると、1つの日本語文字が複数の要素に分かれます。

### A版APIはWindowsコードページを使う

A版のWin32 APIは、`char`の文字列をWindowsコードページに従って解釈します。

```cpp
MessageBoxA(
    nullptr,
    "メッセージ",
    "タイトル",
    MB_OK
);

```

ここで重要なのは、A版が必ずUTF-8を扱うわけではないという点です。

```text
std::string
    ＝ 自動的にUTF-8ではない

char文字列
    ＝ 自動的にShift_JISでもない

```

`char`や`std::string`は、バイトを保存するための型です。

中身がUTF-8なのか、Windowsコードページなのかは、型だけでは判断できません。

A版APIは実行環境のWindowsコードページに依存するため、別の言語環境への移植や、扱えない文字を含む場合に問題が起きやすくなります。

そのため、新規のWindowsアプリケーションではUnicode版の利用が推奨されます。

---

## 4．Unicode文字列とは

Unicodeは、世界中の文字に共通の番号を割り当てるための規格です。

Unicodeの文字をコンピューター上に保存する方法には、次のようなものがあります。

```text
Unicode
  ├─ UTF-8
  ├─ UTF-16
  └─ UTF-32

```

つまり、UnicodeとUTF-16は同じ意味ではありません。

```text
Unicode
    文字を整理する規格

UTF-16
    Unicodeの文字を保存する方式の1つ

```

### Win32 APIのW版はUTF-16を使う

WindowsのW版APIは、基本的にUTF-16の文字列を使用します。

C++では主に次の型で扱います。

```cpp
wchar_t
const wchar_t*
std::wstring

```

Win32 APIでは、次の型名が使われます。

```cpp
WCHAR
LPWSTR
LPCWSTR

```

対応を整理すると次のようになります。

| C++の型            | Win32の型 | 意味            |
| ---------------- | ------- | ------------- |
| wchar\_t         | WCHAR   | ワイド文字の1コード単位  |
| wchar\_t\*       | LPWSTR  | 書き換え可能なワイド文字列 |
| const wchar\_t\* | LPCWSTR | 読み取り専用のワイド文字列 |

例えば、次のコードは`LPCWSTR`へ代入できます。

```cpp
LPCWSTR className =
    L"SampleWindowClass";

```

`LPCWSTR`は特殊な文字列クラスではありません。

実質的には`const wchar_t*`へ付けられたWindows独自の型名です。

### 1文字が必ず1個のwchar\_tとは限らない

Windowsの`wchar_t`は、UTF-16の1コード単位を格納します。

多くの日本語や英数字は1個の`wchar_t`で表現できますが、Unicodeのすべての文字が必ず1個に収まるわけではありません。

絵文字や一部の文字は、2個のUTF-16コード単位で表現されます。

したがって、`std::wstring::size()`が返す値は、必ずしも人間が見た文字数と一致しません。

この記事では、まず次の対応を押さえておけば十分です。

```text
A版
    char
    LPCSTR
    "文字列"

W版
    wchar_t
    LPCWSTR
    L"文字列"

```

---

## 5．4種類をもう一度整理する

文字列の違いまで含めて、4種類を確認します。

### WNDCLASSA

```text
WNDCLASS
    基本版

A
    char系・Windowsコードページ

```

```cpp
WNDCLASSA windowClass{};

windowClass.lpszClassName =
    "SampleWindowClass";

RegisterClassA(
    &windowClass
);

```

### WNDCLASSW

```text
WNDCLASS
    基本版

W
    wchar_t系・UTF-16

```

```cpp
WNDCLASSW windowClass{};

windowClass.lpszClassName =
    L"SampleWindowClass";

RegisterClassW(
    &windowClass
);

```

### WNDCLASSEXA

```text
WNDCLASSEX
    拡張版

A
    char系・Windowsコードページ

```

```cpp
WNDCLASSEXA windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEXA);

windowClass.lpszClassName =
    "SampleWindowClass";

RegisterClassExA(
    &windowClass
);

```

### WNDCLASSEXW

```text
WNDCLASSEX
    拡張版

W
    wchar_t系・UTF-16

```

```cpp
WNDCLASSEXW windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEXW);

windowClass.lpszClassName =
    L"SampleWindowClass";

RegisterClassExW(
    &windowClass
);

```

新規コードでは、最後の`WNDCLASSEXW`を使うのが分かりやすいでしょう。

---

## 6．AもWも付いていないWNDCLASSEXは何なのか

ここまでで、4種類の違いは説明できました。

```text
WNDCLASS / WNDCLASSEX
    基本版か拡張版か

A / W
    使用する文字列の種類

```

ところが、実際のサンプルコードを見ると、新しい疑問が出てきます。

次のように、末尾の`A`や`W`が付いていない型が使われていることがあるからです。

```cpp
WNDCLASSEX windowClass{};

```

関数名も同様です。

```cpp
RegisterClassEx(
    &windowClass
);

```

```cpp
CreateWindowEx(
    // 引数
);

```

これは、第5の構造体や第3の関数が存在するという意味ではありません。

末尾のない名前は、プリプロセッサによってA版またはW版へ置き換えられます。

この切り替えの条件として使われるのが、`UNICODE`というプリプロセッサマクロです。

### UNICODEマクロとは

`UNICODE`は、末尾のないWin32 APIの型名や関数名を、W版へ切り替えるために使われるマクロです。

プリプロセッサマクロとは、C++のコードを本格的にコンパイルする前に、名前の置き換えや条件分岐を行う仕組みです。

Windowsのヘッダーでは、概念的に次のような切り替えが行われています。

```cpp
#ifdef UNICODE

#define WNDCLASSEX      WNDCLASSEXW
#define RegisterClassEx RegisterClassExW
#define CreateWindowEx  CreateWindowExW

#else

#define WNDCLASSEX      WNDCLASSEXA
#define RegisterClassEx RegisterClassExA
#define CreateWindowEx  CreateWindowExA

#endif

```

実際のWindowsヘッダーでは、型の別名や関数マクロが個別に定義されていますが、考え方は同じです。

つまり、`UNICODE`が定義されているかどうかによって、末尾なしの名前がA版・W版のどちらになるかが決まります。

### UNICODEが定義されている場合

```text
WNDCLASS
    → WNDCLASSW

WNDCLASSEX
    → WNDCLASSEXW

RegisterClassEx
    → RegisterClassExW

CreateWindowEx
    → CreateWindowExW

```

### UNICODEが定義されていない場合

```text
WNDCLASS
    → WNDCLASSA

WNDCLASSEX
    → WNDCLASSEXA

RegisterClassEx
    → RegisterClassExA

CreateWindowEx
    → CreateWindowExA

```

では、この`UNICODE`はどこで定義されるのでしょうか。

ここで初めて、Visual Studioのプロジェクト設定にある次の項目が登場します。

```text
Unicode文字セットを使用する
マルチバイト文字セットを使用する

```

この設定は、先ほどまで説明していた4種類そのものを直接選択する画面ではありません。

末尾のない`WNDCLASSEX`や`RegisterClassEx`を、A版とW版のどちらへ切り替えるかを決める設定です。

---

## 7．Visual Studioの「文字セット」設定との関係

Visual StudioのC++プロジェクトには、次の設定があります。

```text
文字セット

```

一般的には、次の手順で確認できます。

```text
ソリューション エクスプローラー
    ↓
対象のプロジェクトを右クリック
    ↓
プロパティ
    ↓
構成プロパティ
    ↓
詳細設定
    ↓
文字セット

```

Visual Studioやプロジェクトの種類によっては、`構成プロパティ > 全般`に表示される場合もあります。

設定には、主に次の選択肢があります。

```text
Unicode文字セットを使用する
マルチバイト文字セットを使用する
設定なし

```

この設定は、末尾のないWin32 APIの型名や関数名をA版・W版のどちらとしてコンパイルするかに関係します。

---

### Unicode文字セットを使用する場合

Visual Studioで次を選択します。

```text
Unicode文字セットを使用する

```

この設定では、プロジェクトに主に次のプリプロセッサマクロが定義されます。

```cpp
UNICODE
_UNICODE

```

`_MBCS`は定義されない構成になります。

Windowsのヘッダーは、主に`UNICODE`マクロを見てA版とW版を切り替えます。

そのため、次のコードは、

```cpp
WNDCLASSEX windowClass{};

RegisterClassEx(
    &windowClass
);

```

実質的に次の組み合わせになります。

```cpp
WNDCLASSEXW windowClass{};

RegisterClassExW(
    &windowClass
);

```

`CreateWindowEx`もW版になります。

```text
CreateWindowEx
    → CreateWindowExW

```

したがって、文字列にはワイド文字列が必要です。

```cpp
WNDCLASSEX windowClass{};

windowClass.lpszClassName =
    L"SampleWindowClass";

```

次のコードは型が一致しません。

```cpp
WNDCLASSEX windowClass{};

windowClass.lpszClassName =
    "SampleWindowClass";

```

Unicode設定では、`WNDCLASSEX`が`WNDCLASSEXW`になっているためです。

```text
必要な型
    LPCWSTR
    const wchar_t*

渡している型
    const char*

```

---

### マルチバイト文字セットを使用する場合

Visual Studioで次を選択します。

```text
マルチバイト文字セットを使用する

```

この設定では、主に次のマクロが定義されます。

```cpp
_MBCS

```

通常、`UNICODE`と`_UNICODE`は定義されません。

そのため、末尾のないWin32 APIの名前はA版になります。

```text
WNDCLASS
    → WNDCLASSA

WNDCLASSEX
    → WNDCLASSEXA

RegisterClassEx
    → RegisterClassExA

CreateWindowEx
    → CreateWindowExA

```

次のコードは、

```cpp
WNDCLASSEX windowClass{};

RegisterClassEx(
    &windowClass
);

```

実質的に次の組み合わせになります。

```cpp
WNDCLASSEXA windowClass{};

RegisterClassExA(
    &windowClass
);

```

`lpszClassName`は`LPCSTR`になるため、通常の文字列を使用します。

```cpp
windowClass.lpszClassName =
    "SampleWindowClass";

```

---

### 設定なしの場合

文字セットを`設定なし`にすると、その設定からは`UNICODE`、`_UNICODE`、`_MBCS`が定義されません。

`UNICODE`が定義されていないため、末尾のないWin32 API名は基本的にA版になります。

```text
WNDCLASSEX
    → WNDCLASSEXA

```

ただし、ソースコードや別のプロジェクト設定で`UNICODE`を手動定義している場合は、その定義が使われます。

---

### 文字セット設定による変化

| Visual Studioの設定 | 主なマクロ             | WNDCLASSEX  | RegisterClassEx  | CreateWindowEx  |
| ---------------- | ----------------- | ----------- | ---------------- | --------------- |
| Unicode文字セット     | UNICODE、\_UNICODE | WNDCLASSEXW | RegisterClassExW | CreateWindowExW |
| マルチバイト文字セット      | \_MBCS            | WNDCLASSEXA | RegisterClassExA | CreateWindowExA |
| 設定なし             | 通常はいずれもなし         | WNDCLASSEXA | RegisterClassExA | CreateWindowExA |

### UNICODEと\_UNICODEの違い

「Unicode文字セットを使用する」を選ぶと、通常は`UNICODE`と`_UNICODE`の両方が定義されます。

名前は似ていますが、主な役割が異なります。

```text
UNICODE
    Windowsヘッダーが使用
    Win32 APIのA版・W版を切り替える

_UNICODE
    CランタイムやMFCなどが使用
    TCHAR系の機能を切り替える

_MBCS
    マルチバイト文字セット用の機能に影響する

```

`WNDCLASSEX`、`RegisterClassEx`、`CreateWindowEx`などの切り替えに直接関係するのは`UNICODE`です。

Visual Studioの文字セット設定を使うと、`UNICODE`と`_UNICODE`が矛盾しないようにまとめて設定されます。

---

## 8．TEXTマクロとの関係

末尾のない型名や関数名と一緒に、次のようなコードを見かけることがあります。

```cpp
TEXT("SampleWindowClass")

```

または、次の書き方です。

```cpp
_T("SampleWindowClass")

```

これらは、プロジェクト設定に応じて文字列の型を切り替えるためのマクロです。

### Unicode文字セットの場合

```cpp
TEXT("SampleWindowClass")

```

は、概念的には次の文字列になります。

```cpp
L"SampleWindowClass"

```

### マルチバイト文字セットの場合

```cpp
TEXT("SampleWindowClass")

```

は、通常の文字列になります。

```cpp
"SampleWindowClass"

```

したがって、末尾のない型と関数を使う場合は、文字列にも`TEXT`を使うとA/Wをまとめて切り替えられます。

```cpp
WNDCLASSEX windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEX);

windowClass.lpszClassName =
    TEXT("SampleWindowClass");

RegisterClassEx(
    &windowClass
);

CreateWindowEx(
    0,
    TEXT("SampleWindowClass"),
    TEXT("Sample Window"),
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    1280,
    720,
    nullptr,
    nullptr,
    hInstance,
    nullptr
);

```

この書き方は、同じソースコードをUnicode版とマルチバイト版の両方でビルドしたい場合に使われます。

ただし、新しくW版専用のコードを書くなら、次のように明示した方が読みやすい場合もあります。

```cpp
WNDCLASSEXW windowClass{};

windowClass.lpszClassName =
    L"SampleWindowClass";

RegisterClassExW(
    &windowClass
);

```

---

## 9．重要：「Unicode文字セット」はソースファイルの保存設定ではない

ここは非常に間違えやすいところです。

Visual Studioの次の設定は、

```text
文字セット
    Unicode文字セットを使用する

```

`.cpp`や`.h`ファイルをUTF-8形式で保存する設定ではありません。

この設定が主に行うことは、次のマクロを定義することです。

```cpp
UNICODE
_UNICODE

```

そして、末尾のないWin32 APIの型名や関数名をW版へ切り替えます。

```text
WNDCLASSEX
    → WNDCLASSEXW

RegisterClassEx
    → RegisterClassExW

CreateWindowEx
    → CreateWindowExW

```

一方、ソースファイルの保存形式は別の設定です。

```text
プロジェクトの文字セット
    Win32 APIをA版・W版のどちらにするか

ソースファイルのエンコーディング
    .cppや.hをどのバイト列で保存するか

```

### ソースファイルのエンコーディング

ソースコードは、例えば次の形式で保存できます。

```text
UTF-8
UTF-8 with BOM
Shift_JIS系のコードページ
UTF-16

```

コンパイラにソースファイルをUTF-8として解釈させる設定には、次のオプションなどがあります。

```text
/utf-8
/source-charset:utf-8

```

これは、Visual Studioの「Unicode文字セットを使用する」とは別の話です。

### 2つは独立している

例えば、次のような組み合わせも理屈上は存在します。

```text
プロジェクトの文字セット
    Unicode文字セット

ソースファイル
    Shift_JIS系で保存

```

逆に、次の組み合わせもあり得ます。

```text
プロジェクトの文字セット
    マルチバイト文字セット

ソースファイル
    UTF-8で保存

```

ソースファイルがUTF-8だからといって、`CreateWindowEx`が自動的に`CreateWindowExW`になるわけではありません。

また、プロジェクトをUnicode文字セットにしたからといって、`.cpp`ファイルが自動的にUTF-8へ変換されるわけでもありません。

この2つは分けて考える必要があります。

---

## 10．プロジェクト設定を変更するときの注意

Visual Studioのプロジェクトプロパティは、構成とプラットフォームごとに値を持つ場合があります。

設定画面の上部には、次の項目があります。

```text
構成
    Debug
    Release
    すべての構成

プラットフォーム
    Win32
    x64
    すべてのプラットフォーム

```

例えば、`Debug | x64`を選んだ状態で文字セットを変更すると、Release側には反映されない場合があります。

プロジェクト全体をUnicodeへ統一するなら、次のように設定します。

```text
構成
    すべての構成

プラットフォーム
    すべてのプラットフォーム

```

その上で、文字セットを変更します。

```text
文字セット
    Unicode文字セットを使用する

```

---

## 11．よくあるエラー

### エラー1：LPCWSTRにconst char\*を代入できない

Unicode文字セットのプロジェクトで、次のコードを書いたとします。

```cpp
WNDCLASSEX windowClass{};

windowClass.lpszClassName =
    "SampleWindowClass";

```

Unicode設定では、`WNDCLASSEX`は`WNDCLASSEXW`になります。

そのため、`lpszClassName`が要求する型は`LPCWSTR`です。

```text
要求される型
    LPCWSTR
    const wchar_t*

渡している型
    const char*

```

修正するには、ワイド文字列を使います。

```cpp
windowClass.lpszClassName =
    L"SampleWindowClass";

```

末尾なしの書き方を維持するなら、`TEXT`も使えます。

```cpp
windowClass.lpszClassName =
    TEXT("SampleWindowClass");

```

---

### エラー2：WNDCLASSEXWとRegisterClassExを混ぜる

次のコードを考えます。

```cpp
WNDCLASSEXW windowClass{};

RegisterClassEx(
    &windowClass
);

```

プロジェクトがUnicode文字セットなら、`RegisterClassEx`は`RegisterClassExW`になるため型が一致します。

しかし、マルチバイト文字セットの場合は、次の組み合わせになります。

```cpp
WNDCLASSEXW windowClass{};

RegisterClassExA(
    &windowClass
);

```

`RegisterClassExA`が要求するのは`WNDCLASSEXA*`なので、型が一致しません。

```text
必要
    const WNDCLASSEXA*

実際
    WNDCLASSEXW*

```

修正方法は、W版へ統一することです。

```cpp
WNDCLASSEXW windowClass{};

RegisterClassExW(
    &windowClass
);

```

または、すべて末尾なしへ統一します。

```cpp
WNDCLASSEX windowClass{};

RegisterClassEx(
    &windowClass
);

```

混ぜないことが重要です。

---

### エラー3：WNDCLASSEXAとRegisterClassExWを混ぜる

逆の組み合わせでも同じ問題が起きます。

```cpp
WNDCLASSEXA windowClass{};

RegisterClassExW(
    &windowClass
);

```

A版の構造体はA版の関数へ渡します。

```cpp
WNDCLASSEXA windowClass{};

RegisterClassExA(
    &windowClass
);

```

---

### エラー4：CreateWindowExWへ普通の文字列を渡す

次のコードは型が一致しません。

```cpp
CreateWindowExW(
    0,
    "SampleWindowClass",
    "Sample Window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    1280,
    720,
    nullptr,
    nullptr,
    hInstance,
    nullptr
);

```

`CreateWindowExW`は`LPCWSTR`を要求します。

文字列をワイド文字列にします。

```cpp
CreateWindowExW(
    0,
    L"SampleWindowClass",
    L"Sample Window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    1280,
    720,
    nullptr,
    nullptr,
    hInstance,
    nullptr
);

```

---

### エラー5：キャストで無理やり直す

次のようにキャストすると、コンパイルエラーだけが消える場合があります。

```cpp
const char* className =
    "SampleWindowClass";

windowClass.lpszClassName =
    reinterpret_cast<LPCWSTR>(
        className
    );

```

しかし、これは文字コード変換ではありません。

`char`のバイト列を、そのまま`wchar_t`の配列だと思って読み込ませているだけです。

```text
キャスト
    型の見方を変える

文字コード変換
    データの並びを別の文字表現へ変換する

```

A版文字列をW版文字列へ変換する必要がある場合は、`MultiByteToWideChar`などを使用します。

この変換については、別の記事で詳しく扱います。

---

## 12．どの書き方に統一するべきか

主な書き方は2種類あります。

### 方法1：末尾なしの名前で統一する

プロジェクト設定に応じて、A版とW版を切り替える方法です。

```cpp
WNDCLASSEX windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEX);

windowClass.lpszClassName =
    TEXT("SampleWindowClass");

RegisterClassEx(
    &windowClass
);

```

利点は、プロジェクト設定を変更するだけでA版とW版を切り替えられることです。

ただし、型が設定によって変わるため、初学者には実際に何を使っているのか分かりにくい場合があります。

### 方法2：W版を明示して統一する

新規コードでは、W版を明示する方法が分かりやすいでしょう。

```cpp
WNDCLASSEXW windowClass{};

windowClass.cbSize =
    sizeof(WNDCLASSEXW);

windowClass.lpszClassName =
    L"SampleWindowClass";

RegisterClassExW(
    &windowClass
);

```

この方法なら、Visual Studioの文字セット設定を見なくても、コードを読んだだけで使用する型が分かります。

```text
WNDCLASSEXW
    W版

RegisterClassExW
    W版

L"文字列"
    wchar_t文字列

```

既存コードが`TCHAR`や末尾なしのAPIを多く使っている場合は、無理に書き換えず、プロジェクト全体の方針へ合わせます。

新しく学習用・教材用のコードを書くなら、次の方針が特に分かりやすいでしょう。

```text
Visual Studio
    Unicode文字セットを使用する

Win32 API
    W版を明示する

```

---

## 13．WNDCLASSEXWを使った最小例

最後に、W版へ統一した最小例を示します。

```cpp
#include <Windows.h>

namespace
{
    constexpr wchar_t WINDOW_CLASS_NAME[] =
        L"SampleWindowClass";
}

LRESULT CALLBACK WindowProc(
    HWND hWnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(
        hWnd,
        message,
        wParam,
        lParam
    );
}

int WINAPI wWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    PWSTR,
    int nCmdShow
)
{
    WNDCLASSEXW windowClass{};

    windowClass.cbSize =
        sizeof(WNDCLASSEXW);

    windowClass.style =
        CS_HREDRAW |
        CS_VREDRAW;

    windowClass.lpfnWndProc =
        WindowProc;

    windowClass.hInstance =
        hInstance;

    windowClass.hIcon =
        LoadIconW(
            nullptr,
            IDI_APPLICATION
        );

    windowClass.hCursor =
        LoadCursorW(
            nullptr,
            IDC_ARROW
        );

    windowClass.hbrBackground =
        reinterpret_cast<HBRUSH>(
            COLOR_WINDOW + 1
        );

    windowClass.lpszClassName =
        WINDOW_CLASS_NAME;

    windowClass.hIconSm =
        LoadIconW(
            nullptr,
            IDI_APPLICATION
        );

    if (RegisterClassExW(
        &windowClass
    ) == 0)
    {
        return -1;
    }

    HWND hWnd =
        CreateWindowExW(
            0,
            WINDOW_CLASS_NAME,
            L"Win32 API Sample",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            1280,
            720,
            nullptr,
            nullptr,
            hInstance,
            nullptr
        );

    if (hWnd == nullptr)
    {
        return -1;
    }

    ShowWindow(
        hWnd,
        nCmdShow
    );

    MSG message{};

    while (GetMessageW(
        &message,
        nullptr,
        0,
        0
    ) > 0)
    {
        TranslateMessage(
            &message
        );

        DispatchMessageW(
            &message
        );
    }

    return static_cast<int>(
        message.wParam
    );
}

```

このコードでは、次の要素をすべてW版へ統一しています。

```cpp
WNDCLASSEXW
RegisterClassExW
CreateWindowExW
DefWindowProcW
GetMessageW
DispatchMessageW

```

文字列もワイド文字列です。

```cpp
L"SampleWindowClass"
L"Win32 API Sample"

```

---

## 14．まとめ

`WNDCLASSA`、`WNDCLASSW`、`WNDCLASSEXA`、`WNDCLASSEXW`の違いは、次の2軸で整理できます。

```text
WNDCLASS
    基本版

WNDCLASSEX
    cbSizeとhIconSmを追加した拡張版

```

```text
A
    char系
    Windowsコードページ

W
    wchar_t系
    UTF-16

```

4種類を整理すると、次のようになります。

| 構造体         | 基本・拡張 | 文字列                |
| ----------- | ----- | ------------------ |
| WNDCLASSA   | 基本版   | char・Windowsコードページ |
| WNDCLASSW   | 基本版   | wchar\_t・UTF-16    |
| WNDCLASSEXA | 拡張版   | char・Windowsコードページ |
| WNDCLASSEXW | 拡張版   | wchar\_t・UTF-16    |

対応する登録関数もそろえます。

| 構造体         | 登録関数             |
| ----------- | ---------------- |
| WNDCLASSA   | RegisterClassA   |
| WNDCLASSW   | RegisterClassW   |
| WNDCLASSEXA | RegisterClassExA |
| WNDCLASSEXW | RegisterClassExW |

末尾のない`WNDCLASSEX`や`RegisterClassEx`は、Visual Studioの「文字セット」設定と`UNICODE`マクロによってA版・W版へ切り替わります。

```text
Unicode文字セットを使用する
    ↓
WNDCLASSEXW
RegisterClassExW
CreateWindowExW

```

```text
マルチバイト文字セットを使用する
    ↓
WNDCLASSEXA
RegisterClassExA
CreateWindowExA

```

また、Visual Studioの「Unicode文字セットを使用する」は、ソースコードをUTF-8で保存する設定ではありません。

```text
プロジェクトの文字セット
    Win32 APIのA/Wを切り替える

ソースファイルのエンコーディング
    .cppや.hをUTF-8などのどの形式で保存するか

```

新しくWin32アプリケーションを作る場合は、次の構成へ統一すると分かりやすくなります。

```text
Visual Studio
    Unicode文字セットを使用する

```

```cpp
WNDCLASSEXW
RegisterClassExW
CreateWindowExW
L"文字列"

```

エラーが出たときは、次の4点を確認します。

```text
1. WNDCLASSかWNDCLASSEXか
2. A版かW版か
3. Visual Studioの文字セット設定はどちらか
4. 構造体・関数・文字列が同じ系統にそろっているか

```

`std::string`、`std::wstring`、`LPSTR`、`LPCWSTR`、UTF-8とUTF-16の変換については、次の記事で詳しく扱います。

> 次の記事：C++とWin32 APIの文字列型を整理する――string・wstring・LPSTR・LPCWSTR・UTF-8・UTF-16