I am trying to get the window handler and press the Save button. I found couple of examples on IE8 & 9. But that code doesn't works on IE 11.
const int BM_CLICK = 0x00F5;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr next, string sClassName, IntPtr sWindowTitle);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern uint GetDlgCtrlID(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
//hDialog - handle of dialog window. idBtn - Id of button
public static bool ClickButtonOnDialog(IntPtr hDialog, UInt32 idBtn)
{
IntPtr res = IntPtr.Zero;
uint id;
IntPtr hOkBtn = IntPtr.Zero;
int attempt = 0;
do
{
Thread.Sleep(300);
//searching for button
hOkBtn = FindWindowEx(hDialog, hOkBtn, "Button", IntPtr.Zero);
id = GetDlgCtrlID(hOkBtn);
attempt++;
} while (id != idBtn && attempt < 20);
if (!hOkBtn.Equals(IntPtr.Zero))
{
//click the button
res = SendMessage(hOkBtn, (int)BM_CLICK, 1, IntPtr.Zero);
}
if (res.ToInt32() == 1)
return true;
return false;
}
public static void FindAndSave()
{
IntPtr hOkBtn = IntPtr.Zero;
uint message = 0xf5;
IntPtr hwnd = FindWindow(null, "Internet Explorer");
hOkBtn = FindWindowEx(hwnd, hOkBtn, "Button", "Cancel");
SendMessage(hOkBtn, (int)message, 1, IntPtr.Zero);
I was able to download and close the file download dialog box using the below code
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void DownLoadFile(IE browser)
{
browser.Link(Find.ByText("download")).ClickNoWait();
Thread.Sleep(1000);
AutomationElementCollection dialogElements = AutomationElement.FromHandle(FindWindow(null, "Internet Explorer")).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement element in dialogElements)
{
if (element.Current.Name.Equals("Save"))
{
var invokePattern = element.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
invokePattern.Invoke();
}
}
}
Here is the IE 11 code that works. Its a mix of System.Windows.Automation and Win32 API. Could probably get it to work with Win32 unmanaged API's. I used WinID to get the menu's class name and then iterate through its child elements.
IE 11 has this download frame.
We needed to access the Save As from the Save's down arrow.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowTitle);
public static void IeDownLoadSaveAs(string windowTitle = null)
{
if (windowTitle == null)
windowTitle = "https://googledownload.com - Internet Explorer";
//get the message handle
//the last param "Untitled...is the title of the window and it must match
IntPtr parentHandle = WindowHandleInfo.FindWindow("IEFrame", windowTitle);
var parentElements = AutomationElement.FromHandle(parentHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement parentElement in parentElements)
{
// Identidfy Download Manager Window in Internet Explorer
if (parentElement.Current.ClassName == "Frame Notification Bar")
{
var childElements = parentElement.FindAll(TreeScope.Children, Condition.TrueCondition);
// Idenfify child window with the name Notification Bar or class name as DirectUIHWND
foreach (AutomationElement childElement in childElements)
{
if (childElement.Current.Name == "Notification bar" || childElement.Current.ClassName == "DirectUIHWND")
{
var downloadCtrls = childElement.FindAll(TreeScope.Descendants, Condition.TrueCondition);
foreach (AutomationElement ctrlButton in downloadCtrls)
{
//Now invoke the button click whichever you wish
if (ctrlButton.Current.Name.ToLower() == "")
{
var saveSubMenu = ctrlButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
saveSubMenu.Invoke();
var saveMenuHandle = WindowHandleInfo.FindWindow("#32768", "");
var subMenuItems = AutomationElement.FromHandle(saveMenuHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement item in subMenuItems)
{
if (item.Current.Name.ToLower() == "save as")
{
var saveAsMenuItem = item.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
saveAsMenuItem.Invoke();
}
}
}
}
}
}
}
}
}
I have achieved in very simple way. Just by using key strokes.
SendKeys.SendWait("%");
SendKeys.SendWait("%{s}");
Hope this will save your lot of time.
I had two IE processes running - the above code was always picking up the wrong 32bit IE process. So, I've merged the answers from multiple StackOverflow questions and wrote the below code to get this done (without the dll imports).
Please note that you need to add a reference to UIAutomationClient in the project - in order to use the AutomationElement method.
public static void IESaveFile(string title)
{
Thread.Sleep(1000);
//Get the Internet Explorer window handle using the window title
var ieWindowHandle = Process.GetProcesses().FirstOrDefault(process => process.MainWindowTitle.Contains(title))?.MainWindowHandle;
var dialogElements = AutomationElement.FromHandle(ieWindowHandle??IntPtr.Zero).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement element in dialogElements)
{
if (element.Current.ClassName != "Frame Notification Bar") continue;
var ChildElements = element.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ChildElement in ChildElements)
{
// Identify child window with the name Notification Bar or class name DirectUIHWND
if (ChildElement.Current.Name != "Notification bar" && ChildElement.Current.ClassName != "DirectUIHWND") continue;
var DownloadCtrls = ChildElement.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ctrlButton in DownloadCtrls)
//Now invoke the button click on the 'Save' button
if (ctrlButton.Current.Name.ToLower().Equals("save"))
((InvokePattern) ctrlButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
}
}
}