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.
|
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 |
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! |