【问题标题】:Automate of file download in IE 11 using c#使用 c# 在 IE 11 中自动下载文件
【发布时间】:2015-06-04 11:58:53
【问题描述】:

我正在尝试获取窗口处理程序并按“保存”按钮。我在 IE8 和 9 上找到了几个示例。但该代码不适用于 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);

【问题讨论】:

  • 您是否尝试过使用 Autoit 之类的工具?
  • powershell是option吗?
  • 我可以选择任何可以在 C# 中使用的选项

标签: c# watin


【解决方案1】:

我可以使用以下代码下载并关闭文件下载对话框

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

            }
        }
    }

【讨论】:

  • 这行不通。 FindWindow 将不接受值为null 的第一个参数。
  • 很遗憾,在 IE11 中无法使用。在 IE11 中没有为文件下载打开单独的窗口:/
  • 查看我对 IE 11 的回答
【解决方案2】:

我的做法:

  1. 使用 windows API 调用通过窗口标题识别窗口。

    [DllImport("user32.dll"]
    static extern IntPtr FindWindowByCaption
    
  2. 循环浏览 IE 窗口,直到找到以“框架通知栏”或“通知栏”为窗口类名的元素

  3. 找到名为“打开”或“保存”的按钮并执行点击。

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

【讨论】:

  • 仅由代码组成的答案永远不是答案。请详细说明。
  • 1.第一步是我们将使用 windows API 调用通过窗口标题识别窗口。 [DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)] static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
  • 我的做法 1. 我们将使用windows API调用通过窗口标题来识别窗口。 [DllImport("user32.dll"] static extern IntPtr FindWindowByCaption 2. 循环浏览 IE 窗口,直到找到窗口类名称为“框架通知栏”或“通知栏”的自动化元素 3.找出按钮自动化元素的名称“打开”或“保存”并执行点击。
【解决方案3】:

这是有效的 IE 11 代码。它是 System.Windows.Automation 和 Win32 API 的混合体。可能可以让它与 Win32 非托管 API 一起工作。我使用WinID 来获取菜单的类名,然后遍历其子元素。

IE 11 有这个下载框架。

我们需要从“保存”的向下箭头访问“另存为”。

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

                            }

                        }
                    }
                }


            }
        }
    }

【讨论】:

  • 如何引用这个“WindowHandleInfo.FindWindow”
【解决方案4】:

我以非常简单的方式实现了。只需使用击键即可。
SendKeys.SendWait("%");
SendKeys.SendWait("%{s}");

希望这会节省您的大量时间。

【讨论】:

    【解决方案5】:

    我有两个 IE 进程正在运行 - 上面的代码总是选择错误的 32 位 IE 进程。因此,我合并了多个 StackOverflow 问题的答案,并编写了以下代码来完成这项工作(没有 dll 导入)。

    请注意,您需要在项目中添加对 UIAutomationClient 的引用 - 以便使用 AutomationElement 方法。

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

    【讨论】:

    • 非常感谢您提供这个可靠的解决方案!它真的很棒!如果其他人正在研究这个问题 - 我很难让它正常工作,这里有什么帮助:“我很难找到 WindowsBase,直到我意识到我的目标环境是 .NET 2.0,所以只是提醒一下,请确保您使用的是 .NET 4.0 或更高版本才能添加该引用。– krs013 2015 年 2 月 5 日 6:05"
    猜你喜欢
    • 2016-04-29
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    • 1970-01-01
    • 2015-06-19
    • 1970-01-01
    • 2019-01-27
    相关资源
    最近更新 更多