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.
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. –