2010-10-13 8 views
5

Tengo el identificador de Internet Explorer y tengo la pestaña Manejar que quiero seleccionar.¿Cómo puedo seleccionar una pestaña de IE desde su identificador

¿Cómo puedo seleccionar esta pestaña?

Sé cómo seleccionar una pestaña por índice (usando IEAccessible), pero no puedo obtener el tabIndex desde el mango. Spy ++ no los ordena en orden.

+0

No puedes. La administración de pestañas es un detalle interno de implementación de IE que no está expuesto de ninguna manera compatible. –

Respuesta

9

Hay una manera hack manera de hacerlo. No es compatible, y depende de la versión de IE, así como del idioma del sistema operativo.

En una instancia de IE, el control con el nombre "Tab Row" es el elemento que contiene todas las pestañas. Dado un IAccessible correspondiente a esa cosa, es fácil enumerar los niños, y obtener las pestañas. Una aplicación de automatización puede inspeccionar la URL en cada pestaña , y luego llamar al IAccessible.accDoDefaultAction(0), para activar una pestaña por URL. Ese es el enfoque básico.

Pero no pude averiguar cómo llegar directamente a la pestaña "Fila" control de una instancia es decir, de un objeto COM SHDocVw.WebBrowser, que es lo que una aplicación se sale de SHDocVw.ShellWindowsClass. Lo intenté millones de formas, y finalmente la forma más simple que pude encontrar para obtenerlo para trabajar es esto: obtener el objeto COM WebBrowser, obtener el HWND de ese objeto (que es realmente el HWND numerosos niveles "arriba") , luego recorra la jerarquía OS HWND para encontrar el HWND con el nombre "DirectUIHWND". Desde allí, camine por el árbol de IAccessible , para encontrar la "Fila de pestañas", luego seleccione la pestaña e invoque el método .

Parece feo describirlo, y me dolió codificarlo de esta manera. I no se pudo encontrar una forma de realizar el recorrido solo en HWND o solo en IAccessible. No tengo idea de por qué necesitaba hacer ambas cosas.


En Código

En primer lugar, obtener el HWND del WebBrowser:

 SHDocVw.WebBrowser ietab = ... ? 
     string urlOfTabToActivate = ietab.LocationURL; 
     IntPtr hwnd = (IntPtr) ietab.HWND; 

continuación, obtener el HWND de la cosa DirectUI en el caso de IE que controla que WebBrowser:

 var directUi = GetDirectUIHWND(hwnd); 

Esta es la parte hacky. El método GetDirectUIHWND se define así:

private static IntPtr GetDirectUIHWND(IntPtr ieFrame) 
    { 
     // try IE 9 first: 
     IntPtr intptr = FindWindowEx(ieFrame, IntPtr.Zero, "WorkerW", null); 
     if (intptr == IntPtr.Zero) 
     { 
      // IE8 and IE7 
      intptr = FindWindowEx(ieFrame, IntPtr.Zero, "CommandBarClass", null); 
     } 
     intptr = FindWindowEx(intptr, IntPtr.Zero, "ReBarWindow32", null); 
     intptr = FindWindowEx(intptr, IntPtr.Zero, "TabBandClass", null); 
     intptr = FindWindowEx(intptr, IntPtr.Zero, "DirectUIHWND", null); 
     return intptr; 
    } 

... donde FindWindowEx es un dllimport:

[DllImport("user32.dll", SetLastError = true)] 
    private static extern IntPtr FindWindowEx(IntPtr hwndParent, 
               IntPtr hwndChildAfter, 
               string lpszClass, 
               string lpszWindow); 

A continuación, obtener el IAccessible para esa cosa DirectUI:

 var iacc = AccessibleObjectFromWindow(directUi); 

.. .donde, por supuesto, es una AccessibleObjectFromWindow DllImport, con un poco de magia a su alrededor:

[DllImport("oleacc.dll")] 
    internal static extern int AccessibleObjectFromWindow 
     (IntPtr hwnd, uint id, ref Guid iid, 
     [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject); 


    private static IAccessible AccessibleObjectFromWindow(IntPtr hwnd) 
    { 
     Guid guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible 
     object obj = null; 
     uint id = 0U; 
     int num = AccessibleObjectFromWindow(hwnd, id, ref guid, ref obj); 
     var acc = obj as IAccessible; 
     return acc; 
    } 

Ok, entonces, se puede caminar por el árbol IAccessible, para encontrar la "ficha fila". En IE esto es lo que muestra todas las pestañas para las diversas ventanas del navegador.

 var tabRow = FindAccessibleDescendant(iacc, "Tab Row"); 

... donde ese método es:

private static IAccessible FindAccessibleDescendant(IAccessible parent, String strName) 
    { 
     int c = parent.accChildCount; 
     if (c == 0) 
      return null; 

     var children = AccChildren(parent); 

     foreach (var child in children) 
     { 
      if (child == null) continue; 
      if (strName.Equals(child.get_accName(0))) 
       return child; 

      var x = FindAccessibleDescendant(child, strName); 
      if (x!=null) return x; 
     } 

     return null; 
    } 
} 

Y ...

private static List<IAccessible> AccChildren(IAccessible accessible) 
    { 
     object[] res = GetAccessibleChildren(accessible); 
     var list = new List<IAccessible>(); 
     if (res == null) return list; 

     foreach (object obj in res) 
     { 
      IAccessible acc = obj as IAccessible; 
      if (acc != null) list.Add(acc); 
     } 
     return list; 
    } 

A continuación, obtener el IAccessible dentro de éste, y haga clic en ella:

 var tabs = AccChildren(tabRow); 
     int tc = tabs.Count; 
     int k = 0; 

     // walk through the tabs and tick the chosen one 
     foreach (var candidateTab in tabs) 
     { 
      k++; 
      // the last tab is "New Tab", which we don't want 
      if (k == tc) continue; 

      // the URL on *this* tab 
      string localUrl = UrlForTab(candidateTab); 

      // same? if so, tick it. This selects the given tab among all 
      // the others, if any. 
      if (urlOfTabToActivate != null 
       && localUrl.Equals(urlOfTabToActivate)) 
      { 
       candidateTab.accDoDefaultAction(0); 
       return; 
      } 
     } 

.... donde UrlForTab es ....

private static string UrlForTab(IAccessible tab) 
    { 
     try 
     { 
      var desc = tab.get_accDescription(0); 
      if (desc != null) 
      { 
       if (desc.Contains("\n")) 
       { 
        string url = desc.Substring(desc.IndexOf("\n")).Trim(); 
        return url; 
       } 
       else 
       { 
        return desc; 
       } 
      } 
     } 
     catch { } 
     return "??"; 
    } 

Uf. No pude encontrar una manera más limpia y simple de hacer esto.

+1

Gran ejemplo, muchas gracias. ¿Podría proporcionar también un ejemplo del método GetAccessibleChildren? Falta en tu ejemplo. –

+1

Vlad, GetAccessibleChildren es una función API de Windows. El código funciona perfectamente y me ayudó mucho. En realidad, porté el código a Delphi. Sin embargo, hay un pequeño problema técnico: "Localizar fila" está localizado. Como estoy usando una versión francesa de IE, tuve que cambiar "Tab Row" a "Onglet Ligne". Me pregunto si hay una forma de obtener esto de otro lado porque causar un gran problema en cualquier IE que no sea la versión estándar. – fpiette

Cuestiones relacionadas