はじめに
Windows10のサポート期限(2025年10月14日)が迫ってきたためWindows11にアップグレードしました。
その時にウィンドウの角がデフォルトで丸くなる仕様とUnityEditorの相性が悪いように思いました(特に、エディター拡張でウィンドウの下側の左右端に大事な表示を置いているカスタムウィンドウなどにレイアウト上の問題が出そうです)。
本題
そこで、UnityEditorをWindows11上で動かす用に、導入したプロジェクトのみエディター本体やEditorWindow系のウィンドウの角を丸めないようにする機能を作りました。
上の画像のように、他ウィンドウ(この動作例ではメモ帳)に影響を与えずに、UnityEditorだけ角の丸めを抑制することができます。
検証した環境
- Windows 11
- Unity 6000.0.53f1
使い方
導入方法
-
導入にはGitが必要になります。事前にインストールを済ませてください
Windows向けのGitの配布サイト -
以下の文字列(Git URL)をコピーする
https://github.com/HWataame/UnityEditorWindowCorner.git?path=Core/6000 -
導入するUnityのプロジェクトを開き、Package Managerを表示し、Install package from git URL...を選択する
パッケージのインストールが完了すると、エディター本体やEditorWindowの角がWindows10のように四角くなります。新しく出したウィンドウに対しても角を処理します。
また、エディターの起動時に自動的に角を処理するようにもなっています。
実装について
概要
この機能は
- C++で記述された、ウィンドウの角を操作する処理群
- C#で記述された、UnityEditorで上の機能を呼び出す処理とUnity用のパッケージデータ
の2系統で構成されています。ウィンドウの角を操作する処理群はDLL(ネイティブプラグイン)化してパッケージ内に配置しています。
ウィンドウの角を操作する
WINAPIのDwmSetWindowAttribute
関数を使用することで、ウィンドウの角を丸めたり、丸めないようにしたり、OSに角の処理を委ねたりすることができます。
(dwmapi.h
をインクルードし、#pragma comment(lib: "dwmapi.lib")
やプロジェクト設定などでdwmapi.lib
を参照できるようにする必要があります)
// ウィンドウの角の種類(この例では「丸めない」)
DWM_WINDOW_CORNER_PREFERENCE cornerPreference = DWMWCP_DONOTROUND ;
// ウィンドウの角の種類を設定する(resultに結果が返される)
HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(DWM_WINDOW_CORNER_PREFERENCE))
DwmSetWindowAttribute
関数は、処理が成功した場合はS_OK
(0)を、失敗した場合はそれ以外の値を返します。
また、トップレベルのウィンドウ以外に対してDwmSetWindowAttribute
関数を実行すると、処理が失敗しE_HANDLE
が返されるため注意が必要です。
新しく生成されたウィンドウに対しても角の処理を適用する
上述の通り、DwmSetWindowAttribute
関数によってウィンドウの角の種類を変更できるのですが、角の処理を適用するウィンドウごとに処理を実行する必要があります。
そのため、「ある時に表示されているウィンドウすべてに対して角の種類を変更する処理」を行ったとしても、それ以降に生成されたウィンドウの角は(個別に設定されていない限りは)デフォルト、すなわち丸められます。
そのため、今回は新しく生成されたウィンドウに対してもDwmSetWindowAttribute
関数を適用するために、IATフックを使用して、同じくWINAPIであるShowWindow
関数の実行と同じタイミングでDwmSetWindowAttribute
関数が実行されるようにする手法を用いました。
IATフックとは
実行時のIAT(インポートテーブル)を書き換え、独自の処理を差し込むAPIフックの一種です。実行時のメモリの一部を書き換えるため、その実行中のみ変更が有効になります。
今回は、実行中のUnityEditorがインポートしているShowWindow
関数の参照1を、角を丸めない処理をした後に本来のShowWindow
関数の処理を行う関数への参照に書き換えています。
さらにひと工夫
先述したように「トップレベルのウィンドウ以外に対してDwmSetWindowAttribute
関数を実行すると、処理が失敗する」ため、GetParent
関数を使用して、渡されたウィンドウハンドルの親ウィンドウを親ウィンドウがいなくなるまで辿っています(親ウィンドウが存在しない=トップレベルのウィンドウ)。
そうして取得したトップレベルのウィンドウに対してDwmSetWindowAttribute
関数を実行しています。
UnityEditorから呼び出す
先述のウィンドウの角の処理をまとめ、機能を呼び出す関数をエクスポートしたDLLをプロジェクトフォルダー内(今回はパッケージフォルダー内)に追加し、エディター実行時のみ参照されるように設定してUnityEditorから参照できるようにします。
そして、C#からP/Invokeを使用してDLLからエクスポートされた関数を呼び出す実装を行います。
呼び出すタイミングについて
「UnityEditorの起動時、またはパッケージの導入直後」に「一度だけ」呼び出されるようにしています。
IATフックは、C#コードの変更などによって発生するドメインリロード(アセンブリの再読み込み)によって結果が変更されないため、一度処理してしまえば書き換えた対象が終了するまでは効果が維持されます。
また、当然ではありますがShowWindow
関数を置き換えても既存のウィンドウには何も起こらないため、処理の置き換え時にEnumWindows
関数を使用して対象のUnityEditorのウィンドウを列挙して角の種類を設定する処理も実行しています。
この処理は起動時ではほとんどのウィンドウが生成される前に実行されるためあまり効果がありませんが、パッケージ導入時など起動後に実行される際に効果を発揮します。
ライセンス
リポジトリ全体、またはパッケージ単体ともにMITライセンスです。
リポジトリ
参考文献
類似機能のご案内
- Unityのエディターではなく、Windowsで動くスタンドアロンプレイヤーのウィンドウを操作する機能 → Windows11で動くUnityのスタンドアロンプレイヤーのウィンドウの角を設定する機能を作りました
-
正確には、
Unity.exe
(エディターのアプリケーション)から参照されているUnity.dll
がインポートしているShowWindow
関数の参照 ↩