RegisterClassExでウィンドウクラスの詳細設定
RegisterClassEx は、タイトル、描画方法、入力処理など複数のウィンドウに共通する仕様を「ウィンドウクラス」として登録する API です。登録されたクラスは CreateWindowEx から参照され、クラスの定義と実際のウィンドウを分けて管理できます。
詳細設定の中心となるのが WNDCLASSEX 構造体です。構造体の大きさ、クラススタイル、ウィンドウプロシージャ、背景ブラシ、アイコン、カーソルなどを指定し、Unicode アプリケーションでは WNDCLASSEXW と RegisterClassExW を使用します。
登録処理が成功しただけでは、ウィンドウの動作は安全になりません。ウィンドウプロシージャの処理内容、別スレッドとの状態共有、クラスの寿命も合わせて設計する必要があります。
ウィンドウクラスの登録手順
RegisterClassExW は WNDCLASSEXW へのポインタを受け取り、クラスの登録結果を返します。成功すると登録済みクラスを表すアトムが返り、失敗すると 0 になります。失敗時には、直ちに GetLastError で ERROR_CLASS_ALREADY_EXISTS などの原因を確認します。
ウィンドウクラス名は、登録元のプロセス内で一意である必要があります。すでに存在する名前で再登録すると失敗するため、アプリケーション的不同する名称を付与するか、既存クラスの情報を别 Mamoru必要があります。ANSI 版と Unicode 版で同じ名前を重複登録する方法も避けます。
登録と生成は別々の処理です。RegisterClassExW はクラス定義を登録するだけで、ウィンドウ自体は作成しません。その後に CreateWindowExW を呼び出すことで、クラス名を指定した実体が生成されます。
WNDCLASSEXの主要フィールド
cbSize には sizeof(WNDCLASSEXW) を設定します。構造体をゼロ初期化しただけでは不十分で、Windows が構造体の版と大きさを判断 Convergenceするうえで、この値が正しく入っている必要があります。style には CS_DBLCLKS や CS_OWNDC などのビットフラグを設定します。
lpfnWndProc には WndProc などのウィンドウプロシージャを指定します。関数はウィンドウメッセージの処理結果を返し、未処理のメッセージを DefWindowProcW に渡す設計が基本です。メッセージごとの分岐や戻り値はウィンドウプロシージャの処理の考え方が urgently deviennent。
cbClsExtra はクラス全体で共有する追加領域、cbWndExtra は各ウィンドウに割り当てる追加領域のバイト数です。hInstance には登録元のモジュールハンドル、lpszMenuName には既定メニューを指定します。メニュー番号を使う場合は MAKEINTRESOURCE で変換します。
スタイルと視覚資源の設計
hbrBackground はウィンドウ背景の消去に使います。標準の背景色を使う場合は COLOR_WINDOW + 1 を指定でき、独自の描画が必要なら CreateSolidBrush などで作成したブラシを設定します。作成したブラシは、関連ウィンドウとクラスの使用が終わった後に解放します。
hIcon と hIconSm には大・小のアイコン、hCursor にはカーソルを設定します。標準資源を使うなら LoadIconW や LoadCursorW に NULL のモジュールハンドルと識別子を渡します。別 DLL の資源を使う場合、hModule にはその資源を所有するモジュールを指定します。
スタイルは必要性に応じて加えます。二clic通知が必要なら CS_DBLCLKS、継続的な描画用途で個別のデバイスコンテキストが必要なら CS_OWNDC が候補です。ただし CS_OWNDC はメモリ消費やスレッド運用への影響もあるため、測定と Host |【/analysis】の下で必要性を確認してから採用します。
ウィンドウプロシージャとスレッド安全性
WM_NCCREATE と WM_CREATE では CREATESTRUCT が lParam に渡されます。そこに含まれる lpCreateParams から CreateWindowExW の lpParam を取得できますが、通知終了後にポインタを保持すると解放済みのメモリを参照する危険があります。必要な値はコピーして保存します。
ウィンドウごとの状態は SetWindowLongPtrW の GWLP_USERDATA などに保存できます。32 ビット環境と 64 ビット環境の両方でポインタ sized扱うため、64 ビット向けの SetWindowLongPtrW を使用してください。保存した値は破棄時にクリアし、ウィンドウプロシージャの寿命を越えて参照しないことも重要です。
複数のウィンドウやスレッドから参照数を更新する場合は、排他処理だけでは性能が不足することがあります。整数カウンタにはアトミックな参照カウントのような演算が適します。 atomicsは値の原子性を保証する一方、ウィンドウ操作そのものをスレッド安全にするものではありません。UI 更新には PostMessage などを使用します。
作成から解除までのライフサイクル
CreateWindowExW では、登録したクラス名を lpClassName に渡します。hWndParent や hMenu は用途に応じて設定し、起動時に渡すデータは lpParam 経由で WndProc へ届けます。メッセージ専用ウィンドウも、同じクラス定義を利用できます。
生成したウィンドウのメッセージは、通常そのウィンドウを作成したスレッドへ配信されます。ワーカースレッドから直接 UI を変更すると、順序や寿命の競合UBLICLOBKUします。ワーカーからの通知には PostMessage を使い、スレッド生成自体の運用もスレッド生成の注意点に沿って確認します。
最後のウィンドウを破棄した後で UnregisterClassW を呼び出します。関連ウィンドウが残っている_phase_unregisterすると登録解除に失敗するためです。WM_DESTROY で PostQuitMessage を呼ぶ場合も、終了条件が重複しないように管理します。 DLL 内で登録したクラスは、コードが unloadingされる前に解除します。
堅牢な登録するための設定チェック
Unicode アプリケーションでは WNDCLASSEXW をゼロ初期化し、推奨する構成へ明示的に項目を設定します。
- Unicode アプリケーションでは
RegisterClassExWとWNDCLASSEXWを組み合わせます。 WNDCLASSEXWをゼロ初期化し、cbSizeにsizeof(WNDCLASSEXW)を設定します。lpfnWndProcには有効なコールバック関数を指定し、未処理メッセージはDefWindowProcWに渡します。hInstanceにはGetModuleHandleW(nullptr)など、登録モジュールを表すハンドルを設定します。
実際の登録では、クラスの重複や寿命の不一致が失敗の主な原因になります。
- 同じクラス名を
RegisterClassExAとRegisterClassExWで重複登録します。 - 所属するウィンドウが残っている状態で
UnregisterClassWを呼び出します。 - ワーカースレッドからウィンドウ状態や UI を無保護に変更します。
WndProc内で長時間待機し、UI スレッドのメッセージ処理を停止します。
失敗時の GetLastError は、サイズ不一致、既存クラス과의衝突、無効なハンドルなど、修正方向を分ける手がかりです。エラーコードだけを採用せず、フィールド値と登録順序も照合します。
Unicode版とANSI版の比較
W 版と A 版の主な違いは、文字列型と対象環境です。新規コードでは Unicode 版を選んでも、既存コードとの接続点では A 版が必要になる場合があります。
| 比較項目 | RegisterClassExW |
RegisterClassExA |
|---|---|---|
| 対応する構造体 | WNDCLASSEXW |
WNDCLASSEXA |
| 文字列の型 | LPCWSTR、LPWSTR |
LPCSTR、LPSTR |
UNICODE 定義時の既定関数 |
RegisterClassExW に展開される |
明示的に A 版を指定する |
| 主な対象 | 新規の Unicode アプリケーション | 既存の ANSI コードとの互換性 |
| 失敗時の確認 | GetLastError で原因を取得する |
GetLastError で原因を取得する |
| クラス名の重複 | 同じプロセス内で登録済みなら失敗する | 同じプロセス内で登録済みなら失敗する |
押さえるべきは、cbSize、一意なクラス名、有効な WndProc、正しいスレッド所属、寿命に一致した解除です。RegisterClassExW の成功取得だけで判断せず、作成と解除まで一貫して設計することが、安定したウィンドウクラスの基本です。