I'm aware of selenium (but for some reason, the real page that has a complex HTML, I couldn't retrieve it well using DOM, so I eventually give up - I'm trying luck with FlaUI). To start out, I'm trying some simple task such as open a page and click on a button. But it isn't working. It didn't result in any error, the application seems to just hangs. What am I missing?
my code:
var process = Process.Start(#"C:\Program Files\Mozilla Firefox\firefox.exe",
"file:///C:/Users/jckj33/Desktop/button.html");
//process.WaitForInputIdle();
var app = FlaUI.Core.Application.Attach(process.Id);
Debug.WriteLine("wating...");
app.WaitWhileBusy();
Debug.WriteLine("waiting done!!");
Thread.Sleep(5000);
using (var automation = new UIA3Automation())
{
var window = app.GetMainWindow(automation);
var button = window
.FindFirstDescendant(cf => cf.ByControlType(FlaUI.Core.Definitions.ControlType.Button))
.AsButton();
button?.Invoke();
}
where button.html is:
<input type='button' value='Click me' onclick='alert("hello, world!")'>
Related
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.
I need to download files from opensubtitles.org trough my application which is written in GTK# and C#. The app is based on .NET 4.0 framework.
At first this was the code I was using:
var tZip = new FastZip();
try {
var tRequest = (HttpWebRequest)HttpWebRequest.Create(tDownloadUrl);
var tZipResponse = (HttpWebResponse)tRequest.GetResponse();
using (var tStream = tZipResponse.GetResponseStream()) {
using (var tMemStream = new MemoryStream()) {
tStream.CopyTo(tMemStream);
var tTempPath = Globals.video_location + "OSD";
Directory.CreateDirectory(tTempPath);
tZip.ExtractZip(tMemStream, tTempPath, FastZip.Overwrite.Always, null, #"\.srt$", null, false, true);
var tDirInfo = new DirectoryInfo(tTempPath);
var tFileInfo = new FileInfo(Globals.location_video);
var tSrtFile = tDirInfo.EnumerateFiles().FirstOrDefault();
if (tSrtFile == null) {
writeLog("No .srt file found in zip..");
goto text;
}
writeLog("Downloaded and unpacked: " + tSrtFile.Name);
File.Copy(tSrtFile.FullName, Globals.video_location+Globals.video_name+".srt", true);
Globals.savedTitle = Globals.video_location+Globals.video_name+".srt";
// clean up..
Directory.Delete(tTempPath, true);
writeLog("Deleted temp folder.");
return true;
}
}}
And that worked really well up until few days ago, now it is returning a bunch of html code instead of .zip file. I tried even something like this:
WebClient client = new WebClient();
client.DownloadFile(link, #"OSD\test.zip");
But everything just keeps returning bunch of html code.
The link I am usually trying to download is something like this:
http://dl.opensubtitles.org/en/download/subad/4287952
If you click on the link above it will just redirect you to the opensubtitles.org page of that particular subtitle. But if you right mouse click on that link and then select "open in new tab" or "open in new window" it will automatically start the download. (Tested in Firefox)
Also as soon as I paste that link in "Internet Download Manager" application, it will start the download of the zip file automatically.
If you can help me to resolve this problem I will truly be grateful.
Kind Regards.
I got into this problem because I was filtering the website xml directly. Like from a link such as this one: opensubtitles.org example
And in the beginning it used to work well, but then they changed something on the website and it stopped working. So what I did was build on top of this: OSHandler
That handler library is using XML-RPC so I believe there won't be any problems in the future.
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 can't seem to get past this exception on my coded UI project for a window dialog box. I've tried using always search and some other options. I was trying to use the option of open a window dialog button but that didn't seem to work since i did not see the open button only "OK" and a few ones that didn't pertain to what i was trying to do.
I see one of my window is returning 5 windows, I'm trying to use the code Order Of overcation but that doesn't seem to work.
Currently here is my latest code which is still not working and still giving me the same exception error
WinWindow myDialogBox = new WinWindow();
myDialogBox.FilterProperties.Add(WinWindow.PropertyNames.OrderOfInvocation, "5");
myDialogBox.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
myDialogBox.SearchProperties[WinWindow.PropertyNames.ClassName] = "DirectUIHWND";
myDialogBox.SearchProperties[WinWindow.PropertyNames.ControlId] = "0";
myDialogBox.DrawHighlight();
WinWindow MyOpbenButton = new WinWindow(myDialogBox);
// myDialogBox.SearchProperties.Add(WinWindow.PropertyNames.OrderOfInvocation, "0");
MyOpbenButton.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
MyOpbenButton.SearchProperties[WinWindow.PropertyNames.Name] = "&Open";
MyOpbenButton.SearchProperties[WinWindow.PropertyNames.ClassName] = "Button";
//MyOpbenButton.SearchProperties[WinWindow.PropertyNames.ControlId] = "0";
MyOpbenButton.DrawHighlight();
//Testing this window produces 5 window with the same name and
control ID.
WinWindow matchingWindows = new WinWindow(myDialogBox);
matchingWindows.SearchProperties.Add("Name", "&Open");
// matchingWindows.SearchProperties.Add("ControlId", "0");
matchingWindows.SearchProperties.Add("ClassName", "Button");
UITestControlCollection windowsFound = matchingWindows.FindMatchingControls();
WinPane MyOpbenPane = new WinPane(MyOpbenButton);
MyOpbenPane.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
//MyOpbenPane.SearchProperties[WinPane.PropertyNames.Name] = "Open";
MyOpbenPane.SearchProperties[WinPane.PropertyNames.ClassName] = "Button";
// var myDialog = sharedControls.UIWindowsInternetExploWindow.UIWindowsInternetExplo_Open_Save_Dialog_LinksPage.UIItemWindow;
MyOpbenPane.DrawHighlight();
return MyOpbenPane;
The error message that this is producing is the following:
Another control is blocking the control. Please make the blocked
control visible and retry the action. Additional Details:
\r\nTechnologyName: 'MSAA'\r\nName: '&Open'\r\nClassName:
'Button'\r\nControlType: 'Window'\r\n
I used the test builder to get around this issue.
I found that the dialog screen was being captured with the
classname of 32770. I also found that i could not pass off my browser window to window from there i hand coded the remaining screen using the test builder.
here is the code below.
WinWindow window = new WinWindow();
window.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
window.SearchConfigurations.Add(SearchConfiguration.VisibleOnly);
window.SearchProperties[WinWindow.PropertyNames.ClassName] = "#32770";
window.SearchProperties[WinWindow.PropertyNames.Name] = "Windows Internet Explorer";
window.WindowTitles.Add("Windows Internet Explorer");
//window.DrawHighlight();
WinCustom myDialogBox = new WinCustom(window);
myDialogBox.SearchConfigurations.Add(SearchConfiguration.VisibleOnly);
myDialogBox.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
myDialogBox.SearchProperties[WinCustom.PropertyNames.ControlType] = "Dialog";
myDialogBox.SearchProperties[WinCustom.PropertyNames.Name] = "Windows Internet Explorer";
myDialogBox.WindowTitles.Add("Windows Internet Explorer");
//myDialogBox.DrawHighlight();
WinWindow myDialogwindow = new WinWindow(window);
myDialogwindow.SearchConfigurations.Add(SearchConfiguration.VisibleOnly);
myDialogwindow.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
myDialogwindow.SearchProperties[WinWindow.PropertyNames.AccessibleName] = "Windows Internet Explorer";
myDialogwindow.SearchProperties[WinWindow.PropertyNames.ClassName] = "DirectUIHWND";
myDialogwindow.WindowTitles.Add("Windows Internet Explorer");
return myDialogwindow;
I run into this a lot. I have an application on which the tabs always get reported as hidden, even though they're clearly visible. What has worked well for me so far has been to use keyboard.sendkeys to send keystrokes that select the tab.
I'm having an issue with automating the process of downloading a file from a website. The website has a Java button, that when clicked, triggers the download of an Excel file. I'm using the most recent build of Watin (v2.1).
I've managed to get Watin to log into the website, navigate to the appropriate page, change parameters on the page, and click the button to start the download.
However, when the download has completed, the IE9 download box appears, and nothing happens, until Watin timesout.
I'd appreciate any suggestions as I can't see any way of downloading a file, or getting it to save the file. Even if it passed 'Alt+S' to the page, that would save it. I've tried running it through WatinTestRecorder and that doesn't prompt for saving.
using (var browser = new IE(sLogin))
{
browser.AddDialogHandler(new OKDialogHandler());
browser.AddDialogHandler(new DialogHandlerHelper());
browser.AddDialogHandler(new ConfirmDialogHandler());
browser.AddDialogHandler(new ReturnDialogHandlerIe9());
browser.TextField(Find.ByName("txtUserID")).TypeText("username");
browser.TextField(Find.ByName("txtPassword")).TypeText("password");
browser.Button(Find.ByName("btnLogin")).Click();
browser.WaitForComplete();
browser.GoTo(targetUri);
browser.SelectList("ctl00_phFormContent_ucOptionParam0_lst").SelectByValue("4");
browser.Button(Find.ByName("ctl00$phFormButtonBar$btnRun")).Click();
browser.WaitForComplete();
//Some code to download the file here!
}
This should be supported since version 1.1.0.4000. The release notes for that version aren't online anymore (http://watin.org/documentation/), but I found it in Googles cache (http://svn6.assembla.com/svn/ci-samples/dotnet/watir/website/releasenotes-1-1-0-4000.html)
It should be something like:
using(IE ie = new IE(someUrlToGoTo))
{
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
ie.AddDialogHandler(fileDownloadHandler);
ie.Link("startDownloadLinkId").Click();
fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
fileDownloadHandler.WaitUntilDownloadCompleted(200);
}
EDIT:
After the comments below, this answer was accepted. So I'm assuming the following code works (which is taken from the link to SourceForge in my last comment, notice the ClickNoWait):
using(IE ie = new IE(someUrlToGoTo))
{
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
ie.AddDialogHandler(fileDownloadHandler);
ie.Link("startDownloadLinkId").ClickNoWait();
fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
fileDownloadHandler.WaitUntilDownloadCompleted(200);
}
The accepted answer did not work for me because IE 9 pops up a "Notification" you have to navigate before you can get to the actual Save As dialog (and WatiN cannot handle notifications automatically). I followed Borris Pavlov's link which did a good job showing the way. I cleaned up the code a bit that was posted there and this was the resulting file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WatiN.Core;
using WatiN.Core.Native.Windows;
using System.Threading;
using System.Windows.Automation;
namespace MyProject
{
public static class BrowserExtensionMethods
{
public static void DownloadIeFile(this IE browser,string saveAsFilename=null)
{
// see information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
Window windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5));
TreeWalker dialogElementTreeWalker = new TreeWalker(Condition.TrueCondition);
AutomationElement mainWindow = dialogElementTreeWalker.GetParent(AutomationElement.FromHandle(browser.hWnd));
Window windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5));
// if doesn't work try to increase sleep interval or write your own waitUntill method
Thread.Sleep(1000);
windowDialog.SetActivate();
AutomationElementCollection dialogElements = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition);
if (string.IsNullOrEmpty(saveAsFilename))
{
ClickSave(dialogElements);
}
else
{
ClickSaveAs(mainWindow, dialogElements,saveAsFilename);
}
}
private static void ClickSaveAs(AutomationElement mainWindow, AutomationElementCollection dialogElements,string filename)
{
foreach (AutomationElement element in dialogElements)
{
if (element.Current.Name.Equals("Save"))
{
AutomationElementCollection dialogSubElements = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
InvokePattern clickPatternForSaveDropdown = (InvokePattern)dialogSubElements[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
clickPatternForSaveDropdown.Invoke();
Thread.Sleep(3000);
AutomationElementCollection dialogElementsInMainWindow = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement currentMainWindowDialogElement in dialogElementsInMainWindow)
{
if (currentMainWindowDialogElement.Current.LocalizedControlType == "menu")
{
// first array element 'Save', second array element 'Save as', third second array element 'Save and open'
InvokePattern clickMenu = (InvokePattern)currentMainWindowDialogElement.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
clickMenu.Invoke();
Thread.Sleep(5000);
ControlSaveDialog(mainWindow, filename);
break;
}
}
}
}
}
private static void ClickSave(AutomationElementCollection dialogElements)
{
foreach (AutomationElement element in dialogElements)
{
// You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
if (element.Current.Name.Equals("Save"))
{
// if doesn't work try to increase sleep interval or write your own waitUntil method
// WaitUntilButtonExsist(element,100);
Thread.Sleep(1000);
AutomationPattern[] automationPatterns = element.GetSupportedPatterns();
// replace this foreach if you need 'Save as' with code bellow
foreach (AutomationPattern currentPattern in automationPatterns)
{
// '10000' button click event id
if (currentPattern.Id == 10000)
{
InvokePattern click = (InvokePattern)element.GetCurrentPattern(currentPattern);
click.Invoke();
}
}
}
}
}
private static void ControlSaveDialog(AutomationElement mainWindow, string path)
{
//obtain the save as dialog
//*** must disable throwing of the NonComVisibleBaseClass "exception" for this to work in debug mode:
// 1. Navigate to Debug->Exceptions...
// 2. Expand "Managed Debugging Assistants"
// 3. Uncheck the NonComVisibleBaseClass Thrown option.
// 4. Click [Ok]
//***copied from http://social.msdn.microsoft.com/Forums/en-US/27c3bae8-41fe-4db4-8022-e27d333f714e/noncomvisiblebaseclass-was-detected?forum=Vsexpressvb
var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Save As"));
//var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "[#] Save As [#]")); //needed if using sandboxie
//get the file name box
var saveAsText = saveAsDialog
.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "File name:"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
//fill the filename box
saveAsText.SetValue(path);
Thread.Sleep(1000);
//find the save button
var saveButton =
saveAsDialog.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "Save"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
//invoke the button
var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
pattern.Invoke();
}
}
}
See the comment in the ControlSaveDialog method for info on how to make this work as a debug build (VS settings).
I actually only tested the "Save As" operation so I hope the other works (it seems that the original poster added the "Save As" as an afterthought so my guess is that he tested the "Save" operation more thoroughly).
To call this you can use some code like:
Link lastMp4Link = mp4Links[mp4Links.Count - 1]; //mp4Links is a WatiN.Core.LinkCollection
lastMp4Link.Click();
browser.DownloadIeFile(string.Format(#"c:\temp\myFile.blah")); //"browser" is a WatiN.Core.IE object
I just got word from the Watin-users mailing list that FileDownloadHandler is broken with IE9. There is no fix yet, however I uninstalled IE9 (roll back to IE8) and it has the old style dialog that is handled by WatiN.
Watin can't find download dialog and dialog buttons. It can resolve with this.
How to test file download with Watin / IE9?
See comments
After many revisions using WaitN, I ended up using Selenium. It gives you a ton more control over what is being processed and used and allows you to use more modern web browsers.