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


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

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

 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

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

   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 & "/" & _
        '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

      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


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