c# - rapido - no puedo regresar a la pagina anterior en google chrome
Obteniendo la URL de la pestaña actual de Google Chrome usando C# (8)
Obtuve los resultados de Chrome 38.0.2125.10 con el siguiente código (el código dentro del bloque ''try'' debe ser reemplazado por este)
var elm1 = elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (elm1 == null) { continue; } // not the right chrome.exe
var elm2 = TreeWalker.RawViewWalker.GetLastChild(elm1);
var elm3 = elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.HelpTextProperty, "TopContainerView"));
var elm4 = elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
var elm5 = elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.HelpTextProperty, "LocationBarView"));
elmUrlBar = elm5.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
Solía haber una manera de obtener la URL de la pestaña activa de Google Chrome utilizando FindWindowEx
en combinación con una llamada a SendMessage
para obtener el texto actualmente en el cuadro multifunción. Parece que una actualización reciente (?) Ha roto este método, ya que Chrome parece estar renderizando todo ahora. (Puede consultar con Spy ++, AHK Window Spy o Window Detective)
Para obtener la URL actual en Firefox y Opera, puede usar DDE y WWW_GetWindowInfo
. Esto no parece ser posible en Chrome (¿ya?).
Esta pregunta tiene una respuesta con más información sobre cómo solía funcionar, que es esta pieza de código (que, como he explicado, ya no funciona - hAddressBox
es 0
):
var hAddressBox = FindWindowEx(
intPtr,
IntPtr.Zero,
"Chrome_OmniboxView",
IntPtr.Zero);
var sb = new StringBuilder(256);
SendMessage(hAddressBox, 0x000D, (IntPtr)256, sb);
temp = sb.ToString();
Entonces mi pregunta es: ¿hay una nueva forma de obtener la URL de la pestaña actualmente enfocada? (Solo el titulo no es suficiente)
A partir de Chrome 54, el siguiente código me funciona:
public static string GetActiveTabUrl()
{
Process[] procsChrome = Process.GetProcessesByName("chrome");
if (procsChrome.Length <= 0)
return null;
foreach (Process proc in procsChrome)
{
// the chrome process must have a window
if (proc.MainWindowHandle == IntPtr.Zero)
continue;
// to find the tabs we first need to locate something reliable - the ''New Tab'' button
AutomationElement root = AutomationElement.FromHandle(proc.MainWindowHandle);
var SearchBar = root.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));
if (SearchBar != null)
return (string)SearchBar.GetCurrentPropertyValue(ValuePatternIdentifiers.ValueProperty);
}
return null;
}
Para la versión 53.0.2785 lo tengo trabajando con esto:
var elm1 = elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (elm1 == null) { continue; } // not the right chrome.exe
var elm2 = elm1.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""))[1];
var elm3 = elm2.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""))[1];
var elm4 = elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "principal"));
var elm5 = elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
elmUrlBar = elm5.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
Para mí, solo la ventana activa de Chrome tiene MainWindowHandle. Lo solucioné mirando a través de todas las ventanas en busca de ventanas de cromo y luego usando esas manijas. Por ejemplo:
public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(Win32Callback enumProc, IntPtr lParam);
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
List<IntPtr> pointers = GCHandle.FromIntPtr(pointer).Target as List<IntPtr>;
pointers.Add(handle);
return true;
}
private static List<IntPtr> GetAllWindows()
{
Win32Callback enumCallback = new Win32Callback(EnumWindow);
List<IntPtr> pointers = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(pointers);
try
{
EnumWindows(enumCallback, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated) listHandle.Free();
}
return pointers;
}
Y luego para obtener todas las ventanas de Chrome:
[DllImport("User32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr windowHandle, StringBuilder stringBuilder, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "GetWindowTextLength", SetLastError = true)]
internal static extern int GetWindowTextLength(IntPtr hwnd);
private static string GetTitle(IntPtr handle)
{
int length = GetWindowTextLength(handle);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(handle, sb, sb.Capacity);
return sb.ToString();
}
y finalmente:
GetAllWindows()
.Select(GetTitle)
.Where(x => x.Contains("Google Chrome"))
.ToList()
.ForEach(Console.WriteLine);
Con suerte, esto le ahorra a alguien más algo de tiempo para descubrir cómo obtener los identificadores de todas las ventanas de Chrome.
Refiriéndose a la solución de Angelo Geels, aquí hay un parche para la versión 35: el código dentro del bloque "try" debe ser reemplazado por este:
var elm1 = elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (elm1 == null) { continue; } // not the right chrome.exe
var elm2 = TreeWalker.RawViewWalker.GetLastChild(elm1); // I don''t know a Condition for this for finding
var elm3 = elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
var elm4 = TreeWalker.RawViewWalker.GetNextSibling(elm3); // I don''t know a Condition for this for finding
var elm7 = elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
elmUrlBar = elm7.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
Lo tomé desde aquí: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/dotNet/System_CodeDom+Grab~URL~from~Chrome.txt
Todos los métodos anteriores están fallando para mí con Chrome V53 y superior.
Esto es lo que funciona:
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome)
{
if (chrome.MainWindowHandle == IntPtr.Zero)
continue;
AutomationElement element = AutomationElement.FromHandle(chrome.MainWindowHandle);
if (element == null)
return null;
Condition conditions = new AndCondition(
new PropertyCondition(AutomationElement.ProcessIdProperty, chrome.Id),
new PropertyCondition(AutomationElement.IsControlElementProperty, true),
new PropertyCondition(AutomationElement.IsContentElementProperty, true),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
AutomationElement elementx = element.FindFirst(TreeScope.Descendants, conditions);
return ((ValuePattern)elementx.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
}
Lo encontré aquí:
Tomé la solución de Angelo y la limpie un poco ... Tengo una fijación con LINQ :)
Este es el método principal por así decirlo; usa un par de métodos de extensión:
public IEnumerable<string> GetTabs()
{
// there are always multiple chrome processes, so we have to loop through all of them to find the
// process with a Window Handle and an automation element of name "Address and search bar"
var processes = Process.GetProcessesByName("chrome");
var automationElements = from chrome in processes
where chrome.MainWindowHandle != IntPtr.Zero
select AutomationElement.FromHandle(chrome.MainWindowHandle);
return from element in automationElements
select element.GetUrlBar()
into elmUrlBar
where elmUrlBar != null
where !((bool) elmUrlBar.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty))
let patterns = elmUrlBar.GetSupportedPatterns()
where patterns.Length == 1
select elmUrlBar.TryGetValue(patterns)
into ret
where ret != ""
where Regex.IsMatch(ret, @"^(https:////)?[a-zA-Z0-9/-/.]+(/.[a-zA-Z]{2,4}).*$")
select ret.StartsWith("http") ? ret : "http://" + ret;
}
Tenga en cuenta que el comentario es engañoso, ya que los comentarios tienden a ser, en realidad no se ve en un solo AutomationElement. Lo dejé allí porque el código de Angelo lo tenía.
Aquí está la clase de extensión:
public static class AutomationElementExtensions
{
public static AutomationElement GetUrlBar(this AutomationElement element)
{
try
{
return InternalGetUrlBar(element);
}
catch
{
// Chrome has probably changed something, and above walking needs to be modified. :(
// put an assertion here or something to make sure you don''t miss it
return null;
}
}
public static string TryGetValue(this AutomationElement urlBar, AutomationPattern[] patterns)
{
try
{
return ((ValuePattern) urlBar.GetCurrentPattern(patterns[0])).Current.Value;
}
catch
{
return "";
}
}
//
private static AutomationElement InternalGetUrlBar(AutomationElement element)
{
// walking path found using inspect.exe (Windows SDK) for Chrome 29.0.1547.76 m (currently the latest stable)
var elm1 = element.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
var elm2 = TreeWalker.RawViewWalker.GetLastChild(elm1); // I don''t know a Condition for this for finding :(
var elm3 = elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
var elm4 = elm3.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
var result = elm4.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
return result;
}
}
Editar: Parece que el código en mi respuesta aquí ya no funciona (aunque la idea de usar AutomationElement
todavía funciona) para las versiones posteriores de Chrome, así que busque en las otras respuestas las diferentes versiones. Por ejemplo, aquí hay uno para Chrome 54: https://.com/a/40638519/377618
El siguiente código parece funcionar, (gracias al comentario de icemanind) pero sin embargo requiere muchos recursos. Se necesitan unos 350 ms para encontrar elmUrlBar ... un poco lento.
Sin mencionar que tenemos el problema de trabajar con múltiples procesos de chrome
que se ejecutan al mismo tiempo.
// there are always multiple chrome processes, so we have to loop through all of them to find the
// process with a Window Handle and an automation element of name "Address and search bar"
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome) {
// the chrome process must have a window
if (chrome.MainWindowHandle == IntPtr.Zero) {
continue;
}
// find the automation element
AutomationElement elm = AutomationElement.FromHandle(chrome.MainWindowHandle);
AutomationElement elmUrlBar = elm.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));
// if it can be found, get the value from the URL bar
if (elmUrlBar != null) {
AutomationPattern[] patterns = elmUrlBar.GetSupportedPatterns();
if (patterns.Length > 0) {
ValuePattern val = (ValuePattern)elmUrlBar.GetCurrentPattern(patterns[0]);
Console.WriteLine("Chrome URL found: " + val.Current.Value);
}
}
}
Editar: no estaba contento con el método lento anterior, así que lo hice más rápido (ahora 50 ms) y agregué un poco de validación de URL para asegurarme de que obtuviéramos la URL correcta en lugar de algo que el usuario podría estar buscando en la web, o aún estar ocupado escribiendo la URL. Aquí está el código:
// there are always multiple chrome processes, so we have to loop through all of them to find the
// process with a Window Handle and an automation element of name "Address and search bar"
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome) {
// the chrome process must have a window
if (chrome.MainWindowHandle == IntPtr.Zero) {
continue;
}
// find the automation element
AutomationElement elm = AutomationElement.FromHandle(chrome.MainWindowHandle);
// manually walk through the tree, searching using TreeScope.Descendants is too slow (even if it''s more reliable)
AutomationElement elmUrlBar = null;
try {
// walking path found using inspect.exe (Windows SDK) for Chrome 31.0.1650.63 m (currently the latest stable)
var elm1 = elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (elm1 == null) { continue; } // not the right chrome.exe
// here, you can optionally check if Incognito is enabled:
//bool bIncognito = TreeWalker.RawViewWalker.GetFirstChild(TreeWalker.RawViewWalker.GetFirstChild(elm1)) != null;
var elm2 = TreeWalker.RawViewWalker.GetLastChild(elm1); // I don''t know a Condition for this for finding :(
var elm3 = elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
var elm4 = elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
elmUrlBar = elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
} catch {
// Chrome has probably changed something, and above walking needs to be modified. :(
// put an assertion here or something to make sure you don''t miss it
continue;
}
// make sure it''s valid
if (elmUrlBar == null) {
// it''s not..
continue;
}
// elmUrlBar is now the URL bar element. we have to make sure that it''s out of keyboard focus if we want to get a valid URL
if ((bool)elmUrlBar.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty)) {
continue;
}
// there might not be a valid pattern to use, so we have to make sure we have one
AutomationPattern[] patterns = elmUrlBar.GetSupportedPatterns();
if (patterns.Length == 1) {
string ret = "";
try {
ret = ((ValuePattern)elmUrlBar.GetCurrentPattern(patterns[0])).Current.Value;
} catch { }
if (ret != "") {
// must match a domain name (and possibly "https://" in front)
if (Regex.IsMatch(ret, @"^(https:////)?[a-zA-Z0-9/-/.]+(/.[a-zA-Z]{2,4}).*$")) {
// prepend http:// to the url, because Chrome hides it if it''s not SSL
if (!ret.StartsWith("http")) {
ret = "http://" + ret;
}
Console.WriteLine("Open Chrome URL found: ''" + ret + "''");
}
}
continue;
}
}