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.
Related
I am working on a C# project where I am trying to send a excel file by WhatsApp. The problem is that whenever I run the code a new instance of command window is open, and a new window is opened for chrome every time I ran the code. Before running the code, I have already log-in to my WhatsApp account and wants to use the same window and tab to avoid logging each time. I have tried to use several ways to stop this but failed to achieve the result. Please correct the code. Please don't mind my bad English.
var options = new ChromeOptions();
options.AddArguments(#"user-data-dir=C:\Users\Umesh Aggarwal\AppData\Local\Google\Chrome\User Data");
IWebDriver driver = new ChromeDriver(#"C:\Users\Umesh Aggarwal\Desktop\chromedriver_win32", options);
////// Navigate to Whatsapp web
driver.Navigate().GoToUrl("https://web.whatsapp.com/");
IReadOnlyCollection<string> windowHandles = driver.WindowHandles;
// Find already opened window with Chrome
string chromeWindow = "";
foreach (string window in windowHandles)
{
driver.SwitchTo().Window(window);
if (driver.Title.Contains("Google Chrome"))
{
chromeWindow = window;
break;
}
}
// Switch to Chrome window
driver.SwitchTo().Window(chromeWindow);
// Get all open tabs in Chrome window
IReadOnlyCollection<string> tabHandles = driver.WindowHandles;
// Find already opened tab of Whatsapp web
string whatsappTab = "";
foreach (string tab in tabHandles)
{
driver.SwitchTo().Window(tab);
if (driver.Title.Contains("Whatsapp"))
{
whatsappTab = tab;
break;
}
}
//// Switch to Whatsapp tab
driver.SwitchTo().Window(whatsappTab);
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;
}
}
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....
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).
I have my application which triggers Web Browser with specific URL .
After my program ends i want to close the web pages/tabs which i have opened..
By calling an EXE file with parameters a. Process Name b. String present in the URL
Detailed problem
How to kill firefox child process/tab from Java/C++
I used C# approach ...
I am able to find the process ID of all the tabs..
foreach (Process theprocess in processlist) {
if (theprocess.ProcessName == "iexplore") {
Console.WriteLine("Process: {0}\tID: {1}\tWindow name: {2}",
theprocess.ProcessName, theprocess.Id, theprocess.MainWindowTitle
);
}
}
Currently i can get only Window Title of the process....and in IE8 only one window title of main process is visible..
Provided i have the pids of each tabs,How to find the URL of the tab ...and kill only that tab ??
I got this help from
Access is denied - when trying to get the url (text) from address bar's handle
using SHDocVw;
.
.
foreach (InternetExplorer ieInst in new ShellWindowsClass())
Console.WriteLine(ieInst.LocationURL);
In IE7 and later versions, below code will kill only the tab which has matching string in its URL.
foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindows())
{
String url = ieInst.LocationURL;
if (url.Contains("google"))
{
ieInst.Quit();
}
}
To focus a specific tab the code is :
foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindows())
{
String url = ieInst.LocationURL;
if (url.Contains("google"))
{
int val = ieInst.HWND;
IntPtr hwnd = new IntPtr(val);
ShowWindow(hwnd, SW_MAXIMISE);
SetForegroundWindow(hwnd);
}
}
There is a way get the URL of each IExplorer instance !!
Add a reference "Microsoft Internet Controls" to the project.
The piece of code is
**foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindowsClass())
{
System.Console.WriteLine(ieInst.LocationURL);
}**
Generate the exe and Interop.SHDocVw.dll
It will work ...:)