GUIRegisterMsg

De Wiki AutoIt Français
Aller à : navigation, rechercher

Traduction et adaptation du tutoriel anglophone : Tutorial GUIRegisterMsg

Ceci est un court tutoriel sur GUIRegisterMsg, l'une des fonctions les plus mystérieuses de AutoIt pour les nouveaux arrivants. Certains détails déplairont sûrement aux puristes, mais il convient bien pour des amateurs comme moi !

Windows fonctionne en envoyant des messages sur tout ce qui se produit dans son environnement, de sorte que chaque élément de l’environnement peut savoir ce qui s’y passe. Un exemple de message est l'événement $GUI_EVENT_CLOSE, qui se produit lorsque nous quittons une interface graphique - mais il y en a beaucoup, beaucoup d’autres : Changement de focus, changement d’état, clique de souris, déplacement de souris, touches appuyées, etc.

GUIRegisterMsg permet d'intercepter certains des messages envoyés par les interfaces graphiques que nous avons créées, afin que nous puissions savoir qui a envoyé quoi. Ainsi quand vous voyez une ligne de script comme GUIRegisterMsg($WM_COMMAND, "MY_WM_COMMAND"), cela se traduit par « Chaque fois qu’un message WM_COMMAND est envoyé par mon interface graphique, interceptez-le SVP et envoyez-le à la fonction MY_WM_COMMAND de ce script. ». Le type du message est spécifié par la constante $WM_COMMAND, constante stockée dans le fichier include WindowsConstant.au3. Bien entendu, vous pouvez utiliser d’autres types de messages (comme $WM_NOTIFY par exemple) et appeler la fonction que vous voulez.

La fonction que vous définissez pour être appelée par GUIRegisterMsg doit avoir au plus 4 paramètres - $hWnd, $iMsg, $wParam, $lParam - car c’est ce qu’utilise Windows pour envoyer ses messages à l’environnement. La plupart du temps, ces paramètres se rapportent à ce qui suit :

  • $hWnd : Handle de la GUI émettrice du message
  • $iMsg : Code du message envoyé
  • $wParam, $iParam : Détails de la composition du message. Ils varient selon le type du message et peuvent être trouvés via MSDN. Ils contiennent souvent le ControleId du contrôle ayant déclenché l’appel à la fonction ainsi que le message envoyé.

Penchons-nous sur quelques exemples :

#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

$hGUI_hW    = GUICreate("Essai", 150, 100)
$iInput1_Id = GUICtrlCreateInput("Input1", 20, 20, 50)
$iInput2_Id = GUICtrlCreateInput("Input2", 80, 20, 50)
$iFermer_Id = GUICtrlCreateButton("Fermer", 45, 50, 55, 25)
GUISetState()
GUIRegisterMsg($WM_COMMAND, "MY_WM_COMMAND")

While 1
  Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE, $iFermer_Id
      ExitLoop
  EndSwitch
  Sleep(50)
WEnd

Func MY_WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  Local $iIDFrom = BitAND($wParam, 0xFFFF) ; LoWord : Fournit le ControleId du contrôle émetteur du message.
  Local $iCode = BitShift($wParam, 16)     ; HiWord : Fournit le sous message envoyé.
  If $iCode = $EN_CHANGE Then ; Si le message est « modification ».
    Switch $iIDFrom ; Choix des contrôles à traiter.
      Case $iInput1_Id
        ConsoleWrite("$iInput1_Id" & @CRLF) ; Traitement désiré.
      Case $iInput2_Id
        ConsoleWrite("$iInput2_Id" & @CRLF) ; Traitement désiré.
    EndSwitch
  EndIf
EndFunc ; ==> MY_WM_COMMAND

Ici nous voulons savoir si les contenus de deux contrôles de saisie ont été modifiés.

Nous avons d'abord intercepter les messages $WM_COMMAND de notre interface graphique en utilisant GUIRegisterMsg. Dans ce cas précis nous pouvons ignorer les informations sur l’interface graphique, $hWnd, et le message envoyé, $iMsg. Si nos contrôles sont correctement identifiés, ils ne peuvent provenir que de notre GUI. Dans certains cas cependant, ces informations sont essentielles.

Pour savoir si une saisie a été modifiée, nous devons tester la valeur $EN_CHANGE d’un sous message contenu dans $wParam. Puis il nous faut déterminer quel contrôle a envoyé le message, ce qui est aussi obtenu par un sous message de $wParam.

Les opérateurs Bit* nous permettent d’extraire ces sous-messages :

 Local $iIDFrom = BitAND($wParam, 0xFFFF) ; LoWord : Fournit le ControleId du contrôle émetteur du message. 
 Local $iCode = BitShift($wParam, 16)     ; HiWord : Fournit le sous message envoyé.

Nous savons maintenant quel contrôle a envoyé le message et quel est son contenu. A nous de faire les traitements voulus dans ce cas.

Voici un autre exemple :

#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>

Global $bDblClk
$hGUI_hW = GUICreate("Fenêtre de test", 340, 240, -1, -1)
$iLV_Id  = GUICtrlCreateListView("Identifiant majeur|Catégorie identifiant|Groupe fonctionnel" _
           , 2, 2, 394, 150, -1, $LVS_EX_FULLROWSELECT + $LVS_EX_GRIDLINES)
GUICtrlCreateLabel("- Faire un double clic dans la liste puis cliquer sur le bouton OK." , 10, 160)
GUICtrlCreateLabel("- Faire un simple clic dans la liste puis cliquer sur le bouton OK.", 10, 175)
$ibOK_Id = GUICtrlCreateButton("Ok", 75,200, 70)
$ibKo_Id = GUICtrlCreateButton("Fermer", 200, 200, 70)
GUICtrlCreateListViewItem("1|2|3", $iLV_Id)
GUICtrlCreateListViewItem("4|5|6", $iLV_Id)
GUICtrlCreateListViewItem("7|8|9", $iLV_Id)
GUISetState()

GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

While True
  Switch GUIGetMsg()
    Case $ibOK_Id
      MsgBox(0, "", "$bDblClk = " & $bDblClk)
      $bDblClk = False
    Case $GUI_EVENT_CLOSE, $ibKo_Id
      ExitLoop
  EndSwitch
WEnd
GUIDelete()

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
  Local $tNMHDR = DllStructCreate("hwnd;int;int", $lParam)
  If @error Then Return

  If DllStructGetData($tNMHDR, 1) = GUICtrlGetHandle($iLV_Id) Then ; S’il s’agit de notre ListView.
    If DllStructGetData($tNMHDR, 3) = $NM_DBLCLK Then $bDblClk = True ; Si l’événement est « double clic ».
  EndIf
  $tNMHDR = 0 ; Ce n’est pas vraiment nécessaire mais ça ne fait pas de mal !

  Return $GUI_RUNDEFMSG ; Ceci indique à AutoIt de traiter le message lui-même.
EndFunc ; ==> WM_NOTIFY

Cette fois nous avons capturé le message $WM_NOTIFY qui nous informe sur les événements comme les clics de souris. Ici nous recherchons un double clic sur une ListView au sein de notre interface graphique. Comme précédemment nous utilisons GUIRegisterMsg pour intercepter le message. De nouveau nous pouvons négliger l’identifiant de la GUI et le message principal pour nous concentrer sur le contrôle proprement dit et son message.

Pour identifier l’événement il nous faut travailler sur le paramètre $lParam, paramètre qui est de type Struct. AutoIt permet d’obtenir assez aisément l’identifiant handle du contrôle et son message grâce à la fonction DllStructGetData.

Lorsque les conditions de test sont réunis, nous positionnons un drapeau à Vrai. Pourquoi ? Continuez la lecture et vous le saurez !

Quand nous avons fini de traiter le message intercepté, celui-ci reste disponible pour d’autres captures éventuelles par d’autres applications. Vous pourriez cependant vouloir interdire tout autre traitement du même message. Quitter la fonction avec un Return $GUI_RUNDEFMSG indique à AutoIt d’exécuter son propre gestionnaire interne de transfert du message.

Revenons maintenant sur l'utilisation d'un drapeau dans le deuxième exemple. Si vous lisez le fichier d'Aide pour GUIREGISTERMSG vous verrez la chose suivante ( mais en anglais ;) ) : "Avertissement : le blocage des fonctions utilisateur qui exécutent des messages Windows avec des commandes comme "Msgbox()" pouvant provoquer un comportement inattendu, le retour au système devrait être aussi rapide que possible !!!"

En fait, si vous passez trop de temps dans votre fonction de gestion de message, le reste du système presse derrière et devient instable. Il faut donc sortir de la fonction aussi vite que possible et certainement ne jamais créer quelque chose qui attend une entrée de l’utilisateur.

Il y a plusieurs possibilités pour exécuter quelque chose de long et compliqué et cependant quitter le gestionnaire rapidement. En voici deux : Valorisation d'un indicateur et activation d'une commande factice. Nous pouvons alors tester l’état du drapeau ou de la commande factice dans notre boucle d'inactivité et exécuter le code bloquant à cet endroit sans affecter le gestionnaire.

Voici un exemple de chacune des deux méthodes :

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

; Quelle que soit la méthode utilisée, il faut déclarer la commande factice
; ou le drapeau comme étant une variable globale.
Global $hClicGauche, $fClicDroit = False

GUICreate("Tester les clics gauche et droit de votre souris.")

; Création d’un contrôle fictif pour le gestionnaire d'action.
$hClicGauche = GUICtrlCreateDummy()

GUISetState()

;  Enregistrement d’une association « Message Windows / Fonction ».
GUIRegisterMsg($WM_LBUTTONUP, "_WM_LBUTTONUP") ; Pour clic gauche.
GUIRegisterMsg($WM_RBUTTONUP, "_WM_RBUTTONUP") ; Pour clic droit.

While 1
  Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
      ExitLoop
    Case $hClicGauche ; Test de l'activation du contrôle fictif.
      ; Quand le contrôle fictif est activé, le code s’exécute.
      MsgBox(0, "Clic", "CLIC GAUCHE.") ; Traitement désiré.
  EndSwitch

  If $fClicDroit = True Then ; Test de l'état du drapeau.
    ; Quand le drapeau est positionné à True, le code s’exécute.
    MsgBox(0, "Clic", "CLIC DROIT.") ; Traitement désiré.
    ; Ne pas oublier de réinitialiser le drapeau !
    $fClicDroit = False
  EndIf
WEnd

Func _WM_LBUTTONUP($hWnd, $iMsg, $wParam, $lParam)
  ; Activation du contrôle fictif.
  GUICtrlSendToDummy($hClicGauche)
EndFunc

Func _WM_RBUTTONUP($hWnd, $iMsg, $wParam, $lParam)
  ; Positionnement à True du drapeau.
  $fClicDroit = True
EndFunc

J’espère que ce petit tutoriel vous aura facilité la compréhension de GUIRegisterMsg. Pour poursuivre, je vous suggère la lecture des nombreux exemples du forum, d’en examiner le fonctionnement et les différences propres à chaque auteur et aux buts des traitements.