Visual Basic Enumeration/Callback Routines

EnumChildWindows: Enumeration of Parent and Child Windows via Callbacks
     
Posted:   Wednesday January 26, 2000
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch
     
 Prerequisites
VB5 / VB6.  Note that the indent may only display correctly on the VB5 version of the demo.

Many Windows' Enum APIs provide callback data for specific tasks.

This project demonstrates using both EnumWindows and EnumChildWindows APIs and their respective EnumWindowProc and EnumChildProc callbacks. The main form in the demo enumerates all top-level windows on the system populating ListView with the window information. Double-clicking a ListItem will open a child form containing the windows under the parent passed.

Where a window does not have text, its class name is substituted in the first column. The second column is the window handle and the third reflects the type of data contained in the first column. The last column is the window class name, redundant for those displaying classes in the first column. And as a bonus, the demo shows how to indent list items for subordinate data. Note however that indentation required that a imagelist be associated with the control.

 

 BAS Module Code
Place the following code into the general declarations area of a bas module:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const LVIF_INDENT As Long = &H10
Private Const LVIF_TEXT As Long = &H1
Private Const LVM_FIRST As Long = &H1000
Private Const LVM_SETITEM As Long = (LVM_FIRST + 6)

Private Type LVITEM
   mask As Long
   iItem As Long
   iSubItem As Long
   state As Long
   stateMask As Long
   pszText As String
   cchTextMax As Long
   iImage As Long
   lParam As Long
   iIndent As Long
End Type

Public Declare Function EnumWindows Lib "user32" _
  (ByVal lpEnumFunc As Long, _
   ByVal lParam As Long) As Long
   
Public Declare Function EnumChildWindows Lib "user32" _
  (ByVal hWndParent As Long, _
   ByVal lpEnumFunc As Long, _
   ByVal lParam As Long) As Long

Private Declare Function GetWindowTextLength Lib "user32" _
    Alias "GetWindowTextLengthA" _
   (ByVal hwnd 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 Declare Function GetClassName Lib "user32" _
    Alias "GetClassNameA" _
   (ByVal hwnd As Long, _
    ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long

Private Declare Function IsWindowVisible Lib "user32" _
   (ByVal hwnd As Long) As Long
   
Private Declare Function GetParent Lib "user32" _
   (ByVal hwnd As Long) As Long

Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long


Public Function EnumWindowProc(ByVal hwnd As Long, _
                               ByVal lParam As Long) As Long
   
  'working vars
   Dim nSize As Long
   Dim sTitle As String
   Dim sClass As String
   
   Dim sIDType As String
   Dim itmX As ListItem
   Dim nodX As Node
   
  'eliminate windows that are not top-level.
   If GetParent(hwnd) = 0& And _
      IsWindowVisible(hwnd) Then
      
     'get the window title / class name
      sTitle = GetWindowIdentification(hwnd, sIDType, sClass)

     'add to the listview
      Set itmX = Form1.ListView1.ListItems.Add(Text:=sTitle, Key:=CStr(hwnd) & "h")
      itmX.SmallIcon = Form1.ImageList1.ListImages("parent").Key
      itmX.SubItems(1) = CStr(hwnd)
      itmX.SubItems(2) = sIDType
      itmX.SubItems(3) = sClass
      
   End If
   
  'To continue enumeration, return True
  'To stop enumeration return False (0).
  'When 1 is returned, enumeration continues
  'until there are no more windows left.
   EnumWindowProc = 1
   
End Function


Private Function GetWindowIdentification(ByVal hwnd As Long, _
                                         sIDType As String, _
                                         sClass As String) As String

   Dim nSize As Long
   Dim sTitle As String

  'get the size of the string required
  'to hold the window title
   nSize = GetWindowTextLength(hwnd)
   
  'if the return is 0, there is no title
   If nSize > 0 Then
   
      sTitle = Space$(nSize + 1)
      Call GetWindowText(hwnd, sTitle, nSize + 1)
      sIDType = "title"
      
      sClass = Space$(64)
      Call GetClassName(hwnd, sClass, 64)
   
   Else
   
     'no title, so get the class name instead
      sTitle = Space$(64)
      Call GetClassName(hwnd, sTitle, 64)
      sClass = sTitle
      sIDType = "class"
   
   End If
   
   GetWindowIdentification = TrimNull(sTitle)

End Function


Public Function EnumChildProc(ByVal hwnd As Long, _
                              ByVal lParam As Long) As Long
   
  'working vars
   Dim sTitle As String
   Dim sClass As String
   Dim sIDType As String
   Dim itmX As ListItem

  'get the window title / class name
   sTitle = GetWindowIdentification(hwnd, sIDType, sClass)

  'add to the listview
   Set itmX = Form2.ListView1.ListItems.Add(,, sTitle)
   itmX.SubItems(1) = hwnd
   itmX.SubItems(2) = sIDType
   itmX.SubItems(3) = sClass
      
   Listview_IndentItem Form2.ListView1.hwnd, CLng(itmX.Index), 1
   
   EnumChildProc = 1
   
End Function


Private Function TrimNull(startstr As String) As String

  Dim pos As Integer

  pos = InStr(startstr, Chr$(0))
  
  If pos Then
      TrimNull = Left$(startstr, pos - 1)
      Exit Function
  End If
  
 'if this far, there was
 'no Chr$(0), so return the string
  TrimNull = startstr
  
End Function


Private Sub Listview_IndentItem(hwnd As Long, _
                                nItem As Long, _
                                nIndent As Long)

   Dim LV As LVITEM

  'if nIndent indicates that indentation
  'is requested nItem is the item to indent
   If nIndent > 0 Then
      
      With LV
        .mask = LVIF_INDENT
        .iItem = nItem - 1 '0-based
        .iIndent = nIndent
      End With
      
      Call SendMessage(hwnd, LVM_SETITEM, 0&, LV)
      
   End If
       
End Sub
 Form1 Code
To Form1, add a listview with 4 columns, a command button (Command1) . Note the illustrations above show ListImages but the code below does not use them in order to provide a rapid demo):

To Form2, just add a listview (Listview1).

Add the following code to Form1:


Option Explicit

Private Sub Command1_Click()

   ListView1.ListItems.Clear
   Call EnumWindows(AddressOf EnumWindowProc, &H0)

End Sub


Private Sub Form_Load()

   Me.Move (Screen.Width - Me.Width) / 2, (Screen.Height - Me.Height) / 2
   
   With ListView1
      .ColumnHeaders.Add , , "Window Class or Title"
      .ColumnHeaders.Add , , "Handle"
      .ColumnHeaders.Add , , "Type"
      .ColumnHeaders.Add , , "Class Name"
      .View = lvwReport
   End With   

End Sub


Private Sub ListView1_DblClick()

   Dim hwndSelected As Long
   
   hwndSelected = Val(ListView1.SelectedItem.Key)
   
   Load Form2
   Call Form2.EnumSelectedWindow(ListView1.SelectedItem.Text, hwndSelected)
   
End Sub
 Form2 Code
Add the following code to Form2 (the illustrations above show ListImages but the code below does not use them in order to provide a rapid demo):

Option Explicit

Public Sub EnumSelectedWindow(sItem As String, hwnd As Long)
 
   ListView1.ListItems.Clear
   ListView1.ListItems.Add ,, sItem
   
   Call EnumChildWindows(hwnd, AddressOf EnumChildProc, &H0)
   
   Me.Show vbModal
   
End Sub


Private Sub Form_Load()

   Me.Move (Screen.Width - Me.Width) / 2, (Screen.Height - Me.Height) / 2
   
   With ListView1
      .ColumnHeaders.Add , , "Window Class or Title"
      .ColumnHeaders.Add , , "Handle"
      .ColumnHeaders.Add , , "Type"
      .ColumnHeaders.Add , , "Class Name"
      .View = lvwReport
   End With   
   
End Sub
 Comments
Clicking the Enum Windows button will fill the listview with the top-level windows on the system. Double clicking an item will display the child windows for the selected window handle.

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter