Automate of file download in IE 11 using c#

2020-07-27 04:18发布

问题:

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);

回答1:

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();

            }
        }
    }


回答2:

My approach:

  1. Identify the window by the window title using a windows API call.

    [DllImport("user32.dll"]
    static extern IntPtr FindWindowByCaption
    
  2. Loop through the IE window until we find the Element with "Frame Notification Bar or "Notification Bar" as Window Class Name

  3. Find the Button named "Open" or "Save" and perform the click.

    public  void DownLoadFile(string strWindowTitle)
    {
        IntPtr TargetHandle = FindWindowByCaption(IntPtr.Zero, strWindowTitle);
        AutomationElementCollection ParentElements = AutomationElement.FromHandle(TargetHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
        foreach (AutomationElement ParentElement in ParentElements)
        {
            // Identidfy Download Manager Window in Internet Explorer
            if (ParentElement.Current.ClassName == "Frame Notification Bar")
            {
                AutomationElementCollection 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")
                    {
    
                        AutomationElementCollection DownloadCtrls = ChildElement.FindAll(TreeScope.Children, Condition.TrueCondition);
                        foreach (AutomationElement ctrlButton in DownloadCtrls)
                        {
                            //Now invoke the button click whichever you wish
                            if (ctrlButton.Current.Name.ToLower() == "save")
                            {
                                var invokePattern = ctrlButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
                                invokePattern.Invoke();
                            }
    
                        }
                    }
                }
    
    
            }
        }
    }
    


回答3:

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();
                                    }
                                }

                            }

                        }
                    }
                }


            }
        }
    }


回答4:

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.



回答5:

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();
            }
        }
    }


标签: c# watin