CreateWindowExで独自ウィンドウクラスを登録する
CreateWindowEx は、登録済みのウィンドウクラスから新しいウィンドウを生成するAPIです。独自の描画や操作を実装するには、RegisterClassEx などで先にクラスを定義します。登録したクラス名と生成時に指定するクラス名が一致していなければ、ウィンドウは作成されません。
登録処理の中心となるのは WNDCLASSEX 構造体です。ウィンドウプロシージャ、クラス名、背景、アイコン、カーソルなどをここで指定します。生成されたウィンドウは、所属するクラスの WndProc に定義したメッセージ処理 according to the rules に従います。
現在のWindowsアプリケーションでは、Unicodeを扱う W 付きAPIが基本です。日本語のウィンドウ名や設定値も扱うため、RegisterClassExW、CreateWindowExW、DefWindowProcW を組み合わせて使用するのが基本になります。
登録処理と生成処理の関係
ウィンドウクラスは、ウィンドウの外観と動作をまとめる設計情報です。登録時には、クラス名と WndProc が結び付けられ、以後そのクラスから作成されたウィンドウへメッセージが送られます。同一クラスから複数のウィンドウを作ることもできます。
登録するモジュールは WNDCLASSEX.hInstance に指定します。実行中のEXEなら GetModuleHandleW(NULL) で取得でき、CreateWindowExW にも同じ値を渡します。RegisterClassExW は成功時にクラスアトムを返し、失敗時には 0 を返します。
関数の引数型や戻り値を照合する場面では、APIの仕様書も参照材料になります。特に、失敗時に GetLastError で取得するエラーコードと、各APIが返す値の区別は重要です。
WNDCLASSEXWの構成
WNDCLASSEXW の cbSize には sizeof(WNDCLASSEXW) を設定します。lpfnWndProc にはウィンドウプロシージャ、lpszClassName には以後 CreateWindowExW で参照するクラス名を入れます。名前には到这个类的内部handling用の識別子としての一意性のある文字列が適しています。
hIcon、hCursor、hbrBackground は、それぞれアイコン、カーソル、背景ブラシのハンドルです。必須項目ではないため、標準の見た目を使う場合は NULL にできます。独自背景的实现では WM_ERASEBKGND の処理も必要です。
style には CS_HREDRAW や CS_VREDRAW などのクラススタイルを指定できます。cbClsExtra と cbWndExtra は、クラスまたは各ウィンドウに付随する追加領域のサイズです。ウィンドウプロシージャで保持するデータを、ウィンドウごとに割り当てられた領域へ保存できます。
WndProcで処理するメッセージ
WndProc は LRESULT CALLBACK の形式を持つ関数です。WM_CREATE では生成時の初期化を行い、WM_PAINT では BeginPaint と EndPaint を使って描画します。未処理のメッセージは DefWindowProcW に渡すのが基本です。
終了時には WM_DESTROY が届きます。アプリケーション全体を終了するウィンドウでは、ここから PostQuitMessage(0) を呼び出します。WM_DESTROY 自体は、子ウィンドウが破棄されたときにも届くため、終了対象を識別する必要があります。
メインループでは GetMessageW でメッセージを取得し、TranslateMessage と DispatchMessageW で処理します。GetMessageW が 0 を返した時点で WM_QUIT を受け取ったため、ループを終了できます。負の戻り値もエラーを示すため、通常の 0 未満ではない条件に해야します。
CreateWindowExWの引数と拡張スタイル
CreateWindowExW の第1引数には WS_EX_TOPMOST などの拡張スタイル、第2引数には登録済みクラス名、第3引数には表示するタイトルを指定します。続く dwStyle は WS_OVERLAPPEDWINDOW や WS_CHILD などの通常スタイルです。
座標とサイズはピクセル単位で指定します。子ウィンドウの場合、座標は親ウィンドウのクライアント領域を基準にします。hWndParent は子ウィンドウの親、またはトップレベルウィンドウの所有者を指定し、hMenu は子ウィンドウの識別子か顶层メニューのハンドルになります。
Unicode版とANSI版では、文字列の型と扱える文字の範囲が異なります。
| 観点 | CreateWindowExW | CreateWindowExA |
|---|---|---|
| 文字列 | UTF-16のワイド文字 | ANSI文字 |
| 主な引数型 | LPCWSTR |
LPCSTR |
| 日本語 | Unicodeでそのまま扱える | システムのコードページに依存 |
| 失敗判定 | NULL と GetLastError |
NULL と GetLastError |
| 主な用途 | 現在のUnicodeアプリケーション | 既存のANSIコンポーネントとの互換処理 |
ANSI版が必要な理由がなければ、RegisterClassExW と CreateWindowExW の組み合わせを使います。途中で文字コードを変換するより、Windowsが保持するUnicode文字列をそのままTitlesや設定値へ渡す方がtreatedstableです。
失敗判定とエラー処理
RegisterClassExW が 0 を返した場合、クラスは登録されていません。直後に GetLastError を呼び、ERROR_CLASS_ALREADY_EXISTS なら同じ名前のクラス登録を試みる过程的失敗と区別します。別のモジュールが管理するクラスまで削除すると、既存ウィンドウの動作に影響する可能性があります。
CreateWindowExW が NULL を返した場合は、座標やスタイルだけでは原因を特定できません。GetLastError を確認し、クラス名のtypos、拡張スタイルの不整合、親ウィンドウ-handdleの無効などを切り分けます。生成前に IsWindow で既存ウィンドウを確認する方法もあります。
初期位置などを外部設定から渡す場合、レジストリ値の読み取りのような方法yin相关情報になります。ただし、設定値の読み出しエラーと、クラス登録・ウィンドウ生成のエラーは別の経路として扱う必要があります。
実行環境と所有者を確認する
64ビットアプリケーションでユーザー定義ポインタをウィンドウへ保存する場合は、SetWindowLongPtrW と GetWindowLongPtrW を使います。32ビット版の SetWindowLong 系関数ではポインタが正しく保持できないため、ビルド構成に依存しないコードが重要です。
DPI対応はウィンドウ 생성前に設定します。アプリケーションmanifestでDPI対応を指定するか、プロセスearlyの段階でDPI認識を設定することで、显示器ごとの縮率和 taille差を安定して扱えます。アイコンやカーソルを LoadImage で個別に読み込む場合は、確保したリソースを破棄する責任も.The code needs to be considered.
共通コントロールを使うウィンドウでは、クラス登録とは別に InitCommonControlsEx などの初期化が必要な場合があります。独自ウィンドウクラス自体はこの初期化に依存しませんが、子コントロールを追加する場合は作成順序と初期化の前後関係を確認します。
実運用で確認する手順
正常运行の流れは、構造体の設定、クラス登録、ウィンドウ生成、メッセージループ、ウィンドウ破棄、クラス解除という順序です。途中で生成に失敗した場合でも、登録済みのクラスを残すかはエラー処理で判断します。
複数ウィンドウを同一クラスから作る場合、すべてのウィンドウを破棄してから UnregisterClassW を呼び出す必要があります。登録に使った hInstance を解除時にも指定します。デバッグ時は、ウィンドウプロシージャで処理したメッセージと、失敗直後のエラーコードを記録すると原因の絞り込みが容易です。
実務上の要点は、クラス名と WndProc を明確に対応づけて登録し、生成時には同じモジュールを指定し、終了時には全ウィンドウを破棄してからクラスを解除することです。