Getting the current tab's URL from Google Chrome using C#
Edit: Seems like the code in my answer here does not work anymore (though the idea of using AutomationElement
does still work) for the later Chrome versions, so look through the other answers for different versions. For example, here's one for Chrome 54: https://stackoverflow.com/a/40638519/377618
The following code seems to work, (thanks to icemanind's comment) but is however resource intensive. It takes about 350ms to find elmUrlBar... a little slow.
Not to mention that we have the problem of working with multiple chrome
processes running at the same time.
// 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);
}
}
}
Edit: I wasn't happy with the slow method above, so I made it faster (now 50ms) and added some URL validation to make sure we got the correct URL instead of something the user might be searching for on the web, or still being busy typing in the URL. Here's the code:
// 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;
}
}
As of Chrome 54, the following code is working for me:
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;
}
All the methods above are failing for me with Chrome V53 and above.
Here's what is working:
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;
}
Found it here:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/93001bf5-440b-4a3a-ad6c-478a4f618e32/how-can-i-get-urls-of-open-pages-from-chrome-and-firefox?forum=csharpgeneral
I got results for Chrome 38.0.2125.10 with the next code (the code inside the 'try' block has to be replaced with this)
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));