Ceci est une page optimisée pour les mobiles. Cliquez sur ce texte pour afficher la vraie page.

XL 2016 VBA - Comment détecter la changement de position d'une fenêtre ?

Dudu2

XLDnaute Barbatruc
Bonjour,

En gestion des évènements d'une fenêtre on a ces options:

Il n'y a pas d'évènement WindowLayout() comme il y a un évènement UserForm_Layout() et donc on ne peut pas détecter de déplacement de la Window.

Y a-t-il une parade à part faire mouliner sans fin une macro pour surveiller le layout de la Window ?
 
Dernière édition:
Solution
Et donc après une bonne dizaine d'heures de tests pour trouver la cause et la parade !!!
Ça m'a épuisé !

La cause: Après une initialisation du projet dans le VBE et uniquement dans ce cas (faut quand même le faire Excel !!!) crash Excel à cause d'un conflit entre l'évènement Application WindowResize et l'évènement SetWinEventHook EVENT_SYSTEM_MOVESIZEEND.

La parade: Désactiver les évènements Application sur l'évènement SetWinEventHook EVENT_SYSTEM_MOVESIZESTART et les réactiver sur EVENT_SYSTEM_MOVESIZEEND pour empêcher l'évènement Application WindowResize de se produire car ce dernier arrive entre les 2.

patricktoulon

XLDnaute Barbatruc
bon j'ai tout essayé
mon xla peut être sub classé mais ça reste imprecis
les seules méthode c'est le settimer en adressof que j'ai fourni au départ mais il y a des gestions d'erreurs à blinder
le hook mouse semble fonctionner là aussi en adressof erreurs à blinder
le hook window semble fonctionné aussi un peu mieux avec le xla mais même sans finalement même si le cuseur tournicote on s'en fou car a ce moment là on est en train de draguer la window

tout le reste c'est des usines a gaz
je vais donc suivre avec beaucoup d'attention au cas ou vous trouvez quelque chose de mieux
 

jurassic pork

XLDnaute Occasionnel
Raoul, dans le classeur du post #71 ça m'a l'air pas trop fiable d'utiliser le rythmeur pour faire quelque chose dans une feuille. Chez moi si j'édite une cellule on revient à l'éditeur VBA et le timer est arrêté.
 

Dudu2

XLDnaute Barbatruc
Est-ce qu'on peut utiliser le rythmeur de Dranreb autre part que dans un userform ?
Je ne pense pas car @FRanreb a commenté:
VB:
Rem. Module de servive effecteur de tâches des objets Rythmeur.
'    NE PAS UTILISER EN PROGRAMMATION APPLICATIVE.

Reste ce fichier exotique du Post #54 fourni par @jurassic pork et reposté avec une légère modif pour visualiser le timer.
 

Pièces jointes

  • DémoStdTimer.xlsm
    44.3 KB · Affichages: 1

jurassic pork

XLDnaute Occasionnel
Hello Dudu,
c'est pas une bonne idée d'utiliser le timer dans une feuille parce que comme pour les rythmeurs de patrick si on fait de l'édition de cellule pendant l'exécution du timer il y a risque de plantage du timer et dans le cas du Timer avec une nouvelle instance d'excel cette instance n'est pas fermée et on peut la voir dans le gestionnaire de tâches dans les processus en arrière plan et il faut la tuer.
 

Dranreb

XLDnaute Barbatruc
Est-ce qu'on peut utiliser le rythmeur de Dranreb autre part que dans un userform ?
Oui, du moment que c'est dans un module objet, car un module standard ne permet pas la prise en charge d'évènements d'objets VBA.
Il faut être prudent, toutefois, dans un module d'objet Excel, tel que Worksheet, Chart ou le ThisWorkbook, car à part pour des modifications de Shape, qui se passent généralement bien, Excel est assez intolérant à des modifications du classeur dans d'autres circonstances que lors d'une exécution de macro ordinaire qu'il a lui même lancée: il veille bien à les refuser dans une fonction perso appelé par une formule, pendant des calculs donc, mais un court-circuit des sécurités par un timer alors qu'il n'y est pas prêt risque son plantage sévère.
 

Dudu2

XLDnaute Barbatruc
si on fait de l'édition de cellule pendant l'exécution du timer il y a risque de plantage du timer
Peut-être faut-il que l'appel à la fonction définie à l'échéance du timer ne soit lancée que si Application.Ready = True et boucle DoEvents en attendant. Je n'ai pas testé donc à vérifier.

Ou sur un Application.OnTime Now.
 

Dranreb

XLDnaute Barbatruc
Je n'ai plus constaté depuis longtemps de plantage dans mon classeur comportant plusieurs exemples d'applications de l'objet Rythmeur. En cours d'édition de cellule il est simplement suspendu, mais sans plantage …
 

Pièces jointes

  • Progression.xlsm
    252.1 KB · Affichages: 1

patricktoulon

XLDnaute Barbatruc
bonsoir
ce qui nous titille depuis le debut c'est adressof
et c'est bien la notre problème c'est la gestion d'erreur qui dans certains cas est l'imité au sabordage
@jurassic pork au depart utilisait app.ontime
ça fonctionne mais c'est lourd car c'est executé dans le stack de la session vba du workbook
on le vois le cuseur se positione n en travailleur

2° en a ensuite testé mon timerx dans un xla et ça fonctionne aussi
petit problème ca fait monter les tour de bourrin jusqu'au rupteur

3° j'ai essayé le même avec un sleep là c'est des sacades


dans ce cas nous avons pas besoins de répétiteur juste d'un retardateur même si c'est peanuts
alors dans cette version je vais l'utiliser le settimer sauf qu'il sera détruit dans les pseudos events
tout simplement

et plus de soucis
je peut faire ce que je veux sur la feuille ecrire selction copier coller dessinner manger boire et dormir

il est evident que des que l'on va dans le vbe et que l'on ajoute un userform par exemple
tout les variables globales module sont detruite bien evidemment et donc si vous faites ca pendant le hook des window c'est lui qui va générer l'erreur
bref voila
VB:
Option Explicit
Private Const EVENT_SYSTEM_MOVESIZESTART As Long = &HA
Private Const EVENT_SYSTEM_MOVESIZEEND = &HB
Private Const WINEVENT_OUTOFCONTEXT = 0
#If VBA7 Then
    #If Win64 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
    #Else
        Private Declare PtrSafe 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
    #End If
    Private Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As Long
    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
    Private Declare PtrSafe Function SetTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare PtrSafe Function KillTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long) As Long
#Else
    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
    Private Declare Function UnhookWinEvent Lib "user32.dll" (ByVal hWinEventHook As Long) As Long
    Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
#End If
Dim TimerID&


Private pRunningHandles As Collection
Public OldWidth As Double
Public OldHeight As Double

Public Function StartEventHook() As Long
    If pRunningHandles Is Nothing Then Set pRunningHandles = New Collection
    StartEventHook = SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, 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
    OldWidth = Application.Width
    OldHeight = Application.Height
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_MOVESIZESTART Then
        GetWindowThreadProcessId Application.hwnd, thePID
        If thePID = GetCurrentProcessId Then
            Select Case True
                Case Application.Width <> OldWidth
                    TimerID = SetTimer(0, 0, 2, AddressOf Event_Resize)
                Case Else
                    TimerID = SetTimer(0, 0, 2, AddressOf Event_MoveStart)
            End Select
        End If
    ElseIf LEvent = EVENT_SYSTEM_MOVESIZEEND Then
        GetWindowThreadProcessId Application.hwnd, thePID
        If thePID = GetCurrentProcessId Then
            TimerID = SetTimer(0, 0, 2, AddressOf Event_MoveEnd)
        End If
    End If
    On Error GoTo 0
End Function
Public Sub Event_MoveStart()
    If TimerID <> 0 Then KillTimer 0, TimerID: TimerID = 0:
    [A1] = "Event_MoveStart"
End Sub
Public Sub Event_MoveEnd()
    [A1] = "Event_MoveEnd"
    If TimerID <> 0 Then KillTimer 0, TimerID: TimerID = 0:
End Sub
Public Sub Event_Resize()
    [A1] = "Event_Resize"
    If TimerID <> 0 Then KillTimer 0, TimerID: TimerID = 0:
    OldWidth = Application.Width
    OldHeight = Application.Height
End Sub
 

jurassic pork

XLDnaute Occasionnel
Hello,
j'ai une nouvelle solution pour le rythmeur et cela m'a l'air fiable et sans plantage.
Voici ce que cela donne avec un séquencement de 100 ms qui en même temps :
1 - écrit l'heure dans la cellule A1 du classeur
2 - Appelle la macro TimerFunc qui incrémente un entier toutes les secondes :
VB:
Sub TimerFunc()
Static i As Integer
If i Mod 10 = 0 Then
   Debug.Print i \ 10
End If
i = i + 1
End Sub



Quand on fait de l'édition de cellule , cela ne plante pas, seulement les actions du Timer ne s'effectuent pas. De plus cela ne consomme presque pas de CPU.
Indice : pas D'API Timer et d'AddressOf et pas de code VBA tout est dans un Addin que j'ai créé. Je ne dis pas ce qu'il y a dans l'Addin sinon Raoul va me dire que c'est pourri et qu'il a mieux
Ami calmant, J.P
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
re
ben moi aussi dans mon addins ya pas de timer et autre
je ne suis pas fermé a ce point quand même
si c'est mieux je prends
ma foi
avec ta version originale j'ai mis le set timer a la place du app.ontime et ca colle
a condition de le détruire immedialtely
mais je veux bien voir cet addin
 

patricktoulon

XLDnaute Barbatruc
juste comme ça en passant
connaissiez vous ces deux écritures
VB:
Option Explicit
#If VBA7 Then
    Private Declare PtrSafe Function SetTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare PtrSafe Function KillTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long) As Long
#Else
    Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
#End If

Dim TimerID&
Dim h As LongPtr

Sub StartTimerV1()
    h = Application.hwnd
    TimerID = SetTimer(h, h, 1000, AddressOf TimerProcV1)

    Debug.Print "depart :"

End Sub

Sub stopTimerV1()
    If TimerID Then KillTimer h, TimerID: TimerID = 0
End Sub


Private Sub TimerProcV1()

    Debug.Print "TimerProc : Id =" & TimerID
End Sub

et la 2d

VB:
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function SetTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare PtrSafe Function KillTimer Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long) As Long
#Else
    Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
    Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
#End If

Dim IdV As Long
Dim h As LongPtr
Sub StartTimerV2()
    h = Application.hwnd
    SetTimer h, h, 1000, AddressOf TimerProcV2

    Debug.Print "depart :"

End Sub

Sub stopTimerV2()
    If IdV Then KillTimer h, IdV
End Sub

Private Sub TimerProcV2(ByVal hwnd As Long, ByVal uMsg As Long, _
                        ByVal idEvent As Long, ByVal dwTime As Long)
    IdV = idEvent
    Debug.Print "TimerProc :ID= " & idEvent
End Sub
 

Discussions similaires

Les cookies sont requis pour utiliser ce site. Vous devez les accepter pour continuer à utiliser le site. En savoir plus…