loop through chrome tabs and close page depending on web address - c#

I would like to be able to loop through all the tabs on a chrome page and close any tabs which are youtube pages.
I have done some googling & found the code below. There are two (well probably more) issues. Firstly I have create a WPF application and added the System.Windows.Automation namespace (using visual studio 2015 .net 4.5) but AutomationElement is not recognised.
Also I am unsure of how to loop through the tabs and test if a page is a youtube page.
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)
{
AutomationPattern[] patterns = SearchBar.GetSupportedPatterns();
if(patterns.Length > 0)
{
ValuePattern val = (ValuePattern)SearchBar.GetCachedPattern(patterns[0]);
if (val.Current.Value.Contains("youtube.com") || val.Current.Value.Contains("youtube.co.uk"))
proc.Close();
}
}
}

System.Windows.Automation is in UIAutomationClient.dll.Did you add UIAutomationClient.dll as a reference to your project?
Check for value "youtube".
if (SearchBar != null)
{
AutomationPattern[] patterns = SearchBar.GetSupportedPatterns();
if (patterns.Length > 0)
{
ValuePattern val = (ValuePattern)SearchBar.GetCurrentPattern(patterns[0]);
if(val.Current.Value.Contains("youtube.com"))
proc.Close();
}
}

Based on your question, I wrote a small program to achieve this. Let me know if it works.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
class Program
{
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void Main()
{
Process[] procs = Process.GetProcessesByName("chrome");
if (procs.Length == 0)
{
Console.WriteLine("Google Chrome is not currently open");
return;
}
List<string> titles = new List<string>();
IntPtr hWnd = IntPtr.Zero;
int id = 0;
int numTabs = procs.Length;
foreach (Process p in procs)
{
if (p.MainWindowTitle.Length > 0)
{
hWnd = p.MainWindowHandle;
id = p.Id;
break;
}
}
bool isMinimized = IsIconic(hWnd);
if (isMinimized)
{
ShowWindow(hWnd, 9); // restore
Thread.Sleep(100);
}
SetForegroundWindow(hWnd);
SendKeys.SendWait("^1"); // change focus to first tab
Thread.Sleep(100);
int next = 1;
string title;
while (next <= numTabs)
{
try
{
title = Process.GetProcessById(id).MainWindowTitle.Replace(" - Google Chrome", "");
if (title.ToLower().Contains("youtube"))
{
SendKeys.SendWait("^{w}"); // close tab.
Thread.Sleep(100);
}
next++;
SendKeys.SendWait("^{TAB}"); // change focus to next tab
Thread.Sleep(100);
}
catch (Exception ex)
{
// Chrome internal process, doesn't have tab.
}
}
if (isMinimized)
{
ShowWindow(hWnd, 6); // minimize again
Thread.Sleep(100);
}
hWnd = Process.GetCurrentProcess().MainWindowHandle;
SetForegroundWindow(hWnd);
Thread.Sleep(100);
Console.WriteLine("Closed youtube tabs");
Console.ReadKey();
}
}

It can be done easily with AutoHotkey
Here is a script that will open Chrome, loop all tabs and close the YouTube ones, then minimize Chrome again
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetTitleMatchMode, 2
IfWinExist, ahk_class Chrome_WidgetWin_1
{
WinActivate, Chrome
WinGetActiveTitle, ActiveWindowOld
Loop {
Send, ^{Tab}
Sleep, 500
WinGetActiveTitle, ActiveWindow
if (ActiveWindow = ActiveWindowOld)
{
break
}
IfInString, ActiveWindow, YouTube - Google Chrome
{
Send, ^{w}
}
}
WinMinimize, Chrome
}
Credit: https://autohotkey.com/board/topic/148742-cycling-through-all-chrome-tabs-and-closing-a-specific-tab/

if (SearchBar != null)
{
bool valuePatternExist = (bool)SearchBar.GetCurrentPropertyValue(AutomationElement.IsValuePatternAvailableProperty);
if (valuePatternExist)
{
ValuePattern val = SearchBar.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (val.Current.Value.Contains("youtube.com") || val.Current.Value.Contains("youtube.co.uk"))
proc.Close();
}
}

Killing just Chrome tabs with YouTube doesn't really seem like a solution to the actual problem here. I think it would fair easier and more reliable to force all the workstations to go to sleep.
Something from this post ought to do the trick.
rundll32.exe powrprof.dll,SetSuspendState 0,1,0 perhaps?

Related

How to check if 2 or 3 apps are open and active in C#

void check()
{
Process[] processes = Process.GetProcessesByName("app name");
if (processes.Length == 0)
{
//Not Running
}
else
{
//Is running
}
how to check it if 2 apps are open
vlc and Notepad for example
You can try querying with a help of Linq:
using System.Diagnostics;
using System.Linq;
...
string[] toFind = new [] {
"vlc",
"Notepad"
};
// If all processes mentioned in toFind are found
bool AllAreFound = !toFind
.Except(Process
.GetProcesses()
.Where(process => !process.HasExited)
.Select(process => process.ProcessName),
StringComparer.OrdinalIgnoreCase)
.Any();
You may want to add
.Where(process => process.MainWindowHandle != IntPtr.Zero)
if "open and active" process also means "has main window"
Edit: If you want just query for a single process, single Any will be enough:
private static bool HasRunningProcess(string processName) => Process
.GetProcesses()
.Any(process => string.Equals(processName,
process.ProcessName,
StringComparison.OrdinalIgnoreCase) &&
!process.HasExited &&
process.MainWindowHandle != IntPtr.Zero);
Then you can put
if (HasRunningProcess("vlc")) {
...
}
else if (HasRunningProcess("Notepad")) {
...
}

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?
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.
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;

Opera - driver.WindowHandles returns wrong count

In my scenario, I'm verifying whether clicking on a link navigates to another page (verifying for the page title). IE, FF and chrome return 2 as expected but Opera returns 4. I didn't have any other Opera instances opened at the time of running tests. It clicks on the link and required page is opened but WindowHandles returns 4.
Code:
string BaseWindow = Drivers._driverInstance.CurrentWindowHandle;
Drivers._driverInstance.SwitchTo().Frame(Drivers._driverInstance.FindElement(By.ClassName("iframe-fix")));
if (Drivers._driverInstance.GetType().Name.ToString() == "InternetExplorerDriver")
{
IJavaScriptExecutor js = (IJavaScriptExecutor)Drivers._driverInstance;
js.ExecuteScript("arguments[0].click();", Drivers._driverInstance.FindElement(By.LinkText("Professional Services.")));
}
else
{
Drivers._driverInstance.FindElement(By.LinkText("Professional Services.")).Click();
}
System.Collections.ObjectModel.ReadOnlyCollection<string> handles = Drivers._driverInstance.WindowHandles;
if (handles.Count == 2)
{
foreach (string handle in handles)
{
if (handle != BaseWindow)
{
string title = Drivers._driverInstance.SwitchTo().Window(handle).Title;
Assert.AreEqual("title of the page", Drivers._driverInstance.Title);
}
}
}
else
{
Assert.Fail("WindowHandles returns " + handles.Count + " instead of 2");
}
Drivers._driverInstance.SwitchTo().Window(BaseWindow);
Can someone suggest why Opera returns 4 instead of 2.
Thanks.
The Opera driver doesn't return the right number of handles. This issue has already been reported to the project but it seems that the project is no longer maintained:
https://github.com/operasoftware/operachromiumdriver/issues/15
I encountered the same thing as you with Opera driver, plus (if I remember it right), the CurrentWindowHandle property doesn't work either.
Workaround:
public static void SwitchToPopup(TestTarget target, bool toPopup)
{
if (target.IsOpera)
{
if (toPopup)
{
_windowIndex += 3;
new WebDriverWait(target.Driver, TimeSpan.FromSeconds(DefaultTimeoutInSeconds)).Until(d => d.WindowHandles.Count > _windowIndex);
}
else
{
_windowIndex -= 3;
}
target.Driver.SwitchTo().Window(target.Driver.WindowHandles[_windowIndex]);
}
else
{
IEnumerable<string> windowHandles = toPopup ? target.Driver.WindowHandles : target.Driver.WindowHandles.Reverse();
bool bFound = false;
foreach (string windowHandle in windowHandles)
{
if (bFound)
{
target.Driver.SwitchTo().Window(windowHandle);
break;
}
bFound = windowHandle == target.Driver.CurrentWindowHandle;
}
}
}

Why Driver.SwitchTo not always works in selenium?

I use the code
windowHandles = SeleniumHelper.WindowHandles();
// click...
if (SeleniumHelper.WindowHandles().Count > windowHandles.Count)
{
windowHandles = SeleniumHelper.WindowHandles();
while (pageTitle == SeleniumHelper.Driver.Title)
{
SeleniumHelper.Driver.SwitchTo().Window(windowHandles[windowHandles.Count - 1]);
Thread.Sleep(2000);
}
// do something...
SeleniumHelper.Driver.Close();
SeleniumHelper.BackToMainWindow();
}
The problem is that the driver finds the window, but does not switch to it.
Maybe there is a different way to switch to another window, like switch by javascript?
The problem is in
SeleniumHelper.Driver.SwitchTo().Window(windowHandles[windowHandles.Count - 1]);
You always switch to the last window regardless the while loop condition. Try this
string currentWindoe = SeleniumHelper.Driver.CurrentWindowHandle();
while (pageTitle != SeleniumHelper.Driver.Title)
{
SeleniumHelper.Driver.SwitchTo().Window(SeleniumHelper.Driver.CurrentWindowHandle());
Thread.Sleep(2000);
}
Or
string currentWindow = SeleniumHelper.Driver.CurrentWindowHandle();
foreach (string window in SeleniumHelper.Driver.WindowHandles())
{
if (!window.equals(currentWindow))
{
SeleniumHelper.Driver.SwitchTo().Window(window));
}
}

Check If process is running every minute

I have this basic code that will check for notepad running every minute.
namespace Watcher
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; ; i--)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains("notepad"))
{
Console.WriteLine("True");
}
Console.WriteLine("NFalse");
}
Thread.Sleep(10000);
}
}
}
}
The problem is that it returns "NFalse" for every running process (It will print 100 of them for example). How can I just make this print once to show that the process is not running?
Refactor your code.
You're doing too much in one method. Put your code that checks to see if notepad is running into a separate method:
static bool CheckIfProcessIsRunning(string nameSubstring)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains(nameSubstring))
{
return true;
}
}
return false;
}
You could simplify this further using LINQ:
static bool CheckIfProcessIsRunning(string nameSubstring)
{
return Process.GetProcesses().Any(p => p.ProcessName.Contains(nameSubstring));
}
Once you have written this method, all that remains is to call it and print the right message depending on whether it returns true or false.
while (true)
{
string message = CheckIfProcessIsRunning("notepad") ? "True" : "NFalse";
Console.WriteLine(message);
Thread.Sleep(10000);
}
Now instead of one long method with complex logic, you have two very simple methods.
You just need to check the process you are interested in. Don't bother looping over all the running processes. Use Process.GetProcessByName().
for (int i = 0; ; i--)
{
Process[] processes = Process.GetProcessByName("notepad++"); // Without extension
if(processes.Length > 0){
Console.WriteLine("True");
}
else{
Console.WriteLine("False");
}
Thread.Sleep(10000);
}
Just change to this, so you only print out once.
var b = false;
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains("notepad"))
{
if (!b) b = true;
}
}
Console.WriteLine(b);
Well this seems to work well.
Dim x = Process.GetProcesses().ToList().FirstOrDefault(Function(p) p.ProcessName.Contains("Notepad"))
if x Is Nothing then
Console.WriteLine("false")
end if

Categories