【VBA×WindowsAPI】指定のウィンドウ(UserForm)を常に最前面に表示する
VBAでマクロ開発をしているとUserFormが他のウィンドウの後ろに隠れてしまうことがままあります。ウィンドウは奥行きの順番である「Zオーダー」という情報を持っていまが、このZオーダーを操作することでUserForm(ウィンドウ)の並び順を変更、設定することができます。
ここではWindows APIを使って指定のウィンドウを常に最前面に表示する方法を解説していきます。
これにより、ウィンドウを”常に”最前面に表示させることができるため、他のウィンドウにフォーカスを当てたとしても指定のウィンドウは常に最前面表示されたままにすることができます。
ウィンドウを最前面に表示する手段として同じくWindows APIのSetForegroundWindow関数を使った方法も存在しますが、この方法はあくまでもウィンドウを最前面に持ってくるだけのものであり、他のウィンドウにフォーカスを当てると最前面のウィンドウは切り替わってしまいます。
指定のウィンドウを常に最前面に表示する
ウィンドウは見た目からもわかる通り前後の関係、つまりは奥行きの順序を表す「Zオーダー」という情報を持っています。Zオーダーはいわばペイント系ソフトのレイヤーのようなもので、このZオーダーがあるおかげで2次元の画面上でもウィンドウ同士の重なりの表現が可能になっています。
Windows APIの一部関数ではZオーダーを操作することが可能で、Zオーダーの順序を変更したり特定のウィンドウを常にZオーダーの最前面になるように設定したりと様々な操作をすることができます。
VBAで指定のウィンドウを常に最前面表示させるには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
FindWindow関数 :ウィンドウハンドルを取得する
SetWindowPos関数 :ウィンドウのZオーダーを変更する
「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
サンプルコード
UserFormを常に最前面に表示するためのサンプルコードは下記の通りです。
UserFormを1つ作成し、下記コードをUserFormのコードに直接コピペします。これによりUserFormを表示させるとフォームのInitializeイベントが実行され、表示と同時に常に最前面に表示されるような設定がなされます。実行後、UserForm以外のウィンドウにフォーカスを当てて最前面に表示させようとしても、UserFormより前側に表示されないことが確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long Const HWND_TOPMOST As Long = -1 Const SWP_NOSIZE As Long = 1 Const SWP_NOMOVE As Long = 2 Private Sub UserForm_Initialize() Dim hWnd As LongPtr '最前面に表示するウィンドウのハンドルを取得(UserForm) hWnd = FindWindow(vbNullString, Me.Caption) 'ウィンドウを常に最前面に配置 Call SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE) End Sub |
最前面表示の設定はUserForm以外のウィンドウに対しても有効でFindWindow関数の取得方法を変えるだけで任意のウィンドウを対象にすることが可能です。以下はメモ帳ウィンドウを常に最前面に表示するためのサンプルコードです。メモ帳ウィンドウを1つ以上開いた状態で実行すると、メモ帳ウィンドウが常に最前面に表示されます。(複数開いている場合は最も全面にあるメモ帳ウィンドウが対象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long Const HWND_TOPMOST As Long = -1 Const SWP_NOSIZE As Long = 1 Const SWP_NOMOVE As Long = 2 Sub main() Dim hWnd As LongPtr '最前面に表示するウィンドウのハンドルを取得(メモ帳ウィンドウ) hWnd = FindWindow("notepad", vbNullString) 'ウィンドウを常に最前面に配置 Call SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE) End Sub |
コード解説
ウィンドウハンドルを取得する
ウィンドウにはそれぞれウィンドウハンドルと呼ばれる、ウィンドウを識別するためのID情報のようなものが付与されています。このウィンドウハンドルを取得することで、”どの”ウィンドウに対して処理を行うのかを簡単に指示することができます。(Windows APIでは頻出ワードです)
今回は指定のウィンドウを最前面に表示させたいので、その対象となるウィンドウのハンドルを取得します。ウィンドウハンドルを取得する方法はいくつも存在しますがここでは常套手段のFindWindow関数を使ってウィンドウハンドルを取得しています。FindWindow関数は下記のように記載することで指定のクラス名もしくはウィンドウ名から該当のウィンドウへのハンドルを取得することができます。
hWnd = FindWindow(“クラス名”, “ウィンドウ名”)
UserFormの場合はCaptionがウィンドウ名となるので引数を「(vbNullString, Me.Caption)」、メモ帳の場合はクラス名がNotepadとなるので引数を「(“Notepad”, vbNullString)」としています。
ウィンドウを最前面に表示する
ウィンドウを常に最前面表示させるためにはSetWindowPos関数を使い下記のように書きます。
lRet = SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, uFlags)
第1引数の「hWnd」には常に最前面表示させたいウィンドウのハンドルを、第2引数には定数値「HWND_TOPMOST」を入力します。この第2引数に入力した定数値でウィンドウを常に最前面表示させるという設定を行っています。他の定数を入力することで最前面表示以外の設定も可能です。
「HWND_TOPMOST」は該当のヘッダーファイル内に定義されていますが、VBAでは関数の呼び出しを行っているだけなのでこれら定数は定義されていません。そのため「Const HWND_TOPMOST= -1」というかたちでコードの初めに定義しています。(引数として直接「-1」を入力しても可)
第7引数の「uFlags」にはウィンドウのサイズを変更しない設定の定数値「SWP_NOSIZE」と、ウィンドウの位置を変更しない設定の定数値「SWP_NOMOVE」を入力しています。つまり、この設定はウィンドウの位置とサイズはそのままでZオーダーのみを最前面に変更することを意味しています。(SWP_NOSIZEとSWP_NOMOVEもHWND_TOPMOSTと同じく定数で値はそれぞれ「1」と「2」)
ウィンドウの位置やサイズは本来、第3~6引数で設定することが可能ですが、第7引数「uFlags」でウィンドウの位置とサイズを変更しない設定をしているため、無効扱いとなっています。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:ウィンドウ機能(Zオーダー)