Skip to content

Instantly share code, notes, and snippets.

@Jiiks
Last active January 27, 2023 09:30
Show Gist options
  • Save Jiiks/fa224592af04cb2dea8a57cbfaaa0a9d to your computer and use it in GitHub Desktop.
Save Jiiks/fa224592af04cb2dea8a57cbfaaa0a9d to your computer and use it in GitHub Desktop.
Browser tab title grabber
/*
* C# Browser Title Grabber by Jiiks
* Grabs titles of all tabs.
* Tested with Chrome and Firefox.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Automation;
namespace YourNameSpace {
public static class BrowserTitleGrabber {
public static readonly Condition TabControlCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab);
public static readonly Condition TabItemCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem);
private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
public static IEnumerable<string> GetTabTitles(EBrowser browser) {
switch (browser) {
case EBrowser.Chrome:
return GetTabTitles("chrome");
case EBrowser.Firefox:
return GetTabTitles("firefox");
default:
throw new ArgumentOutOfRangeException(nameof(browser), browser, null);
}
}
public static IEnumerable<string> GetTabTitles(string procName) {
_cBt = new List<string>();
foreach (var process in Process.GetProcessesByName(procName)) {
foreach (ProcessThread processThread in process.Threads) {
EnumThreadWindows((uint) processThread.Id, EnumThreadWindowsCb, IntPtr.Zero);
}
}
return _cBt;
}
private static List<string> _cBt;
private static bool EnumThreadWindowsCb(IntPtr hWnd, IntPtr lParam) {
try {
_cBt.AddRange(GetTabTitles(hWnd));
}
catch (Exception) {
// ignored
}
return true;
}
private static IEnumerable<string> GetTabTitles(IntPtr handle) {
if (handle == IntPtr.Zero) return new List<string>();
var rootElement = AutomationElement.FromHandle(handle);
if(rootElement == null) return new List<string>();
var tabControl = rootElement.FindFirst(TreeScope.Descendants, TabControlCondition);
return tabControl == null ? new List<string>() : (from AutomationElement tab in tabControl.FindAll(TreeScope.Children, TabItemCondition) select tab.Current.Name).ToList();
}
public enum EBrowser {
Chrome,
Firefox
}
}
}
@authorisation
Copy link

epic

@shivan
Copy link

shivan commented Jan 27, 2023

Seems not to be working anymore on latest chrome 109 😢

New solution:

using interop.UIAutomationCore;

public static List<string> GetTabs()
        {
            List<String> result = new List<string>();

            Process[] allChromeProcesses = Process.GetProcessesByName("chrome");

            if (allChromeProcesses.Length == 0)
                return result;

            Process[] mainChromes = allChromeProcesses.Where(p => !String.IsNullOrEmpty(p.MainWindowTitle)).ToArray();

            if (mainChromes.Length == 0)
                return result;

            var uiaClassObject = new CUIAutomation();

            for (int c = 0; c < mainChromes.Length; c++)
            {
                IUIAutomationElement chromeMainUIAElement = uiaClassObject.ElementFromHandle(mainChromes[c].MainWindowHandle);

                //UIA_ControlTypePropertyId =30003, UIA_TabItemControlTypeId = 50019
                IUIAutomationCondition chromeTabCondition = uiaClassObject.CreatePropertyCondition(30003, 50019);
                var chromeTabCollection = chromeMainUIAElement.FindAll(interop.UIAutomationCore.TreeScope.TreeScope_Descendants, chromeTabCondition);

                for (int i = 0; i < chromeTabCollection.Length; i++)
                {
                    //UIA_LegacyIAccessiblePatternId = 10018, 0 -> Number of Chrome tab you want to activate
                    var lp = chromeTabCollection.GetElement(i).GetCurrentPattern(10018) as IUIAutomationLegacyIAccessiblePattern;
                    //lp.DoDefaultAction(); // to activate tab

                    String tabTitle = lp.CurrentName;
                    result.Add(tabTitle);

                }
            }

            return result;
        }

To generate the interop, start a "Developer Command Prompt" and execute:
tlbimp C:\Windows\System32\UIAutomationCore.dll -out:interop.UIAutomationCore.dll

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment