I'm using this to activate a Modeless userform textbox after switching between windows.
Code from @AndASM and Michael version, with some mods (StopAllEventHooks now working, tested in Excel 32 and 64)
This is a standard module (named "Hooking" for example):
Option Explicit
'https://mcmap.net/q/590464/-detecting-in-vba-when-the-window-containing-an-excel-instance-becomes-active
'https://mcmap.net/q/591254/-detecting-lost-focus-in-excel-application-workbook-or-worksheet
'https://social.msdn.microsoft.com/Forums/office/en-US/70ec18cd-2438-4c96-bbb0-97cdecd3ddbb/detect-the-event-when-someone-minimizes-or-maxmize-the-excel-application?forum=exceldev
'https://learn.microsoft.com/es-es/windows/win32/winmsg/using-hooks?redirectedfrom=MSDN
'https://mcmap.net/q/591255/-call-event-in-excel-vba-when-switching-to-another-app
'https://mcmap.net/q/591256/-forms-gotfocus-event-does-not-seem-to-fire/4659751#4659751
Private Const EVENT_SYSTEM_FOREGROUND = &H3&
Private Const WINEVENT_OUTOFCONTEXT = 0
#If VBA7 Then
Private Declare PtrSafe Function SetWinEventHook Lib "user32.dll" (ByVal EventMin As Long, ByVal EventMax As Long, ByVal hmodWinEventProc As LongLong, ByVal lpfnWinEventProc As LongLong, ByVal idProcess As Long, ByVal idThread As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As Long
'https://mcmap.net/q/591257/-vba-codes-32bit-to-64bit
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As LongPtr, lpdwProcessId As Long) As Long
Private Declare PtrSafe Function UnhookWinEvent Lib "user32.dll" (ByVal hWinEventHook As LongPtr) As Long
#Else
'https://www.vbforums.com/showthread.php?861147-RESOLVED-help-How-to-use-SetWinEventHook-to-determine-an-application-s-window-is-created
Private Declare Function SetWinEventHook Lib "user32.dll" (ByVal EventMin As Long, ByVal EventMax As Long, ByVal hmodWinEventProc As Long, ByVal lpfnWinEventProc As Long, ByVal idProcess As Long, ByVal idThread As Long, ByVal dwFlags As Long) As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
'https://mcmap.net/q/591258/-unhookwinevent-does-not-unhook-in-vba
Private Declare Function UnhookWinEvent Lib "user32.dll" (ByVal hWinEventHook As Long) As Long
#End If
Private pRunningHandles As Collection
Public Function StartEventHook() As Long
If pRunningHandles Is Nothing Then Set pRunningHandles = New Collection
StartEventHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0&, AddressOf WinEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT)
pRunningHandles.Add StartEventHook
End Function
#If VBA7 Then
Public Sub StopEventHook(lHook As LongPtr)
#Else
Public Sub StopEventHook(lHook As Long)
#End If
Dim LRet As Long
If lHook = 0 Then Exit Sub
LRet = UnhookWinEvent(lHook)
End Sub
Public Sub StartHook()
StartEventHook
End Sub
Public Sub StopAllEventHooks()
Dim vHook As Variant
#If VBA7 Then
Dim lHook As LongPtr
#Else
Dim lHook As Long
#End If
For Each vHook In pRunningHandles
#If VBA7 Then
lHook = CLngPtr(vHook)
#Else
lHook = CLng(vHook)
#End If
StopEventHook lHook
Next vHook
End Sub
#If VBA7 Then
Public Function WinEventFunc(ByVal HookHandle As Long, ByVal LEvent As Long, _
ByVal hWnd As LongPtr, ByVal idObject As Long, ByVal idChild As Long, _
ByVal idEventThread As Long, ByVal dwmsEventTime As Long) As Long
#Else
Public Function WinEventFunc(ByVal HookHandle As Long, ByVal LEvent As Long, _
ByVal hWnd As Long, ByVal idObject As Long, ByVal idChild As Long, _
ByVal idEventThread As Long, ByVal dwmsEventTime As Long) As Long
#End If
'This function is a callback passed to the win32 api
'We CANNOT throw an error or break. Bad things will happen.
On Error Resume Next
Dim thePID As Long
If LEvent = EVENT_SYSTEM_FOREGROUND Then
GetWindowThreadProcessId hWnd, thePID
If thePID = GetCurrentProcessId Then
Application.OnTime Now, "Event_GotFocus"
Else
Application.OnTime Now, "Event_LostFocus"
End If
End If
On Error GoTo 0
End Function
Public Sub Event_GotFocus()
'Debug.Print "GOT FOCUS"
Activar_userform_visible
'Dim SoundName As String
'SoundName = "C:\WINDOWS\Media\Chimes.wav"
'PlayWavSound SoundName, 1
End Sub
Public Sub Event_LostFocus()
'Debug.Print "Nope"
'Dim SoundName As String
'SoundName = "C:\WINDOWS\Media\recycle.wav" 'Chimes.wav
'PlayWavSound SoundName, 1
End Sub
Create a userform ("UserForm1") with 2 textboxes.
This is the UserForm1 code module:
Option Explicit
#If VBA7 Then
Dim MeHWnd As LongPtr, lngCurrentStyle As Long, lngNewStyle As Long
#Else
Dim MeHWnd As Long, lngCurrentStyle As Long, lngNewStyle As Long
#End If
'http://www.cpearson.com/excel/SuppressChangeInForms.htm
'https://riptutorial.com/vba/example/19036/best-practices
Private Type TView
IsCancelled As Boolean
EnableEvents As Boolean
End Type
Private this As TView
Public Property Get IsCancelled() As Boolean
IsCancelled = this.IsCancelled
End Property
Public Property Get EnableEvents() As Boolean
EnableEvents = this.EnableEvents
End Property
Private Sub UserForm_Initialize()
On Error GoTo ExceptionHandling
'If Application.Version < 9 Then
' MeHWnd = FindWindow("THUNDERXFRAME", Me.Caption) 'XL97
'Else
MeHWnd = FindWindow("THUNDERDFRAME", Me.Caption) 'XL2000+
'End If
lngCurrentStyle = GetWindowLong(MeHWnd, GWL_STYLE)
lngNewStyle = lngCurrentStyle Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX
SetWindowLong MeHWnd, GWL_STYLE, lngNewStyle
this.EnableEvents = True
CleanUp:
On Error Resume Next
Exit Sub
ExceptionHandling:
'MsgBox "Error: " & Err.Description
Resume CleanUp
'https://mcmap.net/q/591262/-excel-vba-global-variables-are-assigned-when-workbook-is-opened-get-erased-if-an-error-occurs
Resume 'for debugging
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
this.IsCancelled = True
'ListBox1.Clear
'ListBox3.Clear
StopAllEventHooks
Me.Hide
End If
End Sub
Private Sub UserForm_Layout()
'https://www.mrexcel.com/board/threads/how-can-i-tell-if-a-userform-has-been-minimized-vba.920923/
'https://mcmap.net/q/591259/-unhook-scroll-when-userform-minimized
If IsIconic(MeHWnd) Then
'MsgBox "The userform : '" & Me.Name & "' has just been minimized", vbInformation
StopAllEventHooks
ElseIf IsZoomed(MeHWnd) Then
'MsgBox "The userform : '" & Me.Name & "' has just been maximized", vbInformation
Activar_userform_visible
StartHook
Else
'MsgBox "The userform : '" & Me.Name & "' has just been restored", vbInformation
Activar_userform_visible
StartHook
End If
End Sub
Code placed in another standard module ("Module1" for example):
#If VBA7 Then
Public Declare PtrSafe Function PlayWavSound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long
Public Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Public Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
Public Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare PtrSafe Function IsIconic Lib "user32" (ByVal hwnd As LongPtr) As Long
Public Declare PtrSafe Function IsZoomed Lib "user32" (ByVal hWnd As LongPtr) As Long
#Else
Public Declare Function PlayWavSound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal LpszSoundName As String, ByVal uFlags As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function IsIconic Lib "user32" (ByVal hWnd As Long) As Long
Public Declare Function IsZoomed Lib "user32" (ByVal hWnd As Long) As Long
#End If
'Siddharth Rout: https://mcmap.net/q/591260/-minimize-userform-when-macro-in-it-is-running/20558520#20558520
'https://answers.microsoft.com/en-us/msoffice/forum/all/excel-vba-how-to-exclude-minimize-and-esc-button/0617ee7c-6c49-4127-8dba-3f8eb04acb9d
Public Const WS_MINIMIZEBOX As Long = &H20000
Public Const WS_MAXIMIZEBOX As Long = &H10000
Public Const GWL_STYLE As Long = -16
Sub Show_Userform1()
Dim frm As UserForm1
Set frm = New UserForm1
frm.Show vbModeless
End Sub
Sub Activar_userform_visible()
Dim objLoop As Object, Pausa As Single, Inicio As Single
Pausa = 0.2
For Each objLoop In VBA.UserForms
If (objLoop.Name = "UserForm1" Or objLoop.Name = "UserForm1") And objLoop.Visible = True Then
'https://mcmap.net/q/591261/-set-focus-back-to-the-application-window-after-showing-userform
AppActivate objLoop.Caption
Inicio = Timer
Do While Timer < Inicio + Pausa
DoEvents
Loop
CreateObject("WScript.Shell").SendKeys "{TAB}", True
CreateObject("WScript.Shell").SendKeys "+{TAB}", True
End If
Next objLoop
End Sub
WorkbookActivate
will flag when any workbook in that instance is activated. If you use this code in an addin it will work in any instance (but also it will flag when you tab between workbooks within that instance). – HueWorkbook_Activate()
,Workbook_WindowResize(ByVal Wn As Window)
,Workbook_WindowActivate(ByVal Wn As Window)
will fire when the workbook(Excel Application actually) is brought into focus say by minimizing a browser, because the workbook is not getting activated. It is already active. The application is getting activated and unfortunately, the Application itself per se does not have anApplication_Activate
event. What you want can be achieved using vb.net/vb6 though. – Crossmanis brought into focus say by minimizing a browser, because the workbook is not getting activated.
true, although the question did state click. – HueHooking
See THIS – CrossmanHooks
HERE Be careful. One wrong line of code and Excel will crash :) – Crossman