Подтвердить что ты не робот

Как выбрать элемент в TreeView с помощью Win32 API

Я пытаюсь автоматизировать последовательность пользовательских входов в скомпилированное приложение на С# с использованием Win32 API. У меня нет исходного кода для приложения, которое я пытаюсь контролировать, и он работает, пока я пытаюсь его контролировать. В моем коде у меня есть одна кнопка, которая при щелчке должна сделать последовательность из трех входов для приложения, которое я пытаюсь контролировать:

  • Выберите элемент в дереве
  • Нажмите кнопку
  • Нажмите другую кнопку

Как это работает, кнопка на шаге 2 выполняет действие в зависимости от элемента, выбранного в дереве на шаге 1. Я могу получить щелчки на кнопках, чтобы работать нормально, просто отправив сообщение, но я не могу понять как выбрать элемент TreeView, который я хочу. TreeView статичен, поэтому элементы и макет никогда не будут меняться. Он имеет следующий макет:

-itemsA
-itemsB
--itemB1
-itemsC

Если itemB1 - это элемент, который необходимо выбрать, чтобы кнопка нажала на шаги 2 и 3, чтобы работать. По умолчанию ItemsB сбрасывается, поэтому, вероятно, мне нужно его расширить, прежде чем я могу выбрать ItemB1? Вот мой код. Я очень ценю любую помощь!

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

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

//Send 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;

//Method called by button click
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)
    {
        //Get Handle for TreeView, THIS IS WHERE I AM STUCK!!
        treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
        treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
        //Need to Add code to select item in TreeView ???

        //Click First Button
        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);

        //Click Second Button
        prod = FindWindow("#32770", "Product: WPC");
        prodChild = FindWindowEx((IntPtr)prod, IntPtr.Zero, "Button", "&Collect");
        SendMessage((int)prodChild, BN_CLICKED, 0, IntPtr.Zero);
    }
    }//END Start

Ганс,

Можете ли вы привести мне пример того, как я это сделаю? Проблема, с которой я действительно сталкиваюсь, - это найти дескриптор элемента treeview, который я хочу выбрать. Если я использую Spy ++ для поиска текущего дескриптора и жесткого кода в моем методе, он отлично работает, например:

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

Если я использую SendMessage и отправлю TVGN_ROOT в TreeView Handle, он вернет IntPtr с дескриптом элемента, который будет выбран в древовидной структуре, или как это работает? Я также экспериментирую с AutoIt, но я надеялся сохранить весь свой код в одном приложении.

4b9b3361

Ответ 1

Я понял это, так что Ill post для всех, кто интересуется, мне было трудно найти документацию по этому поводу. Вот основная часть моего кода:

//Define TreeView Flags and Messages
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);

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

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

//Send 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()
        {
            //Handle variables
            int hwnd = 0;
            int treeItem = 0;
            IntPtr hwndChild = IntPtr.Zero;
            IntPtr treeChild = IntPtr.Zero;

            hwnd = FindWindow(null, "Application"); //Handle for the application to be controlled
            if (hwnd > 0)
            {
                //Select TreeView Item
                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);

                // ...Continue with my automation...
             }
        }//END Scan

Я все еще не понимаю этого 100%, но, надеюсь, это помогает. Возвращаемое значение SendMessage будет зависеть от того, какое сообщение вы отправляете, в этом случае это был int, содержащий дескриптор элемента TreeView. Первый аргумент - это дескриптор самого TreeView. Второй аргумент - это Сообщение, которое вы хотите отправить. Третий и четвертый аргументы - это флаги. третий специфицирует тип элемента, четвертый - дескриптор текущего элемента дерева.

Спасибо за помощь Ханс! Любой, пожалуйста, не стесняйтесь уточнять.

Ответ 2

Вам нужно будет ходить по узлам с TVM_GETNEXTITEM, начиная с TVGN_ROOT. Затем выберите его с помощью TVM_SELECTITEM. Передайте TVGN_FIRSTVISIBLE, чтобы убедиться, что он виден, не обязательно, если вы просто его автоматизируете.

Взгляните на AutoIt, чтобы избежать написания такого шероховатого кода.

Ответ 3

Я знаю, что это довольно поздно, но если у вас есть аналогичная проблема (как я). Вы можете взглянуть на AutoHotKey, особенно если вы знакомы с SendMessage. Это сэкономит вам потребность в компиляции и большой сложности, но, на ваш взгляд, можно будет перемещаться по структуре с помощью клавиш со стрелками.