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.

Dudu2

XLDnaute Barbatruc
La 2ème question est relative à la méthode Hook des évènements de la fenêtre.

Il faut savoir que ce n'est pas parce que tu déplaces une fenêtre que cette fenêtre est la fenêtre active.
Si tu la déplaces sans cliquer pour l'activer, tu reçois l'évènement avec un Handle dans la Callback Function qui est celui de la fenêtre concernée par le déplacement que tu as besoin de connaître si tu fais des traitements associés (positionnement UserForm ou autre). Ce n'est pas forcément l'ActiveWindow.

Donc l'idée est à partir de ce Handle de retrouver la Window correspondante, d'où la boucle sur les Windows.
Dans ce fichier adapté, je passe la fenêtre concernée par le déplacement à l'évènement Move Start/End dans la Classe des évènements.
 

Pièces jointes

  • Window Events and Move.xlsm
    36.1 KB · Affichages: 1

jurassic pork

XLDnaute Occasionnel
Hello,
Dudu2 est-ce que ça t'intéresse d'avoir l'équivalent d'un Application.OnTime mais avec un délai en millisecondes et tout ceci sans utiliser de Timer système et d'addressOf ? Cela utilise une classe qui crée une autre instance d'Excel avec un classeur vide qui lui injecte une macro qui fait le timer dans une boucle avec un sleep et on récupère les événements dans le classeur principal . Quand on a fini d'utiliser le timer en détruisant la classe , on ferme l'autre instance d'Excel.
stdTimer.gif

Quand on arrête le Timer cela met un certain temps car il faut fermer l'autre instance Excel créée.

Ami calmant, J.P
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
et ben je confirme mon idée était bonne
un do/loop sur un timer de vba dans un xla n'a aucune incidence sur le classeur qui l'utilise
et @jurassic pork a confirmé en utilisant simplement un classeur dynamique

je viens de créer le xla le nom du projet c'est "Xtimer"

1734510366383.png

comme vous le voyez c'est une simple function public dans le module du xla


comme je veux l'utiliser dans le classeur avec le principe de @jurassic pork et remplacer les deux application.ontime
je vais y ajouter la référence dans les ref
et je remplace les app.ontime par timerx et le nombre de milisec
1734510766249.png


et ben voila le resultat
j'ai remplacer les debug pour mettre le message en A1
si vous regarder mon curseur dans la barre de titre vous ne le verrez plus tourner
demo1.gif


conclusion si vous avez un xlam d'utilitaire que vous utilisez tout le temps ben rajoutez lui cette fonction et vous aurez un timer avec un do/loop sur un timer qui n'impacte pas le classeur qui l'utilise
j'avais donc raison hier en même temps je le savais ce phénomène je l'avais remarqué dans mon complément vba Indenter Interface qui travaille sur des grand texte

punaise une chose aussi simple 🤣 ;)
 

Dranreb

XLDnaute Barbatruc
Bonjour.
Pour des exécutions périodiques dans un UserForm j'ai ce court module de classe Rythmeur :
VB:
Option Explicit
Rem. Cette classe a pour but de permettre un traitement périodique dans un module objet.
Event Intervient(ByVal Tic As Long)
Rem. Évènement. Se produit périodiquement.
'           Tic: Nombre de millisecondes écoulées depuis le lancement.
Private Idt As Long, Tac As Long
Public Sub Lancer(Optional ByVal FréqHz As Double = 25#)
Rem. Méthode. Démarrage. Préalablement stoppé s'il était déjà lancé.
'           FréqHz: Fréquence en Hertz des déclenchements de Intervient.
'              Facultatf. Fréquence conseillée d'une animation assumée si omis.
   If Idt > 0 Then XRythmeur.StopperRythmeur Idt
   Idt = XRythmeur.IdtRythmeurLancé(Me, FréqHz, Tac)
   End Sub
Public Sub Stopper()
Rem. Méthode. Arrêt. Sans effet s'il n'est pas lancé.
   If Idt <= 0 Then Exit Sub
   XRythmeur.StopperRythmeur Idt: Idt = 0
   End Sub
Public Function Lancé() As Boolean
Rem. Propriété en lecture seule. Condition Rythmeur actif.
   Lancé = Idt > 0
   End Function
Public Sub Actionner(ByVal Tic As Long)
Rem. Méthode. Déclenche l'évènement Intervient.
'       Son invocation par le module objet détenteur n'est pas pertinente.
'       Elle est réservée au dispositif de service XRythmeur.TimerProc, dont l'exécution
'       pérodique est instaurée par une invocation de la méthode Lancer.
   RaiseEvent Intervient(Tic - Tac)
   End Sub
Elle nécessite un module standard de service XRythmeur :
VB:
Rem. Module de servive effecteur de tâches des objets Rythmeur.
'    NE PAS UTILISER EN PROGRAMMATION APPLICATIVE.
Option Explicit
               #If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32.dll" () As Long
Private Declare PtrSafe Function SetTimer Lib "user32.dll" _
   (ByVal Hwnd As Long, ByVal Idt As Long, ByVal DuréeMS As Long, ByVal AdrSub As LongPtr) As Long
Private Declare PtrSafe Function KillTimer Lib "user32.dll" _
   (ByVal Hwnd As Long, ByVal Idt As Long) As Long
               #Else
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long
Private Declare Function SetTimer Lib "user32.dll" _
   (ByVal Hwnd As Long, ByVal Idt As Long, ByVal DuréeMS As Long, ByVal AdrSub As Long) As Long
Private Declare Function KillTimer Lib "user32.dll" _
   (ByVal Hwnd As Long, ByVal Idt As Long) As Long
                  #End If
Private TRythmeurs() As Rythmeur, Idt As Long
Public Function IdtRythmeurLancé(ByVal Ryth As Rythmeur, ByVal FréqHz As Double, ByRef Tac As Long) As Long
   Dim P As Long, M As Long
   On Error Resume Next: M = UBound(TRythmeurs): If Err Then ReDim TRythmeurs(1 To 1): M = 1
   On Error GoTo 0
   For P = 1 To M: Idt = Idt Mod M + 1
      If TRythmeurs(Idt) Is Nothing Then Exit For
      Next P
   If P > M Then ReDim Preserve TRythmeurs(1 To P): Idt = P
   Set TRythmeurs(Idt) = Ryth
   SetTimer ThisWorkbook.Windows(1).Hwnd, Idt:=Idt, DuréeMS:=Int(1000 / FréqHz + 0.5), AdrSub:=AddressOf TimerProc
   Tac = GetTickCount: IdtRythmeurLancé = Idt
   End Function
Public Sub StopperRythmeur(ByVal Idt As Long)
   On Error Resume Next
   KillTimer ThisWorkbook.Windows(1).Hwnd, Idt
   Set TRythmeurs(Idt) = Nothing
   End Sub
Private Sub TimerProc(ByVal Hwnd As Long, ByVal uMsg As Long, ByVal Idt As Long, ByVal Tic As Long)
   On Error Resume Next
   TRythmeurs(Idt).Actionner Tic
   If Err Then KillTimer Hwnd, Idt
   End Sub
 

jurassic pork

XLDnaute Occasionnel
Hello Patrick,
faudrait peut-être y ajouter un sleep (pas kangourou) à ta boucle sinon ça risque de consommer du CPU pour rien. Et si on veut utiliser une autre fonction du Xlam en même temps que le timer est-ce possible ?

[EDIT] oops le TimerX ce n'est pas un timer mais une tempo comme le sleep
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
Bonjour @jurassic pork
1° les sleep(api sleep) gèlent les procc(tous même ceux en arrière plan ) le doevents suffit
Dans nos fougueuses discussions nous avons démontré qu'il n'y avait pas que des avantages a l'utiliser dans le cadre de boucle d'attentes

2° ben bien sur que non pendant le timer seuls les procc en arrière plans tournent avec le doevents
sinon le timer n'a pas de sens si on pouvait passer par dessus non ?

alors peut être dans la logique de multi instanciation mettre ce timer dans un module classe dans le xlam
et s'en servir em multi instanciation mais attention il y a quand même un écart qui se fait

il faudrait que j'essaie pour voir si on peut instancier x timer en un seul appel
mais on sort un peu de la vocation d'un timer d'attente là on est plutôt dans un Rythmer
ce qui n'a rien a voir et que donc nécessite un appel start et un appel stop
 

jurassic pork

XLDnaute Occasionnel
Hello Dudu2,
En pièce jointe un classeur avec la macro de démo. La classe utilisée est stdTimer de l'excellent sancarn qui a réalisé toute une série de classes dans son projet stdvba. Dans la démo j'ai bricolé une classe qui je ne sais pas si elle est utile. En gros le principe de son timer. Dans un classeur distant créé on injecte un code qui a une boucle qui génère un changement sur la feuille 1 de ce classeur ce qui génère un événement de changement dans la feuille et dans l'événement de cette feuille on génère un événement Tick et on peut appeler une callBack. Je me demande si tout cela ne serait pas faisable dans un xlam puisque dedans on a des feuilles aussi.
Ami calmant, J.P
 

Pièces jointes

  • DémoStdTimer.xlsm
    39.9 KB · Affichages: 4

patricktoulon

XLDnaute Barbatruc
re
Bonjour @Dudu2 les screenshot sont des modes emplois que tu dénigre bien trop souvent
si tu ne suis pas ce qui y est démontré ça ne marchera pas
j'entends par là que tu ne m'écoute jamais
et que tes appels direct (façon @Dudu2) en faisant appel au workbooks("xtimer.xlam").timerx (comme tu a l'habitue de faire)ne marchera pas
tu va faire tourner le timer mais dans le xla tout court tu n'aura aucune attente après l'appel
je te connais je sais comment tu fonctionne (MAIS TU N ÉCOUTE JAMAIS) 🤣 🤣 🤣

1 xla doit être référencé dans le classeur qui l'utilise si on veux utiliser des fonctions génériques travaillant avec des variables

la fonction est simple
tu ouvre un classeur vierge et ajoute un module
dans ce module
VB:
Public Function TimerX(c As Long)
Dim t
t = Timer
Do While Timer - t < c / 1000: DoEvents: Loop
End Function
tu n'oublie SURTOUT PAS de nommer ton vbaproject "Xtimer" par exemple
et tu le sauve en xlam

tu ouvre ton classeur dans le quel tu veux l'utiliser
dans les options tu va cocher xtimer
ensuite dans vbe tu va a outils/référence/et tu coche xtimer
parti de la c'est simple dans tes macro tu fait
timerx 100 : lasubaexecuter '(sans guillemet c'est un appel)
et tu a un beau sleep sans gèle ;)
 

patricktoulon

XLDnaute Barbatruc
oui mais ton timer c'est plutôt une temporisation.
(je répète) c'est un sleep sans gèle et sans buffer mémoire pour le classeur utilisateur
ben c'est ce dont a besoins ton principe sinon sans ça c'est le crach excel
faut il que je vous en re fasse la démo ?

1° l'api settimer a besoin d'un looping en addressoff(ce qui vous cause des petits soucis )

2° le timer de vba c'est simplement une fonction interne qui (donne l'instant en numérique depuis 31/12/1899 (je crois)et donc un do/loop est necessaire pour controler le temps et l’arrêter MAIS EST BLOQUANT DANS L INSTANCE VBA DANS LE QUEL IL EST UTILISE)
bien sur reduit a peau de chagrin avec un doevents interne dans la boucle mais quand même
sauf que le doevents permet que les procc en cour continue à s’exécuter par intermittence
mais ne benifiecie pas a la boucle sur timer


3° le sleep (api)gèle tous les procc

mon principe est simple le buffer mémoire est subit par le classeur 2
le classeur 2 étant (roulement de tambour) le xlam

c'est une logique que j'ai découvert par hasard avec mon complément vba indenter interface
qui travaille sur des texte(des code vba) de très grande taille dans des boucles ne génère absolument rien comme latence dans tout les autres classeurs
en même temps tu l'a decouvert toi aussi aujourd'hui en proposant ton classeur dynamique donc tu a parfaitement compris le principe
sérieux une petite fonction comme ça dans un xla (même existant ça coute rien )
pas d'api ,pas d'adressoff ,pas de sleep gelant
c'est gagnant,gagnant,gagnant
espirituous en fromagoum et panoum !!! Ammenne
 
Dernière édition:

Discussions similaires

Statistiques des forums

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