2010-09-07 22 views
12

Estoy tratando de automatizar una secuencia de entradas de usuario a una aplicación compilada en C# utilizando Win32 API. No tengo ningún código fuente para la aplicación que estoy tratando de controlar y se está ejecutando mientras trato de controlarlo. En mi código, tengo un solo botón que, al hacer clic, tiene que hacer una secuencia de 3 entradas a la aplicación que estoy tratando de controlar:Cómo seleccionar un elemento en un TreeView usando Win32 API

  1. seleccionar un elemento en una vista de árbol
  2. Haga clic en un botón de
  3. Haga clic en otro botón

la forma en que funciona es el botón en el paso 2 realiza una acción en función del elemento seleccionado en la vista de árbol en el paso 1. soy capaz de obtener el botón de clics para funcionar bien con sólo enviando un mensaje, pero no puedo encontrar la manera de seleccionar el elemento TreeView que quiero. TreeView es estático, por lo que los elementos y el diseño nunca cambiarán. Tiene la siguiente distribución:

-itemsA
-itemsB
--itemB1
-itemsC

Dónde itemB1 es el elemento que necesita ser seleccionados con el fin de que el botón de en los pasos 2 y 3 trabajar. Por defecto, ItemsB está contraído, por lo que probablemente necesite expandirlo antes de poder seleccionar ItemB1. Aquí está mi código. ¡¡Realmente aprecio cualquier ayuda!!

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

Hans,

¿Me puede dar un ejemplo de cómo iba a hacerlo? El problema que realmente estoy teniendo es encontrar el identificador para el elemento TreeView que quiero seleccionar. Si uso Spy ++ para encontrar la manija actual y codificar en mi método, que funciona bien, como esto:

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

si uso SendMessage y enviar TVGN_ROOT a la manija TreeView, va a devolver un IntPtr con el mango para el elemento a seleccionar en la vista de árbol, o cómo funciona eso? También estoy experimentando con AutoIt, pero esperaba mantener todo mi código en una sola aplicación.

Respuesta

11

Lo descubrí, por lo que estoy preocupado por encontrar documentación sobre este tema. Aquí está la mayoría de mi código:

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

Todavía no puedo entender esto al 100%, pero espero que esto ayude. El valor de retorno de SendMessage dependerá del mensaje que esté enviando, en este caso, era un int que contiene el identificador de un elemento TreeView. El primer argumento es el manejador de TreeView. El segundo argumento es el mensaje que desea enviar. Los argumentos tercero y cuarto son banderas. el tercero especifica el tipo de artículo, el cuarto es el manejador del elemento actual de treeview.

Gracias por la ayuda Hans! Alguien más, por favor siéntete libre de elaborar.

2

Tendrá que recorrer los nodos con TVM_GETNEXTITEM, comenzando en TVGN_ROOT. Luego selecciónelo con TVM_SELECTITEM. Pase el TVGN_FIRSTVISIBLE para asegurarse de que esté visible, no debería ser necesario si solo lo automatiza.

Eche un vistazo a AutoIt para evitar escribir código grungy como este.

+0

¿Puede darme un ejemplo de cómo haría esto? El problema que realmente estoy teniendo es encontrar el identificador para el elemento TreeView que quiero seleccionar. Si utilizo Spy ++ para encontrar el controlador actual y codificarlo en mi método, funciona bien, así: SendMessage ((int) treeChild, TV_SELECTITEM, TVGN_CARET, (IntPtr) 0x092DCB30); Si uso SendMessage y envío TVGN_ROOT al Mango TreeView, ¿devolverá un IntPtr con el asa del elemento para seleccionar en la vista de árbol, o cómo funciona eso? También estoy experimentando con AutoIt, pero esperaba mantener todo mi código en una sola aplicación. – user441603

+0

TVGN_ROOT es una bandera, no un mensaje. TVM_GETNEXTITEM es el mensaje. Si desea ayuda con su código, publíquelo en su pregunta. –

+0

Mi error. Primera publicación en el foro. – user441603

0

Sé que esto es bastante tarde, pero si tiene un problema similar (como yo). Puede echar un vistazo a AutoHotKey, especialmente si está familiarizado con SendMessage. Esto le ahorraría la necesidad de compilar y mucha complejidad, pero a su punto sería posible navegar a través de la estructura usando las teclas de flecha.

Cuestiones relacionadas