2010-10-13 12 views
5

Ho il manico di Internet Explorer e ho il tab Handle che voglio selezionare.Come posso selezionare una scheda IE dal suo handle

Come posso selezionare questa scheda?

So come selezionare una scheda per Indice (utilizzando IEAccessible), ma non riesco a ottenere il tabIndex dall'handle. Spy ++ non li ordina in ordine.

+0

Non è possibile. La gestione delle schede è un dettaglio di implementazione IE interno che non è esposto in alcun modo supportato. –

risposta

9

C'è un modo mod. modo per farlo. Non supportato e dipende dalla versione di IE e dalla lingua del sistema operativo.

In un'istanza di IE, il controllo con il nome "Tab Row" è la cosa che contiene tutte le varie schede. Dato un IAccessable corrispondente a quella cosa, è facile enumerare i bambini, e ottenere le schede. Un'app di automazione può ispezionare l'URL su ogni scheda e quindi chiamare IAccessible.accDoDefaultAction(0), per attivare una scheda per URL. Questo è l'approccio di base.

Ma io non riuscivo a capire come ottenere direttamente la "scheda Riga" controllo di un'istanza di IE, da un oggetto SHDocVw.WebBrowser COM, che è quello che un app esce di SHDocVw.ShellWindowsClass. L'ho provato a modi, e infine il modo più semplice che ho trovato per farlo funzionare è : ottieni l'oggetto COM WebBrowser, ottieni l'HWND da quell'oggetto (che in realtà è HWND numerosi livelli "su") , quindi attraversare la gerarchia OS HWND per trovare l'HWND con il nome "DirectUIHWND". Da lì, scorri verso il basso la struttura IAccessible , per trovare "Tab Row", quindi seleziona la scheda e invoca il metodo .

Sembra brutto da descrivere e fa male codificarlo in questo modo. I non è riuscito a trovare un modo per eseguire l'attraversamento solo in HWND o solo in IAccessible. Non ho idea del motivo per cui avevo bisogno di fare entrambe le cose.


Nel Codice

primo luogo, ottenere l'HWND del browser web:

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

quindi ottenere il HWND della cosa DirectUI nell'istanza IE che controlla che del browser web:

 var directUi = GetDirectUIHWND(hwnd); 

Questa è la parte dell'hacky. Il metodo GetDirectUIHWND è definito in questo modo:

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; 
    } 

... dove FindWindowEx è un dllimport:

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

Quindi, ottenere l'IAccessible per quella cosa DirectUI:

 var iacc = AccessibleObjectFromWindow(directUi); 

.. .dove, naturalmente, è un AccessibleObjectFromWindow DllImport, con un po 'di magia attorno ad esso:

[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, allora, si cammina lungo l'albero IAccessible, per trovare la "Scheda Row". In IE questa è la cosa che mostra tutte le schede per le varie finestre del browser.

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

... in cui tale metodo è:

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; 
    } 
} 

E ...

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; 
    } 

Quindi, ottenere l'IAccessible all'interno di quella, e fare clic su esso:

 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; 
      } 
     } 

.... dove UrlForTab è ....

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 "??"; 
    } 

Accidenti. Non sono riuscito a trovare un modo più semplice e pulito per farlo.

+1

Ottimo esempio, grazie mille. Potrebbe anche fornire un esempio del metodo GetAccessibleChildren. Manca nel tuo esempio. –

+1

Vlad, GetAccessibleChildren è una funzione API di Windows. Il codice funziona perfettamente e mi ha aiutato molto. In realtà ho portato il codice a Delphi. C'è tuttavia un piccolo problema: "Tab Row" è localizzato. Poiché sto utilizzando una versione francese di IE, ho dovuto modificare "Tab Row" in "Onglet Ligne". Mi chiedo se c'è un modo per ottenere questo da altrove perché causare un grosso problema per qualsiasi IE che non è la versione di enagligh. – fpiette

Problemi correlati