简体   繁体   中英

How to activate a Google Chrome tab item using UI Automation

I am using this code from a C# application to find a tab in Google Chrome:

        Process[] procsChrome = Process.GetProcessesByName("chrome");
        foreach (Process chrome in procsChrome)
        {
            // the chrome process must have a window
            if (chrome.MainWindowHandle == IntPtr.Zero)
            {
                continue;
            }

            AutomationElement root = AutomationElement.FromHandle(chrome.MainWindowHandle);
            /*
            Condition condNewTab = new PropertyCondition(AutomationElement.NameProperty, "Nueva pestaña");
            AutomationElement elmNewTab = root.FindFirst(TreeScope.Descendants, condNewTab);
            // get the tabstrip by getting the parent of the 'new tab' button 
            TreeWalker treewalker = TreeWalker.ControlViewWalker;
            AutomationElement elmTabStrip = treewalker.GetParent(elmNewTab);
             */
            // loop through all the tabs and get the names which is the page title 
            Condition condTabItem = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem);
            foreach (AutomationElement tabitem in root.FindAll(TreeScope.Descendants, condTabItem))
            {
                Console.WriteLine(tabitem.Current.Name);

                // I NEED TO ACTIVATE THE TAB HERE

                break;
            }

            Condition condUrl = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit);
            foreach (AutomationElement edit in root.FindAll(TreeScope.Descendants, condUrl))
            {
                string value = ((System.Windows.Automation.ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern)).Current.Value;
                Console.WriteLine(value);
            }

        }

I need to select certain tab item using UI Automation. How can I do it?

I needed to solve similar problem. Since Chrome doesn't fully implement Windows Automation features, it has to be implemented differently.

Thanks to this GitHub project I was able to activate the correct Chrome tab. The trick is to press Ctrl + tab index to activate the tab in the case its position is between 1 and 8 (9 switches to the last tab, see Chromebook keyboard shortcuts ). For tabs appearing further in the collection Ctrl + Tab is pressed repeatedly until the desired tab is reached.

However, it is not that easy, since sometimes the tabs can appear in the UI automation collection out of order. I have fixed this by calling the TryGetClickablePoint method for each tab and sorting the tabs by the X coordinate of the point returned.

bool ActivateChromeTab(string title)
{
  Process[] procsChrome = Process.GetProcessesByName("chrome");
  foreach (Process proc in procsChrome)
  {
    if (proc.MainWindowHandle == IntPtr.Zero)
    {
        continue;
    }
    AutomationElement root = AutomationElement.FromHandle(proc.MainWindowHandle);
    Condition condNewTab = new PropertyCondition(AutomationElement.NameProperty, "New Tab");
    AutomationElement elmNewTab = root.FindFirst(TreeScope.Descendants, condNewTab);
    TreeWalker treewalker = TreeWalker.ControlViewWalker;
    AutomationElement elmTabStrip = treewalker.GetParent(elmNewTab);
    Condition condTabItem = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem);
    var index = 0;

    var tabItems = elmTabStrip.FindAll(TreeScope.Children, condTabItem);

    var coll = new List<AutomationElement>();
    foreach (AutomationElement element in tabItems)
    {
        coll.Add(element);
    }

    bool NameMatch(string name)
    {
        return name == title || name.StartsWith(title + " ");
    }

    // short-circuit the search when no searched string cannot be found
    if (!coll.Any(e => NameMatch(e.Current.Name)))
    {
        continue;
    }

    var t = new Stopwatch();
    t.Start();
    var withPoints = coll.AsParallel().Select(e =>
    {
        var point = new System.Windows.Point(int.MaxValue, int.MaxValue);
        if (e.TryGetClickablePoint(out point))
        {

        }

        return new
        {
            Name = e.Current.Name,
            Element = e,
            Point = point
        };
    }).OrderBy(e => e.Point.X);

    foreach (var tabItem in withPoints)
    {
        index++;
        var name = tabItem.Name;
        if (NameMatch(name))
        {
            SetForegroundWindow(proc.MainWindowHandle); // activate window
            Select(index); // select tab                
            return true;
        }
    }
  }

  return false;
}

And the method to select the tab:

public void Select(int tabIndex)
{
  const int maxShortcutNumber = 8;

  if (tabIndex <= 0) { return; }

  KeyDown(LCtrl);

  if (tabIndex <= maxShortcutNumber)
  {
    KeyPress(GetKeyNumber(tabIndex));
  }
  else
  {
    KeyPress(GetKeyNumber(maxShortcutNumber));

    for (var i = 0; i < tabIndex - maxShortcutNumber; i++)
    {
        i.Dump();
        const int timeToDigestPreviousKeyPress = 75;
        Thread.Sleep(timeToDigestPreviousKeyPress);
        KeyPress(Tab);
    }
  }
  KeyUp(LCtrl);
}

And keyboard handling methods (adapted from KeyboardSend class) [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

public static byte GetKeyNumber(int number)
{
    if (number < 0 || number > 9)
        throw new ApplicationException("Invalid number for key press.");

    return (byte)(0x30 + number);
}

public static void KeyDown(byte vKey)
{
    keybd_event(vKey, 0, KEYEVENTF_EXTENDEDKEY, 0);
}

public static void KeyUp(byte vKey)
{
    keybd_event(vKey, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
}

public static void KeyPress(byte vKey)
{
    KeyDown(vKey);
    KeyUp(vKey);
}

public static byte LCtrl = 0xA2; //VK_LCONTROL
public static byte LWin = 0x5B; //VK_LWIN
public static byte LAlt = 0xA4; //VK_LMENU
public static byte Tab = 0x09; //VK_TAB
private const int KEYEVENTF_EXTENDEDKEY = 1;
private const int KEYEVENTF_KEYUP = 2;

For those desperate souls, still searching for an answer. Here is my method, based solely on UI Automation API, without focusing windows and sending click events or hotkeys. To use the code below you need to use interop reference for UIAutomationCore.dll as described by Guy Barker .

    Process[] allChromeProcesses = Process.GetProcessesByName("chrome");
    Process[] mainChromes = allChromeProcesses.Where(p => !String.IsNullOrEmpty(p.MainWindowTitle)).ToArray();
    //...
    //Here you need to check if you have found correct chrome instance
    //...
    var uiaClassObject = new CUIAutomation();

    IUIAutomationElement chromeMainUIAElement = uiaClassObject.ElementFromHandle(mainChromes[0].MainWindowHandle);
    //UIA_ControlTypePropertyId =30003, UIA_TabItemControlTypeId = 50019
    IUIAutomationCondition chromeTabCondition = uiaClassObject.CreatePropertyCondition(30003, 50019); 
    var chromeTabCollection = chromeMainUIAElement.FindAll(TreeScope.TreeScope_Descendants, chromeTabCondition);
    //UIA_LegacyIAccessiblePatternId = 10018, 0 -> Number of Chrome tab you want to activate
    var lp = chromeTabCollection.GetElement(0).GetCurrentPattern(10018) as IUIAutomationLegacyIAccessiblePattern;
    lp.DoDefaultAction();

The only thing you need to remember is that searching of tabs for minimized Chrome window is impossible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM