LoginSignup
1
2

More than 3 years have passed since last update.

[VBA]広域変数を使用せずに、EnumChildWindowsの結果を取得する

Last updated at Posted at 2020-04-29

Win32APIのEnumChildWindowsの列挙した結果を、広域変数を使用せずに取得する方法のメモです。

EnumChildWindows について

任意のウィンドウに対して、その子どものウィンドウを取得するための関数で、以下のようなシグネチャとなっています。

ref_EnumChildWindows
/* 返り値は使用されない。 */
BOOL EnumChildWindows(
  HWND        hWndParent /* 親ウィンドウのハンドル。0 の場合は EnumWindows と同等。 */,
  WNDENUMPROC lpEnumFunc /* 各列挙で実行される関数へのポインタ。 */,
  LPARAM      lParam /* 任意の追加情報。 */
);

https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-enumchildwindows
より引用(コメントは記事の作成者による)

lpEnumFuncで指定するコールバック関数WNDENUMPROCは、以下のようなシグネチャとなっています。

ref_EnumChildProc
/* 列挙を続行する場合は TRUE を返す。 */
BOOL CALLBACK EnumChildProc(
  _In_ HWND   hwnd /* 見つかったウィンドウのハンドル。 */,
  _In_ LPARAM lParam /* EnumChildWindows で指定された追加情報。 */
);

https://docs.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/ms633493(v=vs.85)
より引用(コメントは記事の作成者による)

よく見る使われ方と解決したい問題

上記のシグネチャからもわかるように、EnumChildWindowsでは、直接列挙された結果を受け取れるのではなく、コールバック関数の方に結果が渡されるという構成になっています。

そのため、結果に対して何かしら処理をするために以下のような実装がされていることが多いです。

  1. EnumChildProc内に直接処理を書く
  2. モジュール変数(広域変数)に結果を格納し、モジュール変数経由で受け取る

1の方法では、その時々に応じて中のコードを書き直す必要があり、やや再利用がしにくいです。
2の方法では、1に比べて再利用しやすいですが、広域変数を使っているのがなんとなく不安になるところです。

上記の問題を解決する方法として、lParamの引数を使う、というのがこの記事の趣旨になります。

lParamですがポインタを示す引数となっているため、VBA では任意の変数をByRefで渡したり、オブジェクトをByValで渡してもシグネチャ的に問題ありません。

ByRefの場合はその変数へのポインタが関数に渡されますし、オブジェクトをByValで渡した場合はオブジェクト本体への参照アドレスが渡されるためです。

そのため、lParam経由でVBA.Collectionなどを渡せば、広域変数を介さず結果を取得することができます。

実装例

VBA7 以降を想定したコードです。
いまどき居ないとは思いますが、VBA6 以前を使用されている場合はLongPtrLongに変更し、PtrSafeを消してください。

'https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-enumchildwindows
Private Declare PtrSafe Function _
    EnumChildWindows Lib "user32.dll" ( _
        ByVal hWndParent As LongPtr, _
        ByVal lpEnumFunc As LongPtr, _
        ByVal lParam As VBA.Collection _
    ) As Long 'No use.

'https://docs.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/ms633498(v=vs.85)
Private Function EnumChildProc( _
        ByVal hWnd As LongPtr, _
        ByVal lParam As VBA.Collection _
    ) As Boolean 'If True Then Continue.

    lParam.Add hWnd
    Let EnumChildProc = True '列挙続行。
End Function

'inParentHwnd で指定したウィンドウの子ウィンドウのハンドルを格納した VBA.Collection を取得する。
    'inParentHwnd を省略した場合はトップレベルウィンドウのハンドルを取得する。
Public Function GetChildWindows(Optional inParentHwnd As LongPtr = 0) As VBA.Collection 'Of LongPtr
    Dim c As VBA.Collection 'Of LongPtr
    Set c = New VBA.Collection
    Set GetChildWindows = c

    Call EnumChildWindows(inParentHwnd, AddressOf EnumChildProc, c)

End Function

参考

EnumChildWindows function (winuser.h) - Win32 apps | Microsoft Docs
EnumChildProc callback function (Windows) | Microsoft Docs

関連記事

VBAでWindowsAPIを使うには - Qiita

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2