12 votes

Comment sélectionner un élément dans un TreeView en utilisant l'API Win32

Je essaie d'automatiser une séquence d'entrées utilisateur vers une application compilée en C# en utilisant l'API Win32. Je n'ai aucun code source pour l'application que je essaie de contrôler et elle fonctionne pendant que je essaie de la contrôler. Dans mon code, j'ai un seul bouton qui, une fois cliqué, doit effectuer une séquence de 3 entrées vers l'application que je essaie de contrôler :

  1. Sélectionner un élément dans un treeview
  2. Appuyer sur un bouton
  3. Appuyer sur un autre bouton

Le fonctionnement est que le bouton dans l'étape 2 effectue une action en fonction de l'élément sélectionné dans le treeview à l'étape 1. Je parviens à faire fonctionner les clics sur les boutons en envoyant simplement un message, mais je ne parviens pas à comprendre comment sélectionner l'élément TreeView désiré. Le TreeView est statique, donc les éléments et la mise en page ne changeront jamais. Il a la mise en page suivante :

-élémentsA
-élémentsB
--élémentB1
-élémentsC

Où l'élémentB1 est l'élément qui doit être sélectionné pour que les clics sur les boutons dans les étapes 2 et 3 fonctionnent. Par défaut, les élémentsB sont repliés, donc je dois probablement les développer avant de pouvoir sélectionner l'élémentB1 ? Voici mon code. Je vous remercie vraiment de toute aide !!

//Trouver Window API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);

//Trouver WindowEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

//Envoyer un message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);

private const int BN_CLICKED = 245;

//Méthode appelée par un clique de bouton
public static void Start()
{
    int hwnd = 0;
    int prod = 0;
    IntPtr hwndChild = IntPtr.Zero;
    IntPtr treeChild = IntPtr.Zero;
    IntPtr prodChild = IntPtr.Zero;

    hwnd = FindWindow(null, "Application");
    if (hwnd > 0)
    {
        //Obtenir la poignée pour le TreeView, C'EST ICI QUE JE SUIS BLOQUÉ!!
        treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
        //Besoin d'ajouter du code pour sélectionner l'élément dans le TreeView ???

        //Cliquer sur le premier bouton
        hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
        hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "AfxMDIFrame80", null);
        hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "#32770", null);
        IntPtr scanBtn = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "Button", "&Scan");
        SendMessage((int)scanBtn, BN_CLICKED, 0, IntPtr.Zero);

        //Cliquer sur le deuxième bouton
        prod = FindWindow("#32770", "Product: WPC");
        prodChild = FindWindowEx((IntPtr)prod, IntPtr.Zero, "Button", "&Collect");
        SendMessage((int)prodChild, BN_CLICKED, 0, IntPtr.Zero);
    }
    }//FIN Démarrer

Hans,

Pouvez-vous me donner un exemple de comment je pourrais faire ça ? Le problème que j'ai vraiment est de trouver la poignée pour l'élément treeview que je veux sélectionner. Si j'utilise Spy++ pour trouver la poignée actuelle et la coder en dur dans ma méthode, cela fonctionne bien, comme ceci :

SendMessage((int)treeChild, TV_SELECTITEM, TVGN_CARET, (IntPtr)0x092DCB30); 

Si j'utilise SendMessage et envoie TVGN_ROOT à la poignée du TreeView, renverra-t-il un IntPtr avec la poignée de l'élément à sélectionner dans le treeview, ou comment cela fonctionne-t-il ? J'expérimente également avec AutoIt, mais j'espérais conserver tout mon code dans une seule application.

13voto

user441603 Points 457

J'ai compris, donc je vais poster pour toute personne intéressée, j'ai eu du mal à trouver de la documentation à ce sujet. Voici la majeure partie de mon code :

//Définir les indicateurs et messages TreeView
private const int BN_CLICKED = 0xF5;
private const int TV_FIRST = 0x1100;
private const int TVGN_ROOT = 0x0;
private const int TVGN_NEXT = 0x1;
private const int TVGN_CHILD = 0x4;
private const int TVGN_FIRSTVISIBLE = 0x5;
private const int TVGN_NEXTVISIBLE = 0x6;
private const int TVGN_CARET = 0x9;
private const int TVM_SELECTITEM = (TV_FIRST + 11);
private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
private const int TVM_GETITEM = (TV_FIRST + 12);

//Trouver la fenêtre API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);

//Trouver la fenêtreEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

//Envoyer le message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);

public static void Start()
{
    //Variables de gestion
    int treeItem = 0;
    IntPtr treeChild = IntPtr.Zero;

    int hwnd = FindWindow(null, "Application"); //Handle pour l'application à contrôler
    if (hwnd > 0)
    {
        //Sélectionner l'élément TreeView
        treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
        treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_ROOT, IntPtr.Zero);
        treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_NEXT, (IntPtr)treeItem);
        treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_CHILD, (IntPtr)treeItem);
        SendMessage((int)treeChild, TVM_SELECTITEM, TVGN_CARET, (IntPtr)treeItem);

        // ...Continuer avec mon automatisation...
    }
}

Je ne comprends peut-être pas cela à 100%, mais j'espère que cela aidera. La valeur de retour de SendMessage dépendra du message que vous envoyez, dans ce cas, c'était un int contenant la poignée d'un élément TreeView. Le premier argument est la poignée du TreeView lui-même. Le deuxième argument est le Message que vous voulez envoyer. Les 3e et 4e arguments sont des indicateurs. Le 3e spécifie le type d'élément, le 4e est la poignée de l'élément TreeView actuel.

Merci pour l'aide Hans ! Pour les autres, n'hésitez pas à donner plus de détails.

2voto

Hans Passant Points 475940

Vous devrez parcourir les nœuds avec TVM_GETNEXTITEM, en commençant par TVGN_ROOT. Ensuite, sélectionnez-le avec TVM_SELECTITEM. Passez le TVGN_FIRSTVISIBLE pour vous assurer qu'il est visible, cela ne devrait pas être nécessaire si vous l'automatisez simplement.

Jetez un œil à AutoIt pour éviter d'écrire du code crasseux comme celui-ci.

0voto

John Smith Points 97

Je sais que cela arrive assez tardivement, mais si vous rencontrez un problème similaire (comme moi). Vous pourriez jeter un coup d'œil à AutoHotKey, surtout si vous êtes familier avec SendMessage. Cela vous éviterait de devoir compiler et vous simplifierait la tâche, mais comme vous le mentionnez, il serait possible de naviguer dans la structure en utilisant les touches fléchées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X