【VBA×WindowsAPI】ウィンドウ(UserForm)のオーナーウィンドウを変更する
通常VBAのUserFormは、Excelなどのアプリケーションウィンドウを親として作成されます。そのため、親であるアプリケーションウィンドウを最小化すると、UserFormも同時に最小化されます。このUserFormの親であるウィンドウはWindows APIを使用することで変更することができます。
これにより、たとえばExcel VBAでPowerPointを操作する際、本来であればUserFormの親はExcelウィンドウになりますが、親ウィンドウをPowerPointウィンドウに変更することで、Excel VBAのUserFormでありながらPowerPointのウィンドウとして扱うことができるようになります。
オーナーウィンドウの変更
オーナーウィンドウとは、ウィンドウの親子関係における「親」にあたるウィンドウのことを指します。一般的に、モーダルダイアログやサブウィンドウがオーナーウィンドウを持つことが多く、オーナーウィンドウが閉じられると、関連する子ウィンドウも一緒に閉じられます。また、オーナーウィンドウの背面には子ウィンドウが配置されないよう制御され、子ウィンドウが常に前面に表示されます。そのためアプリケーションウィンドウをオーナーとするUserFormやMsgBox関数で表示されるメッセージボックスは、Excelウィンドウの背面に表示されることはありません。
このオーナーウィンドウはWindowsAPIを使うことで変更ができます。上画像はUserFormのオーナーをメモ帳ウィンドウに変更したものです。これによりメモ帳ウィンドウより背面にUserFormが表示されることはなくなり、メモ帳を最小化するとUserFormもあわせて最小化されるようになります。
VBAで指定のウィンドウのオーナーを変更するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
FindWindow関数 :ウィンドウのハンドルを取得する
SetWindowLongPtr関数 :ウィンドウのオーナーを設定する
「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
サンプルコード
UserFormのオーナーウィンドウをメモ帳ウィンドウに変更するためのサンプルコードは下記の通りです。UserFormコードとして下記コードを設定後、メモ帳ウィンドウを起動した状態でUserFormを表示させることで、オーナーウィンドウをメモ帳ウィンドウに変更することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Option Explicit Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Private Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr Const GWL_HWNDPARENT As Long = -8 Private Sub UserForm_Initialize() Dim hWndFrom As LongPtr Dim hWndNotepad As LongPtr 'ウィンドウハンドル取得 hWndFrom = FindWindow(vbNullString, Me.Caption) 'UserForm hWndNotepad = FindWindow("notepad", vbNullString) 'メモ帳 'UserFormの親をメモ帳ウィンドウに変更 Call SetWindowLongPtr(hWndFrom, GWL_HWNDPARENT, hWndNotepad) End Sub |
ここではUserFormのオーナーウィンドウを変更していますが、ウィンドウハンドルさえ取得できればVBAとは関係のない指定のウィンドウのオーナーを変更することも可能です。
コード解説
ウィンドウハンドルを取得する
ウィンドウにはそれぞれウィンドウハンドルと呼ばれる、ウィンドウを識別するためのID情報のようなものが付与されています。このウィンドウハンドルを取得することで、”どの”ウィンドウに対して処理を行うのかを簡単に指示することができます。(Windows APIでは頻出ワードです)
今回はUserFormのオーナーを指定のウィンドウに設定したいので、UserFormとオーナーウィンドウとして設定したいウィンドウのウィンドウハンドルを習得します。ウィンドウハンドルを取得する方法はいくつも存在しますがここでは常套手段のFindWindow関数を使ってウィンドウハンドルを取得しています。FindWindow関数は下記のように記載することで指定のクラス名もしくはウィンドウ名から該当のウィンドウへのハンドルを取得することができます。
hWnd = FindWindow(“クラス名”, “ウィンドウ名”)
UserFormの場合はCaptionがウィンドウ名となるので引数を「(vbNullString, Me.Caption)」、メモ帳の場合はクラス名がNotepadとなるので引数を「(“Notepad”, vbNullString)」としています。
オーナーウィンドウの設定
オーナーウィンドウの変更にはSetWindowLongPtr関数を使い、下記のように記載します。
Call SetWindowLongPtr(hWnd1, GWL_HWNDPARENT, hWnd2)
hWnd1にはオーナーを変更する対象のウィンドウのハンドル、hWnd2には設定するオーナーウィンドウのハンドルをそれぞれ入力します。第2引数は定数値「GWL_HWNDPARENT(=-8)」を入力します。SetWindowLongPtr関数は指定のウィンドウの様々な情報を設定するための関数であり、この定数は設定するウィンドウの情報がオーナーウィンドウであるということを指示するための値です。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:SetWindowLongPtrA 関数 (winuser.h) – Win32 apps