SetWindowLongでウィンドウの拡張スタイルを変更する
Windows APIでウィンドウの見た目や作成後の動作を変える場合、拡張ウィンドウスタイルを表す WS_EX_* 定数を利用します。SetWindowLong は既存ウィンドウの属性を書き換える関数であり、GWL_EXSTYLE を指定すれば拡張スタイルのビット列を変更できます。
たとえば、常に手前に表示する、タスクバーに表示しない、マウス入力を背後のウィンドウへ通過させるといった設定は、拡張スタイルと深く関係します。作成時に CreateWindowEx で指定する方法に加えて、表示中のウィンドウへ後から適用できる点が便利です。
ただし、スタイルを変更しただけでは、枠やタイトルバーの再計算が行われない場合があります。変更後に SetWindowPos と SWP_FRAMECHANGED を組み合わせること、さらに64ビット環境では SetWindowLongPtr も検討することが重要です。
拡張ウィンドウスタイルの仕組み
通常のウィンドウスタイルは GWL_STYLE、拡張ウィンドウスタイルは GWL_EXSTYLE で取得・変更します。スタイル値は複数のフラグをビット単位で組み合わせた整数なので、追加時にはビットOR、削除時にはビット反転した値とのビットANDを使います。
代表的な拡張スタイルには、WS_EX_TOPMOST、WS_EX_TOOLWINDOW、WS_EX_APPWINDOW、WS_EX_LAYERED、WS_EX_TRANSPARENT などがあります。各フラグには適用条件があり、単に値を設定するだけで期待した表示になるとは限りません。たとえば透明度を扱う WS_EX_LAYERED では、別途 SetLayeredWindowAttributes や UpdateLayeredWindow が必要です。
ウィンドウハンドル HWND は、変更対象を一意に識別する値です。子ウィンドウを変更したいのか、トップレベルウィンドウを変更したいのかを確認してから操作します。Windows APIの定数や引数の対応を調べる際は、Windows APIリファレンスも関連情報を探す手がかりになります。
現在の値を読み取ってから変更する
現在の拡張スタイルを保存しておくと、既存の設定を壊さずに特定のフラグだけを追加できます。C言語では、次のように GetWindowLong で値を取得し、SetWindowLong へ変更後の値を渡します。
LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE,
exStyle | WS_EX_TOOLWINDOW);
この例では、既存の拡張スタイルを維持しながら WS_EX_TOOLWINDOW を追加しています。反対に削除する場合は次のように記述します。
LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE,
exStyle & ~WS_EX_TOOLWINDOW);
設定の切り替えを何度も行う処理では、変更前の値を保存し、終了時に元へ戻す設計も有効です。なお、関数の戻り値は変更前の長整数値です。戻り値が有効なスタイル値として 0 になる可能性もあるため、失敗判定まで必要な場合は、呼び出し前に SetLastError(0) を実行してから戻り値と GetLastError を確認します。
変更方法と関連APIの使い分け
ウィンドウを作成する時点で拡張スタイルが確定しているなら、CreateWindowEx の第1引数へ指定する方が自然です。表示後に状態を切り替える場合は SetWindowLong または SetWindowLongPtr が適しています。フレームの再計算が必要な場合には、変更後に SetWindowPos を呼び出します。
| 目的 | 主なAPI | 使用する値・指定 | 注意点 |
|---|---|---|---|
| 作成時に設定 | CreateWindowEx |
WS_EX_* |
初期状態を安全に決めやすい |
| 表示後に変更 | SetWindowLong |
GWL_EXSTYLE |
既存値とのビット演算が必要 |
| 64ビット対応 | SetWindowLongPtr |
GWLP_EXSTYLE |
ポインター幅に適した型を使う |
| 現在値の取得 | GetWindowLong |
GWL_EXSTYLE |
戻り値とエラー判定に注意 |
| 枠の再計算 | SetWindowPos |
SWP_FRAMECHANGED |
サイズ変更なしでも通知できる |
拡張スタイルの変更後に、次のような呼び出しを追加すると、非クライアント領域を更新できます。
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOACTIVATE | SWP_FRAMECHANGED);
WS_EX_TOPMOST の切り替えは、拡張スタイルを書き換えるだけでなく、SetWindowPos の HWND_TOPMOST や HWND_NOTOPMOST を使ってZオーダーを変更する方法が一般的です。目的に応じて、スタイル変更とウィンドウ位置変更を分けて考えます。
変更前に確認する項目
API呼び出しを実装する前に、対象のウィンドウ種別や実行環境を整理しておくと、表示上の不具合を減らせます。特に、スタイルの組み合わせによってはタスクバーやAlt+Tabへの表示が変化します。
- 対象がトップレベルウィンドウか子ウィンドウか確認する
- 追加する拡張スタイルが既存フラグと衝突しないか調べる
- 32ビットと64ビットで使用する関数と型を分ける
- 枠の再描画が必要かどうかを確認する
- 変更後にタスクバーやAlt+Tabの表示を検証する
WS_EX_TOOLWINDOW と WS_EX_APPWINDOW のように、シェル上の表示に影響するスタイルは、組み合わせによって結果が変わります。ポップアップウィンドウを補助画面として使う場合などは、実際のタスクバー表示を確認しながら設定します。
また、モードレスダイアログや独自のツールウィンドウでは、親子関係、所有関係、アクティブ化の動作も関係します。拡張スタイルだけで全表示動作を制御しようとせず、SetParent、SetWindowPos、ダイアログ管理関数などとの関係も確認してください。
64ビット環境とエラー処理
SetWindowLong は古くから使われているAPIですが、ポインターサイズを意識したコードでは SetWindowLongPtr が推奨されます。拡張スタイルの取得には GetWindowLongPtr、変更には SetWindowLongPtr を使い、戻り値を LONG_PTR として扱います。
LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWLP_EXSTYLE);
SetWindowLongPtr(hwnd, GWLP_EXSTYLE,
exStyle | WS_EX_LAYERED);
コンパイラやSDKの定義によっては、32ビットと64ビットで適切な関数へ切り替わるマクロを利用できます。プロジェクト全体で型を統一し、LONG へ不用意にキャストしないことが大切です。
レジストリから設定値を読み込んでウィンドウ状態を決めるアプリケーションでは、レジストリ読み出し手順のような処理と組み合わせることがあります。読み取った値をそのままスタイル値にしないで、許可したフラグだけを抽出してからAPIへ渡すと安全です。
実装時の確認項目
スタイル変更は短いコードで実装できますが、視覚的な結果や入力処理はウィンドウの種類によって異なります。検証用の小さなウィンドウを作り、各フラグを個別に切り替えると原因を切り分けやすくなります。
- 戻り値として変更前のスタイルを必要に応じて保存する
- 変更後に
SWP_FRAMECHANGEDを指定して再計算する - 透明化とマウス入力の挙動を別々に確認する
- 最前面化が本当に必要かZオーダーで検証する
- 終了時やモード解除時に元のスタイルへ戻す
ファイル選択画面を開く処理など、別のウィンドウを生成するコードと組み合わせる場合は、生成されたダイアログのハンドルを正しく取得してから対象を変更します。ファイル選択ダイアログの実装を参考に、APIが返すハンドルの有効期間や所有関係も確認してください。
最終的には、対象ウィンドウの作成時に設定できるものは CreateWindowEx へ集約し、実行中に切り替える必要があるものだけを SetWindowLong 系APIへ任せると、コードの意図が明確になります。拡張スタイルを変更したら、必要に応じて SetWindowPos でフレームを更新し、64ビット対応コードでは SetWindowLongPtr と適切な整数型を使う、という流れを基本にすると安定します。