API function FindWindowEx

Richard Schollar

MrExcel MVP
Joined
Apr 19, 2005
Messages
23,707
Hi

I was playing around with the this function trying to get a handle on a workbook window. I understood that the Windows Class Name of the workbook window was "EXCEL7" and the class name of the top-most level was "XLMAIN" (the handle to this topmost level can most easily be found thru Application.Hwnd, but I was trying things out!).

Following declaring the APIs and writing a bit of code, I thought the following would work to determine the handle to the workbook window:

Code:
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

Sub Test()
Dim hWndParent As Long, sParentClassName As String
Dim hWndChild As Long, sChildClassName As String, sChildWindow As String

sParentClassName = "XLMAIN"

hWndParent = FindWindow(sParentClassName, vbNullString)
'Application.hWnd  returns the same as above

sChildClassName = "EXCEL7"   
sChildWindow = ActiveWindow.Caption 'in my case "Book1"

hWndChild = FindWindowEx(hWndInt, ByVal 0&, sChildClassName, sChildWindow) 

MsgBox hWndChild   'this returns 0 ie the function didn't find the handle
End Sub

But this didn't work, and I kept getting a return value of 0 indicating that the function was not finding the handle.

Subsequent playing around and using Winspector I noticed that there is an 'Intermediate' window between XLMAIN and EXCEL7 - this being XLDESK. Hence, the above code could be made to work by first determining the handle to this Intermediate window and using this handle as the parent handle in the FindWindowEx to return the Book1 Window:

Code:
'Declarations as above
Sub Test()
Dim hWndParent As Long, sParentClassName As String
Dim hWndInt As Long, sClassInt As String
Dim hWndChild As Long, sChildClassName As String, sChildWindow As String

sParentClassName = "XLMAIN"
hWndParent = FindWindow(sParentClassName, vbNullString)

sClassInt = "XLDESK" 
hWndInt = FindWindowEx(hWndParent, ByVal 0&, sClassInt, vbNullString)

sChildClassName = "EXCEL7" 
sChildWindow = ActiveWindow.Caption

hWndChild = FindWindowEx(hWndInt, ByVal 0&, sChildClassName, sChildWindow) 

MsgBox hWndChild  'success!!!
End Sub

So it would appear that the FindWindowEx only finds 'immediate' childs of the Parent, and doesn't delve deeper into the Windows parent-child structure.

Anyway, my questions are thus:

1. Is my thinking on this point correct?

2. Is there no way of determining the handle of a subsequent child that may not be an immediate child without knowing the parent-child structure of all preceding windows?

Thanks for all replies :biggrin:

EDIT: added the type declaration for hWndChild at Mark O'Brien's suggestion :biggrin:
 

Excel Facts

Copy a format multiple times
Select a formatted range. Double-click the Format Painter (left side of Home tab). You can paste formatting multiple times. Esc to stop
Hi Richard

In answer to your first question I believe the answer is yes.

In answer to your second question, you might want to have a look at the FindWindowLike Function.

It appears there may be another technique but that is for VB although I suspect it could be adapted for VBA.

Andrew
 
Upvote 0
Thanks Andrew

I was kinda thinking along the lines of the functions used in constructing the "FindWindowLike".

It would make it a lot easier in VBA if more of the objects had a hWnd property (I believe more do in VB) - then it would be much easier to navigate and identify window handles.
 
Upvote 0
Hi Richard

If you are referring to VBA forms or GUI items then I believe they will have a hWnd property (someone please correct me if I am wrong). Assuming you are working with a 32-bit computer (very high probability) then I reckon you could just about copy/paste the #32 parts of the code I provided in the 3rd link to get the hWnd property for any window using VBA. Do you want to have a crack at it? There is very little difference, if any, between the code in VB and VBA.

Andrew
 
Upvote 0
I don't seem to see a hWnd property for VBA userforms nor their controls - am I doing something wrong? I realise they will have a handle in Wondows, it's just that the VBA object model does not appear to have a built in hWnd property to expose this to the programmer...
 
Upvote 0
Thanks Denis

The Windows API things are starting (note, only starting :biggrin:) to make a little bit more sense. Last night, I could actually read some code that Ivan had created and understand it! Still going to be a long haul before I have a proper hold on this though...
 
Upvote 0
That puts you ahead of me at the moment... :biggrin:

I've seen some cool API stuff but need a good reason to get my hands dirty with them.

Denis
 
Upvote 0
Hi Richard

Just to clarify, as you have found out, XLDESK sits below XLMAIN the Excel application class name.
The XLDESK actually contains all Windows of all Workbooks, even the Addins AND one other interesting
Non std Window whose Class name EXCEL6.
XLDESK is similar to your Desktop, but for all workbooks (as far as I can tell + the ONE special one). This you have
found has the Child name EXCEL7 (=Workbooks etc) and this is the Child of XLDESK. Minimising your workbook
window gives you the XLDESK window (Blank grey area).

So it would appear that the FindWindowEx only finds 'immediate' childs of the Parent, and doesn't delve deeper into the Windows parent-child structure

That function DOES search child windows, beginning with the one following the given child window as opposed to the
FindWindow API which DOES NOT search child windows.

If you iterate through all childs of XLDESK you will get all handles to the Workbook windows including Addins
and your PERSONAL.XLS book. The only difference being in the Windows visible property.

The reason you only got one is because by default the API will return the Top order Window.

Have a look at the following; similar to what you used but expanded to get all.

Code:
Option Explicit

Private Declare Function GetWindow _
    Lib "user32" ( _
        ByVal hwnd As Long, _
        ByVal wCmd As Long) _
As Long

Private Declare Function GetClassName _
    Lib "user32" _
        Alias "GetClassNameA" ( _
            ByVal hwnd As Long, _
            ByVal lpClassName As String, _
            ByVal nMaxCount As Long) _
As Long

Private Declare Function FindWindow _
    Lib "user32" _
        Alias "FindWindowA" ( _
            ByVal lpClassName As String, _
            ByVal lpWindowName As String) _
As Long

Private Declare Function FindWindowEx _
    Lib "user32" _
        Alias "FindWindowExA" ( _
            ByVal hWnd1 As Long, _
            ByVal hWnd2 As Long, _
            ByVal lpsz1 As String, _
            ByVal lpsz2 As String) _
As Long

Private Declare Function GetWindowText _
    Lib "user32" _
        Alias "GetWindowTextA" ( _
            ByVal hwnd As Long, _
            ByVal lpString As String, _
            ByVal cch As Long) _
As Long

Private Declare Function GetWindowTextLength _
    Lib "user32" _
        Alias "GetWindowTextLengthA" ( _
            ByVal hwnd As Long) _
As Long

Const GW_CHILD = 5
Const GW_HWNDNEXT = 2

Sub GetXLDESKChildWindows()
'---------------------------------------------------------------------------------------
' Procedure : GetXLDESKChildWindows
' DateTime  : 11/09/2007 20:01
' Author    : Ivan F Moala
' Purpose   : Get all Child Windows of XLDESK
'           : The result you should get is ALL Workbooks and Addins
'           : which are under EXCEL7
'           : Note Excel cell edit control = EXCEL6
'           : It is avail when in Direct cell edit mode
'---------------------------------------------------------------------------------------
Dim hWndParent As Long, hWndDskTop As Long, hWndChild As Long, Ret As Long
Dim strClass As String * 250
Dim strBuffer As String * 250
Dim i As Long
Dim fn As WorksheetFunction
    
    '// used to Clean the String buffer
    Set fn = Application.WorksheetFunction
    
    '// Find the MAIN handle
    hWndParent = FindWindow("XLMAIN", vbNullString)
    
    'sChildClassName = "XLDESK"
    'sChildWindow = ActiveWindow.Caption 'in my case "Book1"
    hWndDskTop = FindWindowEx(hWndParent, 0&, "XLDESK", vbNullString)

    '// We are searching for all child windows of the XLDESK
    hWndChild = GetWindow(hWndDskTop, GW_CHILD)
    
    '// Start counter
    i = 1
    
    Do Until hWndChild = 0
        '// Get the child window's ClassName
        GetClassName hWndChild, strClass, 250
        '// Setup and get the Window Text
        Ret = GetWindowTextLength(hWndChild)
        strBuffer = Space(Ret)
        GetWindowText hWndChild, strBuffer, Ret + 1
        
        '// Search the next child
        hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)
        
        MsgBox "CLASS:= " & fn.Clean(strClass) & vbCrLf & _
            "Window handle (Hex):= " & Hex(hWndChild) & vbCrLf & _
            "WINDOW CAPATION:= " & fn.Clean(strBuffer), vbInformation, "Childs of XLDESK No." & i
        i = i + 1
    Loop

End Sub

This is an example of iterating through Windows to get what you want.
Similar to this method there are the Enumeration Methods usually via
Addressof function. There are basically 10+ methods used to get Window handles.
These are the ones you would use. Obviously there are prerequisites to get these, eg knowing the
Class Name, Text caption etc.

EXCEL6 is interesting in that it appears to be an Edit control , it has a cursor handle which is the first clue
that it is in fact an edit control, all other windows do not have this Cursor handle. This is in fact true as
double clciking a cell (Edit mode) gives you this cursor. Also SPY++ reveals this. eg Rectangle size of object
container reveals 64 X 16 when a cell is in Edit mode = cells size in pixels.
 
Upvote 0
Hi Ivan

Thanks for that - and I have definitely found the code examples on your site to be extremely helpful in trying to figure out some of the API functions :biggrin:

(y)
 
Upvote 0

Forum statistics

Threads
1,214,591
Messages
6,120,432
Members
448,961
Latest member
nzskater

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top