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:
1734412001934.png

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
les 3 versions fonctionnent très bien chez moi
je préfère la v 2 avec la souris pas par ce que c'est la mienne mais parce que les events sont distinguables
je sais quand je drag et quand ça s’arrête
avec celle de @jurassic pork j'ai du mal a savoir ou me servir que du 1er events
je crois que tu a ta réponse @dud2
j'ai changer les noms des pseudos events il sont plus explicites
VB:
'patricktoulon
#If VBA7 Then
    Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
                              ByVal idHook As Long, ByVal lpfn As LongPtr, ByVal hmod As LongPtr, ByVal dwThreadId As Long) As LongPtr

    Private Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" ( _
                              ByVal hHook As LongPtr) As Long

    Private Declare PtrSafe Function CallNextHookEx Lib "user32" ( _
                              ByVal hHook As LongPtr, ByVal nCode As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr

    Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
                                                ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

#Else
    Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
                              ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long

    Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
                              ByVal hHook As Long) As Long

    Private Declare Function CallNextHookEx Lib "user32" ( _
                              ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
                                        ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
#End If

' Constantes pour les événements souris
Const WM_MOUSEMOVE As Long = &H200
Const WM_LBUTTONDOWN As Long = &H201
Const WM_LBUTTONUP As Long = &H202

Dim MouseIsDown As Boolean ' Variable pour suivre l'état du bouton de la souris

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type MSLLHOOKSTRUCT
    pt As POINTAPI
    mouseData As Long
    flags As Long
    time As Long
    dwExtraInfo As LongPtr
End Type

Dim hHook As LongPtr
Dim oldleft As Long
' Fonction Hook pour la souris
Function MouseProc(ByVal nCode As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    'patricktoulon
    Dim mouseInfo As MSLLHOOKSTRUCT

    If nCode = 0 Then
        ' Copie des données de la souris
        CopyMemory mouseInfo, ByVal lParam, LenB(mouseInfo)

        ' Gestion des événements
        Select Case wParam
            Case WM_LBUTTONDOWN
                MouseIsDown = True
                  
            Case WM_MOUSEMOVE
                If MouseIsDown Then
                    On Error Resume Next
 If oldleft = 0 Then oldleft = Application.Left
            

                    If Application.Left <> oldleft Then oldleft = Application.Left: DragingWindow
                    On Error GoTo 0
                End If
                
            Case WM_LBUTTONUP
                If MouseIsDown Then
                    MouseIsDown = False
                    oldleft = 0
                    If Application.Left <> oldleft Then StopDragingWindow
                End If
        End Select
    End If

    ' Passer au prochain Hook
    MouseProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End Function

' Démarre le Hook
Sub StartMouseHook()
    Const WH_MOUSE_LL As Long = 14
    hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc, 0, 0)
    If hHook = 0 Then MsgBox "Impossible d'installer le Hook", vbCritical
End Sub

' Arrête le Hook
Sub StopMouseHook()
    On Error Resume Next
    If hHook <> 0 Then
        UnhookWindowsHookEx hHook
        hHook = 0
        MsgBox "Hook souris arrêté", vbInformation
    End If
End Sub

'les faux events
Public Sub DragingWindow()
    On Error Resume Next
    [a1] = "déplacement"
End Sub

Public Sub StopDragingWindow()
    On Error Resume Next
    [a1] = "arrêt du déplacement"
End Sub
il faudra faire les declaration win 64 bien sur
 

patricktoulon

XLDnaute Barbatruc
re
ce n'est absolument pas pour te critiquer @Dudu2 mais on a un exemple là manifeste de ce que je n'adhère pas dans tes codes
dans le module windowevent tu as
VB:
Private Sub Event_MoveStart()
    'Debug.Print "Event_MoveStart"
    If Not ClassGeneralEvent Is Nothing Then
        Call ClassGeneralEvent.App_WindowMoveStart
    End If
End Sub

Private Sub Event_MoveEnd()
    'Debug.Print "Event_MoveEnd"
    If Not ClassGeneralEvent Is Nothing Then
        Call ClassGeneralEvent.App_WindowMoveEnd
    End If
End Sub
et tu va chercher les pseudo events analogue dans la classe
pourquoi fait tu ça ? ca ne sert a rien ?
autant gérer ça dans les pseudo event du module et terminé
qu'est ce que tu pourrait bien faire dans la classe que tu ne puisse pas faire dans les sub du module ?
tu a un code event window parfaitement fonctionnel et tu lui ajoute une classe
1734445531934.gif

surtout que dans la classe ce ne sont pas non plus des vrais events je n'ai vu de raiseEvent nul part
 

patricktoulon

XLDnaute Barbatruc
a ben c'est sur l'adressof et surtout le hooking est restrictif je le sais
visiblement @Dudu2 a réussi a bien discerner le startdrag et le enddrag avec ta version initiale
je vais garder ces 3 versions mais sans module classe
c'est pas la peine
voir même tout mettre dans le thisworkbook
comme çà dans l'esprit de la classe a dudu2 je gère les events natifs et les pseudos au même endroit
si c'est possible avec le adressoff j'en doute
 

patricktoulon

XLDnaute Barbatruc
@jurassic pork
voila ce qui se passe si j'essaie de mettre un userform avec la tienne(version @Dudu2)
1734447319990.png

et après bien evidement crach excel bien sur et c'est normal

tu sais ce que ca fait quand tu ajoute un userform ou change quelque chose dans un autre module ou en ajoutant un la question ne se pose même pas ;)
d'après toi qu'est ce que cela fait ?;)
pourquoi mes argument ne sont plus bons ?;)
 
Dernière édition:

Nicolas JACQUIN

XLDnaute Impliqué
Supporter XLD
Bonjour à tous,
Peut-être plus simple, sans api et sans bug, en fouillant dans mais tiroir
Bonne continuation
Nico

VB:
Dim LastTop As Long
Dim LastLeft As Long

Sub SurveillePositionFenetre()
    Dim CurrentTop As Long
    Dim CurrentLeft As Long
    
    CurrentTop = Application.top
    CurrentLeft = Application.left

    If CurrentTop <> LastTop Or CurrentLeft <> LastLeft Then
        MsgBox "La position de la fenêtre Excel a changé !" & vbCrLf & _
               "Nouvelle position : " & vbCrLf & _
               "Top : " & CurrentTop & vbCrLf & _
               "Left : " & CurrentLeft, vbExclamation, "Alerte Position Fenêtre"

        LastTop = CurrentTop
        LastLeft = CurrentLeft
    End If
    
    Application.OnTime Now + TimeValue("00:00:01"), "SurveillePositionFenetre"
End Sub

Sub InitialiserSurveillance()
    LastTop = Application.top
    LastLeft = Application.left
    
    SurveillePositionFenetre
End Sub

Sub StopSurveillance()
    On Error Resume Next
    Application.OnTime Now + TimeValue("00:00:01"), "SurveillePositionFenetre", Schedule:=False
End Sub
 

Dudu2

XLDnaute Barbatruc
Bonjour @Nicolas JACQUIN,
Ok mais ça c'est la solution du début qui pose des petits problèmes:
- La résolution du OnTime c'est 1 seconde, ok c'est pas immense mais c'est une petite latence
- Il n'y a pas de détection si l'Application.Ready = False (si tu es en saisie de cellule par exemple)
- Il n'y a pas d'évènement avant, et moi je m'en sers dans une application pour ne pas laisser trainer des trucs
Move.gif
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
je suis curieux de savoir si ce que j'ai fait dans mon complément vba indenter marcherait pour ca
et finalement mettre ça dans un complément
et que donc le rouli n'affecterait que le complément
et que par contre les éventuelles erreurs dans le code d'un des classeurs ouverts n'affecterait pas le complement
 

Dudu2

XLDnaute Barbatruc
Autre chose...
Comment obternir l'Object UserForm à partir de son nom ou son Caption.
J'ai ça mais ça ne marche pas à 100%. Des effets induits étranges.
VB:
Set UserForm = CallByName(UserForms, "Add", VbMethod, UserFormName)
 

Dudu2

XLDnaute Barbatruc
Et j'aimerais aussi récupérer une Window par son Handle.

Le truc c'est que lorsqu'on déplace une fenêtre, elle ne devient pas active si on n'a pas cliqué dessus préalablement. De sorte que je ne sais pas quelle fenêtre est en Move.

VB:
For Each w In Windows
    If w.Hwnd = h Then MsgBox w.Caption
Next w
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
Autre chose...
Comment obternir l'Object UserForm à partir de son nom ou son Caption.
J'ai ça mais ça ne marche pas à 100%. Des effets induits étranges.
VB:
Set UserForm = CallByName(UserForms, "Add", VbMethod, UserFormName)
VB:
 UserFormName = "UserForm3"
    Set uf = VBA.UserForms.Add(UserFormName)
    uf.Show
Attention!!! passer par la collection vba.UserForms.Add c'est instancier des classes userform
exemple iici je l'affiche 2 fois
Code:
Sub testd()
    UserFormName = "UserForm3"
    Set uf = VBA.UserForms.Add(UserFormName)
    Set uf2 = VBA.UserForms.Add(UserFormName)
    uf.Show 0
    uf2.Show 0
End Sub

pour ta 2d question là c'est obscure
reformule
 
Dernière édition:

Discussions similaires

Statistiques des forums

Discussions
315 088
Messages
2 116 089
Membres
112 658
dernier inscrit
doro 76