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.
Non è possibile. La gestione delle schede è un dettaglio di implementazione IE interno che non è esposto in alcun modo supportato. –