DRC
DRC

Reputation: 629

How to get hWnd for a child window and activate it using FindWindowEx API

I am using MS Access 2016 VBA to automate access to an external application. The external application window has a video that is displayed in a child window.

If I use spyxx.exe, I can use the "Finder Tool" to obtain the video control's Handle (which changes, of course), Caption ("panel"), Class ("wxWindowNR"), and Style (52000000). When I use the handle returned by spyxx.exe I can successfully use that handle to bring the correct application to the top and activate it. But since that handle changes, I need to obtain it dynamically using MS Access VBA.

The problem I'm having is that I cannot figure out how to obtain that same handle that spyxx.exe comes up with when I use the FindWindowEx API to try to get the child video window's handle. Here's what I'm trying (and a number of variations on the theme): The declartion is:

Public Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long

and the code to (try) to get the child video window's handle is (where WinHand is the parent window's handle):

FindWindowEx(WinHand, 0, "wxWindowNR", "panel")

I know WinHand is correct for the parent window because using WinHand I can make the application the active, top-most window. However, I can never get FindWindowEx to return the handle that spyxx.exe shows is the handle of the child "panel" window.

It does return handles when I leave the child window's name blank like this:

FindWindowEx(WinHand, 0, "wxWindowNR", "")

But never the correct handle that spyxx.exe shows.

From searching the net most of the day, it appears I may need to do something with the EnumChildWindows API, but I do not know how to reference it in MS Access VBA nor how to loop through the returned windows to find the "panel" window handle.

Upvotes: 0

Views: 3844

Answers (1)

DRC
DRC

Reputation: 629

For the benefit of those who may run into a similar problem in the future, I am posting the solution I used (although I would love to see a solution offered using EnumChildWindows as that seems like it is probably the more elegant approach, but I am not savvy enough with APIs to know how to set it up).

The FindWindowEx could not find the child video control window I was looking for because that control was buried as a child of a child of a child of a child of a child of a parent. FindWindowEx will only search the children of the immediately preceding parent. Therefore, you have to nest the search for the buried child control.

Steps I followed:

  1. Open the target application whose window and child window you need to access from VBA.
  2. Run Spyxx.exe (aka Spy++)
  3. Using the Find Window tool, get the handle and caption and class of the child window control whose handle you need
  4. Make a note of those items and then close the Find Window tool
  5. Still using Spyxx.exe, use menu item "Tree/Save Tree to File..." (or press CTRL+S) to save out the tree of windows to a text file (Note that the Save defaults to saving it out as a .sxt Spy++ object, which you do not want here)
  6. Open the tree list of windows text file you saved in your favorite text editor (it was nicely formatted for me using Notepad++) and do a search for the window handle you noted in steps #3 and #4 above
  7. This should locate the child window in the tree that you want
  8. Now work backward up the tree doing the following steps until you get to the parent window handle
    1. Make a note of the window caption for the next level up window--the caption is enclosed in double quotes; you will need that caption when you write the VBA code later
    2. Now move to the next window level up and write the caption for that window down
    3. Continue moving up window levels until you get to the parent window for the application itself

Final Step: Now in the VBA code, to find the window handle of the target child window control, you will have to nest the FindWindowEx commands starting with the parent window and working your way down like this (replace "wxWindowNR" with whatever window class is relevant for the child control you are trying to find):

FindWindowEx (ParentWindowHandle, 0, "wxWindowNR", "[caption from steps 9 to 11]")

For example, here is the code I had to use (where ChildWindow5 is the handle of the video control window that I was looking for; the While...Wend commands are needed because in the application I was targeting, not all child windows existed immediately when the application was run):

ParentWindow = 0
While ParentWindow = 0
    ParentWindow = FindWindow("Target Window Caption")
Wend

ChildWindow1 = 0
While ChildWindow1 = 0
    ChildWindow1 = FindWindowEx(CLng(ParentWindow), 0, "wxWindowNR", "ID_BOOKMARK_SPLITTERWINDOW")
Wend

ChildWindow2 = 0
While ChildWindow2 = 0
    ChildWindow2 = FindWindowEx(CLng(ChildWindow1), 0, "wxWindowNR", "ID_TOPPANEL_SPLITTERWINDOW")
Wend

ChildWindow3 = 0
While ChildWindow3 = 0
    ChildWindow3 = FindWindowEx(CLng(ChildWindow2), 0, "wxWindowNR", "ID_MAINDISPLAY_PANEL")
Wend

ChildWindow4 = 0
While ChildWindow4 = 0
    ChildWindow4 = FindWindowEx(CLng(ChildWindow3), 0, "wxWindowNR", "ID_VIDEODISPLAY_PANEL")
Wend

ChildWindow5 = 0
While ChildWindow5 = 0
    ChildWindow5 = FindWindowEx(CLng(ChildWindow4), 0, "wxWindowNR", "panel")
Wend

Notice that each subsequent search for a child window handle references the prior parent window.

That worked for me. I still think that if someone can point out how to do this with EnumChildWindows, the solution would likely be far more elegant.

Upvotes: 2

Related Questions