Obtaining tab title/text using process ID - c#

I don't want to use SetForegroundWindow(), sending keyboard keys or similar techniques, because that can cause issues (unexpected behaviour) in my software.
I have tried to find the title using Cheat Engine program (but haven't found anything useful as Google Chrome seems to work "upside-down").
So I went step ahead, using Process Hacker program I have realized that there is a parent (chrome.exe) process with a valid window handle to the current active tab and all other chrome processes are children of it a.k.a. background processes (with invalid window handle).
By browsing deeper into windows of chrome.exe (parent process), I have found the class name of the window handle being "Chrome_WidgetWin_1" and current active tab's title/text.
Here's a picture of Google Chrome's Task Manager.
I'm looking for a function in C# or C or C++ that will take an integer (process ID) and return a string (tab title/text).
static string GetChromeTabTitle(uint processId)
{
// Assuming I call this function with valid process identifier (PID).
// What do I do next, here??
}

The best way I have found is by using the System.Windows.Automation library. It allows interacting with an application (primarily for accessibility purposes), but you can use it for other purposes like getting Chrome tabs.
Note that this will only work when the Chrome windows is not minimized.
The process is not exactly simple, if you want you can look how I did it in my own project, though it's not something you can just copy it paste, you'll find what you need in the ChromeTabsFinder: https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Plugins.ExpandBrowsersTabs/Chrome/ChromeTabsFinder.cs
Here's the code (you'll need the automation librairies):
public IEnumerable<ITab> GetTabsOfWindow(IntPtr hWnd)
{
var cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(AutomationElement.LocalizedControlTypeProperty);
cacheRequest.Add(SelectionItemPattern.Pattern);
cacheRequest.Add(SelectionItemPattern.SelectionContainerProperty);
cacheRequest.TreeScope = TreeScope.Element;
AutomationElement tabBarElement;
using (cacheRequest.Activate())
{
var chromeWindow = AutomationElement.FromHandle(hWnd);
var mainElement = chromeWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (mainElement == null)
yield break;
tabBarElement = mainElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab"));
}
if(tabBarElement == null)
yield break;
var tabElements = tabBarElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab item"));
for (var tabIndex = 0; tabIndex < tabElements.Count; tabIndex++)
{
yield return "Tab: " + tabElements[tabIndex].Current.Name + ", Index: " + tabIndex + 1;
}
}

Related

How to determine whether the WebView2 DevTools are currently open?

The DevTools that come with WebView2 can either be opened by the user e.g. by pressing the F12 key, and they also can be opened programatically from code.
What I do not know is how to determine whether the DevTools are currently being opened.
I want to store the state (opened/closed) upon the exit of my WPF application so that I can show them automatically upon the next start of my WPF application.
My question
How to get the current showing/not showing state of the WebView2 Developer Tools?
Update 1
I found a dirty hack because of the fact that the window title of the DevTools looks like this:
DevTools - localhost:38472/my/url
Whereas localhost:38472/my/url is the currently loaded URL of the WebView2.
So I'm doing the following.
With the help of this SO answer, I was able to create this method:
public static IEnumerable<string> GetAllDesktopMainWindowTitles()
{
// https://stackoverflow.com/a/7268375/107625
var processlist = Process.GetProcesses();
foreach (var process in processlist)
{
if (!string.IsNullOrEmpty(process.MainWindowTitle))
{
yield return process.MainWindowTitle;
}
}
}
I then can use it like this:
public static bool AreDevToolsOpen(this WebView2? webView)
{
var url = webView?.Source?.ToString();
if (string.IsNullOrEmpty(url)) return false;
url = url.Replace(#"https://", string.Empty).Replace(#"http://", string.Empty);
var titles = GetAllDesktopMainWindowTitles().ToList();
var devToolsOpen = titles.Any(t => t.Contains(#"DevTools") && t.Contains(url));
return devToolsOpen;
}
While this is a total hack, it seems to work good enough for now.
Still, I'm looking for a better way to do it.

WinAppDriver, Appium, C#, Connecting To An Already Running Window on Windows 10

I'm trying to automate the use of an application with WinAppDriver and Appium on Windows 10 using C# .Net.
I'm trying to attach to an already running Window and I've been failing to find a way. I've tried many examples on the web, including from the github site for the WinAppDriver.
https://github.com/microsoft/WinAppDriver
I finally found a way to get a handle for the process, but when I try to connect it fails. It claims it's not a top level window.
The specific error is: "OpenQA.Selenium.WebDriverException: 'b2c is not a top level window handle'"
I've tried the "app" entry instead of "appTopLevelWindow", and it did not work.
Here is the code I'm running:
private void grabWindowsProcesses2()
{
int number = 0;
_txtBxOutput.Text = "";
IntPtr myAppTopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
number++;
_txtBxOutput.Text += number.ToString() + ") " + clsProcess.ProcessName + "\n";
if (clsProcess.ProcessName.Contains("VisualBoyAdvance"))
{
myAppTopLevelWindowHandle = clsProcess.Handle;
break;
}
}
var appTopLevelWindowHandleHex = myAppTopLevelWindowHandle.ToString("x");
AppiumOptions appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("platformName", "Windows");
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
appCapabilities.AddAdditionalCapability("appTopLevelWindow", appTopLevelWindowHandleHex);
/*
* Error I get here is: OpenQA.Selenium.WebDriverException: 'b2c is not a top level window handle'
*/
_visualBoyAdvance = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
}
Does anyone have any ideas about what I'm doing wrong? I'm fairly new to automating a desktop. I don't know, maybe there is a better way.

2 Processes with same name and random PID's - How can I know which one I need?

So I have a piece of software I'm hooking into. My problem is that the software has a launcher-like home window. From that home I click "Start" and it opens a second process with the same name. In my task manager they are identical in processes/details.
My program has to hook into this second process and I can only hook after the process exists. There's no way to launch it directly without the home window.
They both have randomized PID's each time. I can't figure out a way to differentiate the two processes. If my program hooks the wrong one it will freeze up the software.
p = Process.GetProcessesByName("programName");//This is specifically just the window name so that we can interact with the client using keystrokes.
System.Diagnostics.Debug.WriteLine("Attempting to hook into the program...");
process = Process.GetProcessesByName("programName").ToList().FirstOrDefault();
if (process == null) {
System.Diagnostics.Debug.Write("Hook failed! Make sure the program is open.");
Application.Exit();
}
if (process != null) {
mreader.ReadProcess = process;//Set the process we want to read to be the one we just got in the above line.
mreader.OpenProcess();//Gets the handle of the process and hook in.
for (int i = 0; i < process.Modules.Count; i++) {
System.Diagnostics.Debug.WriteLine("Module " + i + ": " + process.Modules[i].ModuleName);
if (process.Modules[i].ModuleName == "programcore.dll") {
System.Diagnostics.Debug.WriteLine("programcore.dll found at Module " + i);
iCounter = i;//Set the number programcore is on to our counter so we can hook in each time, even if it moves.
System.Diagnostics.Debug.WriteLine("Success!");
break;
}
}
}
This is how I'm hooking into the client to read/write the memory. Unfortunately both processes have the programcore.dll that I need so I don't know how to tell them apart.

Stop process if webBrowser control hangs

I am using the WebBrowser control.
This works fine most of the time however wehn navigating to a new page or waiting for a new page to load can sometimes hangs.
Is there a way to catch this? i.e. if the page is failing to navigate or load after a certain amount of time then kill the process?
I am using the - webBrowser1_DocumentCompleted event to pick up ertain behaviours when the page loads/navigates as expected however not sure how to catch if a page is hanging??
Maby you should try to implement some kind of timeout logic? There are quite many samples in web about this. F.e. this one
Also you might be interested in this event of WebBrowserControl ProgressChanged
This is due to that webbrowser component is very basic model of internet explorer, and it get stuck at ajax pages. You can fix this problem explicitly to use latest version of internet explorer... Using this code...
try
{
string installkey = #"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
string entryLabel = "YourExe.exe";
string develop = "YourExe.vshost.exe";//This is for Visual Studio Debugging...
System.OperatingSystem osInfo = System.Environment.OSVersion;
string version = osInfo.Version.Major.ToString() + '.' + osInfo.Version.Minor.ToString();
uint editFlag = (uint)((version == "6.2") ? 0x2710 : 0x2328); // 6.2 = Windows 8 and therefore IE10
Microsoft.Win32.RegistryKey existingSubKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(installkey, false); // readonly key
if (existingSubKey.GetValue(entryLabel) == null)
{
existingSubKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(installkey, true); // writable key
existingSubKey.SetValue(entryLabel, unchecked((int)editFlag), Microsoft.Win32.RegistryValueKind.DWord);
}
if (existingSubKey.GetValue(develop) == null)
{
existingSubKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(installkey, true); // writable key
existingSubKey.SetValue(develop, unchecked((int)editFlag), Microsoft.Win32.RegistryValueKind.DWord);
}
}
catch
{
MessageBox.Show("You Don't Have Admin Previlege to Overwrite System Settings");
}
}
Right Click Both your Exe. And vshost.exe and Run as Administrator To Update Registry for this Application....

C# WebDriver problems with SwitchTo() with IE

When IE opens a second browser window using onclick.window.open() I need to do some processing on the second window and then close the window.
I have tried the following recomendations found on stackoverflow regarding window handles:
string popupHandle = string.Empty;
ReadOnlyCollection<string> windowHandles = selObj.Driver.WindowHandles;
foreach (string handle in windowHandles)
{
if (handle != originalHandle)
{
popupHandle = handle;
break;
}
}
selObj.Driver.SwitchTo().Window((popupHandle));
selObj.Driver.Close();
selObj.Driver.SwitchTo().Window(originalHandle);
Before I execute this code I attempt to click on a control of the new window to change focus.
Webdriver cannot locate the control (I also tried to use the WebDriverWait class to wait for the second browse to fully load.
WebDriverWait also throws an exception after the wait seconds.
Upon attempting to execute the:
selObj.Driver.SwitchTo().Window((popupHandle));
I receive the following exception:
No response from server for url http://
I know this is C# and IE, wondering if anyone has found a work around for this?
Thanks
My trio C#+WebDriver+Ie works fine.
Yes - IE has to be focused.
Please, Try my code. Code works with IE, Chrome and Firefox
protected void SwitchToWindow(string name)
{
foreach (string item in _driver.WindowHandles)
{
if (_driver.SwitchTo().Window(item).Title.Contains(name))
{
_driver.SwitchTo().Window(item);
break;
}
}
}
Where _driver is selObj.Driver for your case.
A little bit late but can be effective :
var ieOptions = new InternetExplorerOptions
{
IntroduceInstabilityByIgnoringProtectedModeSettings = true
};
ieOptions.ForceCreateProcessApi = true;
ieOptions.BrowserCommandLineArguments = "-private";
driver = new InternetExplorerDriver(IE_DRIVER_PATH, ieOptions);
You have to set ForceCreateProcessApi at true. It's only with IE that you have to set this kind of options.
And if the program threw an other error like :
"This issue occurs when the following registry entry is set to 0:
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\TabProcGrowth"
You will have to create a key in register (using regedit) and set this key
to 0 (Type: REG_DWORD and Value Type: Decimal).

Categories

Resources