使用C#从Google Chrome中获取当前标签的url

过去有一种方法可以通过将FindWindowExSendMessage调用结合使用以获取当前在多function框中的文本,从Google Chrome中获取活动选项卡的url。 最近(?)更新似乎打破了这种方法,因为Chrome似乎现在正在渲染一切。 (你可以用Spy ++,AHK Window Spy或Window Detective来查询)

要获取Firefox和Opera上的当前URL,可以使用DDE和WWW_GetWindowInfo 。 这似乎不可能在Chrome上(再?)。

这个问题有一个关于它如何工作的更多信息的答案,这是这段代码(正如我所解释的那样,它不再工作 – hAddressBox0 ):

 var hAddressBox = FindWindowEx( intPtr, IntPtr.Zero, "Chrome_OmniboxView", IntPtr.Zero); var sb = new StringBuilder(256); SendMessage(hAddressBox, 0x000D, (IntPtr)256, sb); temp = sb.ToString(); 

所以我的问题是:有没有一种新的方式来获得当前焦点标签的url? (只是标题是不够的)

编辑:看起来像我在这里的答案代码不再工作(虽然使用AutomationElement的想法仍然工作)为更高的Chrome版本,所以通过其他答案查看不同版本。 例如,以下是Chrome 54的一个: https : //stackoverflow.com/a/40638519/377618

下面的代码似乎工作,(感谢icemanind的评论),但是资源密集型。 大约需要350毫秒才能findelmUrlBar …有点慢。

更不用说,我们chrome了同时运行多个chrome进程的问题。

 // 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); } } } 

编辑:我不满意上面的慢方法,所以我做了更快(现在50毫秒),并添加了一些URLvalidation,以确保我们得到了正确的url,而不是用户可能在网上search的东西,或仍然正在忙于inputurl。 代码如下:

 // 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; } } 

我用下一个代码得到了Chrome 38.0.2125.10的结果(“try”块内的代码必须被replace)

 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)); 

从Chrome 54开始,以下代码正在为我工​​作:

 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; } 

我采取了安吉洛的解决scheme,并清理了一下…我有一个固定的LINQ 🙂

这是主要的方法, 它使用了几个扩展方法:

 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; } 

请注意,该评论是误导,因为评论往往是 – 它实际上并不看一个单一的AutomationElement。 我把它留在那里,因为安杰洛的代码有它。

这是扩展类:

 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; } } 

参考Angelo Geels的解决scheme,这是35版的补丁 – “try”块内的代码必须replace为:

 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)); 

我从这里拿走它: http : //techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/dotNet/System_CodeDom+Grab~URL~~~Chrome.txt

以上所有的方法都与我的Chrome V53及以上的失败。

这是什么工作:

 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; } 

在这里find它:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/93001bf5-440b-4a3a-ad6c-478a4f618e32/how-can-i-get-urls-of-open-pages-from-chrome-和Firefox的?论坛= csharpgeneral

对我来说只有活动的chrome窗口有一个MainWindowHandle。 我通过查看所有窗口的chrome窗口,然后使用这些句柄来解决这个问题。 例如:

  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; } 

然后得到所有的铬窗口:

  [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(); } 

最后:

 GetAllWindows() .Select(GetTitle) .Where(x => x.Contains("Google Chrome")) .ToList() .ForEach(Console.WriteLine); 

希望这可以节省别人一些时间来弄清楚如何真正得到所有铬窗口的句柄。

对于版本53.0.2785得到它与这个工作:

 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));