Visual Basic System Services
GetGuiThreadInfo: Active Application GUI Information
     
Posted:   Sunday January 05, 2003
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   None
Author:   VBnet - Randy Birch
     

Related:  

EnumWindows: Enumerate Windows via a Callback
EnumChildWindows: Enumeration of Parent and Child Windows via Callbacks
GetWindow: Find Applications of a Specific Class or Window Title
     
 Prerequisites
None.

The GetGUIThreadInfo function retrieves information about the active window, or a specified graphical user interface (GUI) thread. The call's idThread parameter identifies the thread for which information is to be retrieved, a value that can be obtained by calling GetWindowThreadProcessId. When idThread is null the function returns information for the foreground thread. This makes it handy to monitor ongoing GUI information for the currently active application as shown in this demo.

The data for the GUI is returned through the lpgui member of the call, which identifies a GUITHREADINFO structure, and in addition to the requisite cbSize member contains:

  • flags (the thread state)
  • hwndActive (Handle to the active window within the thread)
  • hwndFocus (Handle to the window that has the keyboard focus)
  • hwndCapture (Handle to the window that has captured the mouse)
  • hwndMenuOwner (Handle to the window that owns any active menus)
  • hwndMoveSize (Handle to the window in a move or size loop)
  • hwndCaret (Handle to the window that is displaying the caret), and
  • rcCaret (A RECT structure that describes the caret's bounding rectangle, in client coordinates, relative to the window specified by the hwndCaret member).

The flags member returns a bit mask providing the active window status information, such as:

  • GUI_CARETBLINKING (the caret's blink state, set if the caret is visible.)
  • GUI_INMENU (the thread's menu state, set if the thread is in menu mode.)
  • GUI_INMOVESIZE (the thread's move state, set if the thread is in a move or size loop.)
  • GUI_POPUPMENUMODE (the thread's pop-up menu state, set if the thread has an active pop-up menu.)
  • GUI_SYSTEMMENUMODE (the thread's system menu state, set if the thread is in a system menu mode.)
  • GUI_16BITTASK (the thread's application type, set if the thread corresponds to a 16-bit application.) Note: Windows XP only for this flag.

The demo is fairly straightforward. A timer fires at the interval selected calling GetActiveWindowGuiInfo(), the main application method. GetActiveWindowGuiInfo in turn calls GetGuiThreadInfo and loads a listbox with the GUI information pertaining to the active window's thread.

Helper routines GetActiveWindowTitle() and GetCaretWindowText() returns the titlebar caption for the active window, and the text in edit or rich text controls supporting the WM_GETTEXT message, respectively. A counter variable is also used (shown as the first line in the list block (175 in the illustration above) as a visual aid in determining each call is successful and the data being viewed is current.

 BAS Module Code
None.

 Form Code
To a form add a timer control (Timer1), a list box (List1), a combo (Combo1), and two command buttons (Command1/Command2), along with the following code:

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 GUI_CARETBLINKING  As Long = &H1
Private Const GUI_INMOVESIZE     As Long = &H2
Private Const GUI_INMENUMODE     As Long = &H4
Private Const GUI_SYSTEMMENUMODE As Long = &H8
Private Const GUI_POPUPMENUMODE  As Long = &H10
Private Const GUI_16BITTASK      As Long = &H20 'winver >= 5.01
Private Const LB_SETTABSTOPS As Long = &H192
Private Const WM_GETTEXT As Long = &HD
Private Const WM_GETTEXTLENGTH As Long = &HE

Private Type RECT
   Left As Long
   Top As Long
   Right As Long
   Bottom As Long
End Type

Private Type GUITHREADINFO
   cbSize As Long
   flags As Long
   hwndactive As Long
   hwndFocus As Long
   hwndCapture As Long
   hwndMenuOwner As Long
   hwndMoveSize As Long
   hwndcaret As Long
   rcCaret As RECT
End Type

Private Declare Function GetGuiThreadInfo Lib "user32" _
  Alias "GetGUIThreadInfo" (ByVal idThread As _
   Long, lpgui As GUITHREADINFO) 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

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 Sub Form_Load()

  'set up list tabstops 
   ReDim TabArray(0 To 0) As Long
   
   TabArray(0) = 107
   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 0&, ByVal 0&)
   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 1&, TabArray(0))
   
  'set up timer intervals 
   With Combo1
      .AddItem "100 milliseconds"
      .ItemData(.NewIndex) = 100
      .AddItem "1/2 second"
      .ItemData(.NewIndex) = 500
      .AddItem "1 second"
      .ItemData(.NewIndex) = 1000
      .AddItem "2 seconds"
      .ItemData(.NewIndex) = 2000
      .AddItem "3 seconds"
      .ItemData(.NewIndex) = 3000
      .AddItem "5 seconds"
      .ItemData(.NewIndex) = 5000
      .ListIndex = 2  '1 second
   End With

   Command1.Caption = "Start"
   Command2.Caption = "Done"
   
End Sub


Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

   Timer1.Enabled = False
   
End Sub


Private Sub Combo1_Click()

  'if timer running, this will
  'change its update frequency
   Timer1.Interval = Combo1.ItemData(Combo1.ListIndex)

End Sub


Private Sub Command1_Click()

   Timer1.Interval = Combo1.ItemData(Combo1.ListIndex)
   Timer1.Enabled = True
   
End Sub

Private Sub Command2_Click()

   Unload Me
   
End Sub


Private Sub Timer1_Timer()

  'nuke the list if 250 or more entries
   If List1.ListCount > 250 Then List1.Clear
   
  'update with info from the active window
   Call GetActiveWindowGuiInfo
   
End Sub


Private Sub GetActiveWindowGuiInfo()

   Dim gui As GUITHREADINFO
   Static numcalls As Long
   
  'idThread identifies the thread for which
  'information is to be retrieved. Where the
  'purpose is to monitor a specific form or
  'application, GetWindowThreadProcessId()
  'provides the thread ID required.
  '
  'Conveniently for this demo, when idThread
  'is null, GetGUIThreadInfo returns information
  'for the foreground thread, thus allowing
  'us to move between applications and view
  'the activities therein.
  
  'cbSize must be set before calling
   gui.cbSize = Len(gui)

   If GetGuiThreadInfo(0&, gui) <> 0 Then
   
     'numcalls is just a counter to increment
     'a line in the list to show the code is
     'working when you rest in one window
      numcalls = numcalls + 1
   
      With List1
      
         .AddItem CStr(numcalls)
         
         .AddItem "active window hwnd:" & vbTab & gui.hwndactive
         .AddItem "   active window title:" & vbTab & GetActiveWindowTitle(gui.hwndactive)
         .AddItem "mouse capture hwnd:" & vbTab & gui.hwndCapture
         .AddItem "showing caret hwnd:" & vbTab & gui.hwndcaret
         .AddItem "    caret window text:" & vbTab & GetCaretWindowText(gui.hwndcaret)
         .AddItem "keyboard focus hwnd:" & vbTab & gui.hwndFocus
         .AddItem "active menu owner hwnd:" & vbTab & gui.hwndMenuOwner
         .AddItem "move or size loop hwnd:" & vbTab & gui.hwndMoveSize
   
         .AddItem "caret rect (l/r t/b):" & vbTab & _
                   gui.rcCaret.Left & "/" & _
                   gui.rcCaret.Right & " " & _
                   gui.rcCaret.Top & "/" & _
                   gui.rcCaret.Bottom
   
        'AND the flags to return the
        'possible GUI states
         .AddItem "returned flags:" & vbTab & gui.flags
         
         If gui.flags And GUI_CARETBLINKING Then .AddItem "caret is visible"
         If gui.flags And GUI_INMOVESIZE Then .AddItem "thread is in a move or size loop"
         If gui.flags And GUI_INMENUMODE Then .AddItem "thread is in menu mode"
         If gui.flags And GUI_SYSTEMMENUMODE Then .AddItem "thread is in system menu mode"
         If gui.flags And GUI_POPUPMENUMODE Then .AddItem "thread has active popup menu"
         If gui.flags And GUI_16BITTASK Then .AddItem "thread's app is 16-bits"
         
         'add a blank line and ensure the
         'last-added entry is in view
         .AddItem ""
         .TopIndex = .NewIndex
         
   End With  'with list1

   Else
   
      With List1
         .AddItem "Error " & Err.LastDllError
         .AddItem "Error " & Err.Description
         .AddItem ""
         .TopIndex = .NewIndex
      End With  'with list1
      
   End If
   
End Sub


Private Function GetActiveWindowTitle(ByVal hwndactive As Long) As String

   Dim nLength As Long
   Dim res As Long
   Dim buff As String
   
  'GetWindowText returns the title
  'of the window specified as hwndactive
   If hwndactive <> 0 Then
   
      nLength = GetWindowTextLength(hwndactive)
      
      If nLength <> 0 Then
         
         buff = Space$(nLength + 1)
         
         res = GetWindowText(hwndactive, buff, nLength + 1)
         
         If res <> 0 Then
            GetActiveWindowTitle = Left$(buff, res)
            Exit Function
         End If  'if res
         
      End If  'if nlength
   
   End If  'if hwndactive
   
   GetActiveWindowTitle = "(not available)"
   
End Function


Private Function GetCaretWindowText(ByVal hwndcaret As Long) As String

   Dim nLength As Long
   Dim res As Long
   Dim buff As String
   
  'WM_GETTEXT retrieves the text
  'from edit and rich text controls
   If hwndcaret <> 0 Then
   
      nLength = SendMessage(hwndcaret, WM_GETTEXTLENGTH, 0&, ByVal 0&)
      
      If nLength <> 0 Then
         
         buff = Space$(nLength + 1)
         
         res = SendMessage(hwndcaret, WM_GETTEXT, nLength + 1, ByVal buff)
         
         If res <> 0 Then
            GetCaretWindowText = Left$(buff, res)
            Exit Function
         End If  'if res
         
      End If  'if nlength
   
   End If  'if hwndcaret
   
   GetCaretWindowText = "(not available)"
   
End Function
 Comments

 
 

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