Visual Basic Subclassing Routines
WM_ACTIVATEAPP: Detect Application Activation State
     
Posted:   Saturday June 7, 1998
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows 98
OS restrictions:   None
Author:   VBnet - Randy Birch
     
 Prerequisites
VB5 or VB6.

In some situations, developers may need to know when their application has gained or lost focus to another Windows application. While the Form Activate and Deactivate methods are suitable for determining when one form in a Visual Basic app has gained or lost focus to another form in the same application, these events provide no means to detect when the user has switched from or to their application from another Windows app (including the start menu or other system item). By means of a simple subclassing routine, you can trap the Windows messages which are normally excluded from your application's view and use these to determine any change in status to the application.

The method below creates a subclassed form which traps any WM_ACTIVATEAPP message sent to the window. Normally eaten by Visual Basic, the LoWord of the message's wParam member contains the status flag indicating whether the window has been activated or deactivated.

The MSDN states that when focus changes between two totally different applications, a WM_ACTIVATEAPP message is sent to the windows. This I can confirm. The MSDN also implies that a WM_ACTIVATE message is also sent when any other window is activated, but in testing with VB WM_ACTIVATE actually never fired. 

The demo changes the colour of the form as a sign of success in trapping the messages. In the illustration, a grey form indicates the WM_ACTIVATEAPP was received with a WA_INACTIVE status flag, meaning focus was switched away from the project app (in this case to notepad). The form's colour is changed back to salmon when the application receives WM_ACTIVATEAPP and the WA_ACTIVE status flag, indicating the app regained focus. In addition to the colour changes, the application's current activation state is reflected in the app's titlebar caption.

 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.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Declare Function SetWindowLong Lib "user32" _
   Alias "SetWindowLongA" _
  (ByVal hwnd As Long, _
   ByVal nIndex As Long, _
   ByVal wNewWord As Long) As Long
   
Public Declare Function CallWindowProc Lib "user32" _
   Alias "CallWindowProcA" _
  (ByVal lpPrevWndFunc As Long, _
   ByVal hwnd As Long, _
   ByVal msg As Long, _
   ByVal wParam As Long, _
   ByVal lParam As Long) As Long

Public Const GWL_WNDPROC  As Long = (-4)
Public Const WM_ACTIVATE      As Long = &H6
Public Const WM_ACTIVATEAPP   As Long = &H1C
Public Const WA_INACTIVE      As Long = 0
Public Const WA_ACTIVE        As Long = 1
Public Const WA_CLICKACTIVE   As Long = 2

'defWindowProc: Variable to hold the ID of the
'               default window message processing
'               procedure. Returned by SetWindowLong.
Public defWindowProc As Long


Public Function WindowProc(ByVal hwnd As Long, _
                           ByVal uMsg As Long, _
                           ByVal wParam As Long, _
                           ByVal lParam As Long) As Long
  
  'window message procedure
   On Error Resume Next
  
   Select Case hwnd
   
     'If the handle returned is to our form,
     'call a form-specific message handler
     'to deal with the notifications. If it
     'is a general system message, pass it
     'on to the default window procedure.
      Case Form1.hwnd
         
        'form-specific handler
         Select Case uMsg
         
            Case WM_ACTIVATEAPP
            
               Select Case LoWord(wParam)
                  Case WA_INACTIVE: 
                     Form1.BackColor = &H8000000F
                     Form1.Caption = "Inactive fired - App lost focus"
                                    
                  Case WA_ACTIVE:
                     Form1.BackColor = &H8080FF
                     Form1.Caption = "Active fired - App has focus"

               End Select
            
            Case Else
            
               'if subclassing has been activated, pass
               'messages to the default message handler
               'If it hasn't, then the default handler
               'will take care of them by default.
               WindowProc = CallWindowProc(defWindowProc, _
                                           hwnd, _
                                           uMsg, _
                                           wParam, _
                                           lParam)
                                               
                Exit Function
            
         End Select
      
      Case Else
      
         'this takes care of messages when the
         'handle specified is not that of the form
         WindowProc = CallWindowProc(defWindowProc, _
                                     hwnd, _
                                     uMsg, _
                                     wParam, _
                                     lParam)
   End Select
   
End Function


Public Sub UnSubClass(hwnd As Long)

  'restore the default message handling
  'before exiting
   If defWindowProc Then
      SetWindowLong hwnd, GWL_WNDPROC, defWindowProc
      defWindowProc = 0
   End If
   
End Sub


Public Sub SubClass(hwnd As Long)

  'assign our own window message
  'procedure (WindowProc)
   On Error Resume Next
   defWindowProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)
   
End Sub


Public Function LoWord(dw As Long) As Integer

   If dw And &H8000& Then
      LoWord = &H8000& Or (dw And &H7FFF&)
   Else
      LoWord = dw And &HFFFF&
   End If

End Function
 Form Code
Add a single command button (Command1) to a form.  Add the following code:

Option Explicit

Private Sub Command1_Click()

   Unload Me

End Sub


Private Sub Form_Load()

  'prepare to receive the activation events
   Call SubClass(Form1.hwnd)
   
  'use colours and caption to enable easy
  'recognition of the activation changes
   Form1.BackColor = &H8080FF
   Form1.Caption =  "Monitoring - App now has focus"
                                    
End Sub


Private Sub Form_Unload(Cancel As Integer)

  'you must call this before ending
  'to avoid a GPF!
   Call UnSubClass(Form1.hwnd)

End Sub
 Comments
Save then run the project. The form on startup will be pink with the 'waiting' caption. Open or click any other window ... the form colour will change to the buttonface colour, and the caption indicates an inactivation received.  Reactivating the window by clicking, alt+tab'bing, using the taskbar or using task manager will cause the app to return to its pink colour with the caption reading 'Activated'.

Before running, assure your form name has been changed to Form1, and when ending, use the command button .. never use the VB stop button when a form is subclassed!


 
 

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