Obtaining A List All Window Handles

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 5

Obtaining A List all Window Handles, Class names and Titles

written by Mark Rowlinson - Last updated Oct 2004

Introduction
The most important feature when first looking into programming with the Win32 API is to being able to find the handles and class

names of the windows. Many API functions require the window handle before anything can be acheived. The code in this article

will enable you to obtain a list of all the window handles, class names and titles currently running in Excel. In effect it is doing

one of the things done by a program such as Spy++.

API Declarations
There are 3 API functions used in this code:

 FindWindowEx - retrieves window handles


 GetClassName - obtains the class of the window given a handle
 GetWindowText - obtains the text in the window title bar or text of the control if available given a window handle

The functions are declared as follows:

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 GetClassName Lib "user32" Alias "GetClassNameA" _


(ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _


(ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Buffers
The latter 2 API functions require the use of buffers. It is very common for API functions to use buffers. In Visual Basic and VBA

we are used to calling functions and using their return value. In API programming you often pass a variable to the function that is

written to by the function. In the case of strings this means passing a buffer. You can create a buffer in several ways but the way

I tend to use is to use the 'String$()' function to create a string of null characters as follows:

strText = String$(100, Chr$(0))

strText now contains a string of 100 null characters. We pass this string and its length that we know to be 100 to the functions.

The function then returns the length of the string it has written to the buffer so we can determine the string which to output

using the 'Left$()' function passing it our buffer and the API function's return value. Putting this together we have:

'create the buffer


strText = String$(100, Chr$(0))
'call the function
lngRet = GetClassName(hWnd, strText, 100)
'determine the string we wish to output
strOutput = Left$(strText, lngRet)

Recursion
The code we are creating in this article uses a technique called recursion. A ecursive procedure is a proedure that calls itself. At

first this may seem like a never ending loop is created and it is possible if you are not careful to create one! The key to it is to

create a condition in the function that when satisfied no further calls are made. In the case of the code on this page the condition

in a 'while loop'. The routine loops through calling itself until no further windows are found. ecursion is an area that can be quite

difficult to get your head round but when you do you will find it a very useful technique

Finding The Windows


The first job of the code is to find the windows that are running. To do this we use the FindWindowEx function We pass it the

parent window we want to search, the child window we want to search next in the z-order from and then the class name and

window title of the window we want to find. In this case we want to find all windows so we use 'vbNullString' in place of the last

2 arguments. It is important that we use vbNullString and not "" as vbNullString is a special value. Alternatively we could declare

the function:

ByVal lpsz1 As Long

and pass the value 0& (The & indicates it is a long).

The parent argument we will make flexible and pass this as an agument to the Sub. The child argument should first be 0& but for

each subsequent call should be the previous handle we found so that we loop through all the windows.

Putting something simple together


Ok, now we know how to use the functions we can put together our sub. We will just consider the case of obtaining a list of class

names for now. The output of the sub will be produced on an excel sheet and will start in cell A1. We will then offset by 1 column

as we enter a new level of child windows and 1 row for each window found. As the column offset is dependent on the level in the

heirachy we are looking at we shall pass this as another argument to the sub. The row however needs to be incremented for

every window found so we will declare that at module level. We also need to declare a variable in the sub to hold the class name

and window handle of each window and also one to be the return value of our API call. We therefore have the following:

Dim x As Integer

Private Sub GetWinInfo(hParent As Long, intOffset As Integer)


Dim hWnd As Long, lngRet As Long
Dim strText As String

The next step is to find the first child of the hParent window. We can easily do this using:

hWnd = FindWindowEx(hParent, 0&, vbNullString, vbNullString)

We want to loop through every child window we find i.e. while we find a window handle so we can set up a loop:

While hWnd <> 0


'notice that this time our call to FindWindowEx passes the previously found
window
hWnd = FindWindowEx(hParent, hWnd, vbNullString, vbNullString)
Wend

For each of these windows though we wnat to find the class name of the window and output it. We therefore need to use the

GetClassName function to retrieve the class of the window and output it to the excel sheet as follows:

strText = String$(100, Chr$(0))


lngRet = GetClassName(hWnd, strText, 100)
Range("a1").Offset(x, intOffset) = Left$(strText, lngRet)

Finally we want to increment the row by 1 and also check for children of the window we have found. These child windows need

to output with an offset of 1 more than the parent. We can therefore add the following:

GetWinInfo hWnd, intOffset + 1


x=x+1

Putting it All Together


We now have a working Sub as follows:

Dim x As Integer

Private Sub GetWinInfo(hParent As Long, intOffset As Integer)


Dim hWnd As Long, lngRet As Long
Dim strText As String

While hWnd <> 0


'Output class name
strText = String$(100, Chr$(0))
lngRet = GetClassName(hWnd, strText, 100)
Range("a1").Offset(x, intOffset) = Left$(strText, lngRet)
'Check for children and increment row
GetWinInfo hWnd, intOffset + 1
x=x+1
'Find next window
hWnd = FindWindowEx(hParent, hWnd, vbNullString, vbNullString)
Wend
End Sub

This sub can be started from any window and will display all children of that window when passed the relevent window handle.

Alternatively you can pass it 0& as the hParent argument and it will find all top level windows and all their children.

Summary
Hopefully the above text will adi your understanding of using API functions as well as showing a few useful techniques. As always

if you have any commenst, suggestions or spot any errors please email [email protected]. I have added to the

code above to provide the added functionality of displaying any or all of the window handles, class names and window text and

the full code follows below. All you need to do is call the GetWindows sub. To change the output change the third argument
passed to GetWinInfo to be one of those given in the enumeration. Feel free to use and modify the code but I would appreciate it

if credit is given where used.

Option Explicit

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 GetClassName Lib "user32" Alias "GetClassNameA" _
(ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) 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 x As Integer

Private Enum winOutputType


winHandle = 0
winClass = 1
winTitle = 2
winHandleClass = 3
winHandleTitle = 4
winHandleClassTitle = 5
End Enum

Public Sub GetWindows()


x = 0
GetWinInfo 0&, 0, winHandleClassTitle
End Sub

Private Sub GetWinInfo(hParent As Long, intOffset As Integer, OutputType As


winOutputType)
'Sub to recursively obtain window handles, classes and text
'given a parent window to search
'Written by Mark Rowlinson
'www.markrowlison.co.uk - The Programming Emporium
Dim hWnd As Long, lngRet As Long, y As Integer
Dim strText As String
hWnd = FindWindowEx(hParent, 0&, vbNullString, vbNullString)
While hWnd <> 0
Select Case OutputType
Case winOutputType.winClass
strText = String$(100, Chr$(0))
lngRet = GetClassName(hWnd, strText, 100)
Range("a1").Offset(x, intOffset) = Left$(strText, lngRet)
Case winOutputType.winHandle
Range("a1").Offset(x, intOffset) = hWnd
Case winOutputType.winTitle
strText = String$(100, Chr$(0))
lngRet = GetWindowText(hWnd, strText, 100)
If lngRet > 0 Then
Range("a1").Offset(x, intOffset) = Left$(strText, lngRet)
Else
Range("a1").Offset(x, intOffset) ="N/A"
End If
Case winOutputType.winHandleClass
Range("a1").Offset(x, intOffset) = hWnd
strText = String$(100, Chr$(0))
lngRet = GetClassName(hWnd, strText, 100)
Range("a1").Offset(x, intOffset + 1) = Left$(strText, lngRet)
Case winOutputType.winHandleTitle
Range("a1").Offset(x, intOffset) = hWnd
strText = String$(100, Chr$(0))
lngRet = GetWindowText(hWnd, strText, 100)
If lngRet > 0 Then
Range("a1").Offset(x, intOffset + 1) = Left$(strText, lngRet)
Else
Range("a1").Offset(x, intOffset + 1) ="N/A"
End If
Case winOutputType.winHandleClassTitle
Range("a1").Offset(x, intOffset) = hWnd
strText = String$(100, Chr$(0))
lngRet = GetClassName(hWnd, strText, 100)
Range("a1").Offset(x, intOffset + 1) = Left$(strText, lngRet)
strText = String$(100, Chr$(0))
lngRet = GetWindowText(hWnd, strText, 100)
If lngRet > 0 Then
Range("a1").Offset(x, intOffset + 2) = Left$(strText, lngRet)
Else
Range("a1").Offset(x, intOffset + 2) ="N/A"
End If
End Select
'check for children
y = x
Select Case OutputType
Case Is > 4
GetWinInfo hWnd, intOffset + 3, OutputType
Case Is > 2
GetWinInfo hWnd, intOffset + 2, OutputType
Case Else
GetWinInfo hWnd, intOffset + 1, OutputType
End Select
'increment by 1 row if no children found (added from above article to remove
blank lines)
If y = x Then
x = x + 1
End If
'now get next window
hWnd = FindWindowEx(hParent, hWnd, vbNullString, vbNullString)
Wend

End Sub

You might also like