はじめに
Unityで作った画面を強制的に最前面にしたい!という場面がありました。
そこで
- ウィンドウを最前面にする
- 右上の最小化、最大化、閉じるボタンを消す
という普段ゲームを作っていたら作らない機能を実装しました。
これを作るためにChatGPTを使用したため、知らないことをアウトプットするためにまとめてみます。
ソースコード
ウィンドウを最前面にするコードはこちら
ウィンドウのボタンを消すコードはこちら
ウィンドウを最前面にする
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, uint uFlags);
Windowsにある画面を設定するメソッドたちをUnityでも使用できるようにします。
DllImport
で使用するdllの名前を指定するとそこにある関数を使えるようになります。
普段見かけないextern
は外部で実装されるメソッドを宣言するために使用するらしいです。
DllImport
を使う場合はstatic
にする必要性があるようです。
SetForegroundWindow
SetForegroundWindow
は指定したウィンドウをフォアグラウンドウィンドウに設定します。
フォアグラウンドウィンドウは、ユーザーが現在操作しているウィンドウであり、ユーザーからの入力(キーボードやマウスの操作)を受け取ることができます。
GetActiveWindow
GetActiveWindow
は現在アクティブなウィンドウのハンドルを取得するために使用します。
SetWindowPos
SetWindowPos
はウィンドウのサイズ、位置、ウィンドウの前後関係を変更するために使います。
constたち
private const uint SWP_NOMOVE = 0x0002; // 現在位置を保持します
private const uint SWP_NOSIZE = 0x0001; // 現在のサイズを保持します
private const uint SWP_NOZORDER = 0x0010; // 現在の Z オーダーを保持します
private const uint SWP_SHOWWINDOW = 0x0040; // ウィンドウが表示されます。
private const int HWND_TOPMOST = -1; // 最上位以外のすべてのウィンドウの上にウィンドウをPlacesします。
private const int HWND_NOTOPMOST = -2; // 最上位以外のすべてのウィンドウ (つまり、すべての最上位ウィンドウの背後) の上にウィンドウをPlacesします。
SetWindowPos
に入れるための値です。これの最後の引数にはフラグをいくつか組み合わせて設定を渡します。
これらの値が何を表しているのかはドキュメントに書いてあります。
メソッド
public void SetWindowOnTop(bool isOnTop)
{
IntPtr hWnd = GetActiveWindow();
if (isOnTop)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
}
else
{
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
}
// アクティブウィンドウに戻す
SetForegroundWindow(hWnd);
}
まず、GetActiveWindow
を使用してアクティブなウィンドウを取得します。
最前面にするかの部分でSetWindowPos
に入れる引数がHWND_TOPMOST
を使うか、代わりにHWND_NOTOPMOST
を使うかの違いです。この値がウィンドウを最前面にするかの設定を変えます。
最後に設定したウィンドウをユーザーの操作を受け付ける状態にするためにSetForegroundWindow
を使います。
これでウィンドウを最前面にできます!
ウィンドウのボタンを消す方法
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
GetWindowLong
ウィンドウのスタイル情報を取得します。
SetWindowLong
ウィンドウのスタイル情報を設定します。
const
private const int GWL_STYLE = -16; // ウィンドウスタイルを取得 します。
private const int WS_MINIMIZEBOX = 0x00020000; // 最小化ボタンのスタイル
private const int WS_MAXIMIZEBOX = 0x00010000; // 最大化ボタンのスタイル
private const int WS_SYSMENU = 0x00080000; // システムメニュー(閉じるボタン)のスタイル
スタイルのボタンには定数が決まってるらしいのでそれを宣言します。
メソッド
public void SetWindowButtonHide(bool isHide)
{
IntPtr windowHandle = GetActiveWindow();
int style = GetWindowLong(windowHandle, GWL_STYLE);
if (isHide)
{
style &= ~WS_MINIMIZEBOX; // 最小化ボタンを無効化
style &= ~WS_MAXIMIZEBOX; // 最大化ボタンを無効化
style &= ~WS_SYSMENU; // システムメニュー(閉じるボタン)を無効化
}
else
{
style |= WS_MINIMIZEBOX; // 最小化ボタンを有効化
style |= WS_MAXIMIZEBOX; // 最大化ボタンを有効化
style |= WS_SYSMENU; // システムメニュー(閉じるボタン)を有効化
}
SetWindowLong(windowHandle, GWL_STYLE, style);
}
まず、GetActiveWindow
を使用してアクティブなウィンドウを取得します。
次にGetWindowLong
を使うとウィンドウのスタイルを取得できるので取得します。
ボタンにビット演算で無効化したり、有効化したりします。
無効化
&
はAND演算子です。ビットが両方とも1の場合にだけ1になります。
~
はNOT演算子です。ビットを反転させます。
つまり、ボタンを無効化するときは
style
のビット値と反転したWS_MINIMIZEBOX
をAND演算し、WS_MINIMIZEBOX
に対応するビットをクリアします。残りのパラメータも同じように処理します。
style
が00000000 11111111 00000000 00000000
WS_MINIMIZEBOX
が00000000 00000010 00000000 00000000
~WS_MINIMIZEBOX
は反転するので11111111 11111101 11111111 11111111
ANDすると00000000 11111101 00000000 00000000
となります。
有効化
|
はOR演算子です。ビットにどちらかが1なら1になります。
つまり、ボタンを有効化するときは
style
のビット値とWS_MINIMIZEBOX
をOR演算し、WS_MINIMIZEBOX
に対応するビットをセットします。
残りのパラメータも同じように処理します。
一度無効化されてるのでstyle
は00000000 11111101 00000000 00000000
です。
WS_MINIMIZEBOX
は00000000 00000010 00000000 00000000
なので
ORすると00000000 11111111 00000000 00000000
になります。
そして決まった設定をSetWindowLong
でセットしています。
これでウィンドウのボタンを消せます。