【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を利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square FindWindow関数  :ウィンドウハンドルを取得する
icon-check-square SetWindowPos関数  :ウィンドウのZオーダーを変更する

「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。

サンプルコード

UserFormを常に最前面に表示するためのサンプルコードは下記の通りです。
UserFormを1つ作成し、下記コードをUserFormのコードに直接コピペします。これによりUserFormを表示させるとフォームのInitializeイベントが実行され、表示と同時に常に最前面に表示されるような設定がなされます。実行後、UserForm以外のウィンドウにフォーカスを当てて最前面に表示させようとしても、UserFormより前側に表示されないことが確認できます。

 
最前面表示の設定はUserForm以外のウィンドウに対しても有効でFindWindow関数の取得方法を変えるだけで任意のウィンドウを対象にすることが可能です。以下はメモ帳ウィンドウを常に最前面に表示するためのサンプルコードです。メモ帳ウィンドウを1つ以上開いた状態で実行すると、メモ帳ウィンドウが常に最前面に表示されます。(複数開いている場合は最も全面にあるメモ帳ウィンドウが対象)

コード解説

icon-edit ウィンドウハンドルを取得する

ウィンドウにはそれぞれウィンドウハンドルと呼ばれる、ウィンドウを識別するためのID情報のようなものが付与されています。このウィンドウハンドルを取得することで、”どの”ウィンドウに対して処理を行うのかを簡単に指示することができます。(Windows APIでは頻出ワードです)

今回は指定のウィンドウを最前面に表示させたいので、その対象となるウィンドウのハンドルを取得します。ウィンドウハンドルを取得する方法はいくつも存在しますがここでは常套手段のFindWindow関数を使ってウィンドウハンドルを取得しています。FindWindow関数は下記のように記載することで指定のクラス名もしくはウィンドウ名から該当のウィンドウへのハンドルを取得することができます。

icon-code FindWindow関数 

hWnd = FindWindow(クラス名”, “ウィンドウ名”)

UserFormの場合はCaptionがウィンドウ名となるので引数を「(vbNullString, Me.Caption)」、メモ帳の場合はクラス名がNotepadとなるので引数を「(“Notepad”, vbNullString)」としています。
 

icon-edit ウィンドウを最前面に表示する

ウィンドウを常に最前面表示させるためにはSetWindowPos関数を使い下記のように書きます。

icon-code 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」でウィンドウの位置とサイズを変更しない設定をしているため、無効扱いとなっています。

関連情報

icon-share-square VBA×WindowsAPIまとめページ

その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。

icon-share-square 参考

Microsoft公式:ウィンドウ機能(Zオーダー)

Excel, VBA, Windows API

Posted by Lic