Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the current tab's URL from Google Chrome using C#

There used to be a way to get the active tab's URL from Google Chrome by using FindWindowEx in combination with a SendMessage call to get the text currently in the omnibox. A recent (?) update seems to have broken this method, since Chrome seems to be rendering everything itself now. (You can check with Spy++, AHK Window Spy or Window Detective)

To get the current URL on Firefox and Opera, you can use DDE and WWW_GetWindowInfo. This doesn't seem to be possible on Chrome (anymore?).

This question has an answer with more info about how it used to work, which is this piece of code (which, as I explained, doesn't work anymore - hAddressBox is 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(); 

So my question is: Is there a new way to get the currently focused tab's URL? (Just the title is not enough)

like image 214
Codecat Avatar asked Sep 19 '13 14:09

Codecat


People also ask

How do I view URL in Chrome?

Just right-click in Chrome's address bar select “Always show full URLs” to make Chrome show full URLs. Chrome will now always show the full URL of every web address you open. To disable this feature, right-click in the address bar again and uncheck it.

How do I get a new tab URL?

Chrome doesn't allow you to configure a new tab URL. It always opens its New Tab Page with a search bar and some browsing history. With this extension, you can use the options page to store a link to a page or even a path to a local file that will be loaded when a new tab is created.

How do I copy a URL from all open tabs in my browser?

Press Ctrl+C on Windows and Linux or Command+C on Mac to copy all your bookmarks. You can also right-click the list of selected bookmarks and choose “Copy.” Now, open the program where you want to paste all your open tab URLs (such as Notepad or TextEdit).


1 Answers

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;   } } 
like image 176
Codecat Avatar answered Sep 19 '22 16:09

Codecat