VBA UI自动化-Internet Explorer“;另存为;

VBA UI自动化-Internet Explorer“;另存为;,vba,user-interface,ms-access,automation,Vba,User Interface,Ms Access,Automation,我正在使用MS Access和Internet Explorer 10 我正在尝试每天自动下载一系列文档。文件类型可能不同。使用下面的代码,我成功地将文档保存到一个临时文件夹中,但是我最终希望“另存为”,并根据下载的文件将文档保存到一个具有特定名称的预定义文件夹中 Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _ (ByVal hWnd1 As Long, ByVal hWnd2

我正在使用MS Access和Internet Explorer 10

我正在尝试每天自动下载一系列文档。文件类型可能不同。使用下面的代码,我成功地将文档保存到一个临时文件夹中,但是我最终希望“另存为”,并根据下载的文件将文档保存到一个具有特定名称的预定义文件夹中

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
        (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
        ByVal lpsz2 As String) As Long

Dim IE As InternetExplorer
Dim h As LongPtr
    'Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr

Sub Download(IE As InternetExplorer)
Dim o As IUIAutomation
Dim e As IUIAutomationElement
Dim h As Long
Dim iCnd As IUIAutomationCondition
Dim Button As IUIAutomationElement
Dim InvokePattern As IUIAutomationInvokePattern

On Error GoTo errorh

Set o = New CUIAutomation
h = IE.hwnd
h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString)
If h = 0 Then Exit Sub

Set e = o.ElementFromHandle(ByVal h)
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Save")

'Set Button = e.FindFirst(TreeScope_Subtree, iCnd)
Set Button = e.FindFirst(TreeScope_Subtree, iCnd)
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke

exitsub:
Exit Sub

errorh:
MsgBox Err.Number & "; " & Err.Description
Resume exitsub

End Sub
在创建IUIAutomationCondition UIA_NamePropertyID时,我尝试过用“另存为”、“另存为”等替换“保存”,并尝试了树域枚举的不同迭代以及IUIAutomationElement的.FindFirst和.FindAll方法(FindAll导致类型不匹配错误)

我的问题是:这可以通过Treewalker的FindAll方法实现吗?如果两者中有一个,如何着手做这件事?如何找到UI元素的“名称”?如果元素是子元素,如何引用它

excel文档的另一种解决方案是启动文档的“打开”并保存活动工作簿,但文件类型可能不同,因此此解决方案仅适用于特定的文件类型


非常感谢您的帮助。

由于没有更好的答案,我将在这里发布我的解决方案。“另存为”功能在不使用SendKeys的情况下似乎无法访问…这当然不是最优的,因为用户可以在进程运行时主动在桌面上工作,从而很容易达到此目的。无论如何,这个过程是通过调用Download()过程启动的,传递浏览器、文件名,以及如果文件已经存在,他们是否愿意替换它。如果未传递任何文件名,将调用默认的“保存”功能,默认文件名将保存在默认文件夹中。这些数据是从StackOverflow和其他地方的各种来源积累和改编的,在MS Access中应该是一个比较有效的解决方案

Option Explicit

Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr

Declare PtrSafe Sub Sleep Lib "kernel32" _
    (ByVal dwMilliseconds As Long)

Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr


Declare PtrSafe Function SetForegroundWindow Lib "user32" _
    (ByVal hWnd As LongPtr) As Long

Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" _
    (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long



Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" _
    (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long



Public Const BM_CLICK = &HF5
Public Const WM_GETTEXT = &HD
Public Const WM_GETTEXTLENGTH = &HE

Public Sub Download(ByRef oBrowser As InternetExplorer, _
                     ByRef sFilename As String, _
                     ByRef bReplace As Boolean)

    If sFilename = "" Then
        Call Save(oBrowser)
    Else
        Call SaveAs(oBrowser, sFilename, bReplace)
    End If

End Sub

'https://stackoverflow.com/questions/26038165/automate-saveas-dialouge-for-ie9-vba
Public Sub Save(ByRef oBrowser As InternetExplorer)

    Dim AutomationObj As IUIAutomation
    Dim WindowElement As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim hWnd As LongPtr

    Set AutomationObj = New CUIAutomation

    hWnd = oBrowser.hWnd
    hWnd = FindWindowEx(hWnd, 0, "Frame Notification Bar", vbNullString)
    If hWnd = 0 Then Exit Sub

    Set WindowElement = AutomationObj.ElementFromHandle(ByVal hWnd)
    Dim iCnd As IUIAutomationCondition
    Set iCnd = AutomationObj.CreatePropertyCondition(UIA_NamePropertyId, "Save")

    Set Button = WindowElement.FindFirst(TreeScope_Subtree, iCnd)
    Dim InvokePattern As IUIAutomationInvokePattern
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke

End Sub

Sub SaveAs(ByRef oBrowser As InternetExplorer, _
                     sFilename As String, _
                     bReplace As Boolean)

    'https://msdn.microsoft.com/en-us/library/system.windows.automation.condition.truecondition(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
    Dim AllElements As IUIAutomationElementArray
    Dim Element As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim iCnd As IUIAutomationCondition
    Dim AutomationObj As IUIAutomation
    Dim FrameElement As IUIAutomationElement
    Dim bFileExists As Boolean
    Dim hWnd As LongPtr

    'create the automation object
    Set AutomationObj = New CUIAutomation

    WaitSeconds 3

    'get handle from the browser
    hWnd = oBrowser.hWnd

    'get the handle to the Frame Notification Bar
    hWnd = FindWindowEx(hWnd, 0, "Frame Notification Bar", vbNullString)
    If hWnd = 0 Then Exit Sub

    'obtain the element from the handle
    Set FrameElement = AutomationObj.ElementFromHandle(ByVal hWnd)

    'Get split buttons elements
    Set iCnd = AutomationObj.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
    Set AllElements = FrameElement.FindAll(TreeScope_Subtree, iCnd)

    'There should be only 2 split buttons only
    If AllElements.length = 2 Then

        'Get the second split button which when clicked shows the other three Save, Save As, Save and Open
        Set Element = AllElements.GetElement(1)

        'click the second spin button to display Save, Save as, Save and open options
        Set InvokePattern = Element.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke

        'Tab across from default Open to Save, down twice to click Save as
        'Displays Save as window
        SendKeys "{TAB}"
        SendKeys "{DOWN}"
        SendKeys "{ENTER}"

        'Enter Data into the save as window


        Call SaveAsFilename(sFilename)

        bFileExists = SaveAsSave
        If bFileExists Then
            Call File_Already_Exists(bReplace)
        End If
    End If
End Sub

Private Sub SaveAsFilename(filename As String)

    Dim hWnd As LongPtr
    Dim Timeout As Date
    Dim fullfilename As String
    Dim AutomationObj As IUIAutomation
    Dim WindowElement As IUIAutomationElement


    'Find the Save As window, waiting a maximum of 10 seconds for it to appear
    Timeout = Now + TimeValue("00:00:10")
    Do
        hWnd = FindWindow("#32770", "Save As")
        DoEvents
        Sleep 200
    Loop Until hWnd Or Now > Timeout

    If hWnd Then

        SetForegroundWindow hWnd

        'create the automation object
        Set AutomationObj = New CUIAutomation

        'obtain the element from the handle
        Set WindowElement = AutomationObj.ElementFromHandle(ByVal hWnd)

        'Set the filename into the filename control only when one is provided, else use the default filename
        If filename <> "" Then Call SaveAsSetFilename(filename, AutomationObj, WindowElement)

    End If

End Sub

'Set the filename to the Save As Dialog
Private Sub SaveAsSetFilename(ByRef sFilename As String, ByRef AutomationObj As IUIAutomation, _
                                ByRef WindowElement As IUIAutomationElement)

    Dim Element As IUIAutomationElement
    Dim ElementArray As IUIAutomationElementArray
    Dim iCnd As IUIAutomationCondition

    'Set the filename control
    Set iCnd = AutomationObj.CreatePropertyCondition(UIA_AutomationIdPropertyId, "FileNameControlHost")
    Set ElementArray = WindowElement.FindAll(TreeScope_Subtree, iCnd)

    If ElementArray.length <> 0 Then
        Set Element = ElementArray.GetElement(0)
        'should check that it is enabled

        'Update the element
        Element.SetFocus

        ' Delete existing content in the control and insert new content.
        SendKeys "^{HOME}" ' Move to start of control
        SendKeys "^+{END}" ' Select everything
        SendKeys "{DEL}" ' Delete selection
        SendKeys sFilename
    End If

End Sub



'Get the window text
Private Function Get_Window_Text(hWnd As LongPtr) As String

    'Returns the text in the specified window

    Dim Buffer As String
    Dim length As Long
    Dim result As Long

    SetForegroundWindow hWnd
    length = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0)
    Buffer = Space(length + 1) '+1 for the null terminator
    result = SendMessage(hWnd, WM_GETTEXT, Len(Buffer), ByVal Buffer)


    Get_Window_Text = Left(Buffer, length)

End Function

'Click Save on the Save As Dialog
Private Function SaveAsSave() As Boolean

    'Click the Save button in the Save As dialogue, returning True if the ' already exists'
    'window appears, otherwise False

    Dim hWndButton As LongPtr
    Dim hWndSaveAs As LongPtr
    Dim hWndConfirmSaveAs As LongPtr
    Dim Timeout As Date


    'Find the Save As window, waiting a maximum of 10 seconds for it to appear
    Timeout = Now + TimeValue("00:00:10")
    Do
        hWndSaveAs = FindWindow("#32770", "Save As")
        DoEvents
        Sleep 200
    Loop Until hWndSaveAs Or Now > Timeout

    If hWndSaveAs Then

        SetForegroundWindow hWndSaveAs

        'Get the child Save button
        hWndButton = FindWindowEx(hWndSaveAs, 0, "Button", "&Save")
    End If

    If hWndButton Then

        'Click the Save button


        Sleep 100
        SetForegroundWindow hWndButton
        PostMessage hWndButton, BM_CLICK, 0, 0
    End If


    'Set function return value depending on whether or not the ' already exists' popup window exists
    Sleep 500
    hWndConfirmSaveAs = FindWindow("#32770", "Confirm Save As")

    If hWndConfirmSaveAs Then
        SaveAsSave = True
    Else
        SaveAsSave = False
    End If

End Function

'Addresses the case when saving the file when it already exists.
'The file can be overwritten if Replace boolean is set to True
Private Sub File_Already_Exists(Replace As Boolean)

    'Click Yes or No in the ' already exists. Do you want to replace it?' window

    Dim hWndSaveAs As LongPtr
    Dim hWndConfirmSaveAs As LongPtr
    Dim AutomationObj As IUIAutomation
    Dim WindowElement As IUIAutomationElement
    Dim Element As IUIAutomationElement
    Dim iCnd As IUIAutomationCondition
    Dim InvokePattern As IUIAutomationInvokePattern


    hWndConfirmSaveAs = FindWindow("#32770", "Confirm Save As")

    Set AutomationObj = New CUIAutomation
    Set WindowElement = AutomationObj.ElementFromHandle(ByVal hWndConfirmSaveAs)

    If hWndConfirmSaveAs Then

        If Replace Then
            Set iCnd = AutomationObj.CreatePropertyCondition(UIA_NamePropertyId, "Yes")
        Else
            Set iCnd = AutomationObj.CreatePropertyCondition(UIA_NamePropertyId, "No")
        End If

        Set Element = WindowElement.FindFirst(TreeScope_Subtree, iCnd)
        Set InvokePattern = Element.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
    End If

End Sub


Public Sub WaitSeconds(intSeconds As Integer)
  On Error GoTo Errorh

  Dim datTime As Date

  datTime = DateAdd("s", intSeconds, Now)

  Do
    Sleep 100
    DoEvents
  Loop Until Now >= datTime

exitsub:
  Exit Sub

Errorh:
  MsgBox "Error: " & Err.Number & ". " & Err.Description, , "WaitSeconds"
  Resume exitsub
End Sub
选项显式
将PtrSafe函数FindWindowEx Lib“user32”别名“FindWindowExA”(ByVal hWnd1为LongPtr,ByVal hWnd2为LongPtr,ByVal lpsz1为String,ByVal lpsz2为String)声明为LongPtr
声明PtrSafe子睡眠库“kernel32”_
(ByVal的长度为毫秒)
声明PtrSafe函数findwindowlib“user32”别名“FindWindowA”_
(ByVal lpClassName作为字符串,ByVal lpWindowName作为字符串)作为LongPtr
声明PtrSafe函数setForeGroundIndow Lib“user32”_
(ByVal hWnd作为LongPtr)作为LongPtr
声明PtrSafe函数SendMessage Lib“user32”别名“SendMessageA”_
(ByVal hWnd为LongPtr,ByVal wMsg为Long,ByVal wParam为Long,lParam为任意)为Long
声明PtrSafe函数PostMessage Lib“user32”别名“PostMessageA”_
(ByVal hWnd为LongPtr,ByVal wMsg为Long,ByVal wParam为Long,lParam为任意)为Long
公用工程BM_点击=&HF5
Public Const WM_GETTEXT=&HD
Public Const WM_GETTEXTLENGTH=&HE
公共子下载(ByRef oBrowser作为InternetExplorer_
ByRef sFilename作为字符串_
ByRef(以布尔形式替换)
如果sFilename=“”,则
呼叫保存(oBrowser)
其他的
调用SaveAs(oBrowser、sFilename、bReplace)
如果结束
端接头
'https://stackoverflow.com/questions/26038165/automate-saveas-dialouge-for-ie9-vba
公共子存储(ByRef oBrowser作为InternetExplorer)
Dim Automation作为iUI自动化
将WindowElement调暗为IUIAutomationeElement
调暗按钮作为IUIAutomationeElement
变暗hWnd为长PTR
Set AutomationObj=新CUI自动化
hWnd=oBrowser.hWnd
hWnd=FindWindowEx(hWnd,0,“帧通知栏”,vbNullString)
如果hWnd=0,则退出Sub
设置WindowElement=AutomationObj.ElementFromHandle(ByVal hWnd)
Dim iCnd作为IUIAutomation条件
设置iCnd=AutomationObj.CreatePropertyCondition(UIA_NamePropertyId,“保存”)
Set Button=WindowElement.FindFirst(TreeScope\u子树,iCnd)
Dim InvokePattern作为IUIAutomationInvokePattern
设置InvokePattern=Button.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
端接头
子存储组件(ByRef oBrowser作为InternetExplorer_
sFilename作为字符串_
bReplace(作为布尔值)
'https://msdn.microsoft.com/en-us/library/system.windows.automation.condition.truecondition(v=vs.110).aspx?cs save lang=1&cs lang=vb#code-snippet-1
作为IUIAutomationElementArray的Dim等位基因
作为IUIAutomationeElement的Dim元素
Dim InvokePattern作为IUIAutomationInvokePattern
Dim iCnd作为IUIAutomation条件
Dim Automation作为iUI自动化
作为IUIAutomationeElement的Dim FrameElement
Dim bfiles以布尔形式存在
变暗hWnd为长PTR
'创建自动化对象
Set AutomationObj=新CUI自动化
等待秒3
'从浏览器获取句柄
hWnd=oBrowser.hWnd
'获取帧通知栏的句柄
hWnd=FindWindowEx(hWnd,0,“帧通知栏”,vbNullString)
如果hWnd=0,则退出Sub
'从句柄获取元素
Set FrameElement=AutomationObj.ElementFromHandle(ByVal hWnd)
'获取分割按钮元素
设置iCnd=AutomationObj.CreatePropertyCondition(UIA\u ControlTypePropertyId,UIA\u SplitButtonControlTypeId)
Set-Allegements=FrameElement.FindAll(TreeScope_子树,iCnd)
'应该只有两个拆分按钮
如果等位基因长度=2,则
'获取第二个拆分按钮,单击该按钮时显示其他三个“保存”、“另存为”、“保存”和“打开”
Set Element=allegements.GetElement(1)
'单击第二个旋转按钮以显示保存、另存为、保存和打开选项
设置InvokePattern=Element.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
'选项卡从默认打开到保存,向下两次单击另存为
'显示另存为窗口
发送键“{TAB}”
SendKeys“{DOWN}”
SendKeys“{ENTER}”
    public void SetSaveDialogFilePath(string filePath)
    {
        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }

        var fileNameElement = app.FindFirst(TreeScope.Subtree, new AndCondition(
                                                             new PropertyCondition(AutomationElement.ClassNameProperty, "AppControlHost"),
                                                             new PropertyCondition(AutomationElement.AutomationIdProperty, "FileNameControlHost")));

        var valuePattern = (ValuePattern)fileNameElement.GetCurrentPattern(ValuePattern.Pattern);
        fileNameElement.SetFocus();
        valuePattern.SetValue(filePath);
        Thread.Sleep(100);
        // Even if text value is set we have to select it from drop down as well otherwise it is not applied
        var expandPattern = (ExpandCollapsePattern)fileNameElement.GetCurrentPattern(ExpandCollapsePattern.Pattern);
        if (expandPattern != null)
        {
            expandPattern.Expand();
            AutomationElement item = null;
            while (item == null)
            {
                Thread.Sleep(10);
                item = fileNameElement.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, filePath));
            }
            ((SelectionItemPattern)item.GetCurrentPattern(SelectionItemPattern.Pattern)).Select();
            expandPattern.Collapse();
        }
        var button = app.FindFirst(TreeScope.Subtree, new AndCondition(
                                                             new PropertyCondition(AutomationElement.ClassNameProperty, "Button"),
                                                             new PropertyCondition(AutomationElement.AutomationIdProperty, "1")));
        ((TogglePattern)button.GetCurrentPattern(TogglePattern.Pattern)).Toggle();
    }
Set o = New CUIAutomation
Dim h As Long
h = IE.hWnd
h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString)
If h = 0 Then Exit Sub

Set e = o.ElementFromHandle(ByVal h)
Dim iCnd As IUIAutomationCondition
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Save")

Dim Button As IUIAutomationElement
Set Button = e.FindFirst(TreeScope_Subtree, iCnd)
Dim InvokePattern As IUIAutomationInvokePattern
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke