Mouse Wheel Hook (faire défiler le contenu d'une combobox/listbox avec la roulette)

  • Initiateur de la discussion Compte Supprimé 979
  • Date de début

Dudu2

XLDnaute Barbatruc
Bonjour,

Pour une certaine application, j'ai été amené à autoriser le Scroll dans une ComboBox.
Le problème de ces Hook Mouse, c'est que si on sort du cadre de la ComboBox et qu'on n'a pas défait le Hook, les scrolls d'Excel et des applications ne fonctionnent plus, d'où la nécessité de bien contrôler ce système.

Pour info, je me suis livré à quelques essais et d'une manière extrêmement simple et avec 1 seul évènement on peut avoir un contrôle à 100% du Scroll ComboBox à condition d'accepter qu'il ne commence que lorsqu'on clique sur la flèche droite de la ComboBox faisant apparaitre la drop down list (plutôt que par un "Mouse Over"), ce qui est cohérent avec la logique de ce Control. De toutes façons l'évènement ComboBox1_MouseMove() n'affiche pas la drop down list donc on n'y gagne rien.

Le fichier ci-dessous affiche 2 UserForms.
- Le 1er UserFormTrace donne des infos sur les évènements liés à la ComboBox et le setting ON/OFF du Hook Mouse
- Le 2ème, UserFormModèle, est celui qui donne la manière de gérer le Scroll dans le UserForm sur la base du Module_HookMouse. qui est la bidouille savante de Hook Mouse qu'on trouve un peu partout.

Malheureusement, pour la ListBox, il n'y a pas de moyen sûr à 100% de garantir la sortie du Hook Mouse quand on sort de la ListBox.
Le seul moyen efficace d'activer le Scroll sur la ListBox c'est d'utiliser l'évènement ListBox1_MouseMove().
Le seul moyen pas très efficace de désactiver le Scroll quand on sort de la ListBox c'est d'utiliser l'évènement UserForm_MouseMove().
Hélas, si les espaces entre la ListBox et le UserForm sont faibles ou que la souris est bougée rapidement, l'évènement UserForm_MouseMove() ne se déclenche pas.
Il faut donc sécuriser la désactivation du Scroll sur les évènements ListBox1_Exit() et UserForm_QueryClose().
Mais entre-temps, si on n'est pas passé par l'évènement UserForm_MouseMove(), le Scroll des applications, y compris Excel (UserForm ouvert en vbModeless), ne fonctionne plus.

Il y aurait une façon de davantage sécuriser la sortie de ListBox, c'est de tester la position du curseur par rapport à la ListBox ou du UserForm à intervalles réguliers ou lors des Scroll. Pas forcément facile.

Edit: Je m'y suis quand même aventuré histoire de vérifier ce que ça donne. Faut voir le message 88 pour les fichiers qui font ça !
 
Dernière édition:

Gégé-45550

XLDnaute Accro
Bonjour,
Je dispose de la version Excel365 64 bits.
Je voulais tester votre fichier mais j'obtiens toujours une erreur "Incompatibilité de type" sur la ligne :
VB:
plHooking = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, GetWindowLong(hWnd, GWL_HINSTANCE), 0)

Une idée du pourquoi ?
Merci et bravo pour votre travail et les explications lumineuses.
 

Lorenzini

XLDnaute Occasionnel
Bonjour,

Pour une certaine application, j'ai été amené à autoriser le Scroll dans une ComboBox.
Le problème de ces Hook Mouse, c'est que si on sort du cadre de la ComboBox et qu'on n'a pas défait le Hook, les scrolls d'Excel et des applications ne fonctionnent plus, d'où la nécessité de bien contrôler ce système.

Pour info, je me suis livré à quelques essais et d'une manière extrêmement simple et avec 1 seul évènement on peut avoir un contrôle à 100% du Scroll ComboBox à condition d'accepter qu'il ne commence que lorsqu'on clique sur la flèche droite de la ComboBox faisant apparaitre la drop down list (plutôt que par un "Mouse Over"), ce qui est cohérent avec la logique de ce Control. De toutes façons l'évènement ComboBox1_MouseMove() n'affiche pas la drop down list donc on n'y gagne rien.

Le fichier ci-dessous affiche 2 UserForms.
- Le 1er UserFormTrace donne des infos sur les évènements liés à la ComboBox et le setting ON/OFF du Hook Mouse
- Le 2ème, UserFormModèle, est celui qui donne la manière de gérer le Scroll dans le UserForm sur la base du Module_HookMouse. qui est la bidouille savante de Hook Mouse qu'on trouve un peu partout.

Malheureusement, pour la ListBox, il n'y a pas de moyen sûr à 100% de garantir la sortie du Hook Mouse quand on sort de la ListBox.
Le seul moyen efficace d'activer le Scroll sur la ListBox c'est d'utiliser l'évènement ListBox1_MouseMove().
Le seul moyen pas très efficace de désactiver le Scroll quand on sort de la ListBox c'est d'utiliser l'évènement UserForm_MouseMove().
Hélas, si les espaces entre la ListBox et le UserForm sont faibles ou que la souris est bougée rapidement, l'évènement UserForm_MouseMove() ne se déclenche pas.
Il faut donc sécuriser la désactivation du Scroll sur les évènements ListBox1_Exit() et UserForm_QueryClose().
Mais entre-temps, si on n'est pas passé par l'évènement UserForm_MouseMove(), le Scroll des applications, y compris Excel (UserForm ouvert en vbModeless), ne fonctionne plus.

Il y aurait une façon de davantage sécuriser la sortie de ListBox, c'est de tester la position du curseur par rapport à la ListBox ou du UserForm à intervalles réguliers.
C'est faisable, malgré la difficulté initiale de convertir la position du UserForm en Pixels pour la comparer à la position de la souris, mais ça induit une complexité qui élève la maintenance au rang de prise de tête, donc, mieux vaut ne pas s'y aventurer.
Bonjour, Waw quel boulot ! J'ai testé et çà marche nickel !
 

Dranreb

XLDnaute Barbatruc
Une idée du pourquoi ?
Oui, c'est vraisemblablement dû à ce que le second paramètre de SetWindowsHookEx n'y a pas été déclaré du bon type de donnée
L'expression AddressOf LowLevelMouseProc est de type LongLong sur office 64bit, type assumé automatiquement par le méta-type LongPtr sur de tels systèmes
 

Dranreb

XLDnaute Barbatruc
Je vois une incohérence dans la définition de Private Function GetHookStruct(ByVal lParam As Long) As MSLLHOOKSTRUCT.
Il faudrait logiquement Private Function GetHookStruct(ByVal lParam As LongPtr) As MSLLHOOKSTRUCT
puisque lParam y est spécifié comme argument du CopyMemory
 

Gégé-45550

XLDnaute Accro
Bonjour,
Je dispose de la version Excel365 64 bits.
Je voulais tester votre fichier mais j'obtiens toujours une erreur "Incompatibilité de type" sur la ligne :
VB:
plHooking = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, GetWindowLong(hWnd, GWL_HINSTANCE), 0)

Une idée du pourquoi ?
Merci et bravo pour votre travail et les explications lumineuses.

Finalement, après quelques tests, le remède consiste as modifier le type Long en LongPtr et tout rentre dans l'ordre
VB:
Dim hWnd As LongPtr
    Dim hWnd_App As LongPtr
    Dim hWnd_Desk As LongPtr
    Dim hWnd_Sheet As LongPtr
    Dim hWnd_UserForm As LongPtr
Si ça peut être utile.
 

Dranreb

XLDnaute Barbatruc
À ma connaissance il n'y a pas lieu de typer les handle Windows As LongPtr, puisque ce ne sont pas des adresses.
Il va de soit que si ma connaissance est correcte, il n'y pas lieu non plus de les typer ainsi dans les instructions Declare.
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
Bonjour
je vous propose ma version compatible 32/64 bits
et travaillant sur x listbox ou x combobox ou x frames avec le même code
fichier en exemple avec 3 userforms
c'est pas la peine pour les listbox d'avoir le focus le move déclenche le hook
pour les combo c'est le dropbutton_click
pour les frame c'est aussi le move qui déclenche le hook
autrement il suffit d’être dessus avec la souris
;)
le move dans l'userform termine n’importe quel hook

demo4.gif
 

Pièces jointes

  • molette souris pour listebox2 et frame combobox .xlsm
    40.5 KB · Affichages: 25
Dernière édition:

Dudu2

XLDnaute Barbatruc
Finalement, après quelques tests, le remède consiste as modifier le type Long en LongPtr et tout rentre dans l'ordre
Ok, j'ai modifé les 2 fichiers du post #61 qui sont mis à jour avec LongPtr
Merci pour le retour.
 
Dernière édition:

Dranreb

XLDnaute Barbatruc
Est-ce que quelqu'un finira par comprendre un jour que dans le principe général LongPtr c'est à utiliser partout, dans tous le code, et pas seulement dans les Declare, mais uniquement pour des variables destinée à accueillir des adresses et pas autre chose.
Ce n'est pas un truc obscur à mettre aveuglément partout pour que ça marche mais uniquement dans les Déclare
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
Bonjour @Dranreb
pour la faire courte longptr c'est pour les howner ou handle comme vous voulez(lecture/écriture)
ça n'est pas plus compliqué que cela

exemple ecriture
Private Declare PtrSafe Function FindWindow Lib "USER32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
le résultat est un longlong
exemple lecture et ecriture
Private Declare PtrSafe Function GetWindowLong Lib "USER32" Alias "GetWindowLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
on cherche un longlong et restitue un longlong
 

Dranreb

XLDnaute Barbatruc
Non, c'est pour le adresses, pas les handle, Il n'y a pas de raison: un handle c'est simplement un indice dans un tableau d'adresses géré dans et par Windows.
 

Dudu2

XLDnaute Barbatruc
Et simplement ref à la doc ?
LongPtr is not a true data type because it transforms to a Long in 32-bit environments, or a LongLong in 64-bit environments. Using LongPtr enables writing portable code that can run in both 32-bit and 64-bit environments. Use LongPtr for pointers and handles.
 

patricktoulon

XLDnaute Barbatruc
re
si tu préfère l’appeler comme ça
entre nous par exemple findwindow ne cherche pas une adresse mais bien un handle de fenêtre

pour lever toute ambiguïté
Fonction FindWindow

Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Versions :
. Windows 95/98 : Oui
. Windows NT : A partir de 3.1
. Windows 2000/XP : Oui

Description :
Cette fonction permet de rechercher une fenêtre. Elle ne fonctionne que sur les fenêtres principales (pas les filles des MDI).
lpClassName est le nom de la classe qui identifie la fenêtre (rarement connu et donc souvent égal à une chaîne vide).
lpWindowName est le titre (complet) de la fenêtre.
La fonctionne retourne alors le handle de la fenêtre, en cas d'échec, elle renvoie 0.
 

Dranreb

XLDnaute Barbatruc
Oui j'avais déjà lu ce genre de 'and handles' à la fin de de cette phrase mais je doute de son exactitude pour trois raisons :
1) — je n'ai jamais eu de retour d'un demandeur me disant qu'un code où j'avais laissé As Long dans un Declare pour un handle ne fonctionnait pas
2) — L'application Excel possède aussi en propriété un handle Windows, et son type est resté As Long.
3) — Je soupçonne office 32bits pour permettre le fonctionnement de code fonctionnant sur matériel à bus adresse de 64bits, de remplacer dans le paramétrage des procédures toutes les adresses réelles en handles Windows qui les représentent. Si cette hypothèse est exacte il est nécessaires que les handles soient restés à 32bit, si elle ne l'est pas, j'aimerais bien qu'on me dises alors comment il fait ! Il ne s'abstient quand même pas, j'espère, d'utiliser toute région de mémoire d'adresse située au delà de la capacité d'adressage sur 32 bits, si ?
 
Dernière édition:

Statistiques des forums

Discussions
312 104
Messages
2 085 349
Membres
102 869
dernier inscrit
radyreth