VBA - évènement à l'activation d'Excel
Bonjour,
Je souhaite qu'une macro s'exécute quand la fenêtre Excel devient active, par exemple quand je bascule du navigateur à Excel ou de Word à Excel.
Comment faire ?
Merci d'avance.
Daniel
Hello,
Private Sub Workbook_Activate()
End Subnon ?
Bonjour,
Merci. Non, ça fonctionne quand tu actives un classeur dans Excel. Pas quand tu actives l'application Excel. Ce que je veux obtenir :
1. Firefox actif
2. raccourci Alt+Tab pour activer la fenêtre Excel => exécution de la macro.
Daniel
Bonjour le fil.
Daniel, c'est le serpent qui se mord la queue non ?
Pour qu'une macro fonctionne il faut que le classeur soit activé. Rien ne permet ce que tu veux faire; hormis une application externe en C# par exemple, qui écouterait les évènements Windows, puis si Excel est détectée comme application active, lancer une procédure via « COM ».
Mais ce dois être extérieur à Excel.
Hello,
@danielc ce que vous cherchez à faire n'est pas très net je trouve.
J'ai interrogé l'IA et au bout de moultes relances elle a fini par me donner un code qui a l'air fonctionnel, mais pu***naise faut vraiment insister pour avoir qqchose de fonctionnel.
je copie/colle
Pour intercepter le changement d'application Windows (de n'importe où vers Excel), il faut faire appel aux API Windows (User32.dll) avec la fonction SetWinEventHook ou surveiller le focus de l'application via un timer OnTime.Voici la méthode la plus stable et légère, basée sur une boucle de vérification du focus Windows :
1.Ajouter les déclarations d'API dans un Module standard :Dans PERSONAL.XLSB.Dans l'éditeur VBA (Alt + F11), va dans Insertion > Module (dans PERSONAL.XLSB) et colle ce code
#If VBA7 Then
Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As LongPtr, lpdwProcessId As Long) As Long
Private Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As Long
#Else
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
#End If
Public EstActive As Boolean
Sub DemarrerSurveillance()
Dim hwndActif As LongPtr
Dim pidActif As Long
hwndActif = GetForegroundWindow()
Call GetWindowThreadProcessId(hwndActif, pidActif)
' Vérifie si la fenêtre active appartient au processus Excel
If pidActif = GetCurrentProcessId() Then
If Not EstActive Then
EstActive = True
' --- TA MACRO ICI ---
MsgBox "Excel a repris le focus depuis Windows !"
' ---------------------
End If
Else
EstActive = False
End If
' Reboucle toutes les secondes
Application.OnTime Now + TimeValue("00:00:01"), "DemarrerSurveillance"
End Sub
Sub ArreterSurveillance()
On Error Resume Next
Application.OnTime Now + TimeValue("00:00:01"), "DemarrerSurveillance", , False
End Sub2.Lancer la surveillance au démarrage :Dans ThisWorkbook.Double-clique sur ThisWorkbook (dans PERSONAL.XLSB) et colle :
Private Sub Workbook_Open()
Call DemarrerSurveillance
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call ArreterSurveillance
End SubComment vérifier :
Déclenche une première fois Workbook_Open (touche F5 dans le code) pour lancer la boucle.
Clique sur Firefox ou une autre application Windows.
Bascule à nouveau sur Excel : le message s'affiche immédiatement.
@Jean-Paul
Je me demandais si une API pouvait détecter que l’application était active (en avant plan) . Couplée avec une macro avec OnTime testant l'état de l’application par exemple. Si ce n'est pas possible, tant pis.
Daniel
bonjour
Moi:
je voulais avoir un évènement dans excel VBa qui ce déclenche a la prise de focus excel alors que même cet événement n'existe pas
DeepSeek :
la prise de focus d'Excel en VBA
Excel VBA n'expose pas d'événement natif pour la prise/perte de focus de l'application. Il faut passer par subclassing : intercepter le message Windows WM_ACTIVATEAPP envoyé à la fenêtre principale d'Excel via l'API SetWindowLongPtr.
1. Module standard (ex : modHook)
```vba
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" _
(ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
Private Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As LongPtr, ByVal hWnd As LongPtr, ByVal Msg As Long, _
ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
#End If
Private Const GWL_WNDPROC As Long = -4
Private Const WM_ACTIVATEAPP As Long = &H1C
#If VBA7 Then
Private hWndExcel As LongPtr
Private pPrevWndProc As LongPtr
#End If
Public Sub HookExcelWindow()
hWndExcel = Application.hWnd ' handle de la fenêtre principale Excel
If hWndExcel <> 0 Then
pPrevWndProc = SetWindowLongPtr(hWndExcel, GWL_WNDPROC, AddressOf WndProc)
End If
End Sub
Public Sub UnhookExcelWindow()
If hWndExcel <> 0 And pPrevWndProc <> 0 Then
SetWindowLongPtr hWndExcel, GWL_WNDPROC, pPrevWndProc
hWndExcel = 0
pPrevWndProc = 0
End If
End Sub
' Procédure de rappel : DOIT être dans un module standard (pas ThisWorkbook, pas un UserForm)
Public Function WndProc(ByVal hWnd As LongPtr, ByVal Msg As Long, _
ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
If Msg = WM_ACTIVATEAPP Then
If wParam <> 0 Then
On Error Resume Next
ThisWorkbook.ExcelGotFocus ' ← à personnaliser
On Error GoTo 0
Else
On Error Resume Next
ThisWorkbook.ExcelLostFocus ' ← à personnaliser
On Error GoTo 0
End If
End If
WndProc = CallWindowProc(pPrevWndProc, hWnd, Msg, wParam, lParam)
End Function
```
2. Dans ThisWorkbook
```vba
Private Sub Workbook_Open()
HookExcelWindow
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
UnhookExcelWindow ' ⚠ indispensable, sinon crash Excel à la fermeture
End Sub
' Événement personnalisé appelé par le WndProc
Public Sub ExcelGotFocus()
' Ton code ici, ex :
' ThisWorkbook.Sheets(1).Range("A1").Value = Now
End Sub
Public Sub ExcelLostFocus()
' Optionnel : code quand Excel perd le focus
End Sub
```
Points importants
· Application.hWnd = handle de la fenêtre principale d'Excel. Pas besoin de FindWindow.
· WM_ACTIVATEAPP (&H1C) : wParam = 1 → l'appli prend le focus ; wParam = 0 → elle le perd. C'est bien plus fiable que WM_ACTIVATE, qui se déclenche aussi pour les UserForms internes.
· Le WndProc doit impérativement être dans un module standard (une Function Public), sinon AddressOf échoue.
· Toujours restaurer l'ancienne procédure (UnhookExcelWindow) dans Workbook_BeforeClose. Sinon Excel plante à la fermeture ("Excel a cessé de fonctionner").
· Sauvegarde le projet avant de tester : un crash pendant le hook peut empêcher l'enregistrement.
· Si tu veux tester à chaud sans rouvrir le fichier : mets HookExcelWindow dans un Sub et exécute-le manuellement, puis UnhookExcelWindow avant toute modif.
Hello,
en utilisant un serveur COM C# (sous forme d'une dll) qui surveille Windows avec UI Automation. Il détecte les événements (ouverture/fermeture de fenêtre, changement de focus, etc.), récupère les informations de l'élément et les transmet à VBA.
Du côté VBA : pilote le système de surveillance. Il démarre/arrête l'écoute, reçoit les événements envoyés par C#, et les affiche dans la ListView d'un formulaire.
Voici le code VBA :
module de classe CUIAEvents :
Option Explicit
Private WithEvents mUIA As comServ.UIAElemInfo
Public Sub Initialize(ByVal objUIA As comServ.UIAElemInfo)
Set mUIA = objUIA
End Sub
Private Sub mUIA_OnUIEvent( _
ByVal eventType As String, _
ByVal elementName As String)
HandleUIEvent eventType, elementName
End Sub
Public Sub StopEvents()
Set mUIA = Nothing
End Submodule standard :
Option Explicit
Public gUIA As comServ.UIAElemInfo
Public gUIAEvents As CUIAEvents
'==========================================================
' DÉMARRER
'==========================================================
Public Sub StartUIA()
On Error GoTo ErrHandler
Set gUIA = New comServ.UIAElemInfo
Set gUIAEvents = New CUIAEvents
gUIAEvents.Initialize gUIA
'WindowOpened + WindowClosed + FocusChanged
gUIA.SetEventFilter 67
gUIA.StartListening
Debug.Print "UI Automation démarrée."
Exit Sub
ErrHandler:
MsgBox "Erreur StartUIA :" & vbCrLf & _
Err.Number & " - " & Err.Description, vbCritical
End Sub
'==========================================================
' ARRÊTER
'==========================================================
Public Sub StopUIA()
On Error Resume Next
If Not gUIA Is Nothing Then gUIA.StopListening
If Not gUIAEvents Is Nothing Then gUIAEvents.StopEvents
Set gUIAEvents = Nothing
Set gUIA = Nothing
Debug.Print "UI Automation arrêtée."
End Sub
'==========================================================
' RÉCEPTION DES ÉVÉNEMENTS
'==========================================================
Public Sub HandleUIEvent(ByVal eventType As String, _
ByVal elementName As String)
Dim itm As ListItem
On Error GoTo ErrHandler
Debug.Print Format(Now, "dd/mm/yyyy hh:nn:ss") & _
" | " & eventType & " | " & elementName
If Not IsUserFormLoaded("frmUIA") Then Exit Sub
With frmUIA.lvEvents
Set itm = .ListItems.Add(, , _
Format(Now, "dd/mm/yyyy hh:nn:ss"))
itm.SubItems(1) = eventType
itm.SubItems(2) = elementName
itm.EnsureVisible
End With
Exit Sub
ErrHandler:
Debug.Print "Erreur HandleUIEvent : " & _
Err.Number & " - " & Err.Description
End Sub
'==========================================================
' VÉRIFIER SI LE USERFORM EST CHARGÉ
'==========================================================
Public Function IsUserFormLoaded(ByVal FormName As String) As Boolean
Dim frm As Object
For Each frm In VBA.UserForms
If frm.name = FormName Then
IsUserFormLoaded = True
Exit Function
End If
Next frm
End Function
'==========================================================
' AFFICHER LE FORMULAIRE
'==========================================================
Public Sub AfficherUIA()
frmUIA.Show vbModeless
End SubVoici ce que cela donne :
Ami calmant, J.P