# Using UIAutomationClient to automate the Save As file download in IE11



## John_w (Feb 7, 2019)

Here is VBA code which automates the complete 'Save As' file download procedure in IE11, when a download is offered by IE's Download Notification Bar.

To use for your own web site downloads you would automate IE and do whatever is necessary to make the Download Notification Bar appear at the bottom of the IE window and then call IE_Download_File_Using_UIAutomation.  It has the following parameters:

Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean


IEhwnd                 - The handle of the IE window. The Download Notification Bar must be displayed in the active tab. If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function. 
saveInFolder           - The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder. 
saveAsFileName         - The file name which the downloaded file will be given.  Specify "" to use the file name provided by the web site. 
replaceExistingFile    - True to replace the file if it already exists; False to overwrite the file. 
downloadResult         - Output string returned to the caller showing whether the file was successfully download or not, including the file name of the downloaded file (including its path if saveInFolder was specified). 
Function return value -  True: the file was downloaded; False: the file was not downloaded.

*NOTE *- The only issue I had was that after calling SetValue to put a specific file name in the Save As dialogue and clicking Save (Invoke), the download doesn't recognise the specified file name and still uses the default file name provided by the web site.  A simple workaround which has worked in all tests is SendKeys " ", True to insert a space character at start of the file name input element, however I know that SendKeys is unreliable and this might not always work for some people.

Also note that the IE tab offering the download must be the active tab, and the IE_Click_Tab_Like routine should be called to activate it if necessary.  The code also includes several debugging functions which can be helpful to understand the UIAutomation hierarchy.

Here is the complete code, including a test procedure for downloading a 2 Kb csv file from morningstar.com


```
'07-Feb-2019
'Use UIAutomationClient to automate the Save As file download in IE11

'References:
'Microsoft Internet Controls
'Microsoft HTML Object Library
'UIAutomationClient


Option Explicit

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As LongPtr, ByVal hwndChildAfter As LongPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If


Public Sub IE_Download_File_Save_As()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim exportLink As HTMLLinkElement
    Dim saveInFolder As String
    Dim saveAsFileName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
    
    saveInFolder = ""                           'Save in IE11's default download folder
    saveInFolder = "c:\path\to\folder"          'Save in this folder
    saveAsFileName = ""                         'Save As the file name provided by web site
    saveAsFileName = "My Csv Data"              'Save As this file name
    replaceExistingFile = True
    
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
    
    'See if an IE window is already open at the site
    
    Set IE = Get_IE_Window(CStr(URL))
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            SetForegroundWindow .hWnd
            .Visible = True
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
           
    Set exportLink = HTMLdoc.getElementsByClassName("rf_export")(0)
    
    If Not exportLink Is Nothing Then
    
        'Click the Export link, which opens IE's Download Notification Bar
        '
        '   Do you want to open or save INTC Balance Sheet.csv from financials.morningstar.com?
        '       with buttons [Open] [Save][Down Arrow] [Cancel] [X]
        
        exportLink.Click
        
        'Activate the morningstar.com IE tab and download the file using Save As
        
        IE_Click_Tab_Like IE.hWnd, "*morningstar.com*"
        downloadStatus = IE_Download_File_Using_UIAutomation(IE.hWnd, saveInFolder, saveAsFileName, replaceExistingFile, downloadResult)
        Debug.Print "Download status = " & downloadStatus
        MsgBox "Download result = " & downloadResult
        
    Else
    
        MsgBox "Export link not found - file not downloaded"
        
    End If
    
End Sub

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If

    'Automate IE11's Download Notification Bar in the active tab, by clicking the Save As item, downloading the file and closing the Notification Bar.
    'The IE option 'Notify when downloads complete' must be enabled.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveInFolder           The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder.
    'saveAsFileName         The file name which the downloaded file will be given.  If file extension is omitted, the file extension provided by
    '                       the web site is used. Specify "" to use the file name provided by the web site.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file
    'downloadResult         Output string returned to the caller showing whether the file was successfully download or not, including the file name of the
    '                       downloaded file (including its path if saveInFolder was specified).
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
    
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
        Dim hWnd As LongPtr
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
        Dim hWnd As Long
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
    Dim UIAutomation As IUIAutomation
    Dim DesktopRoot As IUIAutomationElement
    Dim FrameNotificationBarPane As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim TreeWalker As IUIAutomationTreeWalker
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim downloaded As Boolean
    Dim destCell As Range, numRows As Long
    
    Const DebugMode As Boolean = True
    
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
    
    'If specified, ensure folder ends with \
    
    If saveInFolder <> "" And Right(saveInFolder, 1) <> "" Then saveInFolder = saveInFolder & ""
    
    'Create main UIAutomation object
    
    Set UIAutomation = New CUIAutomation
    
    'Find the IE11 Frame Notification Bar, waiting until it exists
    
    Do
        hWnd = FindWindowEx(IEhwnd, 0, "Frame Notification Bar", vbNullString)
        DoEvents
        Sleep 200
    Loop While hWnd = 0
    If DebugMode Then Debug.Print Time; "Frame Notification Bar " & hWnd

    'Get the Frame Notification Bar pane from the window frame
    'Class         = Frame Notification Bar
    'Ctrl type     = UIA_PaneControlTypeId

    Set FrameNotificationBarPane = UIAutomation.ElementFromHandle(ByVal hWnd)
    'DumpElement FrameNotificationBarPane
        
    If DebugMode Then
        With Worksheets(1)
            .Cells.Clear
            Set destCell = .Range("A1")
        End With
        destCell.Value = "  FrameNotificationBarPane children"
        numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If

    'Find the Notification tool bar, a child of the Frame Notification Bar pane, waiting until it exists
    'Name:          "Notification"
    'ControlType:   UIA_ToolBarControlTypeId

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set NotificationToolbar = FrameNotificationBarPane.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Notification tool bar"
        DoEvents
    Loop While NotificationToolbar Is Nothing
    If DebugMode Then
        destCell.Value = "  NotificationToolbar children"
        numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
    
    'Find the Notification tool bar text element and extract the default file name from it
    'Name:          "Notification bar Text"
    'ControlType:   UIA_TextControlTypeId
    'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
        
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
    p1 = InStr(NotificationBarTextString, "Do you want to open or save ")
    If p1 = 1 Then
        p1 = p1 + Len("Do you want to open or save ")
        p2 = InStr(p1, NotificationBarTextString, " from ")
        defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
    Else
        defaultFileName = ""
    End If
    
    'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
    
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
    Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType)
    Set DownArrow = SplitButtons.GetElement(1)
        
    'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
    'with the following properties:
    '
    'Name:          "Context"
    'ControlType:   UIA_MenuControlTypeId
    '
    'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
    
    'Create criteria to find the Context menu
    
    Set DesktopRoot = UIAutomation.GetRootElement
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Context")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
    'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
    'not TreeScope_Descendants, to reduce the number of elements searched.
    'See [url]https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst[/url]
    
    Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
    Do
        DownArrow.SetFocus
        InvokePattern.Invoke
        DoEvents
        Sleep 200
        Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
    Loop While ContextMenu Is Nothing
    
    If DebugMode Then
        destCell.Value = "  ContextMenu children"
        numRows = UIElements_To_Cells(UIAutomation, ContextMenu, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
        
    'Find the Save as item in the Context menu
    'Name:          "Save as"
    'ControlType:   UIA_MenuItemControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save as")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save as item to display the Save As dialogue window
    
    Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    
    'Find the Save As dialogue window, which is a child of the Desktop, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 100
    Loop While SaveAsWindow Is Nothing
            
    If DebugMode Then
        destCell.Value = "  SaveAsWindow children"
        numRows = UIElements_To_Cells(UIAutomation, SaveAsWindow, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
            
    'If the caller has specified either the folder or the file name, then populate the file name input box in the Save As window
    
    If saveInFolder <> "" Or saveAsFileName <> "" Then
    
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
        
        'Construct the full file name
        
        fullFileName = saveInFolder & saveAsFileName
        
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
    
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "File name:")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the file name input box
        
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
        
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
        
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern.
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
        
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
        
        SendKeys " ", True          'press space key
             
    Else
    
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
        
        fullFileName = defaultFileName
    
    End If
    
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Find the Save button, a child of the Save As window
    
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Save clicked"
        
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
        
    
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Confirm Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
    
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Confirm Save As"
        DoEvents
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
       
    If Not ConfirmSaveAsWindow Is Nothing Then
        
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
    
        If replaceExistingFile Then
        
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Yes")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the Yes button
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the Yes button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "Yes clicked"
        
            downloaded = True

        Else 'replaceExistingFile = False
        
            'Extract the text warning from the Confirm Save As window
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
        
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "No")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the No button, waiting until it exists
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the No button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "No clicked"
                        
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
            
        End If
    
    Else
    
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
        
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        If Not SaveAsWarningWindow Is Nothing Then
        
            'The Save As warning window exists, so extract the text warning from it
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
            
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "OK")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
            'Find the OK button - a child of the Save As warning window
            
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
            
            'Click the OK button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "OK clicked"
            
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
            
        Else
        
            'The Save As warning window doesn't exist, so the file was downloaded
            
            IE_Download_File_Using_UIAutomation = True
            
        End If
            
    End If
    
    If IE_Download_File_Using_UIAutomation = True Then
    
        If DebugMode Then
            destCell.Value = "  FrameNotificationBarPane children"
            numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    
        'Create criteria to find the "Notification bar Text" element
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   The xxxx yyyy.zzz download has completed.
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
        
        NotificationBarTextString = ""
        Do
            Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
            Sleep 200
            DoEvents
            If Not NotificationBarText Is Nothing Then
                NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
            End If
            If DebugMode Then Debug.Print Time; NotificationBarTextString
        Loop Until InStr(NotificationBarTextString, "download has completed")

        'Extract file name from Notification bar text, e.g. "The xxxx yyyy.zzz download has completed."
        
        p1 = InStr(NotificationBarTextString, "The ") + Len("The ")
        p2 = InStr(p1, NotificationBarTextString, " download has completed")
        
        Debug.Print "Notification Bar downloaded = " & Mid(NotificationBarTextString, p1, p2 - p1)
        Debug.Print "Full file name = " & fullFileName
        downloadResult = fullFileName & " - SUCCESSFULLY DOWNLOADED"
        
    Else
    
        'Not downloaded, so click the Cancel button in the Save As window
            
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Cancel")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Cancel button, waiting until it exists
        
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Cancel button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugMode Then Debug.Print Time; "Cancel clicked"

    End If
             
    'Create criteria to find the Close (X) button on the Notification pane
    'Name:          "Close"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Close")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Close button in the IE Download Notification Bar
    
    Set Button = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    
    'Click the Close button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Close clicked"
                 
End Function


'This finds all TabItemControls of the IE element and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - [url]https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator[/url])
'to specify the tab name to be found and activated.
'This is useful because sometimes although the visible the tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
'according to UIAutomation is "xxxxx Tab Group 1".

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
   
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
    
    'Create UIAutomation object
    
    Set UIauto = New CUIAutomation
    
    'Get Internet Explorer UIAutomation element
    
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
    
    'Create condition to find a TabItemControl
    
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
 
    'Find all tabs
    
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
    
    'Look for the tab which matches the specified tab name
    
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
        
    If Not IEtab Is Nothing Then
    
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)
    
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
    
    Else
    
        MsgBox "IE tab with name '" & findTabName & "' not found"
        
    End If
        
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
    
End Sub


Public Sub DumpElement(UIAutoElem As IUIAutomationElement)
    Dim ct As Long
    ct = Get_UIA_PropertyValue(UIAutoElem, UIA_ControlTypePropertyId)
    Debug.Print "----------------"
    Debug.Print "Name                 = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NamePropertyId)
    Debug.Print "Class                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ClassNamePropertyId)
    Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
    Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LocalizedControlTypePropertyId)
    Debug.Print "Value                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ValueValuePropertyId)
    Debug.Print "Handle               = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId)
    Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(UIAutoElem, UIA_AccessKeyPropertyId)
    Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LegacyIAccessibleDefaultActionPropertyId)
    Debug.Print "Description          = " & Get_UIA_PropertyValue(UIAutoElem, UIA_FullDescriptionPropertyId)
    Debug.Print "NativeWindowHandle   = " & "0x" & Hex(Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId))
End Sub


'Get property value for a UI element
Private Function Get_UIA_PropertyValue(UIAutoElem As IUIAutomationElement, propertyId As Long)
    
    Dim tVal As Variant
    Dim tStr As String, i As Integer
    
    tVal = UIAutoElem.GetCurrentPropertyValue(propertyId)
    
    If IsArray(tVal) Then
        tStr = tVal(0)
        For i = 1 To UBound(tVal)
            tStr = tStr & "; " & tVal(i)
        Next
        Get_UIA_PropertyValue = tStr
    Else
        Get_UIA_PropertyValue = tVal
    End If
    
End Function


'Convert a UI property id to its constant name
Private Function Get_UIA_ControlType(propertyId As Long) As String

    Dim cn As String
    
    Select Case propertyId
        Case 50040: cn = "UIA_AppBarControlTypeId"
        Case 50000: cn = "UIA_ButtonControlTypeId"
        Case 50001: cn = "UIA_CalendarControlTypeId"
        Case 50002: cn = "UIA_CheckBoxControlTypeId"
        Case 50003: cn = "UIA_ComboBoxControlTypeId"
        Case 50025: cn = "UIA_CustomControlTypeId"
        Case 50028: cn = "UIA_DataGridControlTypeId"
        Case 50029: cn = "UIA_DataItemControlTypeId"
        Case 50030: cn = "UIA_DocumentControlTypeId"
        Case 50004: cn = "UIA_EditControlTypeId"
        Case 50026: cn = "UIA_GroupControlTypeId"
        Case 50034: cn = "UIA_HeaderControlTypeId"
        Case 50035: cn = "UIA_HeaderItemControlTypeId"
        Case 50005: cn = "UIA_HyperlinkControlTypeId"
        Case 50006: cn = "UIA_ImageControlTypeId"
        Case 50008: cn = "UIA_ListControlTypeId"
        Case 50007: cn = "UIA_ListItemControlTypeId"
        Case 50010: cn = "UIA_MenuBarControlTypeId"
        Case 50009: cn = "UIA_MenuControlTypeId"
        Case 50011: cn = "UIA_MenuItemControlTypeId"
        Case 50033: cn = "UIA_PaneControlTypeId"
        Case 50012: cn = "UIA_ProgressBarControlTypeId"
        Case 50013: cn = "UIA_RadioButtonControlTypeId"
        Case 50014: cn = "UIA_ScrollBarControlTypeId"
        Case 50039: cn = "UIA_SemanticZoomControlTypeId"
        Case 50038: cn = "UIA_SeparatorControlTypeId"
        Case 50015: cn = "UIA_SliderControlTypeId"
        Case 50016: cn = "UIA_SpinnerControlTypeId"
        Case 50031: cn = "UIA_SplitButtonControlTypeId"
        Case 50017: cn = "UIA_StatusBarControlTypeId"
        Case 50018: cn = "UIA_TabControlTypeId"
        Case 50019: cn = "UIA_TabItemControlTypeId"
        Case 50036: cn = "UIA_TableControlTypeId"
        Case 50020: cn = "UIA_TextControlTypeId"
        Case 50027: cn = "UIA_ThumbControlTypeId"
        Case 50037: cn = "UIA_TitleBarControlTypeId"
        Case 50021: cn = "UIA_ToolBarControlTypeId"
        Case 50022: cn = "UIA_ToolTipControlTypeId"
        Case 50023: cn = "UIA_TreeControlTypeId"
        Case 50024: cn = "UIA_TreeItemControlTypeId"
        Case 50032: cn = "UIA_WindowControlTypeId"
        Case Else: cn = "UNKNOWN"
    End Select
    
    Get_UIA_ControlType = cn
    
End Function


'Recursively walk the hierarchy of UIAutomationElements starting at the specified parent element and output UI elements to Excel cells.

Public Function UIElements_To_Cells(UIAutomation As IUIAutomation, parentElement As IUIAutomationElement, destCell As Range) As Long

    Dim n As Long
    Dim sibling As Long
    Static TreeWalker As IUIAutomationTreeWalker
    Dim childElement As IUIAutomationElement
    Dim ct As Long
    
    n = 0
    
    If Not parentElement Is Nothing Then
    
        'Any of these tree walkers can be used
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.RawViewWalker
        If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ControlViewWalker
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ContentViewWalker
        
        Set childElement = TreeWalker.GetFirstChildElement(parentElement)
        sibling = 0
        While Not childElement Is Nothing
            ct = Get_UIA_PropertyValue(childElement, UIA_ControlTypePropertyId)
            sibling = sibling + 1
            Debug.Print "-- Sibling " & sibling
            Debug.Print "Name                 = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            Debug.Print "Class                = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            Debug.Print "Value                = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            Debug.Print "Handle               = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            Debug.Print "Description          = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            destCell.Offset(n + 0).Value = "'-- Sibling " & sibling
            destCell.Offset(n + 1).Value = "Name = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            destCell.Offset(n + 2).Value = "Class = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            destCell.Offset(n + 3).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            destCell.Offset(n + 4).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            destCell.Offset(n + 5).Value = "Value = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            destCell.Offset(n + 6).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            destCell.Offset(n + 7).Value = "AccessKey = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            destCell.Offset(n + 8).Value = "DefaultAction = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            destCell.Offset(n + 9).Value = "Description = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            
            n = n + 10 + UIElements_To_Cells(UIAutomation, childElement, destCell.Offset(n + 10, 1))
            
            Set childElement = TreeWalker.GetNextSiblingElement(childElement)
        Wend
    
    End If
    
    UIElements_To_Cells = n

End Function


Private Function Get_IE_Window(URL As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the domain of the specified URL (which can start with [url]http://,[/url] https://
    'or nothing) and, if found, return that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim domain As String
    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
    Dim p1 As Integer, p2 As Integer
    
    p1 = InStr(URL, "://")
    If p1 = 0 Then
        p1 = 1
    Else
        p1 = p1 + 3
    End If
    p2 = InStr(p1, URL, "/")
    If p2 = 0 Then p2 = Len(URL) + 1
    domain = Mid(URL, p1, p2 - p1)
    
    Set Shell = CreateObject("Shell.Application")
    
    i = 0
    Set Get_IE_Window = Nothing
    While i < Shell.Windows.Count And Get_IE_Window Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If TypeName(IE.document) = "HTMLDocument" Then
                If InStr(IE.LocationURL, domain) > 0 Then
                    Set Get_IE_Window = IE
                End If
            End If
        End If
        i = i + 1
    Wend
     
End Function
```


----------



## Kyle123 (Feb 7, 2019)

My god that's a lot of code :shocked: - it must have taken you ages 

It doesn't actually work for me though. It identifies the download bar, clicks save as, opens the save as dialog and then gets stuck in a loop waiting for input (in here: Function UIElements_To_Cells)

I'm intrigued as to your reason for this epic amount of work, as I know you know full well how to download things from the internet "properly" without automating IE


----------



## John_w (Feb 7, 2019)

Yes, it's certainly a lot of code!  It was simply a development exercise to see if it could be done, because I've never seen UIAutomation used this way before.  

Using Microsoft's Inspect.exe tool, I discovered that the Save As option is in a context menu which is a child of the Desktop element, not the Download Toolbar, as you might expect.  I also spent some time trying different methods to fix the issue with the input file name not being 'taken' by the download. You can turn off the calls the UIElements_To_Cells by setting the DebugMode Const to False, but I don't know why it's waiting for input.

Although this was a lot of work initially, callng this function would probably be easier and far less work than writing custom code to download using XMLhttp requests.


----------



## Kyle123 (Feb 7, 2019)

Fairy nuff, I can see why it would be useful to point people to on forums, guiding them through doing it the proper way is often impossible. I'm not sure it's easier to do the if you know what you're doing though as you're likely needing to change code whether automating IE or direct requests for each site anyway. I often find it easier to use winhttp rather than xmlhttp too due to the additional authentication options and automatic cookie handling.

Nevertheless - great work


----------



## John_w (Feb 7, 2019)

Thanks 

You're right about WinHttp. My default method is usually XMLhttp for simple requests, however one disadvantage is that it hides cookies in the response, whereas WinHttp provides them in the Set-Cookie headers.

Another thing, which you probably know about, is that the following is needed when using Fiddler to capture the WinHttp requests and responses:


```
Const HTTPREQUEST_PROXYSETTING_PROXY = 2
WinHttp.SetProxy HTTPREQUEST_PROXYSETTING_PROXY, "127.0.0.1:8888"
```


----------



## Kyle123 (Feb 7, 2019)

I don't really use fiddler - I just use Chrome dev tools for that. 

That's not quite what I meant with the cookies, winhttp will automatically send received cookies with each request, with xml you need to add them manually. This is especially useful for scraping pages which require logging in, once you have the authenticated session cookie, winhttp just sends it automatically - xml doesn't. I tend to use xml on forums though as it has fewer issues with corporate networks, winhttp sometimes requires fiddling with for some posters on the forum; xml request also allows for more asynchronousity.


----------



## John_w (Feb 21, 2019)

Here is a list of useful links for UI Automation:

UI Automation introduction
https://docs.microsoft.com/en-gb/windows/desktop/WinAuto/entry-uiauto-win32

UI Automation Fundamentals
https://docs.microsoft.com/en-us/windows/desktop/winauto/entry-uiautocore-overview

UI Automation Client Programmer's Guide
https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-clientportal

An introduction to UI Automation
http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html

Introduction to UIA: Microsoft's Accessibility API, including using Inspect tool
https://www.youtube.com/watch?v=6b0K2883rXA

How to install the Inspect tool on Windows 10?
https://stackoverflow.com/questions/34760513/how-to-install-the-inspect-tool-on-windows-10


----------



## Rajkumar_h (Feb 28, 2019)

Thanks a lot John.

So you soon with other scenario.

Cheers!
Raj


----------



## John_w (Oct 7, 2019)

*Update

*I have come across some websites where, when a file download is offered, IE11 displays a dialogue asking "What do you want to do with xxxxx.xxx?", instead of the normal Download Notification Bar at the bottom of the window.  Here's an example:







To see this dialogue follow these steps:


In IE, open website https://www.abs.gov.au/Price-Indexes-and-Inflation 
Click on link “Consumer Price Index” on that page. 
Click on “Download” tab on that page. 
Click on any of the green ".xls" links on that page. 
 (Thread ref. https://www.mrexcel.com/forum/excel-questions/1110318-navigate-getting-data-website-post5344650.html)

See below for my analysis of this issue and why the dialogue is displayed.

Here is the *updated VBA UIAutomation code* which handles both this dialogue and the Download Notification Bar.


```
#If VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If

    'Automate IE11's Download Notification Bar (and 'What do you want to do with xxxx.xx?' dialogue window) in the active tab, by clicking the Save As item,
    'downloading the file and closing the Notification Bar.  The IE option 'Notify when downloads complete' must be enabled.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveInFolder           The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder.
    'saveAsFileName         The file name which the downloaded file will be given.  Specify "" to use the file name provided by the web site.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file
    'downloadResult         Output string returned to the caller showing whether the file was successfully download or not, including the file name of the
    '                       downloaded file (including its path if saveInFolder was specified).
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
    
    Dim UIAutomation As IUIAutomation
    Dim IEmain As IUIAutomationElement
    Dim IEdialogue As IUIAutomationElement
    Dim IEdialogueText As IUIAutomationElement
    Dim IEdialogueSaveAsButton As IUIAutomationElement
    Dim searchSaveAsWindowFrom As IUIAutomationElement
    Dim DesktopRoot As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim IEdialogueCond As IUIAutomationCondition, NotificationToolbarCond As IUIAutomationCondition
    Dim TreeWalker As IUIAutomationTreeWalker
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, IEdialogueTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim downloaded As Boolean
    Dim destCell As Range, numRows As Long
    
    Const DebugMode As Boolean = False
    
    'If debug mode is on then also output UI elements to the "UI debug" sheet, if it exists
    
    Set destCell = Nothing
    If DebugMode Then
        On Error Resume Next
        Set destCell = ThisWorkbook.Worksheets("UI debug").Range("A1")
        On Error GoTo 0
        If Not destCell Is Nothing Then destCell.Parent.Cells.Clear
    End If
    
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
    
    'If specified, ensure folder ends with \
    
    If saveInFolder <> "" And Right(saveInFolder, 1) <> "" Then saveInFolder = saveInFolder & ""
    
    'Create main UIAutomation object
    
    Set UIAutomation = New CUIAutomation
    
    'Get the IE automation element from its window handle
    
    Set IEmain = UIAutomation.ElementFromHandle(ByVal IEhwnd)
    If DebugMode Then DumpElement IEmain
    
    'Conditions to find the IE Notification tool bar, a child of the main IE window
    'Name:                  "Notification"
    'ControlType:           UIA_ToolBarControlTypeId
    'LocalizedControlType:  "tool bar"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'If the server sends an incorrect response header, for example the misspelling of 'attachment' in:
    '
    '   Content-Disposition: attachement; filename="8d72ee3290adc3d.zip"
    '
    'then IE displays a modal dialogue box instead of the normal Download Notification Bar at the bottom of the IE window:
    '
    '   -------------------------------------------------
    '   Internet Explorer                             [X]
    '
    '   What do you want to do with xxxxx.xxx?
    '
    '   Size: 54.2 MB
    '   From: hostname.com
    '
    '   --> Open
    '       The file won't be saved automatically
    '
    '   --> Save
    '
    '   --> Save as
    '
    '                                            [Cancel]
    '   -------------------------------------------------
    
    'Conditions to find this dialogue, which is a child of the main IE window.
    'Note - the dialogue isn't a true window, so FindWindowEx(IEhwnd, 0, "Internet Explorer", vbNullString) doesn't find the dialogue.
    
    'Name:                  "Internet Explorer"
    'ControlType:           UIA_WindowControlTypeId (0xC370)
    'LocalizedControlType:  "dialogue"
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Internet Explorer")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set IEdialogueCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Wait until either the IE11 Notification tool bar exists or the "What do you want to do with xxxx.xxx?" dialogue exists
    
    Set IEdialogue = Nothing
    Set NotificationToolbar = Nothing
    Do
        'New method of finding notification bar, instead of FindWindowEx
        Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
        If NotificationToolbar Is Nothing Then Set IEdialogue = IEmain.FindFirst(TreeScope_Children, IEdialogueCond)
        DoEvents
        Sleep 200
    Loop While NotificationToolbar Is Nothing And IEdialogue Is Nothing
    
    If Not NotificationToolbar Is Nothing Then
    
        'The Notification tool bar exists
    
        If DebugMode Then
            'DumpElement IEmain
            DumpElement NotificationToolbar
            'Create Tree Walker to recursively traverse the automation elements for debugging purposes
            'Set TreeWalker = UIAutomation.RawViewWalker
            'ListDescendants TreeWalker, IEmain, 0, 0
            'ListDescendants TreeWalker, NotificationToolBar, 0, 0
            If Not destCell Is Nothing Then
                destCell.Value = "  IEmain children"
                numRows = UIElements_To_Cells(UIAutomation, IEmain, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
            If Not destCell Is Nothing Then
                destCell.Value = "  NotificationToolBar children"
                numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
        
        'Find the Notification tool bar text element and extract the default file name from it
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
            
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
        p1 = InStr(NotificationBarTextString, "Do you want to open or save ")
        If p1 = 1 Then
            p1 = p1 + Len("Do you want to open or save ")
            p2 = InStr(p1, NotificationBarTextString, " from ")
            defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
        Else
            defaultFileName = ""
        End If
        If DebugMode Then Debug.Print "Default file name = " & defaultFileName
        
        'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
        
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
        Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType) 'TreeScope_Descendants necessary
        Set DownArrow = SplitButtons.GetElement(1)
            
        'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
        'with the following properties:
        '
        'Name:          "Context"
        'ControlType:   UIA_MenuControlTypeId
        '
        'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
        
        'Create criteria to find the Context menu
        
        Set DesktopRoot = UIAutomation.GetRootElement
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Context")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
           
        'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
        'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
        'not TreeScope_Descendants, to reduce the number of elements searched.
        'See [url]https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst[/url]
        
        Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
        Do
            DownArrow.SetFocus
            InvokePattern.Invoke
            DoEvents
            Sleep 200
            Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
        Loop While ContextMenu Is Nothing
        
        If DebugMode Then
            If Not destCell Is Nothing Then
                destCell.Value = "  ContextMenu children"
                numRows = UIElements_To_Cells(UIAutomation, ContextMenu, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
            
        'Find the Save as item in the Context menu
        'Name:          "Save as"
        'ControlType:   UIA_MenuItemControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save as")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
            
        'Click the Save as item to display the Save As dialogue window
        
        Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        
        'Search for the Save as window, when it appears, from the Desktop root element
        
        Set searchSaveAsWindowFrom = DesktopRoot
        
    ElseIf Not IEdialogue Is Nothing Then
    
        'The IE dialogue window exists
        
        If DebugMode Then
            'DumpElement IEmain
            DumpElement IEdialogue
            'Create Tree Walker to recursively traverse the automation elements for debugging purposes
            Set TreeWalker = UIAutomation.RawViewWalker
            ListDescendants TreeWalker, IEmain, 0, 0
            ListDescendants TreeWalker, IEdialogue, 0, 0
            If Not destCell Is Nothing Then
                destCell.Value = "  IEmain children"
                numRows = UIElements_To_Cells(UIAutomation, IEmain, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
            If Not destCell Is Nothing Then
                destCell.Value = "  IEdialogue children"
                numRows = UIElements_To_Cells(UIAutomation, IEdialogue, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
    
        'See if the first child of the dialogue window contains the text "What do you want to do with "
        
        'Name:   "What do you want to do with 8d731f569075f6d.zip?"
        'ControlType:    UIA_TextControlTypeId (0xC364)
        'LocalizedControlType:   "text"

        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set IEdialogueText = IEdialogue.FindFirst(TreeScope_Children, ControlType)
        
        If Not IEdialogueText Is Nothing Then
        
            'IEdialogueTextString = IEdialogueText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)  'either of these
            IEdialogueTextString = IEdialogueText.CurrentName
            p1 = InStr(IEdialogueTextString, "What do you want to do with ")
            If p1 = 1 Then
                p1 = p1 + Len("What do you want to do with ")
                p2 = InStr(p1, IEdialogueTextString, "?")
                defaultFileName = Mid(IEdialogueTextString, p1, p2 - p1)
                p1 = 1
            Else
                defaultFileName = ""
            End If
            If DebugMode Then Debug.Print "Default file name = " & defaultFileName
        
            'Get the 'Save as' button in the dialogue
            'Name:   "Save as"
            'ControlType:    UIA_ButtonControlTypeId (0xC350)
            'LocalizedControlType:   "button"

            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save as")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            Set IEdialogueSaveAsButton = IEdialogue.FindFirst(TreeScope_Children, NameAndType)

            If Not IEdialogueSaveAsButton Is Nothing Then
            
                'Click the Save as button to display the Save As dialogue window
    
                Set InvokePattern = IEdialogueSaveAsButton.GetCurrentPattern(UIA_InvokePatternId)
                InvokePattern.Invoke
                
                'Search for the Save as window, when it appears, from the main IE window element
                
                Set searchSaveAsWindowFrom = IEmain
                
            End If
            
        End If

    End If
        
    'Find the Save As dialogue window, which is either a child of the Desktop if the Notification Bar was displayed, or a child
    'of the main IE window if the IE dialogue window was displayed, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = searchSaveAsWindowFrom.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 100
    Loop While SaveAsWindow Is Nothing
            
    If DebugMode Then
        If Not destCell Is Nothing Then
            destCell.Value = "  SaveAsWindow children"
            numRows = UIElements_To_Cells(UIAutomation, SaveAsWindow, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    End If
            
    'If the caller has specified either the folder or the file name, then populate the file name input box in the Save As window
    
    If saveInFolder <> "" Or saveAsFileName <> "" Then
    
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
        
        'Construct the full file name
        
        fullFileName = saveInFolder & saveAsFileName
        
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
    
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "File name:")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the file name input box
        
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
        
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
        
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
        
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
        
        SendKeys " ", True          'press space key
             
    Else
    
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
        
        fullFileName = defaultFileName
    
    End If
    
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Find the Save button, a child of the Save As window
    
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Save clicked"
        
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
    
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Confirm Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
    
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Confirm Save As"
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
       
    If Not ConfirmSaveAsWindow Is Nothing Then
        
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
    
        If replaceExistingFile Then
        
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Yes")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the Yes button
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the Yes button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "Yes clicked"
        
            downloaded = True

        Else 'replaceExistingFile = False
        
            'Extract the text warning from the Confirm Save As window
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
        
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "No")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the No button, waiting until it exists
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the No button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "No clicked"
                        
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
            
        End If
    
    Else
    
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
        
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        If Not SaveAsWarningWindow Is Nothing Then
        
            'The Save As warning window exists, so extract the text warning from it
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
            
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "OK")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
            'Find the OK button - a child of the Save As warning window
            
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
            
            'Click the OK button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "OK clicked"
            
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
            
        Else
        
            'The Save As warning window doesn't exist, so the file was downloaded
            
            IE_Download_File_Using_UIAutomation = True
            
        End If
            
    End If
    
    If IE_Download_File_Using_UIAutomation = True Then
    
        If DebugMode Then
            If Not destCell Is Nothing Then
                destCell.Value = "  FrameNotificationBarPane children"
                numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
                End If
        End If
    
        'If the file download was offered via the IE dialogue pane, rather than the normal Frame Notification Bar, then the Notification toolbar
        'is displayed as the download progresses.   Therefore get the Notification toolbar at this point
        
        If NotificationToolbar Is Nothing Then
        
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
        
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                Sleep 200
            Loop While NotificationToolbar Is Nothing
        
        End If
        
        'Create criteria to find the "Notification bar Text" element
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   The xxxx yyyy.zzz download has completed.
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
        
        NotificationBarTextString = ""
        Do
            Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
            DoEvents
            Sleep 200
            If Not NotificationBarText Is Nothing Then
                NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
            End If
            If DebugMode Then Debug.Print Time; NotificationBarTextString
        Loop Until InStr(NotificationBarTextString, "download has completed")

        'Extract file name from Notification bar text, e.g. "The xxxx yyyy.zzz download has completed."
        
        p1 = InStr(NotificationBarTextString, "The ") + Len("The ")
        p2 = InStr(p1, NotificationBarTextString, " download has completed")
        
        If DebugMode Then
            Debug.Print "Notification Bar downloaded = " & Mid(NotificationBarTextString, p1, p2 - p1)
            Debug.Print "Full file name = " & fullFileName
        End If
        downloadResult = fullFileName & " - SUCCESSFULLY DOWNLOADED"
        
    Else
    
        'Not downloaded, so click the Cancel button in the Save As window
            
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Cancel")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Cancel button, waiting until it exists
        
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Cancel button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugMode Then Debug.Print Time; "Cancel clicked"
        
        'In this case, does the Notification tool bar pane exist?

    End If
             
    If Not NotificationToolbar Is Nothing Then
    
        'Create criteria to find the Close (X) button on the Notification pane
        'Name:          "Close"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Close")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
        'Find the Close button in the IE Download Notification Bar
        
        Set Button = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Close button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugMode Then Debug.Print Time; "Close clicked"
    
    End If
                 
End Function
```

*Analysis*

Why does IE11 display the "What do you want to do with xxxxx.xxx?" dialogue when a file download is offered?

It happens because the website doesn't send a "Content-Disposition" header, or there is a misspelling in the header's value string, or incorrect syntax.

A well-behaved website (e.g. http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US) sends the following correct response header with the file download:

Content-Disposition: attachment;filename="INTC Balance Sheet.csv"

and the Download Notification Bar is displayed.

However, https://www.abs.gov.au (https://www.ausstats.abs.gov.au for the actual download) doesn't send the Content-Disposition header.  (One of the headers it sends is content-type: application/vnd.ms-excel).

The dialogue is also displayed if the header's value is misspelt, for example:

Content-Disposition: attachement;filename="Data.zip"

Here, attachment is misspelt as "attachement".

*Using Fiddler to fix or reproduce this issue

*By running Fiddler, we can use FiddlerScript to fix or reproduce the issue with the dialogue.  To fix the issue for https://www.abs.gov.au, add the following code to the OnPeekAtResponseHeaders function:

```
if (oSession.HostnameIs("www.ausstats.abs.gov.au") && oSession.oResponse.headers.ExistsAndContains("Content-Type","ms-excel")){
            oSession.oResponse.headers.Add("Content-Disposition", "attachment; filename='Stats_Data.xls'");    
        }
```
and click the Save Script button.  Fiddler calls the OnPeekAtResponseHeaders function before the response headers are sent to the client, hence the above code adds the Content-Disposition header with a fixed file name to the response headers and therefore IE will display the normal Download Notification Bar instead of the dialogue when a file download is offered.

To reproduce the issue, add the following code to the OnPeekAtResponseHeaders function:

```
if (oSession.HostnameIs("your.hostname.com") && oSession.oResponse.headers.ExistsAndContains("Content-Disposition","attachment")){
            oSession.oResponse.headers.Remove("Content-Disposition");
            oSession.oResponse.headers.Add("Content-Disposition", "attachement; filename='Data.zip'");    
        }
```
The above code checks to see if the Content-Disposition response header contains "attachment" and if so changes it to the misspelt "attachement" with a fixed file name.  With this code, IE will display the "What do you want to do with xxxxx.xxx?" dialogue instead of the normal Download Notification Bar.  I think the dialogue is displayed only for binary files (.zip, .xls, etc.) and not text files (.txt, .csv).  With text files, IE displays the file data in the browser.


----------



## John_w (Oct 11, 2019)

*UPDATE *- Here is a list of useful links for UI Automation:

UI Automation introduction
https://docs.microsoft.com/en-gb/windows/desktop/WinAuto/entry-uiauto-win32

UI Automation Fundamentals
https://docs.microsoft.com/en-us/windows/desktop/winauto/entry-uiautocore-overview

UI Automation Client Programmer's Guide
https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-clientportal

An introduction to UI Automation
http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html

Introduction to UIA: Microsoft's Accessibility API, including using Inspect tool
https://www.youtube.com/watch?v=6b0K2883rXA

How to install the Inspect tool on Windows 10?
https://stackoverflow.com/questions/34760513/how-to-install-the-inspect-tool-on-windows-10

Using the Inspect tool
https://docs.microsoft.com/en-us/windows/win32/winauto/inspect-objects

Inspect is a legacy tool.  Microsoft recommended using Accessibility Insights instead.

Accessibility Insights
https://accessibilityinsights.io/


----------



## John_w (Feb 7, 2019)

Here is VBA code which automates the complete 'Save As' file download procedure in IE11, when a download is offered by IE's Download Notification Bar.

To use for your own web site downloads you would automate IE and do whatever is necessary to make the Download Notification Bar appear at the bottom of the IE window and then call IE_Download_File_Using_UIAutomation.  It has the following parameters:

Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean


IEhwnd                 - The handle of the IE window. The Download Notification Bar must be displayed in the active tab. If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function. 
saveInFolder           - The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder. 
saveAsFileName         - The file name which the downloaded file will be given.  Specify "" to use the file name provided by the web site. 
replaceExistingFile    - True to replace the file if it already exists; False to overwrite the file. 
downloadResult         - Output string returned to the caller showing whether the file was successfully download or not, including the file name of the downloaded file (including its path if saveInFolder was specified). 
Function return value -  True: the file was downloaded; False: the file was not downloaded.

*NOTE *- The only issue I had was that after calling SetValue to put a specific file name in the Save As dialogue and clicking Save (Invoke), the download doesn't recognise the specified file name and still uses the default file name provided by the web site.  A simple workaround which has worked in all tests is SendKeys " ", True to insert a space character at start of the file name input element, however I know that SendKeys is unreliable and this might not always work for some people.

Also note that the IE tab offering the download must be the active tab, and the IE_Click_Tab_Like routine should be called to activate it if necessary.  The code also includes several debugging functions which can be helpful to understand the UIAutomation hierarchy.

Here is the complete code, including a test procedure for downloading a 2 Kb csv file from morningstar.com


```
'07-Feb-2019
'Use UIAutomationClient to automate the Save As file download in IE11

'References:
'Microsoft Internet Controls
'Microsoft HTML Object Library
'UIAutomationClient


Option Explicit

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As LongPtr, ByVal hwndChildAfter As LongPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If


Public Sub IE_Download_File_Save_As()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim exportLink As HTMLLinkElement
    Dim saveInFolder As String
    Dim saveAsFileName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
    
    saveInFolder = ""                           'Save in IE11's default download folder
    saveInFolder = "c:\path\to\folder"          'Save in this folder
    saveAsFileName = ""                         'Save As the file name provided by web site
    saveAsFileName = "My Csv Data"              'Save As this file name
    replaceExistingFile = True
    
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
    
    'See if an IE window is already open at the site
    
    Set IE = Get_IE_Window(CStr(URL))
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            SetForegroundWindow .hWnd
            .Visible = True
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
           
    Set exportLink = HTMLdoc.getElementsByClassName("rf_export")(0)
    
    If Not exportLink Is Nothing Then
    
        'Click the Export link, which opens IE's Download Notification Bar
        '
        '   Do you want to open or save INTC Balance Sheet.csv from financials.morningstar.com?
        '       with buttons [Open] [Save][Down Arrow] [Cancel] [X]
        
        exportLink.Click
        
        'Activate the morningstar.com IE tab and download the file using Save As
        
        IE_Click_Tab_Like IE.hWnd, "*morningstar.com*"
        downloadStatus = IE_Download_File_Using_UIAutomation(IE.hWnd, saveInFolder, saveAsFileName, replaceExistingFile, downloadResult)
        Debug.Print "Download status = " & downloadStatus
        MsgBox "Download result = " & downloadResult
        
    Else
    
        MsgBox "Export link not found - file not downloaded"
        
    End If
    
End Sub

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If

    'Automate IE11's Download Notification Bar in the active tab, by clicking the Save As item, downloading the file and closing the Notification Bar.
    'The IE option 'Notify when downloads complete' must be enabled.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveInFolder           The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder.
    'saveAsFileName         The file name which the downloaded file will be given.  If file extension is omitted, the file extension provided by
    '                       the web site is used. Specify "" to use the file name provided by the web site.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file
    'downloadResult         Output string returned to the caller showing whether the file was successfully download or not, including the file name of the
    '                       downloaded file (including its path if saveInFolder was specified).
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
    
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
        Dim hWnd As LongPtr
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
        Dim hWnd As Long
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
    Dim UIAutomation As IUIAutomation
    Dim DesktopRoot As IUIAutomationElement
    Dim FrameNotificationBarPane As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim TreeWalker As IUIAutomationTreeWalker
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim downloaded As Boolean
    Dim destCell As Range, numRows As Long
    
    Const DebugMode As Boolean = True
    
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
    
    'If specified, ensure folder ends with \
    
    If saveInFolder <> "" And Right(saveInFolder, 1) <> "" Then saveInFolder = saveInFolder & ""
    
    'Create main UIAutomation object
    
    Set UIAutomation = New CUIAutomation
    
    'Find the IE11 Frame Notification Bar, waiting until it exists
    
    Do
        hWnd = FindWindowEx(IEhwnd, 0, "Frame Notification Bar", vbNullString)
        DoEvents
        Sleep 200
    Loop While hWnd = 0
    If DebugMode Then Debug.Print Time; "Frame Notification Bar " & hWnd

    'Get the Frame Notification Bar pane from the window frame
    'Class         = Frame Notification Bar
    'Ctrl type     = UIA_PaneControlTypeId

    Set FrameNotificationBarPane = UIAutomation.ElementFromHandle(ByVal hWnd)
    'DumpElement FrameNotificationBarPane
        
    If DebugMode Then
        With Worksheets(1)
            .Cells.Clear
            Set destCell = .Range("A1")
        End With
        destCell.Value = "  FrameNotificationBarPane children"
        numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If

    'Find the Notification tool bar, a child of the Frame Notification Bar pane, waiting until it exists
    'Name:          "Notification"
    'ControlType:   UIA_ToolBarControlTypeId

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set NotificationToolbar = FrameNotificationBarPane.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Notification tool bar"
        DoEvents
    Loop While NotificationToolbar Is Nothing
    If DebugMode Then
        destCell.Value = "  NotificationToolbar children"
        numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
    
    'Find the Notification tool bar text element and extract the default file name from it
    'Name:          "Notification bar Text"
    'ControlType:   UIA_TextControlTypeId
    'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
        
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
    p1 = InStr(NotificationBarTextString, "Do you want to open or save ")
    If p1 = 1 Then
        p1 = p1 + Len("Do you want to open or save ")
        p2 = InStr(p1, NotificationBarTextString, " from ")
        defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
    Else
        defaultFileName = ""
    End If
    
    'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
    
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
    Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType)
    Set DownArrow = SplitButtons.GetElement(1)
        
    'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
    'with the following properties:
    '
    'Name:          "Context"
    'ControlType:   UIA_MenuControlTypeId
    '
    'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
    
    'Create criteria to find the Context menu
    
    Set DesktopRoot = UIAutomation.GetRootElement
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Context")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
    'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
    'not TreeScope_Descendants, to reduce the number of elements searched.
    'See [url]https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst[/url]
    
    Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
    Do
        DownArrow.SetFocus
        InvokePattern.Invoke
        DoEvents
        Sleep 200
        Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
    Loop While ContextMenu Is Nothing
    
    If DebugMode Then
        destCell.Value = "  ContextMenu children"
        numRows = UIElements_To_Cells(UIAutomation, ContextMenu, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
        
    'Find the Save as item in the Context menu
    'Name:          "Save as"
    'ControlType:   UIA_MenuItemControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save as")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save as item to display the Save As dialogue window
    
    Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    
    'Find the Save As dialogue window, which is a child of the Desktop, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 100
    Loop While SaveAsWindow Is Nothing
            
    If DebugMode Then
        destCell.Value = "  SaveAsWindow children"
        numRows = UIElements_To_Cells(UIAutomation, SaveAsWindow, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
            
    'If the caller has specified either the folder or the file name, then populate the file name input box in the Save As window
    
    If saveInFolder <> "" Or saveAsFileName <> "" Then
    
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
        
        'Construct the full file name
        
        fullFileName = saveInFolder & saveAsFileName
        
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
    
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "File name:")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the file name input box
        
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
        
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
        
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern.
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
        
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
        
        SendKeys " ", True          'press space key
             
    Else
    
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
        
        fullFileName = defaultFileName
    
    End If
    
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Find the Save button, a child of the Save As window
    
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Save clicked"
        
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
        
    
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Confirm Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
    
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Confirm Save As"
        DoEvents
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
       
    If Not ConfirmSaveAsWindow Is Nothing Then
        
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
    
        If replaceExistingFile Then
        
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Yes")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the Yes button
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the Yes button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "Yes clicked"
        
            downloaded = True

        Else 'replaceExistingFile = False
        
            'Extract the text warning from the Confirm Save As window
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
        
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "No")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the No button, waiting until it exists
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the No button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "No clicked"
                        
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
            
        End If
    
    Else
    
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
        
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        If Not SaveAsWarningWindow Is Nothing Then
        
            'The Save As warning window exists, so extract the text warning from it
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
            
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "OK")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
            'Find the OK button - a child of the Save As warning window
            
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
            
            'Click the OK button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "OK clicked"
            
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
            
        Else
        
            'The Save As warning window doesn't exist, so the file was downloaded
            
            IE_Download_File_Using_UIAutomation = True
            
        End If
            
    End If
    
    If IE_Download_File_Using_UIAutomation = True Then
    
        If DebugMode Then
            destCell.Value = "  FrameNotificationBarPane children"
            numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    
        'Create criteria to find the "Notification bar Text" element
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   The xxxx yyyy.zzz download has completed.
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
        
        NotificationBarTextString = ""
        Do
            Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
            Sleep 200
            DoEvents
            If Not NotificationBarText Is Nothing Then
                NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
            End If
            If DebugMode Then Debug.Print Time; NotificationBarTextString
        Loop Until InStr(NotificationBarTextString, "download has completed")

        'Extract file name from Notification bar text, e.g. "The xxxx yyyy.zzz download has completed."
        
        p1 = InStr(NotificationBarTextString, "The ") + Len("The ")
        p2 = InStr(p1, NotificationBarTextString, " download has completed")
        
        Debug.Print "Notification Bar downloaded = " & Mid(NotificationBarTextString, p1, p2 - p1)
        Debug.Print "Full file name = " & fullFileName
        downloadResult = fullFileName & " - SUCCESSFULLY DOWNLOADED"
        
    Else
    
        'Not downloaded, so click the Cancel button in the Save As window
            
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Cancel")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Cancel button, waiting until it exists
        
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Cancel button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugMode Then Debug.Print Time; "Cancel clicked"

    End If
             
    'Create criteria to find the Close (X) button on the Notification pane
    'Name:          "Close"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Close")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Close button in the IE Download Notification Bar
    
    Set Button = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    
    'Click the Close button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Close clicked"
                 
End Function


'This finds all TabItemControls of the IE element and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - [url]https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator[/url])
'to specify the tab name to be found and activated.
'This is useful because sometimes although the visible the tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
'according to UIAutomation is "xxxxx Tab Group 1".

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
   
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
    
    'Create UIAutomation object
    
    Set UIauto = New CUIAutomation
    
    'Get Internet Explorer UIAutomation element
    
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
    
    'Create condition to find a TabItemControl
    
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
 
    'Find all tabs
    
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
    
    'Look for the tab which matches the specified tab name
    
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
        
    If Not IEtab Is Nothing Then
    
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)
    
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
    
    Else
    
        MsgBox "IE tab with name '" & findTabName & "' not found"
        
    End If
        
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
    
End Sub


Public Sub DumpElement(UIAutoElem As IUIAutomationElement)
    Dim ct As Long
    ct = Get_UIA_PropertyValue(UIAutoElem, UIA_ControlTypePropertyId)
    Debug.Print "----------------"
    Debug.Print "Name                 = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NamePropertyId)
    Debug.Print "Class                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ClassNamePropertyId)
    Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
    Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LocalizedControlTypePropertyId)
    Debug.Print "Value                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ValueValuePropertyId)
    Debug.Print "Handle               = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId)
    Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(UIAutoElem, UIA_AccessKeyPropertyId)
    Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LegacyIAccessibleDefaultActionPropertyId)
    Debug.Print "Description          = " & Get_UIA_PropertyValue(UIAutoElem, UIA_FullDescriptionPropertyId)
    Debug.Print "NativeWindowHandle   = " & "0x" & Hex(Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId))
End Sub


'Get property value for a UI element
Private Function Get_UIA_PropertyValue(UIAutoElem As IUIAutomationElement, propertyId As Long)
    
    Dim tVal As Variant
    Dim tStr As String, i As Integer
    
    tVal = UIAutoElem.GetCurrentPropertyValue(propertyId)
    
    If IsArray(tVal) Then
        tStr = tVal(0)
        For i = 1 To UBound(tVal)
            tStr = tStr & "; " & tVal(i)
        Next
        Get_UIA_PropertyValue = tStr
    Else
        Get_UIA_PropertyValue = tVal
    End If
    
End Function


'Convert a UI property id to its constant name
Private Function Get_UIA_ControlType(propertyId As Long) As String

    Dim cn As String
    
    Select Case propertyId
        Case 50040: cn = "UIA_AppBarControlTypeId"
        Case 50000: cn = "UIA_ButtonControlTypeId"
        Case 50001: cn = "UIA_CalendarControlTypeId"
        Case 50002: cn = "UIA_CheckBoxControlTypeId"
        Case 50003: cn = "UIA_ComboBoxControlTypeId"
        Case 50025: cn = "UIA_CustomControlTypeId"
        Case 50028: cn = "UIA_DataGridControlTypeId"
        Case 50029: cn = "UIA_DataItemControlTypeId"
        Case 50030: cn = "UIA_DocumentControlTypeId"
        Case 50004: cn = "UIA_EditControlTypeId"
        Case 50026: cn = "UIA_GroupControlTypeId"
        Case 50034: cn = "UIA_HeaderControlTypeId"
        Case 50035: cn = "UIA_HeaderItemControlTypeId"
        Case 50005: cn = "UIA_HyperlinkControlTypeId"
        Case 50006: cn = "UIA_ImageControlTypeId"
        Case 50008: cn = "UIA_ListControlTypeId"
        Case 50007: cn = "UIA_ListItemControlTypeId"
        Case 50010: cn = "UIA_MenuBarControlTypeId"
        Case 50009: cn = "UIA_MenuControlTypeId"
        Case 50011: cn = "UIA_MenuItemControlTypeId"
        Case 50033: cn = "UIA_PaneControlTypeId"
        Case 50012: cn = "UIA_ProgressBarControlTypeId"
        Case 50013: cn = "UIA_RadioButtonControlTypeId"
        Case 50014: cn = "UIA_ScrollBarControlTypeId"
        Case 50039: cn = "UIA_SemanticZoomControlTypeId"
        Case 50038: cn = "UIA_SeparatorControlTypeId"
        Case 50015: cn = "UIA_SliderControlTypeId"
        Case 50016: cn = "UIA_SpinnerControlTypeId"
        Case 50031: cn = "UIA_SplitButtonControlTypeId"
        Case 50017: cn = "UIA_StatusBarControlTypeId"
        Case 50018: cn = "UIA_TabControlTypeId"
        Case 50019: cn = "UIA_TabItemControlTypeId"
        Case 50036: cn = "UIA_TableControlTypeId"
        Case 50020: cn = "UIA_TextControlTypeId"
        Case 50027: cn = "UIA_ThumbControlTypeId"
        Case 50037: cn = "UIA_TitleBarControlTypeId"
        Case 50021: cn = "UIA_ToolBarControlTypeId"
        Case 50022: cn = "UIA_ToolTipControlTypeId"
        Case 50023: cn = "UIA_TreeControlTypeId"
        Case 50024: cn = "UIA_TreeItemControlTypeId"
        Case 50032: cn = "UIA_WindowControlTypeId"
        Case Else: cn = "UNKNOWN"
    End Select
    
    Get_UIA_ControlType = cn
    
End Function


'Recursively walk the hierarchy of UIAutomationElements starting at the specified parent element and output UI elements to Excel cells.

Public Function UIElements_To_Cells(UIAutomation As IUIAutomation, parentElement As IUIAutomationElement, destCell As Range) As Long

    Dim n As Long
    Dim sibling As Long
    Static TreeWalker As IUIAutomationTreeWalker
    Dim childElement As IUIAutomationElement
    Dim ct As Long
    
    n = 0
    
    If Not parentElement Is Nothing Then
    
        'Any of these tree walkers can be used
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.RawViewWalker
        If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ControlViewWalker
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ContentViewWalker
        
        Set childElement = TreeWalker.GetFirstChildElement(parentElement)
        sibling = 0
        While Not childElement Is Nothing
            ct = Get_UIA_PropertyValue(childElement, UIA_ControlTypePropertyId)
            sibling = sibling + 1
            Debug.Print "-- Sibling " & sibling
            Debug.Print "Name                 = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            Debug.Print "Class                = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            Debug.Print "Value                = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            Debug.Print "Handle               = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            Debug.Print "Description          = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            destCell.Offset(n + 0).Value = "'-- Sibling " & sibling
            destCell.Offset(n + 1).Value = "Name = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            destCell.Offset(n + 2).Value = "Class = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            destCell.Offset(n + 3).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            destCell.Offset(n + 4).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            destCell.Offset(n + 5).Value = "Value = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            destCell.Offset(n + 6).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            destCell.Offset(n + 7).Value = "AccessKey = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            destCell.Offset(n + 8).Value = "DefaultAction = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            destCell.Offset(n + 9).Value = "Description = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            
            n = n + 10 + UIElements_To_Cells(UIAutomation, childElement, destCell.Offset(n + 10, 1))
            
            Set childElement = TreeWalker.GetNextSiblingElement(childElement)
        Wend
    
    End If
    
    UIElements_To_Cells = n

End Function


Private Function Get_IE_Window(URL As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the domain of the specified URL (which can start with [url]http://,[/url] https://
    'or nothing) and, if found, return that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim domain As String
    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
    Dim p1 As Integer, p2 As Integer
    
    p1 = InStr(URL, "://")
    If p1 = 0 Then
        p1 = 1
    Else
        p1 = p1 + 3
    End If
    p2 = InStr(p1, URL, "/")
    If p2 = 0 Then p2 = Len(URL) + 1
    domain = Mid(URL, p1, p2 - p1)
    
    Set Shell = CreateObject("Shell.Application")
    
    i = 0
    Set Get_IE_Window = Nothing
    While i < Shell.Windows.Count And Get_IE_Window Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If TypeName(IE.document) = "HTMLDocument" Then
                If InStr(IE.LocationURL, domain) > 0 Then
                    Set Get_IE_Window = IE
                End If
            End If
        End If
        i = i + 1
    Wend
     
End Function
```


----------



## John_w (Dec 30, 2019)

*Latest UIAutomation download code and test routine*

Changes:

1. Language-dependent string definitions (English and Spanish) for IE11 UI control names and text strings.
2. The two function arguments, ByVal saveInFolder As String and ByVal saveAsFileName As String, have been replaced by a single argument, ByRef saveAsFullName.
3. Const DebugLevel provides 3 levels of debug output.
4. The development/debugging function UIElements_To_Cells now outputs the first parent element.

*UIAutomation download function*


```
'References required:
'UIAutomationClient

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
#End If

'Note - UI Control Names are case-sensitive, hence the two 'Save As' versions

'------- English string definitions for Internet Explorer 11 -------
Const sControlName_Notification As String = "Notification"
Const sControlName_Notification_bar_Text As String = "Notification bar Text"
Const sControlName_Context As String = "Context"
Const sControlName_Save_as1 As String = "Save as"
Const sControlName_Save_As2 As String = "Save As"
Const sControlName_File_name As String = "File name:"
Const sControlName_Save As String = "Save"
Const sControlName_Confirm_Save_As As String = "Confirm Save As"
Const sControlName_Yes As String = "Yes"
Const sControlName_No As String = "No"
Const sControlName_OK As String = "OK"
Const sControlName_Cancel As String = "Cancel"
Const sControlName_Close As String = "Close"
Const sText_NotificationBarP1 As String = "Do you want to open or save "
Const sText_NotificationBarP2 As String = " from "
Const sText_IEdialogueP1 As String = "What do you want to do with "
Const sText_IEdialogueP2 As String = "?"
Const sText_The As String = "The "
Const sText_Download_has_Completed As String = " download has completed"
'-------------------------------------------------------------------


'------- Spanish string definitions for Internet Explorer 11 -------
'Const sControlName_Notification As String = "Notificación"
'Const sControlName_Notification_bar_Text As String = "Texto de la barra de notificación"
'Const sControlName_Context As String = "Contexto"
'Const sControlName_Save_as1 As String = "Guardar como"
'Const sControlName_Save_As2 As String = "Guardar Como"
'Const sControlName_File_name As String = "Nombre:"
'Const sControlName_Save As String = "Guardar"
'Const sControlName_Confirm_Save_As As String = "Confirmar Guardar Como"
'Const sControlName_Yes As String = "Si"
'Const sControlName_No As String = "No"
'Const sControlName_OK As String = "OK"
'Const sControlName_Cancel As String = "Cancelar"
'Const sControlName_Close As String = "Cerrar"
'Const sText_NotificationBarP1 As String = "¿Quieres abrir o guardar "  '?? check
'Const sText_NotificationBarP2 As String = " desde "
'Const sText_IEdialogueP1 As String = "¿Qué quieres hacer con "
'Const sText_IEdialogueP2 As String = "?"
'Const sText_The As String = "La descarga de "
'Const sText_Download_has_Completed As String = " se completó"
'-------------------------------------------------------------------

#If VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByRef saveAsFullName, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#Else
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByRef saveAsFullName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#End If

    'Automate IE11's Download Notification Bar (or 'What do you want to do with xxxx.xx?' dialogue window) in the active tab, by clicking the Save As item,
    'downloading the file and closing the Notification Bar (or dialogue window).  The IE option 'Notify when downloads complete' must be enabled.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveAsFileName         The optional folder path where the downloaded file will be saved and the optional file name which the downloaded file will be given.
    '                       Specify "" to use IE's default download folder and the file name provided by the web site.
    '                       This argument is passed ByRef so that the actual full name of the downloaded file, if successfully downloaded, is returned to the caller.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file.
    'downloadResult         Output string returned to the caller showing whether the file was successfully download or not.
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
    
    Dim UIAutomation As IUIAutomation
    Dim IEmain As IUIAutomationElement
    Dim IEdialogue As IUIAutomationElement
    Dim IEdialogueText As IUIAutomationElement
    Dim IEdialogueSaveAsButton As IUIAutomationElement
    Dim searchSaveAsWindowFrom As IUIAutomationElement
    Dim DesktopRoot As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim IEdialogueCond As IUIAutomationCondition, NotificationToolbarCond As IUIAutomationCondition
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, IEdialogueTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim downloaded As Boolean
    Dim destCell As Range, numRows As Long
    Dim saveInFolder As String, saveAsFileName As String
    
    'DebugLevel values
    '0 = No debug output
    '1 = Debug.Print
    '2 = Debug.Print and to "UI debug" sheet if it exists
    
    Const DebugLevel = 0
    
    'If debug mode is on then also output UI elements to the "UI debug" sheet, if it exists
    
    Set destCell = Nothing
    If DebugLevel = 2 Then
        On Error Resume Next
        Set destCell = ThisWorkbook.Worksheets("UI debug").Range("A1")
        On Error GoTo 0
        If Not destCell Is Nothing Then destCell.Parent.Cells.Clear
    End If
    
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
    
    'Get folder and file name from full file name
    
    p1 = InStrRev(saveAsFullName, "\")
    If p1 = 0 Then
        saveInFolder = ""
        saveAsFileName = saveAsFullName
    Else
        saveInFolder = Left(saveAsFullName, p1)
        saveAsFileName = Mid(saveAsFullName, p1 + 1)
    End If
    
    'Create main UIAutomation object
    
    Set UIAutomation = New CUIAutomation
    
    'Get the IE automation element from its window handle
    
    Set IEmain = UIAutomation.ElementFromHandle(ByVal IEhwnd)
    If DebugLevel = 1 Then DumpElement IEmain
    
    'Conditions to find the IE Notification tool bar, a child of the main IE window
    'Name:                  "Notification"
    'ControlType:           UIA_ToolBarControlTypeId
    'LocalizedControlType:  "tool bar"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'If the server sends an incorrect response header, for example the misspelling of 'attachment' in:
    '
    '   Content-Disposition: attachement; filename="8d72ee3290adc3d.zip"
    '
    'then IE displays a modal dialogue box instead of the normal Download Notification Bar at the bottom of the IE window:
    '
    '   -------------------------------------------------
    '   Internet Explorer                             [X]
    '
    '   What do you want to do with xxxxx.xxx?
    '
    '   Size: 54.2 MB
    '   From: hostname.com
    '
    '   --> Open
    '       The file won't be saved automatically
    '
    '   --> Save
    '
    '   --> Save as
    '
    '                                            [Cancel]
    '   -------------------------------------------------
    
    'Conditions to find this dialogue, which is a child of the main IE window.
    'Note - the dialogue isn't a true window, so FindWindowEx(IEhwnd, 0, "Internet Explorer", vbNullString) doesn't find the dialogue.
    
    'Name:                  "Internet Explorer"
    'ControlType:           UIA_WindowControlTypeId (0xC370)
    'LocalizedControlType:  "dialogue"
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Internet Explorer")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set IEdialogueCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Wait until either the IE11 Notification tool bar exists or the "What do you want to do with xxxx.xxx?" dialogue exists
    
    Set IEdialogue = Nothing
    Set NotificationToolbar = Nothing
    Do
        'New method of finding notification bar, instead of FindWindowEx
        Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
        If NotificationToolbar Is Nothing Then Set IEdialogue = IEmain.FindFirst(TreeScope_Children, IEdialogueCond)
        DoEvents
        Sleep 200
    Loop While NotificationToolbar Is Nothing And IEdialogue Is Nothing
    
    If Not NotificationToolbar Is Nothing Then
    
        'The Notification tool bar exists
    
        If DebugLevel = 1 Then DumpElement IEmain
        If DebugLevel = 1 Then DumpElement NotificationToolbar
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  IEmain"
                numRows = UIElements_To_Cells(UIAutomation, IEmain, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
            If Not destCell Is Nothing Then
                destCell.Value = "  NotificationToolBar"
                numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
        
        'Find the Notification tool bar text element and extract the default file name from it
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
            
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification_bar_Text)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
        If DebugLevel >= 1 Then Debug.Print "NotificationBarTextString = " & NotificationBarTextString

        p1 = InStr(NotificationBarTextString, sText_NotificationBarP1)
        If p1 = 1 Then
            p1 = p1 + Len(sText_NotificationBarP1)
            p2 = InStr(p1, NotificationBarTextString, " (")
            If p2 = 0 Then p2 = InStr(p1, NotificationBarTextString, sText_NotificationBarP2)
            defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
        Else
            defaultFileName = ""
        End If
        If DebugLevel >= 1 Then Debug.Print "Default file name = " & defaultFileName
        
        'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
        
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
        Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType) 'TreeScope_Descendants necessary
        Set DownArrow = SplitButtons.GetElement(1)
            
        'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
        'with the following properties:
        '
        'Name:          "Context"
        'ControlType:   UIA_MenuControlTypeId
        '
        'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
        
        'Create criteria to find the Context menu
        
        Set DesktopRoot = UIAutomation.GetRootElement
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Context)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
          
        'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
        'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
        'not TreeScope_Descendants, to reduce the number of elements searched.
        'See https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst
        
        If DebugLevel >= 1 Then Debug.Print "Click Down arrow"
        Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
        Do
            DownArrow.SetFocus
            InvokePattern.Invoke
            DoEvents
            Sleep 200
            Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
        Loop While ContextMenu Is Nothing
        
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  ContextMenu"
                numRows = UIElements_To_Cells(UIAutomation, ContextMenu, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
            
        'Find the Save as item in the Context menu
        'Name:          "Save as"
        'ControlType:   UIA_MenuItemControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
            
        'Click the Save as item to display the Save As dialogue window
        
        Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        
        'Search for the Save as window, when it appears, from the Desktop root element
        
        Set searchSaveAsWindowFrom = DesktopRoot
        
    ElseIf Not IEdialogue Is Nothing Then
    
        'The IE dialogue window exists
        
        If DebugLevel = 1 Then DumpElement IEmain
        If DebugLevel = 1 Then DumpElement IEdialogue
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  IEmain"
                numRows = UIElements_To_Cells(UIAutomation, IEmain, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
            If Not destCell Is Nothing Then
                destCell.Value = "  IEdialogue"
                numRows = UIElements_To_Cells(UIAutomation, IEdialogue, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
    
        'See if the first child of the dialogue window contains the text "What do you want to do with "
        
        'Name:   "What do you want to do with 8d731f569075f6d.zip?"
        'ControlType:    UIA_TextControlTypeId (0xC364)
        'LocalizedControlType:   "text"

        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set IEdialogueText = IEdialogue.FindFirst(TreeScope_Children, ControlType)
        
        If Not IEdialogueText Is Nothing Then
        
            'IEdialogueTextString = IEdialogueText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)  'either of these
            IEdialogueTextString = IEdialogueText.CurrentName
            p1 = InStr(IEdialogueTextString, sText_IEdialogueP1)
            If p1 = 1 Then
                p1 = p1 + Len(sText_IEdialogueP1)
                p2 = InStr(p1, IEdialogueTextString, sText_IEdialogueP2)
                defaultFileName = Mid(IEdialogueTextString, p1, p2 - p1)
                p1 = 1
            Else
                defaultFileName = ""
            End If
            If DebugLevel >= 1 Then Debug.Print "Default file name = " & defaultFileName
        
            'Get the 'Save as' button in the dialogue
            'Name:   "Save as"
            'ControlType:    UIA_ButtonControlTypeId (0xC350)
            'LocalizedControlType:   "button"

            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            Set IEdialogueSaveAsButton = IEdialogue.FindFirst(TreeScope_Children, NameAndType)

            If Not IEdialogueSaveAsButton Is Nothing Then
            
                'Click the Save as button to display the Save As dialogue window
    
                Set InvokePattern = IEdialogueSaveAsButton.GetCurrentPattern(UIA_InvokePatternId)
                InvokePattern.Invoke
                
                'Search for the Save as window, when it appears, from the main IE window element
                
                Set searchSaveAsWindowFrom = IEmain
                
            End If
            
        End If

    End If
        
    'Find the Save As dialogue window, which is either a child of the Desktop if the Notification Bar was displayed, or a child
    'of the main IE window if the IE dialogue window was displayed, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = searchSaveAsWindowFrom.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 100
    Loop While SaveAsWindow Is Nothing
            
    If DebugLevel = 2 Then
        If Not destCell Is Nothing Then
            destCell.Value = "  SaveAsWindow"
            numRows = UIElements_To_Cells(UIAutomation, SaveAsWindow, True, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    End If
            
    'If the caller has specified either the folder or the file name, then populate the file name input box in the Save As window
    
    If saveInFolder <> "" Or saveAsFileName <> "" Then
    
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
        
        'Construct the full file name
        
        fullFileName = saveInFolder & saveAsFileName
        
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
    
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_File_name)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the file name input box
        
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
        
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
        
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
        
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
        
        SendKeys " ", True          'press space key
            
    Else
    
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
        
        fullFileName = defaultFileName
    
    End If
    
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Find the Save button, a child of the Save As window
    
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugLevel >= 1 Then Debug.Print Time; "Save clicked"
        
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
    
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Confirm_Save_As)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
    
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 200
        If DebugLevel >= 1 Then Debug.Print Time; "Find Confirm Save As"
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
      
    If Not ConfirmSaveAsWindow Is Nothing Then
        
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
    
        If replaceExistingFile Then
        
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Yes)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the Yes button
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the Yes button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugLevel >= 1 Then Debug.Print Time; "Yes clicked"
        
            downloaded = True

        Else 'replaceExistingFile = False
        
            'Extract the text warning from the Confirm Save As window
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
        
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_No)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the No button, waiting until it exists
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the No button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugLevel >= 1 Then Debug.Print Time; "No clicked"
                        
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
            
        End If
    
    Else
    
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
        
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        If Not SaveAsWarningWindow Is Nothing Then
        
            'The Save As warning window exists, so extract the text warning from it
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
            
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_OK)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
            'Find the OK button - a child of the Save As warning window
            
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
            
            'Click the OK button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugLevel >= 1 Then Debug.Print Time; "OK clicked"
            
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
            
        Else
        
            'The Save As warning window doesn't exist, so the file was downloaded
            
            saveAsFullName = fullFileName
            IE_Download_File_Using_UIAutomation = True
            
        End If
            
    End If
    
    If IE_Download_File_Using_UIAutomation = True Then
    
        'If the file download was offered via the IE dialogue pane, rather than the normal Frame Notification Bar, then the Notification toolbar
        'is displayed as the download progresses.   Therefore get the Notification toolbar at this point
        
        If NotificationToolbar Is Nothing Then
        
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
        
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                Sleep 200
            Loop While NotificationToolbar Is Nothing
        
        End If
        
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  NotificationToolbar before download complete"
                numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
        
        'Create criteria to find the "Notification bar Text" element
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   The xxxx yyyy.zzz download has completed.
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification_bar_Text)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains " download has completed"
        
        NotificationBarTextString = ""
        Do
            Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
            DoEvents
            Sleep 200
            If Not NotificationBarText Is Nothing Then
                NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
            End If
            If DebugLevel >= 1 Then Debug.Print Time; NotificationBarTextString
        Loop Until InStr(NotificationBarTextString, sText_Download_has_Completed)

        'Extract file name from Notification bar text, e.g. "xxxx yyyy.zzz" from "The xxxx yyyy.zzz download has completed."
        
        p1 = InStr(NotificationBarTextString, sText_The) + Len(sText_The)
        p2 = InStr(p1, NotificationBarTextString, " (")
        If p2 = 0 Then p2 = InStr(p1, NotificationBarTextString, sText_Download_has_Completed)
        
        If DebugLevel >= 1 Then
            Debug.Print "NotificationBarTextString = " & NotificationBarTextString
            Debug.Print "Notification Bar downloaded = " & Mid(NotificationBarTextString, p1, p2 - p1)
            Debug.Print "Full file name = " & fullFileName
        End If
        
        If DebugLevel = 1 Then DumpElement NotificationToolbar
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  NotificationToolbar after download complete"
                numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
        
        downloadResult = "SUCCESSFULLY DOWNLOADED"
        saveAsFullName = fullFileName
        
    Else
    
        'Not downloaded, so click the Cancel button in the Save As window
            
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Cancel)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Cancel button, waiting until it exists
        
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Cancel button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugLevel >= 1 Then Debug.Print Time; "Cancel clicked"
        
        'In this case, does the Notification tool bar pane exist?

    End If
            
    If Not NotificationToolbar Is Nothing Then
    
        'Create criteria to find the Close (X) button on the Notification pane
        'Name:          "Close"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Close)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
        'Find the Close button in the IE Download Notification Bar
        
        Set Button = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Close button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugLevel >= 1 Then Debug.Print Time; "Close clicked"
    
    End If
                
End Function


Private Function Get_IE_Window2(URLorName As String) As SHDocVw.InternetExplorer

    'Look for an IE browser window or tab already open at the (partial) URL or location name and, if found, return
    'that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim Shell As Object
    Dim IE As SHDocVw.InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
    
    Set Shell = CreateObject("Shell.Application")
    
    i = 0
    Set Get_IE_Window2 = Nothing
    While i < Shell.Windows.count And Get_IE_Window2 Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            'Debug.Print IE.LocationURL, IE.LocationName, IE.Name
            'If TypeOf IE Is SHDocVw.InternetExplorer And InStr(IE.LocationURL, "file://") <> 1 Then
            If IE.Name = "Internet Explorer" And InStr(IE.LocationURL, "file://") <> 1 Then
                If InStr(1, IE.LocationURL, URLorName, vbTextCompare) > 0 Or InStr(1, IE.LocationName, URLorName, vbTextCompare) > 0 Then
                    Set Get_IE_Window2 = IE
                End If
            End If
        End If
        i = i + 1
    Wend
    
End Function

'========================================= DEBUGGING FUNCTIONS =========================================

Private Sub DumpElement(UIAutoElem As IUIAutomationElement)
    Dim ct As Long
    ct = Get_UIA_PropertyValue(UIAutoElem, UIA_ControlTypePropertyId)
    Debug.Print "----------------"
    Debug.Print "Name                 = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NamePropertyId)
    Debug.Print "Class                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ClassNamePropertyId)
    Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
    Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LocalizedControlTypePropertyId)
    Debug.Print "Value                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ValueValuePropertyId)
    Debug.Print "Handle               = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId)
    Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(UIAutoElem, UIA_AccessKeyPropertyId)
    Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LegacyIAccessibleDefaultActionPropertyId)
    Debug.Print "Description          = " & Get_UIA_PropertyValue(UIAutoElem, UIA_FullDescriptionPropertyId)
    Debug.Print "NativeWindowHandle   = " & "0x" & Hex(Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId))
End Sub


'Get property value for a UI element
Private Function Get_UIA_PropertyValue(UIAutoElem As IUIAutomationElement, propertyId As Long)
    
    Dim tVal As Variant
    Dim tStr As String, i As Integer
    
    tVal = UIAutoElem.GetCurrentPropertyValue(propertyId)
    
    If IsArray(tVal) Then
        tStr = tVal(0)
        For i = 1 To UBound(tVal)
            tStr = tStr & "; " & tVal(i)
        Next
        Get_UIA_PropertyValue = tStr
    Else
        Get_UIA_PropertyValue = tVal
    End If
    
End Function


'Convert a UI property id to its constant name
Private Function Get_UIA_ControlType(propertyId As Long) As String

    Dim cn As String
    
    Select Case propertyId
        Case 50040: cn = "UIA_AppBarControlTypeId"
        Case 50000: cn = "UIA_ButtonControlTypeId"
        Case 50001: cn = "UIA_CalendarControlTypeId"
        Case 50002: cn = "UIA_CheckBoxControlTypeId"
        Case 50003: cn = "UIA_ComboBoxControlTypeId"
        Case 50025: cn = "UIA_CustomControlTypeId"
        Case 50028: cn = "UIA_DataGridControlTypeId"
        Case 50029: cn = "UIA_DataItemControlTypeId"
        Case 50030: cn = "UIA_DocumentControlTypeId"
        Case 50004: cn = "UIA_EditControlTypeId"
        Case 50026: cn = "UIA_GroupControlTypeId"
        Case 50034: cn = "UIA_HeaderControlTypeId"
        Case 50035: cn = "UIA_HeaderItemControlTypeId"
        Case 50005: cn = "UIA_HyperlinkControlTypeId"
        Case 50006: cn = "UIA_ImageControlTypeId"
        Case 50008: cn = "UIA_ListControlTypeId"
        Case 50007: cn = "UIA_ListItemControlTypeId"
        Case 50010: cn = "UIA_MenuBarControlTypeId"
        Case 50009: cn = "UIA_MenuControlTypeId"
        Case 50011: cn = "UIA_MenuItemControlTypeId"
        Case 50033: cn = "UIA_PaneControlTypeId"
        Case 50012: cn = "UIA_ProgressBarControlTypeId"
        Case 50013: cn = "UIA_RadioButtonControlTypeId"
        Case 50014: cn = "UIA_ScrollBarControlTypeId"
        Case 50039: cn = "UIA_SemanticZoomControlTypeId"
        Case 50038: cn = "UIA_SeparatorControlTypeId"
        Case 50015: cn = "UIA_SliderControlTypeId"
        Case 50016: cn = "UIA_SpinnerControlTypeId"
        Case 50031: cn = "UIA_SplitButtonControlTypeId"
        Case 50017: cn = "UIA_StatusBarControlTypeId"
        Case 50018: cn = "UIA_TabControlTypeId"
        Case 50019: cn = "UIA_TabItemControlTypeId"
        Case 50036: cn = "UIA_TableControlTypeId"
        Case 50020: cn = "UIA_TextControlTypeId"
        Case 50027: cn = "UIA_ThumbControlTypeId"
        Case 50037: cn = "UIA_TitleBarControlTypeId"
        Case 50021: cn = "UIA_ToolBarControlTypeId"
        Case 50022: cn = "UIA_ToolTipControlTypeId"
        Case 50023: cn = "UIA_TreeControlTypeId"
        Case 50024: cn = "UIA_TreeItemControlTypeId"
        Case 50032: cn = "UIA_WindowControlTypeId"
        Case Else: cn = "UNKNOWN"
    End Select
    
    Get_UIA_ControlType = cn
    
End Function


'Recursively walk the hierarchy of UIAutomationElements starting at the specified parent element and output them to Excel cells.

Private Function UIElements_To_Cells(UIAutomation As IUIAutomation, parentElement As IUIAutomationElement, firstTime As Boolean, destCell As Range) As Long

    Dim n As Long
    Dim sibling As Long
    Dim childElement As IUIAutomationElement
    Static TreeWalker As IUIAutomationTreeWalker
    Dim ct As Long
    
    n = 0
    
    If firstTime Then
        ct = Get_UIA_PropertyValue(parentElement, UIA_ControlTypePropertyId)
        destCell.Offset(0, 0).Value = "'-- Parent"
        destCell.Offset(1, 0).Value = "Name = " & Get_UIA_PropertyValue(parentElement, UIA_NamePropertyId)
        destCell.Offset(2, 0).Value = "Class = " & Get_UIA_PropertyValue(parentElement, UIA_ClassNamePropertyId)
        destCell.Offset(3, 0).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
        destCell.Offset(4, 0).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(parentElement, UIA_LocalizedControlTypePropertyId)
        destCell.Offset(5, 0).Value = "Value = " & Get_UIA_PropertyValue(parentElement, UIA_ValueValuePropertyId)
        destCell.Offset(6, 0).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(parentElement, UIA_NativeWindowHandlePropertyId))
        destCell.Offset(7, 0).Value = "AccessKey = " & Get_UIA_PropertyValue(parentElement, UIA_AccessKeyPropertyId)
        destCell.Offset(8, 0).Value = "DefaultAction = " & Get_UIA_PropertyValue(parentElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
        destCell.Offset(9, 0).Value = "Description = " & Get_UIA_PropertyValue(parentElement, UIA_FullDescriptionPropertyId)
        n = n + 10
    End If
    
    'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.RawViewWalker
    If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ControlViewWalker
    'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ContentViewWalker
    
    Set childElement = TreeWalker.GetFirstChildElement(parentElement)
    
    sibling = 0
    While Not childElement Is Nothing
        ct = Get_UIA_PropertyValue(childElement, UIA_ControlTypePropertyId)
        sibling = sibling + 1
        destCell.Offset(n + 0, 1).Value = "'-- Sibling " & sibling
        destCell.Offset(n + 1, 1).Value = "Name = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
        destCell.Offset(n + 2, 1).Value = "Class = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
        destCell.Offset(n + 3, 1).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
        destCell.Offset(n + 4, 1).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
        destCell.Offset(n + 5, 1).Value = "Value = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
        destCell.Offset(n + 6, 1).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
        destCell.Offset(n + 7, 1).Value = "AccessKey = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
        destCell.Offset(n + 8, 1).Value = "DefaultAction = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
        destCell.Offset(n + 9, 1).Value = "Description = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
        
        n = n + 10 + UIElements_To_Cells(UIAutomation, childElement, False, destCell.Offset(n + 10, 1))
        
        Set childElement = TreeWalker.GetNextSiblingElement(childElement)
    Wend
    
    UIElements_To_Cells = n

End Function
```

*Test routine*


```
'References required:
'Microsoft Internet Controls
'Microsoft HTML Object Library

Public Sub Test_IE_Download()

    Dim URL As String
    Dim IE As SHDocVw.InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim saveAsFullName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
    
    saveAsFullName = "C:\Temp\"          'Save in this folder with the file name provided by web site.  Folder must end with "\"
    replaceExistingFile = True
    
    'List of URLs - Income Statement, Balance Sheet and Cash Flow
    
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
    
    'See if an IE window is already open at the site
    
    Set IE = Get_IE_Window2(URL)
    If IE Is Nothing Then
        Set IE = New SHDocVw.InternetExplorer
        With IE
            'SetForegroundWindow .hwnd
            .Visible = True
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
      
    'Click Export link and display the IE file download dialogue
    
    IE.document.parentWindow.execScript "SRT_stocFund.Export()"
    
    'Activate the morningstar.com IE tab and download the file using Save As
    
    IE_Click_Tab_Like IE.hwnd, "*morningstar.com*"  'see OP for IE_Click_Tab_Like code
    downloadStatus = IE_Download_File_Using_UIAutomation(IE.hwnd, saveAsFullName, replaceExistingFile, downloadResult)
    Debug.Print "Download result = " & downloadResult
    Debug.Print "Download saveAsFullName = " & saveAsFullName
    Debug.Print "Download status = " & downloadStatus
    
End Sub
```


----------



## sonicroc (Apr 6, 2020)

Wonderful code, I did not want to integrate everything you have her.
Suffice it to say I cannot seem to get the context menu to pop..
_______________________
This is code i used to validate that i am getting the right hWnd, and yes it was a challenge to get it to the right window every time but now this works 100%
                    Set e = o.ElementFromHandle(ByVal cwindow)
                    Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Open")
                    Set childwindow = e.FindFirst(TreeScope_Subtree, iCnd)
                    Set InvokePattern = childwindow.GetCurrentPattern(UIA_InvokePatternId)
                    InvokePattern.Invoke
___________________________
however this adaptation of your code does not seem to function.
                        Set e = o.ElementFromHandle(ByVal cwindow)
                        Set iCnd = o.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
                        Set SplitButtons = e.FindAll(TreeScope_Descendants, iCnd)
                        Set DownArrow = SplitButtons.GetElement(1)

Note:
Cwindow is the hWnd of the DirectUIHWND 
no errors, just doesn't open the context menu..  Any thoughts?


----------



## sonicroc (Apr 6, 2020)

sonicroc said:


> Wonderful code, I did not want to integrate everything you have her.
> Suffice it to say I cannot seem to get the context menu to pop..
> _______________________
> This is code i used to validate that i am getting the right hWnd, and yes it was a challenge to get it to the right window every time but now this works 100%
> ...


Please disregard  I see what i missed.


----------



## Thundereagle (Jan 27, 2021)

Hi John

Thanks for that great piece of code! It worked flawlessly to save files in IE11 
Would it be possible for you to adapt the code to open the file instead of saving it?


----------



## John_w (Jan 29, 2021)

Thundereagle said:


> Hi John
> 
> Thanks for that great piece of code! It worked flawlessly to save files in IE11
> Would it be possible for you to adapt the code to open the file instead of saving it?


Welcome to MrExcel forums. 

Here is the code modified to *open* the file offered by the IE11 download and a test routine.  I've again included all the debugging procedures and calls so the code is much longer than really needed.

Public functions *IE_Open_File_Using_UIAutomation *and *IE_Click_Tab_Like.*


```
'References required:
'UIAutomationClient

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
#End If


'------- English string definitions for Internet Explorer 11 -------
Const sControlName_Notification As String = "Notification"
Const sControlName_Open As String = "Open"
Const sControlName_Notification_bar_Text As String = "Notification bar Text"
Const sText_NotificationBarP1 As String = "Do you want to open or save "
Const sText_NotificationBarP2 As String = " from "
'-------------------------------------------------------------------

'------- Spanish string definitions for Internet Explorer 11 -------
'Const sControlName_Notification As String = "Notificación"
'Const sControlName_Open As String = "Abierto"
'Const sControlName_Notification_bar_Text As String = "Texto de la barra de notificación"
'Const sText_NotificationBarP1 As String = "¿Quieres abrir o guardar "  '?? check
'Const sText_NotificationBarP2 As String = " desde "
'-------------------------------------------------------------------

#If VBA7 Then
Public Function IE_Open_File_Using_UIAutomation(IEhwnd As LongPtr, ByRef openResult As String) As Boolean
#Else
Public Function IE_Open_File_Using_UIAutomation(IEhwnd As Long, ByRef openResult As String) As Boolean
#End If

    'Automate IE11's Download Notification Bar (and 'What do you want to do with xxxx.xx?' dialogue window) in the active tab, by clicking the Open button
    'to open the file offered by the web site.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar (or IE dialogue window) must be already displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'openResult             Output string returned to the caller showing whether the file was successfully opened or not.
    '
    'Function return value  True: the file was opened; False: the file was not opened
   
    Dim UIAutomation As IUIAutomation
    Dim IEmain As IUIAutomationElement
    Dim DesktopRoot As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim NotificationToolbarOpenButton As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim IEdialogue As IUIAutomationElement
    Dim IEdialogueText As IUIAutomationElement
    Dim IEdialogueOpenButton As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim IEdialogueCond As IUIAutomationCondition, NotificationToolbarCond As IUIAutomationCondition
    Dim NotificationBarTextString As String, p1 As Long, p2 As Long
    Dim destCell As Range, numRows As Long
    Dim defaultFileName As String
   
    'DebugLevel values
    '0 = No debug output
    '1 = Debug.Print
    '2 = Debug.Print and to "UI debug" sheet if it exists
   
    Const DebugLevel = 0
   
    'If DebugLevel = 2, set destination cell for outputting UI elements to the "UI debug" sheet, if it exists
   
    Set destCell = Nothing
    If DebugLevel = 2 Then
        On Error Resume Next
        Set destCell = ThisWorkbook.Worksheets("UI debug").Range("A1")
        On Error GoTo 0
        If Not destCell Is Nothing Then destCell.Parent.Cells.Clear
    End If
   
    IE_Open_File_Using_UIAutomation = True
    openResult = ""
   
    'Create main UIAutomation object
   
    Set UIAutomation = New CUIAutomation
   
    'Get the IE automation element from its window handle
   
    Set IEmain = UIAutomation.ElementFromHandle(ByVal IEhwnd)
    If DebugLevel = 1 Then DumpElement IEmain
   
    'Conditions to find the IE Notification tool bar, a child of the main IE window
    'Name:                  "Notification"
    'ControlType:           UIA_ToolBarControlTypeId
    'LocalizedControlType:  "tool bar"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'If the server sends an incorrect response header, for example the misspelling of 'attachment' in:
    '
    '   Content-Disposition: attachement; filename="8d72ee3290adc3d.zip"
    '
    'then IE11 first displays a modal dialogue box instead of the normal Download Notification Bar at the bottom of the IE window:
    '
    '   -------------------------------------------------
    '   Internet Explorer                             [X]
    '
    '   What do you want to do with xxxxx.xxx?
    '
    '   Size: 54.2 MB
    '   From: hostname.com
    '
    '   --> Open
    '       The file won't be saved automatically
    '
    '   --> Save
    '
    '   --> Save as
    '
    '                                            [Cancel]
    '   -------------------------------------------------
   
    'When the Open button is clicked, IE11 displays the normal Notification tool bar with Open, Save and Cancel buttons.
   
    'Conditions to find this dialogue, which is a child of the main IE window.
    'Note - the dialogue isn't a true window, so FindWindowEx(IEhwnd, 0, "Internet Explorer", vbNullString) doesn't find the dialogue.
   
    'Name:                  "Internet Explorer"
    'ControlType:           UIA_WindowControlTypeId (0xC370)
    'LocalizedControlType:  "dialogue"
   
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Internet Explorer")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set IEdialogueCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
   
    'Wait until either the IE11 Notification tool bar or the "What do you want to do with xxxxx.xxx?" dialogue exists
   
    Set IEdialogue = Nothing
    Set NotificationToolbar = Nothing
    Do
        Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
        If NotificationToolbar Is Nothing Then Set IEdialogue = IEmain.FindFirst(TreeScope_Children, IEdialogueCond)
        DoEvents
        Sleep 200
    Loop While NotificationToolbar Is Nothing And IEdialogue Is Nothing
       
    If Not IEdialogue Is Nothing Then
   
        'The IE dialogue window exists
       
        If DebugLevel = 1 Then DumpElement IEmain
        If DebugLevel = 1 Then DumpElement IEdialogue
        If DebugLevel = 2 Then
            If Not destCell Is Nothing Then
                destCell.Value = "  IEmain"
                numRows = UIElements_To_Cells(UIAutomation, IEmain, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
            If Not destCell Is Nothing Then
                destCell.Value = "  IEdialogue"
                numRows = UIElements_To_Cells(UIAutomation, IEdialogue, True, destCell.Offset(1))
                Set destCell = destCell.Offset(numRows + 2)
            End If
        End If
   
        'See if the first child of the IE dialogue window is a text control (the text "What do you want to do with xxxxx.xxx?")
       
        'Name:   "What do you want to do with 8d731f569075f6d.zip?"
        'ControlType:    UIA_TextControlTypeId (0xC364)
        'LocalizedControlType:   "text"

        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set IEdialogueText = IEdialogue.FindFirst(TreeScope_Children, ControlType)
       
        If Not IEdialogueText Is Nothing Then
       
            'Find the Open button in the IE dialogue window
       
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Open)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            Set IEdialogueOpenButton = IEdialogue.FindFirst(TreeScope_Children, NameAndType)

            If Not IEdialogueOpenButton Is Nothing Then
           
                'Click the Open button.  This opens the normal IE11 Notification tool bar at the bottom of the IE11 window
   
                Set InvokePattern = IEdialogueOpenButton.GetCurrentPattern(UIA_InvokePatternId)
                InvokePattern.Invoke
               
                'Wait until the IE11 Notification tool bar exists
               
                Do
                    Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                    DoEvents
                    Sleep 200
                Loop While NotificationToolbar Is Nothing
               
            End If
           
        End If

    End If
                
    'At this point, IE should be displaying the Notification tool bar with 3 buttons: Open, Save, Cancel
   
    If DebugLevel = 1 Then DumpElement IEmain
    If DebugLevel = 1 Then DumpElement NotificationToolbar
    If DebugLevel = 2 Then
        If Not destCell Is Nothing Then
            destCell.Value = "  IEmain"
            numRows = UIElements_To_Cells(UIAutomation, IEmain, True, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
        If Not destCell Is Nothing Then
            destCell.Value = "  NotificationToolBar"
            numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, True, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    End If
   
    'Find the Notification tool bar text element and extract the default file name from it
    'Name:          "Notification bar Text"
    'ControlType:   UIA_TextControlTypeId
    'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
       
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification_bar_Text)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
    If DebugLevel >= 1 Then Debug.Print "NotificationBarTextString = " & NotificationBarTextString

    p1 = InStr(NotificationBarTextString, sText_NotificationBarP1)
    If p1 = 1 Then
        p1 = p1 + Len(sText_NotificationBarP1)
        p2 = InStr(p1, NotificationBarTextString, " (")
        If p2 = 0 Then p2 = InStr(p1, NotificationBarTextString, sText_NotificationBarP2)
        defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
    Else
        defaultFileName = ""
    End If
    If DebugLevel >= 1 Then Debug.Print "Default file name = " & defaultFileName
   
    'Find the 'Open' button in the Notification tool bar
    'Name:   "Open"
    'ControlType:    UIA_ButtonControlTypeId (0xC350)
    'LocalizedControlType:   "button"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Open)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set NotificationToolbarOpenButton = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)

    If Not NotificationToolbarOpenButton Is Nothing Then
   
        'Click the Open button to open the file offered by the web site

        Sleep 200
        Set InvokePattern = NotificationToolbarOpenButton.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        DoEvents
        openResult = defaultFileName & " - SUCCESSFULLY OPENED"
       
    Else
   
        openResult = "ERROR - Open button on Notification tool bar not found"
        IE_Open_File_Using_UIAutomation = False
       
    End If
       
End Function


#If VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
#Else
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
#End If

    'This finds all TabItemControls of the IE window and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
    'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator)
    'to specify the tab name to be found and activated.
    'This is useful because sometimes the visible tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
    'according to UIAutomation is "xxxxx Tab Group 1".
  
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
   
    'Create UIAutomation object
   
    Set UIauto = New CUIAutomation
   
    'Get Internet Explorer UIAutomation element
   
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
   
    'Create condition to find a TabItemControl
   
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
 
    'Find all tabs
   
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
   
    'Look for the tab which matches the specified tab name
   
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        'Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
       
    If Not IEtab Is Nothing Then
   
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)
   
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
   
    Else
   
        MsgBox "IE tab with name '" & findTabName & "' not found"
       
    End If
       
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
   
End Sub


'========================================= DEBUGGING FUNCTIONS =========================================

Private Sub DumpElement(UIAutoElem As IUIAutomationElement)
    Dim ct As Long
    ct = Get_UIA_PropertyValue(UIAutoElem, UIA_ControlTypePropertyId)
    Debug.Print "----------------"
    Debug.Print "Name                 = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NamePropertyId)
    Debug.Print "Class                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ClassNamePropertyId)
    Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
    Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LocalizedControlTypePropertyId)
    Debug.Print "Value                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ValueValuePropertyId)
    Debug.Print "Handle               = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId)
    Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(UIAutoElem, UIA_AccessKeyPropertyId)
    Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LegacyIAccessibleDefaultActionPropertyId)
    Debug.Print "Description          = " & Get_UIA_PropertyValue(UIAutoElem, UIA_FullDescriptionPropertyId)
    Debug.Print "NativeWindowHandle   = " & "0x" & Hex(Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId))
End Sub


'Get property value for a UI element
Private Function Get_UIA_PropertyValue(UIAutoElem As IUIAutomationElement, propertyId As Long)
   
    Dim tVal As Variant
    Dim tStr As String, i As Integer
   
    tVal = UIAutoElem.GetCurrentPropertyValue(propertyId)
   
    If IsArray(tVal) Then
        tStr = tVal(0)
        For i = 1 To UBound(tVal)
            tStr = tStr & "; " & tVal(i)
        Next
        Get_UIA_PropertyValue = tStr
    Else
        Get_UIA_PropertyValue = tVal
    End If
   
End Function


'Convert a UI property id to its constant name
Private Function Get_UIA_ControlType(propertyId As Long) As String

    Dim cn As String
   
    Select Case propertyId
        Case 50040: cn = "UIA_AppBarControlTypeId"
        Case 50000: cn = "UIA_ButtonControlTypeId"
        Case 50001: cn = "UIA_CalendarControlTypeId"
        Case 50002: cn = "UIA_CheckBoxControlTypeId"
        Case 50003: cn = "UIA_ComboBoxControlTypeId"
        Case 50025: cn = "UIA_CustomControlTypeId"
        Case 50028: cn = "UIA_DataGridControlTypeId"
        Case 50029: cn = "UIA_DataItemControlTypeId"
        Case 50030: cn = "UIA_DocumentControlTypeId"
        Case 50004: cn = "UIA_EditControlTypeId"
        Case 50026: cn = "UIA_GroupControlTypeId"
        Case 50034: cn = "UIA_HeaderControlTypeId"
        Case 50035: cn = "UIA_HeaderItemControlTypeId"
        Case 50005: cn = "UIA_HyperlinkControlTypeId"
        Case 50006: cn = "UIA_ImageControlTypeId"
        Case 50008: cn = "UIA_ListControlTypeId"
        Case 50007: cn = "UIA_ListItemControlTypeId"
        Case 50010: cn = "UIA_MenuBarControlTypeId"
        Case 50009: cn = "UIA_MenuControlTypeId"
        Case 50011: cn = "UIA_MenuItemControlTypeId"
        Case 50033: cn = "UIA_PaneControlTypeId"
        Case 50012: cn = "UIA_ProgressBarControlTypeId"
        Case 50013: cn = "UIA_RadioButtonControlTypeId"
        Case 50014: cn = "UIA_ScrollBarControlTypeId"
        Case 50039: cn = "UIA_SemanticZoomControlTypeId"
        Case 50038: cn = "UIA_SeparatorControlTypeId"
        Case 50015: cn = "UIA_SliderControlTypeId"
        Case 50016: cn = "UIA_SpinnerControlTypeId"
        Case 50031: cn = "UIA_SplitButtonControlTypeId"
        Case 50017: cn = "UIA_StatusBarControlTypeId"
        Case 50018: cn = "UIA_TabControlTypeId"
        Case 50019: cn = "UIA_TabItemControlTypeId"
        Case 50036: cn = "UIA_TableControlTypeId"
        Case 50020: cn = "UIA_TextControlTypeId"
        Case 50027: cn = "UIA_ThumbControlTypeId"
        Case 50037: cn = "UIA_TitleBarControlTypeId"
        Case 50021: cn = "UIA_ToolBarControlTypeId"
        Case 50022: cn = "UIA_ToolTipControlTypeId"
        Case 50023: cn = "UIA_TreeControlTypeId"
        Case 50024: cn = "UIA_TreeItemControlTypeId"
        Case 50032: cn = "UIA_WindowControlTypeId"
        Case Else: cn = "UNKNOWN"
    End Select
   
    Get_UIA_ControlType = cn
   
End Function


'Recursively walk the hierarchy of UIAutomationElements starting at the specified parent element and output them to Excel cells.

Private Function UIElements_To_Cells(UIAutomation As IUIAutomation, parentElement As IUIAutomationElement, firstTime As Boolean, destCell As Range) As Long

    Dim n As Long
    Dim sibling As Long
    Dim childElement As IUIAutomationElement
    Static TreeWalker As IUIAutomationTreeWalker
    Dim ct As Long
   
    n = 0
   
    If firstTime Then
        ct = Get_UIA_PropertyValue(parentElement, UIA_ControlTypePropertyId)
        destCell.Offset(0, 0).Value = "'-- Parent"
        destCell.Offset(1, 0).Value = "Name = " & Get_UIA_PropertyValue(parentElement, UIA_NamePropertyId)
        destCell.Offset(2, 0).Value = "Class = " & Get_UIA_PropertyValue(parentElement, UIA_ClassNamePropertyId)
        destCell.Offset(3, 0).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
        destCell.Offset(4, 0).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(parentElement, UIA_LocalizedControlTypePropertyId)
        destCell.Offset(5, 0).Value = "Value = " & Get_UIA_PropertyValue(parentElement, UIA_ValueValuePropertyId)
        destCell.Offset(6, 0).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(parentElement, UIA_NativeWindowHandlePropertyId))
        destCell.Offset(7, 0).Value = "AccessKey = " & Get_UIA_PropertyValue(parentElement, UIA_AccessKeyPropertyId)
        destCell.Offset(8, 0).Value = "DefaultAction = " & Get_UIA_PropertyValue(parentElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
        destCell.Offset(9, 0).Value = "Description = " & Get_UIA_PropertyValue(parentElement, UIA_FullDescriptionPropertyId)
        n = n + 10
    End If
   
    'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.RawViewWalker
    If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ControlViewWalker
    'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ContentViewWalker
   
    Set childElement = TreeWalker.GetFirstChildElement(parentElement)
   
    sibling = 0
    While Not childElement Is Nothing
        ct = Get_UIA_PropertyValue(childElement, UIA_ControlTypePropertyId)
        sibling = sibling + 1
        destCell.Offset(n + 0, 1).Value = "'-- Sibling " & sibling
        destCell.Offset(n + 1, 1).Value = "Name = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
        destCell.Offset(n + 2, 1).Value = "Class = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
        destCell.Offset(n + 3, 1).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
        destCell.Offset(n + 4, 1).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
        destCell.Offset(n + 5, 1).Value = "Value = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
        destCell.Offset(n + 6, 1).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
        destCell.Offset(n + 7, 1).Value = "AccessKey = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
        destCell.Offset(n + 8, 1).Value = "DefaultAction = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
        destCell.Offset(n + 9, 1).Value = "Description = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
       
        n = n + 10 + UIElements_To_Cells(UIAutomation, childElement, False, destCell.Offset(n + 10, 1))
       
        Set childElement = TreeWalker.GetNextSiblingElement(childElement)
    Wend
   
    UIElements_To_Cells = n

End Function
```

*Test routine*

```
'References required:
'Microsoft Internet Controls
'Microsoft HTML Object Library

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
#End If


Public Sub Test_IE_Download_Open_File()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim openResult As String, openStatus As Boolean
   
    'Income Statement, Balance Sheet and Cash Flow
   
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
   
    'See if an IE window is already open at the site
   
    Set IE = Get_IE_Window2(URL)
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            SetForegroundWindow .hwnd
            .Visible = True
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
      
    'Click Export link and display the IE file download dialogue
   
    IE.document.parentWindow.execScript "SRT_stocFund.Export()"
   
    'Activate the morningstar.com IE tab and open the file by clicking the Open button
   
    IE_Click_Tab_Like IE.hwnd, "*morningstar.com*"
    openStatus = IE_Open_File_Using_UIAutomation(IE.hwnd, openResult)
    Debug.Print Time; "Download result = " & openResult
    Debug.Print Time; "Download status = " & openStatus
   
End Sub


Private Function Get_IE_Window2(URLorName As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the (partial) URL or location name and, if found, return
    'that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
   
    Set Shell = CreateObject("Shell.Application")
   
    i = 0
    Set Get_IE_Window2 = Nothing
    While i < Shell.Windows.count And Get_IE_Window2 Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If IE.Name = "Internet Explorer" And InStr(IE.LocationURL, "file://") <> 1 Then
                If InStr(1, IE.LocationURL, URLorName, vbTextCompare) > 0 Or InStr(1, IE.LocationName, URLorName, vbTextCompare) > 0 Then
                    Set Get_IE_Window2 = IE
                End If
            End If
        End If
        i = i + 1
    Wend
   
End Function
```


----------



## Dan_W (Apr 5, 2021)

I only just stumbled across this thread now looking for info re: HTTP headers - thank you very much for this - I had wondered how to go about delving into UI Automation, and this answers that! Out of curiosity, are these types of posts better put on the forum as posts or articles? I've seen them posted in either section, and I didn't want to misstep should I ever post anything in future.


----------



## John_w (Apr 5, 2021)

I don't really look at the Excel Articles, so only considered posting it in the forum, as that seems the more 'visible' place to post it, and more likely that other people will find it.


----------



## Dan_W (Apr 5, 2021)

Understood - and thank you.


----------



## MehdiB (Oct 27, 2021)

Hello John,

I have a quite an issue with this kind of automation. I've been using a similar one for over a year and now after an office update, the code breaks at invokePattern.invoke making excel crash (no error whatsoever). The step where this happens is when the code tries to get elements from the notification Bar and click on the saveAs button. 

I know for sure it's the update as I got the help desk of my company to force reinstall an older version of excel and the code worked normally. Unfortunately, for reasons they can't have me keep a workstation with the office updates turned off ! 

Would you please have any insights regarding this ? the version of excel that causes the issue is this one 2110 Build 16.0.14527.20008 (10 days ago release I think) and the latest version where the code was working is this one :  2110 Build 16.0.14026.20304

Thank you in advance !


----------



## John_w (Oct 30, 2021)

MehdiB said:


> I have a quite an issue with this kind of automation. I've been using a similar one for over a year and now after an office update, the code breaks at invokePattern.invoke making excel crash (no error whatsoever). The step where this happens is when the code tries to get elements from the notification Bar and click on the saveAs button.
> 
> I know for sure it's the update as I got the help desk of my company to force reinstall an older version of excel and the code worked normally. Unfortunately, for reasons they can't have me keep a workstation with the office updates turned off !
> 
> Would you please have any insights regarding this ? the version of excel that causes the issue is this one 2110 Build 16.0.14527.20008 (10 days ago release I think) and the latest version where the code was working is this one : 2110 Build 16.0.14026.20304



I tested the code on two computers and Excel crashes and closes with no error on only computer A (see below), on 2 versions of Excel. Like you, the code attempts to execute the `invokePattern.invoke` line for a few seconds and then crashes.  It also crashes on `IEtabPattern.DoDefaultAction` in the `IE_Click_Tab_Like` routine.  So the crash seems to be caused by UIAutomation clicking/interacting with Internet Explorer.

Here are details of the 2 computers:

*Computer A - Excel crashes on both Excel versions*

Windows Version 10.0.19043 Build 19043
About Excel 2019 - Version 2109 (Build 16.0.14430.20154) 32-bit

After installing the latest Office updates:
About Excel 2019 - Version 2110 Build 16.0.14527.20234 32-bit

About Internet Explorer - Version 21H1 (OS Build 19043.1288)

*Computer B - Excel OK and code runs correctly on both Excel versions*

Windows Version 10.0.19042 Build 19042
About Excel 2019 - Version 2109 Build 16.0.14430.20154 32-bit

After installing the latest Office updates:
About Excel 2019 - Version 2110 Build 16.0.14527.20234 32-bit

About Internet Explorer - Version 20H2 (OS Build 19042.1288)

After installing the latest Office updates on both computers, the Excel versions are the same - Version 2110 Build 16.0.14527.20234 32-bit - yet Excel only crashes on computer A.  One difference between them is the different versions of Internet Explorer.

Here is a test routine which opens IE with 2 tabs and attempts to click the Mr Excel tab with `IEtabPattern.DoDefaultAction`:


```
'Required references:
'UIAutomationClient - C:\Windows\SysWOW64\UIAutomationCore.dll

Public Sub IE_UIAutomation_Crash_Test()

    Dim IE As Object
    #If VBA7 Then
        Dim hwnd As LongPtr
    #Else
        Dim hwnd As Long
    #End If
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
    
    Set IE = CreateObject("InternetExplorer.Application")
    With IE
        .Visible = True
        .navigate "www.mrexcel.com"
        .navigate "www.microsoft.com", CLng(2048) 'new tab, becomes active tab
        While .Busy Or .readyState <> 4: DoEvents: Wend
        hwnd = .hwnd
    End With
    
    Set UIauto = New CUIAutomation
    Set IEwindow = UIauto.ElementFromHandle(ByVal hwnd)
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        If InStr(IEtabs.GetElement(i).CurrentName, "Excel") Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
    If Not IEtab Is Nothing Then
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction   '<--- AFTER A FEW SECONDS 'EXECUTING' THIS LINE, EXCEL CRASHES AND CLOSES
        'Another way of clicking the tab - ALSO CRASHES EXCEL
        'IEtabPattern.Select 2
    End If
        
End Sub
```


----------



## John_w (Feb 7, 2019)

Here is VBA code which automates the complete 'Save As' file download procedure in IE11, when a download is offered by IE's Download Notification Bar.

To use for your own web site downloads you would automate IE and do whatever is necessary to make the Download Notification Bar appear at the bottom of the IE window and then call IE_Download_File_Using_UIAutomation.  It has the following parameters:

Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean


IEhwnd                 - The handle of the IE window. The Download Notification Bar must be displayed in the active tab. If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function. 
saveInFolder           - The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder. 
saveAsFileName         - The file name which the downloaded file will be given.  Specify "" to use the file name provided by the web site. 
replaceExistingFile    - True to replace the file if it already exists; False to overwrite the file. 
downloadResult         - Output string returned to the caller showing whether the file was successfully download or not, including the file name of the downloaded file (including its path if saveInFolder was specified). 
Function return value -  True: the file was downloaded; False: the file was not downloaded.

*NOTE *- The only issue I had was that after calling SetValue to put a specific file name in the Save As dialogue and clicking Save (Invoke), the download doesn't recognise the specified file name and still uses the default file name provided by the web site.  A simple workaround which has worked in all tests is SendKeys " ", True to insert a space character at start of the file name input element, however I know that SendKeys is unreliable and this might not always work for some people.

Also note that the IE tab offering the download must be the active tab, and the IE_Click_Tab_Like routine should be called to activate it if necessary.  The code also includes several debugging functions which can be helpful to understand the UIAutomation hierarchy.

Here is the complete code, including a test procedure for downloading a 2 Kb csv file from morningstar.com


```
'07-Feb-2019
'Use UIAutomationClient to automate the Save As file download in IE11

'References:
'Microsoft Internet Controls
'Microsoft HTML Object Library
'UIAutomationClient


Option Explicit

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As LongPtr, ByVal hwndChildAfter As LongPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If


Public Sub IE_Download_File_Save_As()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim exportLink As HTMLLinkElement
    Dim saveInFolder As String
    Dim saveAsFileName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
    
    saveInFolder = ""                           'Save in IE11's default download folder
    saveInFolder = "c:\path\to\folder"          'Save in this folder
    saveAsFileName = ""                         'Save As the file name provided by web site
    saveAsFileName = "My Csv Data"              'Save As this file name
    replaceExistingFile = True
    
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
    
    'See if an IE window is already open at the site
    
    Set IE = Get_IE_Window(CStr(URL))
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            SetForegroundWindow .hWnd
            .Visible = True
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
           
    Set exportLink = HTMLdoc.getElementsByClassName("rf_export")(0)
    
    If Not exportLink Is Nothing Then
    
        'Click the Export link, which opens IE's Download Notification Bar
        '
        '   Do you want to open or save INTC Balance Sheet.csv from financials.morningstar.com?
        '       with buttons [Open] [Save][Down Arrow] [Cancel] [X]
        
        exportLink.Click
        
        'Activate the morningstar.com IE tab and download the file using Save As
        
        IE_Click_Tab_Like IE.hWnd, "*morningstar.com*"
        downloadStatus = IE_Download_File_Using_UIAutomation(IE.hWnd, saveInFolder, saveAsFileName, replaceExistingFile, downloadResult)
        Debug.Print "Download status = " & downloadStatus
        MsgBox "Download result = " & downloadResult
        
    Else
    
        MsgBox "Export link not found - file not downloaded"
        
    End If
    
End Sub

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByVal saveInFolder As String, ByVal saveAsFileName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If

    'Automate IE11's Download Notification Bar in the active tab, by clicking the Save As item, downloading the file and closing the Notification Bar.
    'The IE option 'Notify when downloads complete' must be enabled.
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveInFolder           The folder path where the downloaded file will be saved.  Specify "" to save the file in IE's default download folder.
    'saveAsFileName         The file name which the downloaded file will be given.  If file extension is omitted, the file extension provided by
    '                       the web site is used. Specify "" to use the file name provided by the web site.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file
    'downloadResult         Output string returned to the caller showing whether the file was successfully download or not, including the file name of the
    '                       downloaded file (including its path if saveInFolder was specified).
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
    
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
        Dim hWnd As LongPtr
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
        Dim hWnd As Long
    [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
    Dim UIAutomation As IUIAutomation
    Dim DesktopRoot As IUIAutomationElement
    Dim FrameNotificationBarPane As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim TreeWalker As IUIAutomationTreeWalker
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim downloaded As Boolean
    Dim destCell As Range, numRows As Long
    
    Const DebugMode As Boolean = True
    
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
    
    'If specified, ensure folder ends with \
    
    If saveInFolder <> "" And Right(saveInFolder, 1) <> "" Then saveInFolder = saveInFolder & ""
    
    'Create main UIAutomation object
    
    Set UIAutomation = New CUIAutomation
    
    'Find the IE11 Frame Notification Bar, waiting until it exists
    
    Do
        hWnd = FindWindowEx(IEhwnd, 0, "Frame Notification Bar", vbNullString)
        DoEvents
        Sleep 200
    Loop While hWnd = 0
    If DebugMode Then Debug.Print Time; "Frame Notification Bar " & hWnd

    'Get the Frame Notification Bar pane from the window frame
    'Class         = Frame Notification Bar
    'Ctrl type     = UIA_PaneControlTypeId

    Set FrameNotificationBarPane = UIAutomation.ElementFromHandle(ByVal hWnd)
    'DumpElement FrameNotificationBarPane
        
    If DebugMode Then
        With Worksheets(1)
            .Cells.Clear
            Set destCell = .Range("A1")
        End With
        destCell.Value = "  FrameNotificationBarPane children"
        numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If

    'Find the Notification tool bar, a child of the Frame Notification Bar pane, waiting until it exists
    'Name:          "Notification"
    'ControlType:   UIA_ToolBarControlTypeId

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set NotificationToolbar = FrameNotificationBarPane.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Notification tool bar"
        DoEvents
    Loop While NotificationToolbar Is Nothing
    If DebugMode Then
        destCell.Value = "  NotificationToolbar children"
        numRows = UIElements_To_Cells(UIAutomation, NotificationToolbar, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
    
    'Find the Notification tool bar text element and extract the default file name from it
    'Name:          "Notification bar Text"
    'ControlType:   UIA_TextControlTypeId
    'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
        
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
    p1 = InStr(NotificationBarTextString, "Do you want to open or save ")
    If p1 = 1 Then
        p1 = p1 + Len("Do you want to open or save ")
        p2 = InStr(p1, NotificationBarTextString, " from ")
        defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
    Else
        defaultFileName = ""
    End If
    
    'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
    
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
    Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType)
    Set DownArrow = SplitButtons.GetElement(1)
        
    'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
    'with the following properties:
    '
    'Name:          "Context"
    'ControlType:   UIA_MenuControlTypeId
    '
    'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
    
    'Create criteria to find the Context menu
    
    Set DesktopRoot = UIAutomation.GetRootElement
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Context")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
    'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
    'not TreeScope_Descendants, to reduce the number of elements searched.
    'See [url]https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst[/url]
    
    Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
    Do
        DownArrow.SetFocus
        InvokePattern.Invoke
        DoEvents
        Sleep 200
        Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
    Loop While ContextMenu Is Nothing
    
    If DebugMode Then
        destCell.Value = "  ContextMenu children"
        numRows = UIElements_To_Cells(UIAutomation, ContextMenu, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
        
    'Find the Save as item in the Context menu
    'Name:          "Save as"
    'ControlType:   UIA_MenuItemControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save as")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save as item to display the Save As dialogue window
    
    Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    
    'Find the Save As dialogue window, which is a child of the Desktop, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        Sleep 100
    Loop While SaveAsWindow Is Nothing
            
    If DebugMode Then
        destCell.Value = "  SaveAsWindow children"
        numRows = UIElements_To_Cells(UIAutomation, SaveAsWindow, destCell.Offset(1))
        Set destCell = destCell.Offset(numRows + 2)
    End If
            
    'If the caller has specified either the folder or the file name, then populate the file name input box in the Save As window
    
    If saveInFolder <> "" Or saveAsFileName <> "" Then
    
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
        
        'Construct the full file name
        
        fullFileName = saveInFolder & saveAsFileName
        
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
    
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "File name:")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the file name input box
        
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
        
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
        
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern.
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
        
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
        
        SendKeys " ", True          'press space key
             
    Else
    
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
        
        fullFileName = defaultFileName
    
    End If
    
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    
    'Find the Save button, a child of the Save As window
    
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
    'Click the Save button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Save clicked"
        
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
        
    
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Confirm Save As")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
    
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        Sleep 200
        If DebugMode Then Debug.Print Time; "Find Confirm Save As"
        DoEvents
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
       
    If Not ConfirmSaveAsWindow Is Nothing Then
        
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
    
        If replaceExistingFile Then
        
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Yes")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the Yes button
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the Yes button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "Yes clicked"
        
            downloaded = True

        Else 'replaceExistingFile = False
        
            'Extract the text warning from the Confirm Save As window
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
        
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "No")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
            'Find the No button, waiting until it exists
            
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
            'Click the No button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "No clicked"
                        
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
            
        End If
    
    Else
    
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
        
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        If Not SaveAsWarningWindow Is Nothing Then
        
            'The Save As warning window exists, so extract the text warning from it
            
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
            
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
            
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "OK")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            
            'Find the OK button - a child of the Save As warning window
            
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
            
            'Click the OK button
            
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
            If DebugMode Then Debug.Print Time; "OK clicked"
            
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
            
        Else
        
            'The Save As warning window doesn't exist, so the file was downloaded
            
            IE_Download_File_Using_UIAutomation = True
            
        End If
            
    End If
    
    If IE_Download_File_Using_UIAutomation = True Then
    
        If DebugMode Then
            destCell.Value = "  FrameNotificationBarPane children"
            numRows = UIElements_To_Cells(UIAutomation, FrameNotificationBarPane, destCell.Offset(1))
            Set destCell = destCell.Offset(numRows + 2)
        End If
    
        'Create criteria to find the "Notification bar Text" element
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   The xxxx yyyy.zzz download has completed.
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
        
        NotificationBarTextString = ""
        Do
            Set NotificationBarText = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
            Sleep 200
            DoEvents
            If Not NotificationBarText Is Nothing Then
                NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
            End If
            If DebugMode Then Debug.Print Time; NotificationBarTextString
        Loop Until InStr(NotificationBarTextString, "download has completed")

        'Extract file name from Notification bar text, e.g. "The xxxx yyyy.zzz download has completed."
        
        p1 = InStr(NotificationBarTextString, "The ") + Len("The ")
        p2 = InStr(p1, NotificationBarTextString, " download has completed")
        
        Debug.Print "Notification Bar downloaded = " & Mid(NotificationBarTextString, p1, p2 - p1)
        Debug.Print "Full file name = " & fullFileName
        downloadResult = fullFileName & " - SUCCESSFULLY DOWNLOADED"
        
    Else
    
        'Not downloaded, so click the Cancel button in the Save As window
            
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
        
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Cancel")
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
        'Find the Cancel button, waiting until it exists
        
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        
        'Click the Cancel button
        
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
        If DebugMode Then Debug.Print Time; "Cancel clicked"

    End If
             
    'Create criteria to find the Close (X) button on the Notification pane
    'Name:          "Close"
    'ControlType:   UIA_ButtonControlTypeId
    
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Close")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        
    'Find the Close button in the IE Download Notification Bar
    
    Set Button = FrameNotificationBarPane.FindFirst(TreeScope_Descendants, NameAndType)
    
    'Click the Close button
    
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    If DebugMode Then Debug.Print Time; "Close clicked"
                 
End Function


'This finds all TabItemControls of the IE element and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - [url]https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator[/url])
'to specify the tab name to be found and activated.
'This is useful because sometimes although the visible the tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
'according to UIAutomation is "xxxxx Tab Group 1".

[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL]  VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL] 
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL]  If
   
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
    
    'Create UIAutomation object
    
    Set UIauto = New CUIAutomation
    
    'Get Internet Explorer UIAutomation element
    
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
    
    'Create condition to find a TabItemControl
    
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
 
    'Find all tabs
    
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
    
    'Look for the tab which matches the specified tab name
    
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
        
    If Not IEtab Is Nothing Then
    
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)
    
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
    
    Else
    
        MsgBox "IE tab with name '" & findTabName & "' not found"
        
    End If
        
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
    
End Sub


Public Sub DumpElement(UIAutoElem As IUIAutomationElement)
    Dim ct As Long
    ct = Get_UIA_PropertyValue(UIAutoElem, UIA_ControlTypePropertyId)
    Debug.Print "----------------"
    Debug.Print "Name                 = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NamePropertyId)
    Debug.Print "Class                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ClassNamePropertyId)
    Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
    Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LocalizedControlTypePropertyId)
    Debug.Print "Value                = " & Get_UIA_PropertyValue(UIAutoElem, UIA_ValueValuePropertyId)
    Debug.Print "Handle               = " & Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId)
    Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(UIAutoElem, UIA_AccessKeyPropertyId)
    Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(UIAutoElem, UIA_LegacyIAccessibleDefaultActionPropertyId)
    Debug.Print "Description          = " & Get_UIA_PropertyValue(UIAutoElem, UIA_FullDescriptionPropertyId)
    Debug.Print "NativeWindowHandle   = " & "0x" & Hex(Get_UIA_PropertyValue(UIAutoElem, UIA_NativeWindowHandlePropertyId))
End Sub


'Get property value for a UI element
Private Function Get_UIA_PropertyValue(UIAutoElem As IUIAutomationElement, propertyId As Long)
    
    Dim tVal As Variant
    Dim tStr As String, i As Integer
    
    tVal = UIAutoElem.GetCurrentPropertyValue(propertyId)
    
    If IsArray(tVal) Then
        tStr = tVal(0)
        For i = 1 To UBound(tVal)
            tStr = tStr & "; " & tVal(i)
        Next
        Get_UIA_PropertyValue = tStr
    Else
        Get_UIA_PropertyValue = tVal
    End If
    
End Function


'Convert a UI property id to its constant name
Private Function Get_UIA_ControlType(propertyId As Long) As String

    Dim cn As String
    
    Select Case propertyId
        Case 50040: cn = "UIA_AppBarControlTypeId"
        Case 50000: cn = "UIA_ButtonControlTypeId"
        Case 50001: cn = "UIA_CalendarControlTypeId"
        Case 50002: cn = "UIA_CheckBoxControlTypeId"
        Case 50003: cn = "UIA_ComboBoxControlTypeId"
        Case 50025: cn = "UIA_CustomControlTypeId"
        Case 50028: cn = "UIA_DataGridControlTypeId"
        Case 50029: cn = "UIA_DataItemControlTypeId"
        Case 50030: cn = "UIA_DocumentControlTypeId"
        Case 50004: cn = "UIA_EditControlTypeId"
        Case 50026: cn = "UIA_GroupControlTypeId"
        Case 50034: cn = "UIA_HeaderControlTypeId"
        Case 50035: cn = "UIA_HeaderItemControlTypeId"
        Case 50005: cn = "UIA_HyperlinkControlTypeId"
        Case 50006: cn = "UIA_ImageControlTypeId"
        Case 50008: cn = "UIA_ListControlTypeId"
        Case 50007: cn = "UIA_ListItemControlTypeId"
        Case 50010: cn = "UIA_MenuBarControlTypeId"
        Case 50009: cn = "UIA_MenuControlTypeId"
        Case 50011: cn = "UIA_MenuItemControlTypeId"
        Case 50033: cn = "UIA_PaneControlTypeId"
        Case 50012: cn = "UIA_ProgressBarControlTypeId"
        Case 50013: cn = "UIA_RadioButtonControlTypeId"
        Case 50014: cn = "UIA_ScrollBarControlTypeId"
        Case 50039: cn = "UIA_SemanticZoomControlTypeId"
        Case 50038: cn = "UIA_SeparatorControlTypeId"
        Case 50015: cn = "UIA_SliderControlTypeId"
        Case 50016: cn = "UIA_SpinnerControlTypeId"
        Case 50031: cn = "UIA_SplitButtonControlTypeId"
        Case 50017: cn = "UIA_StatusBarControlTypeId"
        Case 50018: cn = "UIA_TabControlTypeId"
        Case 50019: cn = "UIA_TabItemControlTypeId"
        Case 50036: cn = "UIA_TableControlTypeId"
        Case 50020: cn = "UIA_TextControlTypeId"
        Case 50027: cn = "UIA_ThumbControlTypeId"
        Case 50037: cn = "UIA_TitleBarControlTypeId"
        Case 50021: cn = "UIA_ToolBarControlTypeId"
        Case 50022: cn = "UIA_ToolTipControlTypeId"
        Case 50023: cn = "UIA_TreeControlTypeId"
        Case 50024: cn = "UIA_TreeItemControlTypeId"
        Case 50032: cn = "UIA_WindowControlTypeId"
        Case Else: cn = "UNKNOWN"
    End Select
    
    Get_UIA_ControlType = cn
    
End Function


'Recursively walk the hierarchy of UIAutomationElements starting at the specified parent element and output UI elements to Excel cells.

Public Function UIElements_To_Cells(UIAutomation As IUIAutomation, parentElement As IUIAutomationElement, destCell As Range) As Long

    Dim n As Long
    Dim sibling As Long
    Static TreeWalker As IUIAutomationTreeWalker
    Dim childElement As IUIAutomationElement
    Dim ct As Long
    
    n = 0
    
    If Not parentElement Is Nothing Then
    
        'Any of these tree walkers can be used
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.RawViewWalker
        If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ControlViewWalker
        'If TreeWalker Is Nothing Then Set TreeWalker = UIAutomation.ContentViewWalker
        
        Set childElement = TreeWalker.GetFirstChildElement(parentElement)
        sibling = 0
        While Not childElement Is Nothing
            ct = Get_UIA_PropertyValue(childElement, UIA_ControlTypePropertyId)
            sibling = sibling + 1
            Debug.Print "-- Sibling " & sibling
            Debug.Print "Name                 = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            Debug.Print "Class                = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            Debug.Print "ControlType          = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            Debug.Print "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            Debug.Print "Value                = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            Debug.Print "Handle               = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            Debug.Print "AccessKey            = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            Debug.Print "DefaultAction        = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            Debug.Print "Description          = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            destCell.Offset(n + 0).Value = "'-- Sibling " & sibling
            destCell.Offset(n + 1).Value = "Name = " & Get_UIA_PropertyValue(childElement, UIA_NamePropertyId)
            destCell.Offset(n + 2).Value = "Class = " & Get_UIA_PropertyValue(childElement, UIA_ClassNamePropertyId)
            destCell.Offset(n + 3).Value = "ControlType = " & Get_UIA_ControlType(ct) & " (0x" & Hex(ct) & ")"
            destCell.Offset(n + 4).Value = "LocalisedControlType = " & Get_UIA_PropertyValue(childElement, UIA_LocalizedControlTypePropertyId)
            destCell.Offset(n + 5).Value = "Value = " & Get_UIA_PropertyValue(childElement, UIA_ValueValuePropertyId)
            destCell.Offset(n + 6).Value = "Handle = 0x" & Hex(Get_UIA_PropertyValue(childElement, UIA_NativeWindowHandlePropertyId))
            destCell.Offset(n + 7).Value = "AccessKey = " & Get_UIA_PropertyValue(childElement, UIA_AccessKeyPropertyId)
            destCell.Offset(n + 8).Value = "DefaultAction = " & Get_UIA_PropertyValue(childElement, UIA_LegacyIAccessibleDefaultActionPropertyId)
            destCell.Offset(n + 9).Value = "Description = " & Get_UIA_PropertyValue(childElement, UIA_FullDescriptionPropertyId)
            
            n = n + 10 + UIElements_To_Cells(UIAutomation, childElement, destCell.Offset(n + 10, 1))
            
            Set childElement = TreeWalker.GetNextSiblingElement(childElement)
        Wend
    
    End If
    
    UIElements_To_Cells = n

End Function


Private Function Get_IE_Window(URL As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the domain of the specified URL (which can start with [url]http://,[/url] https://
    'or nothing) and, if found, return that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim domain As String
    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
    Dim p1 As Integer, p2 As Integer
    
    p1 = InStr(URL, "://")
    If p1 = 0 Then
        p1 = 1
    Else
        p1 = p1 + 3
    End If
    p2 = InStr(p1, URL, "/")
    If p2 = 0 Then p2 = Len(URL) + 1
    domain = Mid(URL, p1, p2 - p1)
    
    Set Shell = CreateObject("Shell.Application")
    
    i = 0
    Set Get_IE_Window = Nothing
    While i < Shell.Windows.Count And Get_IE_Window Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If TypeName(IE.document) = "HTMLDocument" Then
                If InStr(IE.LocationURL, domain) > 0 Then
                    Set Get_IE_Window = IE
                End If
            End If
        End If
        i = i + 1
    Wend
     
End Function
```


----------



## MehdiB (Nov 2, 2021)

John_w said:


> I tested the code on two computers and Excel crashes and closes with no error on only computer A (see below), on 2 versions of Excel. Like you, the code attempts to execute the `invokePattern.invoke` line for a few seconds and then crashes.  It also crashes on `IEtabPattern.DoDefaultAction` in the `IE_Click_Tab_Like` routine.  So the crash seems to be caused by UIAutomation clicking/interacting with Internet Explorer.
> 
> Here are details of the 2 computers:
> 
> ...


Thank you so much for taking the time to look into this ! 

My computer : 

Windows Version 10.0.19042 Build 19042 64-bit
Microsoft® Excel® for Microsoft 365 MSO (Version 2110 Build 16.0.14527.20234) 32-bit 
Internet Explorer - Version 20H2 (OS Build 19042.1288)

The code is still crashing even though I have the same IE version as your Computer B

I tried the test code for mrexcel it crashes also with the same fashion !

Thank you again for replying and hoping you might have any other insight I could investigate


----------



## MehdiB (Nov 4, 2021)

MehdiB said:


> Thank you so much for taking the time to look into this !
> 
> My computer :
> 
> ...


Just getting back to this to let people that might encounter the same issue that I solved mine by just installing the 64 bit version !

I still have no idea why the uiAutomation started having issues with the IE notification bar though.


----------



## John_w (Jan 28, 2022)

*Latest UIAutomation download code and test routine*

Here is the latest code, based on my post #11 with the following changes:

1. Close the Notification bar when the download has finished only if the IE setting 'Notify when downloads complete' is ticked, otherwise IE itself closes the bar.  This setting is determined by the registry value "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\NotifyDownloadComplete".






2. The loop which clicks the Down arrow next to the Save button in the Notification bar until the Save As context menu appears was sometimes stuck.   The problem seemed to be caused by `DoEvents`, so this line has been removed.

3. Removed all debugging functions and calls, so the code is shorter.

I have kept the language-dependent string definitions (currently English and Spanish) for IE11 UI control names and text strings.

*UIAutomation code *with public functions *IE_Download_File_Using_UIAutomation* and *IE_Click_Tab_Like*


```
'References required:
'UIAutomationClient

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

'Note - UI Control Names are case-sensitive, hence the 2 'Save As' versions

'------- English string definitions for Internet Explorer 11 -------
Const sControlName_Notification As String = "Notification"
Const sControlName_Notification_bar_Text As String = "Notification bar Text"
Const sControlName_Context As String = "Context"
Const sControlName_Save_as1 As String = "Save as"
Const sControlName_Save_As2 As String = "Save As"
Const sControlName_File_name As String = "File name:"
Const sControlName_Save As String = "Save"
Const sControlName_Confirm_Save_As As String = "Confirm Save As"
Const sControlName_Yes As String = "Yes"
Const sControlName_No As String = "No"
Const sControlName_OK As String = "OK"
Const sControlName_Cancel As String = "Cancel"
Const sControlName_Close As String = "Close"
Const sText_NotificationBarP1 As String = "Do you want to open or save "
Const sText_NotificationBarP2 As String = " from "
Const sText_IEdialogueP1 As String = "What do you want to do with "
Const sText_IEdialogueP2 As String = "?"
Const sText_The As String = "The "
Const sText_Download_has_Completed As String = " download has completed"
'-------------------------------------------------------------------


'------- Spanish string definitions for Internet Explorer 11 -------
'Const sControlName_Notification As String = "Notificación"
'Const sControlName_Notification_bar_Text As String = "Texto de la barra de notificación"
'Const sControlName_Context As String = "Contexto"
'Const sControlName_Save_as1 As String = "Guardar como"
'Const sControlName_Save_As2 As String = "Guardar Como"
'Const sControlName_File_name As String = "Nombre:"
'Const sControlName_Save As String = "Guardar"
'Const sControlName_Confirm_Save_As As String = "Confirmar Guardar Como"
'Const sControlName_Yes As String = "Si"
'Const sControlName_No As String = "No"
'Const sControlName_OK As String = "OK"
'Const sControlName_Cancel As String = "Cancelar"
'Const sControlName_Close As String = "Cerrar"
'Const sText_NotificationBarP1 As String = "¿Quieres abrir o guardar "  '?? check
'Const sText_NotificationBarP2 As String = " desde "
'Const sText_IEdialogueP1 As String = "¿Qué quieres hacer con "
'Const sText_IEdialogueP2 As String = "?"
'Const sText_The As String = "La descarga de "
'Const sText_Download_has_Completed As String = " se completó"
'-------------------------------------------------------------------


#If VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByRef saveAsFullName, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#Else
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByRef saveAsFullName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#End If

    'Automate IE11's Download Notification Bar (or 'What do you want to do with xxxx.xx?' dialogue window) in the active tab, by clicking the Save As item,
    'downloading the file and closing the Notification Bar only if the IE setting 'Notify when downloads complete' is ticked, (or dialogue window).
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveAsFileName         The optional folder path where the downloaded file will be saved and the optional file name which the downloaded file will be given.
    '                       Specify "" to use IE's default download folder and the file name provided by the web site.
    '                       This argument is passed ByRef so that the actual full name of the downloaded file, if successfully downloaded, is returned to the caller.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file.
    'downloadResult         Output string returned to the caller showing whether the file was successfully downloaded or not.
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
  
    Dim UIAutomation As IUIAutomation
    Dim IEmain As IUIAutomationElement
    Dim IEdialogue As IUIAutomationElement
    Dim IEdialogueText As IUIAutomationElement
    Dim IEdialogueSaveAsButton As IUIAutomationElement
    Dim searchSaveAsWindowFrom As IUIAutomationElement
    Dim DesktopRoot As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim IEdialogueCond As IUIAutomationCondition, NotificationToolbarCond As IUIAutomationCondition
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, IEdialogueTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim saveInFolder As String, saveAsFileName As String
    Dim NotifyDownloadComplete As String
  
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
  
    'Get folder and file name from full file name
  
    p1 = InStrRev(saveAsFullName, "\")
    If p1 = 0 Then
        saveInFolder = ""
        saveAsFileName = saveAsFullName
    Else
        saveInFolder = Left(saveAsFullName, p1)
        saveAsFileName = Mid(saveAsFullName, p1 + 1)
    End If
  
    'Get IE's 'Notify when downloads complete' setting from the registry.  This setting is either "yes" or "no"
  
    NotifyDownloadComplete = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\NotifyDownloadComplete")
  
    'Create main UIAutomation object
  
    Set UIAutomation = New CUIAutomation
    Set DesktopRoot = UIAutomation.GetRootElement

    'Get the IE automation element from its window handle
  
    Set IEmain = UIAutomation.ElementFromHandle(ByVal IEhwnd)
  
    'Conditions to find the IE Notification tool bar, a child of the main IE window
    'Name:                  "Notification"
    'ControlType:           UIA_ToolBarControlTypeId
    'LocalizedControlType:  "tool bar"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
    'If the server sends an incorrect response header, for example the misspelling of 'attachment' in:
    '
    '   Content-Disposition: attachement; filename="8d72ee3290adc3d.zip"
    '
    'then IE displays a modal dialogue box instead of the normal Download Notification Bar at the bottom of the IE window:
    '
    '   -------------------------------------------------
    '   Internet Explorer                             [X]
    '
    '   What do you want to do with xxxxx.xxx?
    '
    '   Size: 54.2 MB
    '   From: hostname.com
    '
    '   --> Open
    '       The file won't be saved automatically
    '
    '   --> Save
    '
    '   --> Save as
    '
    '                                            [Cancel]
    '   -------------------------------------------------
  
    'Conditions to find this dialogue, which is a child of the main IE window.
    'Note - the dialogue isn't a true window, so FindWindowEx(IEhwnd, 0, "Internet Explorer", vbNullString) doesn't find the dialogue.
  
    'Name:                  "Internet Explorer"
    'ControlType:           UIA_WindowControlTypeId (0xC370)
    'LocalizedControlType:  "dialogue"
  
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Internet Explorer")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set IEdialogueCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
  
    'Wait until either the IE11 Notification tool bar exists or the "What do you want to do with xxxx.xxx?" dialogue exists
  
    Set IEdialogue = Nothing
    Set NotificationToolbar = Nothing
    Do
        'New method of finding notification bar, instead of FindWindowEx
        Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
        If NotificationToolbar Is Nothing Then Set IEdialogue = IEmain.FindFirst(TreeScope_Children, IEdialogueCond)
        DoEvents
        Sleep 200
    Loop While NotificationToolbar Is Nothing And IEdialogue Is Nothing
  
    If Not NotificationToolbar Is Nothing Then
  
        'The Notification tool bar exists.  Find its child text element and extract the default file name from it
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
          
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification_bar_Text)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)

        p1 = InStr(NotificationBarTextString, sText_NotificationBarP1)
        If p1 = 1 Then
            p1 = p1 + Len(sText_NotificationBarP1)
            p2 = InStr(p1, NotificationBarTextString, " (")
            If p2 = 0 Then p2 = InStr(p1, NotificationBarTextString, sText_NotificationBarP2)
            defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
        Else
            defaultFileName = ""
        End If
      
        'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
      
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
        Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType) 'TreeScope_Descendants necessary
        Set DownArrow = SplitButtons.GetElement(1)
        Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
          
        'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
        'with the following properties:
        '
        'Name:          "Context"
        'ControlType:   UIA_MenuControlTypeId
        '
        'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
      
        'Create criteria to find the Context menu
      
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Context)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
         
        'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
        'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
        'not TreeScope_Descendants, to reduce the number of elements searched.
        'See https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst
      
        Do
            DownArrow.SetFocus
            InvokePattern.Invoke
            Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
            If ContextMenu Is Nothing Then Sleep 500
        Loop While ContextMenu Is Nothing
          
        'Find the Save as item in the Context menu
        'Name:          "Save as"
        'ControlType:   UIA_MenuItemControlTypeId
      
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
          
        'Click the Save as item to display the Save As dialogue window
      
        Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
      
        'Search for the Save as window, when it appears, from the Desktop root element
      
        Set searchSaveAsWindowFrom = DesktopRoot
      
    ElseIf Not IEdialogue Is Nothing Then
  
        'The IE dialogue window exists.  See if the first child of the dialogue window contains the text "What do you want to do with "
      
        'Name:   "What do you want to do with 8d731f569075f6d.zip?"
        'ControlType:    UIA_TextControlTypeId (0xC364)
        'LocalizedControlType:   "text"

        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set IEdialogueText = IEdialogue.FindFirst(TreeScope_Children, ControlType)
      
        If Not IEdialogueText Is Nothing Then
      
            IEdialogueTextString = IEdialogueText.CurrentName
            p1 = InStr(IEdialogueTextString, sText_IEdialogueP1)
            If p1 = 1 Then
                p1 = p1 + Len(sText_IEdialogueP1)
                p2 = InStr(p1, IEdialogueTextString, sText_IEdialogueP2)
                defaultFileName = Mid(IEdialogueTextString, p1, p2 - p1)
                p1 = 1
            Else
                defaultFileName = ""
            End If
      
            'Get the 'Save as' button in the dialogue
            'Name:   "Save as"
            'ControlType:    UIA_ButtonControlTypeId (0xC350)
            'LocalizedControlType:   "button"

            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            Set IEdialogueSaveAsButton = IEdialogue.FindFirst(TreeScope_Children, NameAndType)

            If Not IEdialogueSaveAsButton Is Nothing Then
          
                'Click the Save as button to display the Save As dialogue window
  
                Set InvokePattern = IEdialogueSaveAsButton.GetCurrentPattern(UIA_InvokePatternId)
                InvokePattern.Invoke
              
                'Search for the Save as window, when it appears, from the main IE window element
              
                Set searchSaveAsWindowFrom = IEmain
              
            End If
          
        End If

    End If
      
    'Find the Save As dialogue window, which is either a child of the Desktop if the Notification Bar was displayed, or a child
    'of the main IE window if the IE dialogue window was displayed, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
  
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = searchSaveAsWindowFrom.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        If SaveAsWindow Is Nothing Then Sleep 200
    Loop While SaveAsWindow Is Nothing
          
    'If the caller has specified either the folder or the file name, populate the file name input box in the Save As window
  
    If saveInFolder <> "" Or saveAsFileName <> "" Then
  
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
      
        'Construct the full file name
      
        fullFileName = saveInFolder & saveAsFileName
      
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
  
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_File_name)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
        'Find the file name input box
      
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
      
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
      
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
      
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
      
        SendKeys " ", True          'press space key
           
    Else
  
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
      
        fullFileName = defaultFileName
  
    End If
  
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
  
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
  
    'Find the Save button, a child of the Save As window
  
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
      
    'Click the Save button
  
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
      
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
  
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
  
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Confirm_Save_As)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
  
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        If ConfirmSaveAsWindow Is Nothing Then Sleep 200
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
     
    If Not ConfirmSaveAsWindow Is Nothing Then
      
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
  
        If replaceExistingFile Then
      
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
          
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Yes)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
            'Find the Yes button
          
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
      
            'Click the Yes button
          
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke

        Else 'replaceExistingFile = False
      
            'Extract the text warning from the Confirm Save As window
          
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
      
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
          
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_No)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
            'Find the No button, waiting until it exists
          
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
      
            'Click the No button
          
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
                      
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
          
        End If
  
    Else
  
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
      
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
      
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
      
        If Not SaveAsWarningWindow Is Nothing Then
      
            'The Save As warning window exists, so extract the text warning from it
          
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
          
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
          
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_OK)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
          
            'Find the OK button - a child of the Save As warning window
          
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
          
            'Click the OK button
          
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
          
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
          
        Else
      
            'The Save As warning window doesn't exist, so the file was downloaded
          
            saveAsFullName = fullFileName
            IE_Download_File_Using_UIAutomation = True
          
        End If
          
    End If
  
    If IE_Download_File_Using_UIAutomation = True Then
  
        'If the file download was offered via the IE dialogue pane, rather than the normal Frame Notification Bar, then the Notification toolbar
        'is displayed as the download progresses.   Therefore get the Notification toolbar at this point
      
        If NotificationToolbar Is Nothing Then
      
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
      
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                Sleep 200
            Loop While NotificationToolbar Is Nothing
      
        End If
      
        If LCase(NotifyDownloadComplete) = "yes" Then
      
            'IE setting 'Notify when downloads complete' is ticked, which means that the Notification bar will contain "download has completed" when the
            'download has finished
          
            'Create criteria to find the "Notification bar Text" element within the Notifcation bar
            'Name:          "Notification bar Text"
            'ControlType:   UIA_TextControlTypeId
            'Value.Value:   The xxxx yyyy.zzz download has completed.
          
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
          
            'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
          
            NotificationBarTextString = ""
            Do
                Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
                DoEvents
                Sleep 200
                If Not NotificationBarText Is Nothing Then
                    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
                End If
            Loop Until InStr(NotificationBarTextString, sText_Download_has_Completed)
          
        Else
      
            'IE setting 'Notify when downloads complete' is not ticked
            'Wait until the Notification bar doesn't exist
      
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
      
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                If Not NotificationToolbar Is Nothing Then Sleep 200
            Loop Until NotificationToolbar Is Nothing

        End If
      
        downloadResult = "SUCCESSFULLY DOWNLOADED"
        saveAsFullName = fullFileName
      
    Else
  
        'Not downloaded, so click the Cancel button in the Save As window
          
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
      
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Cancel)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
      
        'Find the Cancel button, waiting until it exists
      
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
      
        'Click the Cancel button
      
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
      
        'In this case, does the Notification tool bar pane exist?

    End If
           
    If Not NotificationToolbar Is Nothing Then
  
        'Create criteria to find the Close (X) button on the Notification pane
        'Name:          "Close"
        'ControlType:   UIA_ButtonControlTypeId
      
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Close)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
          
        'Find the Close button in the IE Download Notification Bar
      
        Set Button = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
      
        'Click the Close button
      
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
  
    End If
               
End Function


#If VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
#Else
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
#End If
 
    'This finds all TabItemControls of the IE window and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
    'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator)
    'to specify the tab name to be found and activated.
    'This is useful because sometimes the visible tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
    'according to UIAutomation is "xxxxx Tab Group 1".
 
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
  
    'Create UIAutomation object
  
    Set UIauto = New CUIAutomation
  
    'Get Internet Explorer UIAutomation element
  
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
  
    'Create condition to find a TabItemControl
  
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
  
    'Find all tabs
  
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
  
    'Look for the tab which matches the specified tab name
  
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        'Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
      
    '==== OR look for the tab which matches the specified tab name and its URL.  The tab name AND URL is shown when you hover the mouse on the IE tab
  
    'LegacyIAccessible.Description:  "Balance Sheet for Intel Corp (INTC) from Morningstar.com
    '                                http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
  
'    Set IEtab = Nothing
'    i = 0
'    While i < IEtabs.Length And IEtab Is Nothing
'        Debug.Print i; IEtabs.GetElement(i).GetCurrentPropertyValue(UIA_LegacyIAccessibleDescriptionPropertyId)
'        If LCase(IEtabs.GetElement(i).GetCurrentPropertyValue(UIA_LegacyIAccessibleDescriptionPropertyId)) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
'        i = i + 1
'    Wend
  
    If Not IEtab Is Nothing Then
  
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)  (Press, 27-Oct-2021)
      
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
        'Another way of clicking the tab
        'IEtabPattern.Select 2
      
    Else
  
        MsgBox "IE tab with name '" & findTabName & "' not found"
      
    End If
      
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
  
End Sub
```

*Test routine *which downloads a .csv file from financials.morningstar.com using the two public functions in the above code.


```
'References required:
'Microsoft Internet Controls
'Microsoft HTML Object Library

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
#End If


Public Sub Test_IE_Download()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim saveAsFullName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
  
    saveAsFullName = "C:\Temp\"          'Save in this folder with the file name provided by web site.  Folder must end with "\"
    replaceExistingFile = True
  
    'List of URLs - Income Statement, Balance Sheet and Cash Flow
  
    URL = "http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
  
    'See if an IE window is already open at the site
  
    Set IE = Get_IE_Window2(URL)
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            .Visible = True
            SetForegroundWindow .hwnd
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
     
    'Click Export link and display the IE file download dialogue
  
    IE.document.parentWindow.execScript "SRT_stocFund.Export()"
  
    'Activate the morningstar.com IE tab and download the file using Save As
  
    IE_Click_Tab_Like IE.hwnd, "*morningstar.com*"
    downloadStatus = IE_Download_File_Using_UIAutomation(IE.hwnd, saveAsFullName, replaceExistingFile, downloadResult)
    Debug.Print "Download result = " & downloadResult
    Debug.Print "Download saveAsFullName = " & saveAsFullName
    Debug.Print "Download status = " & downloadStatus
  
End Sub


Private Function Get_IE_Window2(URL As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the domain of the specified URL (which can start with http://, https://
    'or nothing) and, if found, return that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
  
    Set Shell = CreateObject("Shell.Application")
  
    i = 0
    Set Get_IE_Window2 = Nothing
    While i < Shell.Windows.count And Get_IE_Window2 Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If TypeOf IE Is InternetExplorer Then
                If InStr(IE.LocationURL, URL) = 1 Then Set Get_IE_Window2 = IE
            End If
        End If
        i = i + 1
    Wend
  
End Function
```


----------



## John_w (Jul 25, 2022)

*Latest UIAutomation download code and test routine*

Changes:

1. The IE setting 'Notify when downloads complete' is in different registry branches for Windows 10 and Windows 11.  The UIAutomation code reads the setting from either branch:

Windows 10 - HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\NotifyDownloadComplete
Windows 11 - HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\AdvancedOptions\BROWSE\NOTIFYDOWNLOADCOMPLETE

2. In the IE11 test routine, the URL `http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US` no longer works and has been changed to `https://tools.morningstar.co.uk/uk/stockreport/default.aspx?tab=10&vw=bs&SecurityToken=0P0000APA1%5D3%5D0%5DE0WWE%24%24ALL&Id=0P0000APA1&ClientFund=0&CurrencyId=BAS`

*UIAutomation code *with public functions *IE_Download_File_Using_UIAutomation* and *IE_Click_Tab_Like*


```
'References required:
'UIAutomationClient

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

'Note - UI Control Names are case-sensitive, hence the 2 'Save As' versions

'------- English string definitions for Internet Explorer 11 -------
Const sControlName_Notification As String = "Notification"
Const sControlName_Notification_bar_Text As String = "Notification bar Text"
Const sControlName_Context As String = "Context"
Const sControlName_Save_as1 As String = "Save as"
Const sControlName_Save_As2 As String = "Save As"
Const sControlName_File_name As String = "File name:"
Const sControlName_Save As String = "Save"
Const sControlName_Confirm_Save_As As String = "Confirm Save As"
Const sControlName_Yes As String = "Yes"
Const sControlName_No As String = "No"
Const sControlName_OK As String = "OK"
Const sControlName_Cancel As String = "Cancel"
Const sControlName_Close As String = "Close"
Const sText_NotificationBarP1 As String = "Do you want to open or save "
Const sText_NotificationBarP2 As String = " from "
Const sText_IEdialogueP1 As String = "What do you want to do with "
Const sText_IEdialogueP2 As String = "?"
Const sText_The As String = "The "
Const sText_Download_has_Completed As String = " download has completed"
'-------------------------------------------------------------------


'------- Spanish string definitions for Internet Explorer 11 -------
'Const sControlName_Notification As String = "Notificación"
'Const sControlName_Notification_bar_Text As String = "Texto de la barra de notificación"
'Const sControlName_Context As String = "Contexto"
'Const sControlName_Save_as1 As String = "Guardar como"
'Const sControlName_Save_As2 As String = "Guardar Como"
'Const sControlName_File_name As String = "Nombre:"
'Const sControlName_Save As String = "Guardar"
'Const sControlName_Confirm_Save_As As String = "Confirmar Guardar Como"
'Const sControlName_Yes As String = "Si"
'Const sControlName_No As String = "No"
'Const sControlName_OK As String = "OK"
'Const sControlName_Cancel As String = "Cancelar"
'Const sControlName_Close As String = "Cerrar"
'Const sText_NotificationBarP1 As String = "¿Quieres abrir o guardar "  '?? check
'Const sText_NotificationBarP2 As String = " desde "
'Const sText_IEdialogueP1 As String = "¿Qué quieres hacer con "
'Const sText_IEdialogueP2 As String = "?"
'Const sText_The As String = "La descarga de "
'Const sText_Download_has_Completed As String = " se completó"
'-------------------------------------------------------------------


#If VBA7 Then
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As LongPtr, ByRef saveAsFullName, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#Else
Public Function IE_Download_File_Using_UIAutomation(IEhwnd As Long, ByRef saveAsFullName As String, ByVal replaceExistingFile As Boolean, ByRef downloadResult As String) As Boolean
#End If

    'Automate IE11's Download Notification Bar (or 'What do you want to do with xxxx.xx?' dialogue window) in the active tab, by clicking the Save As item,
    'downloading the file and closing the Notification Bar only if the IE setting 'Notify when downloads complete' is ticked, (or dialogue window).
    '
    'Parameters:
    'IEhwnd                 The handle of the IE window. The Download Notification Bar must be displayed in the active tab.
    '                       If necessary, call IE_Click_Tab_Like to activate the required tab before calling this function.
    'saveAsFileName         The optional folder path where the downloaded file will be saved and the optional file name which the downloaded file will be given.
    '                       Specify "" to use IE's default download folder and the file name provided by the web site.
    '                       This argument is passed ByRef so that the actual full name of the downloaded file, if successfully downloaded, is returned to the caller.
    'replaceExistingFile    True to replace the file if it already exists; False to overwrite the file.
    'downloadResult         Output string returned to the caller showing whether the file was successfully downloaded or not.
    '
    'Function return value  True: the file was downloaded; False: the file was not downloaded
   
    Dim UIAutomation As IUIAutomation
    Dim IEmain As IUIAutomationElement
    Dim IEdialogue As IUIAutomationElement
    Dim IEdialogueText As IUIAutomationElement
    Dim IEdialogueSaveAsButton As IUIAutomationElement
    Dim searchSaveAsWindowFrom As IUIAutomationElement
    Dim DesktopRoot As IUIAutomationElement
    Dim NotificationToolbar As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim NotificationBarText As IUIAutomationElement
    Dim SplitButtons As IUIAutomationElementArray
    Dim DownArrow As IUIAutomationElement
    Dim InvokePattern As IUIAutomationInvokePattern
    Dim ContextMenu As IUIAutomationElement
    Dim SaveAsMenuItem As IUIAutomationElement
    Dim SaveAsWindow As IUIAutomationElement
    Dim FileNameInput As IUIAutomationElement
    Dim FileNameInputPattern As IUIAutomationValuePattern, FileNameInputPatternLegacy As IUIAutomationLegacyIAccessiblePattern
    Dim ConfirmSaveAsWindow As IUIAutomationElement
    Dim ConfirmSaveAsWindowText As IUIAutomationElement
    Dim SaveAsWarningWindow As IUIAutomationElement, SaveAsWarningText As IUIAutomationElement
    Dim ControlName As IUIAutomationCondition, ControlType As IUIAutomationCondition, NameAndType As IUIAutomationCondition
    Dim IEdialogueCond As IUIAutomationCondition, NotificationToolbarCond As IUIAutomationCondition
    Dim defaultFileName As String, fullFileName As String
    Dim ConfirmSaveAsWindowTextString As String
    Dim SaveAsWarningTextString As String
    Dim NotificationBarTextString As String, IEdialogueTextString As String, p1 As Long, p2 As Long
    Dim timeout As Date
    Dim saveInFolder As String, saveAsFileName As String
    Dim NotifyDownloadComplete As String
   
    IE_Download_File_Using_UIAutomation = True
    downloadResult = ""
   
    'Get folder and file name from full file name
   
    p1 = InStrRev(saveAsFullName, "\")
    If p1 = 0 Then
        saveInFolder = ""
        saveAsFileName = saveAsFullName
    Else
        saveInFolder = Left(saveAsFullName, p1)
        saveAsFileName = Mid(saveAsFullName, p1 + 1)
    End If
   
    'Get IE's 'Notify when downloads complete' setting from the registry.  This setting is either "yes" or "no"
    'The setting in the IE UI is Internet Options -> Advanced tab -> Browsing section -> Notify when downloads complete
   
    NotifyDownloadComplete = ""
    On Error Resume Next
    'IE on Windows 10
    NotifyDownloadComplete = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\NotifyDownloadComplete")
    If Err.Number <> 0 Then
        'IE on Windows 11
        NotifyDownloadComplete = CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\AdvancedOptions\BROWSE\NOTIFYDOWNLOADCOMPLETE")
    End If
    On Error GoTo 0
    If NotifyDownloadComplete = "" Then NotifyDownloadComplete = "yes"
   
    'Create main UIAutomation object
   
    Set UIAutomation = New CUIAutomation
    Set DesktopRoot = UIAutomation.GetRootElement

    'Get the IE automation element from its window handle
   
    Set IEmain = UIAutomation.ElementFromHandle(ByVal IEhwnd)
   
    'Conditions to find the IE Notification tool bar, a child of the main IE window
    'Name:                  "Notification"
    'ControlType:           UIA_ToolBarControlTypeId
    'LocalizedControlType:  "tool bar"

    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
    Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'If the server sends an incorrect response header, for example the misspelling of 'attachment' in:
    '
    '   Content-Disposition: attachement; filename="8d72ee3290adc3d.zip"
    '
    'then IE displays a modal dialogue box instead of the normal Download Notification Bar at the bottom of the IE window:
    '
    '   -------------------------------------------------
    '   Internet Explorer                             [X]
    '
    '   What do you want to do with xxxxx.xxx?
    '
    '   Size: 54.2 MB
    '   From: hostname.com
    '
    '   --> Open
    '       The file won't be saved automatically
    '
    '   --> Save
    '
    '   --> Save as
    '
    '                                            [Cancel]
    '   -------------------------------------------------
   
    'Conditions to find this dialogue, which is a child of the main IE window.
    'Note - the dialogue isn't a true window, so FindWindowEx(IEhwnd, 0, "Internet Explorer", vbNullString) doesn't find the dialogue.
   
    'Name:                  "Internet Explorer"
    'ControlType:           UIA_WindowControlTypeId (0xC370)
    'LocalizedControlType:  "dialogue"
   
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Internet Explorer")
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set IEdialogueCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
   
    'Wait until either the IE11 Notification tool bar exists or the "What do you want to do with xxxx.xxx?" dialogue exists
   
    Set IEdialogue = Nothing
    Set NotificationToolbar = Nothing
    Do
        'New method of finding notification bar, instead of FindWindowEx
        Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
        If NotificationToolbar Is Nothing Then Set IEdialogue = IEmain.FindFirst(TreeScope_Children, IEdialogueCond)
        DoEvents
        Sleep 200
    Loop While NotificationToolbar Is Nothing And IEdialogue Is Nothing
   
    If Not NotificationToolbar Is Nothing Then
   
        'The Notification tool bar exists.  Find its child text element and extract the default file name from it
        'Name:          "Notification bar Text"
        'ControlType:   UIA_TextControlTypeId
        'Value.Value:   "Do you want to open or save xxxx.csv from yyyy.com?"
           
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification_bar_Text)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
        NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)

        p1 = InStr(NotificationBarTextString, sText_NotificationBarP1)
        If p1 = 1 Then
            p1 = p1 + Len(sText_NotificationBarP1)
            p2 = InStr(p1, NotificationBarTextString, " (")
            If p2 = 0 Then p2 = InStr(p1, NotificationBarTextString, sText_NotificationBarP2)
            defaultFileName = Mid(NotificationBarTextString, p1, p2 - p1)
        Else
            defaultFileName = ""
        End If
       
        'Get 2nd split button, which is the Down arrow next to Save in the Notification tool bar
       
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_SplitButtonControlTypeId)
        Set SplitButtons = NotificationToolbar.FindAll(TreeScope_Descendants, ControlType) 'TreeScope_Descendants necessary
        Set DownArrow = SplitButtons.GetElement(1)
        Set InvokePattern = DownArrow.GetCurrentPattern(UIA_InvokePatternId)
           
        'When the Down arrow is clicked, 3 items are displayed: Save; Save as; Save and open.  These 3 items are children of an element
        'with the following properties:
        '
        'Name:          "Context"
        'ControlType:   UIA_MenuControlTypeId
        '
        'IMPORTANT - this Context menu is a child of the Desktop element, NOT the Notification tool bar, nor the Down arrow
       
        'Create criteria to find the Context menu
       
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Context)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
          
        'Click the Down arrow repeatedly, until the Context menu, containing the 3 items (Save; Save as; Save and open) exists.
        'Note - Because the Context menu is a child of the Desktop element, the FindFirst call specifies TreeScope_Children,
        'not TreeScope_Descendants, to reduce the number of elements searched.
        'See https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationClient/nf-uiautomationclient-iuiautomationelement-findfirst
       
        Do
            DownArrow.SetFocus
            InvokePattern.Invoke
            Set ContextMenu = DesktopRoot.FindFirst(TreeScope_Children, NameAndType)
            If ContextMenu Is Nothing Then Sleep 500
        Loop While ContextMenu Is Nothing
           
        'Find the Save as item in the Context menu
        'Name:          "Save as"
        'ControlType:   UIA_MenuItemControlTypeId
       
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
        Set SaveAsMenuItem = ContextMenu.FindFirst(TreeScope_Children, NameAndType)
           
        'Click the Save as item to display the Save As dialogue window
       
        Set InvokePattern = SaveAsMenuItem.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
       
        'Search for the Save as window, when it appears, from the Desktop root element
       
        Set searchSaveAsWindowFrom = DesktopRoot
       
    ElseIf Not IEdialogue Is Nothing Then
   
        'The IE dialogue window exists.  See if the first child of the dialogue window contains the text "What do you want to do with "
       
        'Name:   "What do you want to do with 8d731f569075f6d.zip?"
        'ControlType:    UIA_TextControlTypeId (0xC364)
        'LocalizedControlType:   "text"

        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
        Set IEdialogueText = IEdialogue.FindFirst(TreeScope_Children, ControlType)
       
        If Not IEdialogueText Is Nothing Then
       
            IEdialogueTextString = IEdialogueText.CurrentName
            p1 = InStr(IEdialogueTextString, sText_IEdialogueP1)
            If p1 = 1 Then
                p1 = p1 + Len(sText_IEdialogueP1)
                p2 = InStr(p1, IEdialogueTextString, sText_IEdialogueP2)
                defaultFileName = Mid(IEdialogueTextString, p1, p2 - p1)
                p1 = 1
            Else
                defaultFileName = ""
            End If
       
            'Get the 'Save as' button in the dialogue
            'Name:   "Save as"
            'ControlType:    UIA_ButtonControlTypeId (0xC350)
            'LocalizedControlType:   "button"

            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_as1)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
            Set IEdialogueSaveAsButton = IEdialogue.FindFirst(TreeScope_Children, NameAndType)

            If Not IEdialogueSaveAsButton Is Nothing Then
           
                'Click the Save as button to display the Save As dialogue window
   
                Set InvokePattern = IEdialogueSaveAsButton.GetCurrentPattern(UIA_InvokePatternId)
                InvokePattern.Invoke
               
                'Search for the Save as window, when it appears, from the main IE window element
               
                Set searchSaveAsWindowFrom = IEmain
               
            End If
           
        End If

    End If
       
    'Find the Save As dialogue window, which is either a child of the Desktop if the Notification Bar was displayed, or a child
    'of the main IE window if the IE dialogue window was displayed, looping until it exists.
    'Again, the FindFirst specifies TreeScope_Children to reduce the number of elements searched.
    'Name:          "Save As"
    'ControlType:   UIA_WindowControlTypeId
   
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
    Do
        Set SaveAsWindow = searchSaveAsWindowFrom.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        If SaveAsWindow Is Nothing Then Sleep 200
    Loop While SaveAsWindow Is Nothing
           
    'If the caller has specified either the folder or the file name, populate the file name input box in the Save As window
   
    If saveInFolder <> "" Or saveAsFileName <> "" Then
   
        If saveAsFileName = "" Then
            'The caller has not specified the file name, so use the default file name from the Notification bar
            saveAsFileName = defaultFileName
        Else
            'If the caller has not specified an extension in the file name, append the extension from the default file name
            p1 = InStrRev(saveAsFileName, ".")
            If p1 = 0 Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, "."))
            ElseIf p1 = Len(saveAsFileName) Then
                saveAsFileName = saveAsFileName & Mid(defaultFileName, InStrRev(defaultFileName, ".") + 1)
            End If
        End If
       
        'Construct the full file name
       
        fullFileName = saveInFolder & saveAsFileName
       
        'Create criteria to find the file name input box, which is a child of the Save As window
        'Name:          "File name:"
        'ControlType:   UIA_EditControlTypeId
   
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_File_name)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
        'Find the file name input box
       
        Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, NameAndType)
       
        'Put the full file name in the input box, using IUIAutomationValuePattern

        Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
        FileNameInput.SetFocus
        FileNameInputPattern.SetValue fullFileName
       
        'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern
        'Same effect as using UIA_ValuePatternId above.
        '
        'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
        'FileNameInputPatternLegacy.SetValue fullFileName
       
        'If the Save button is clicked now, the Save As thinks the default file name is still being used.
        'To overcome this, and use the specified file name, we put a single space at the start of the input box with SendKeys
       
        SendKeys " ", True          'press space key
            
    Else
   
        'The caller has specified neither the folder nor the file name, so use the default file name provided by the remote site.
        'The file, if downloaded, will be saved in IE's default download folder
       
        fullFileName = defaultFileName
   
    End If
   
    'Create criteria to find the Save button
    'Name:          "Save"
    'ControlType:   UIA_ButtonControlTypeId
   
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
   
    'Find the Save button, a child of the Save As window
   
    Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
       
    'Click the Save button
   
    Button.SetFocus
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
       
    'Logical steps after clicking the Save button
    '
    'Find the Confirm Save As window, if it exists
    'If the Confirm Save As window was found Then
    '   If replaceExistingFile Then
    '       Click Yes
    '       downloaded = True
    '   Else
    '       Click No
    '       Click Cancel in Notification Bar
    '       downloaded = False
    '   End If
    'Else
    '   downloaded = True
    'End If
    'If downloaded Then
    '   Wait until Notification Bar contains "download has completed"
    '   Extract downloaded file name from Notification Bar
    'End If
    'Close Notification Bar
   
    'Create criteria to find the Confirm Save As dialogue window, if it exists
    'Name:          "Confirm Save As"
    'ControlType:   UIA_WindowControlTypeId
   
    Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Confirm_Save_As)
    Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
    Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
    'Find the Confirm Save As window, a child of the Save As window, waiting a maximum of 3 seconds
   
    timeout = DateAdd("s", 3, Now)
    Do
        Set ConfirmSaveAsWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
        DoEvents
        If ConfirmSaveAsWindow Is Nothing Then Sleep 200
    Loop While ConfirmSaveAsWindow Is Nothing And Now < timeout
      
    If Not ConfirmSaveAsWindow Is Nothing Then
       
        'The Confirm Save As window exists, so click the Yes or No button depending on the replaceExistingFile flag
   
        If replaceExistingFile Then
       
            'Criteria to find Yes button
            'Name:          "Yes"
            'ControlType:   UIA_ButtonControlTypeId
           
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Yes)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
            'Find the Yes button
           
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
       
            'Click the Yes button
           
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke

        Else 'replaceExistingFile = False
       
            'Extract the text warning from the Confirm Save As window
           
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set ConfirmSaveAsWindowText = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, ControlType)
            ConfirmSaveAsWindowTextString = ConfirmSaveAsWindowText.GetCurrentPropertyValue(UIA_NamePropertyId)
       
            'Criteria to find No button
            'Name:          "No"
            'ControlType:   UIA_ButtonControlTypeId
           
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_No)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
            'Find the No button, waiting until it exists
           
            Set Button = ConfirmSaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
       
            'Click the No button
           
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
                       
            downloadResult = fullFileName & " NOT DOWNLOADED - " & ConfirmSaveAsWindowTextString & " - replaceExistingFile = False"
            IE_Download_File_Using_UIAutomation = False
           
        End If
   
    Else
   
        'The Confirm Save As window doesn't exist.  This means that either the file was downloaded, or a Save As warning is
        'being displayed, giving the reason why it was not downloaded.
       
        'Criteria to find Save As warning window
        'Name:          "Save As"
        'ControlType:   UIA_WindowControlTypeId
       
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Save_As2)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)

        'Find the Save As warning window - a child of the main Save As window

        Set SaveAsWarningWindow = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
       
        If Not SaveAsWarningWindow Is Nothing Then
       
            'The Save As warning window exists, so extract the text warning from it
           
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set SaveAsWarningText = SaveAsWarningWindow.FindFirst(TreeScope_Children, ControlType)
            SaveAsWarningTextString = SaveAsWarningText.GetCurrentPropertyValue(UIA_NamePropertyId)
           
            'Create criteria to find the OK button in the Save As warning window
            'Name:          "OK"
            'ControlType:   UIA_ButtonControlTypeId
           
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_OK)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
           
            'Find the OK button - a child of the Save As warning window
           
            Set Button = SaveAsWarningWindow.FindFirst(TreeScope_Children, NameAndType)
           
            'Click the OK button
           
            Button.SetFocus
            Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
            InvokePattern.Invoke
           
            IE_Download_File_Using_UIAutomation = False
            downloadResult = fullFileName & " - NOT DOWNLOADED - " & SaveAsWarningTextString
           
        Else
       
            'The Save As warning window doesn't exist, so the file was downloaded
           
            saveAsFullName = fullFileName
            IE_Download_File_Using_UIAutomation = True
           
        End If
           
    End If
   
    If IE_Download_File_Using_UIAutomation = True Then
   
        'If the file download was offered via the IE dialogue pane, rather than the normal Frame Notification Bar, then the Notification toolbar
        'is displayed as the download progresses.   Therefore get the Notification toolbar at this point
       
        If NotificationToolbar Is Nothing Then
       
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
       
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Notification)
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                Sleep 200
            Loop While NotificationToolbar Is Nothing
       
        End If
       
        If LCase(NotifyDownloadComplete) = "yes" Then
       
            'IE setting 'Notify when downloads complete' is ticked, which means that the Notification bar will contain "download has completed" when the
            'download has finished
           
            'Create criteria to find the "Notification bar Text" element within the Notifcation bar
            'Name:          "Notification bar Text"
            'ControlType:   UIA_TextControlTypeId
            'Value.Value:   The xxxx yyyy.zzz download has completed.
           
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification bar Text")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
            Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
           
            'Find the Notification bar Text element in the Frame Notification Bar pane and wait until it contains "download has completed"
           
            NotificationBarTextString = ""
            Do
                Set NotificationBarText = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
                DoEvents
                Sleep 200
                If Not NotificationBarText Is Nothing Then
                    NotificationBarTextString = NotificationBarText.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
                End If
            Loop Until InStr(NotificationBarTextString, sText_Download_has_Completed)
           
        Else
       
            'IE setting 'Notify when downloads complete' is not ticked
            'Wait until the Notification bar doesn't exist
       
            'Conditions to find the Notification tool bar, a child of the main IE window
            'Name:                  "Notification"
            'ControlType:           UIA_ToolBarControlTypeId
            'LocalizedControlType:  "tool bar"
       
            Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, "Notification")
            Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
            Set NotificationToolbarCond = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
            Do
                Set NotificationToolbar = IEmain.FindFirst(TreeScope_Descendants, NotificationToolbarCond)
                DoEvents
                If Not NotificationToolbar Is Nothing Then Sleep 200
            Loop Until NotificationToolbar Is Nothing

        End If
       
        downloadResult = "SUCCESSFULLY DOWNLOADED"
        saveAsFullName = fullFileName
       
    Else
   
        'Not downloaded, so click the Cancel button in the Save As window
           
        'Create criteria to find the Cancel button in the Save As window
        'Name:          "Cancel"
        'ControlType:   UIA_ButtonControlTypeId
       
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Cancel)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
       
        'Find the Cancel button, waiting until it exists
       
        Set Button = SaveAsWindow.FindFirst(TreeScope_Children, NameAndType)
       
        'Click the Cancel button
       
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
       
        'In this case, does the Notification tool bar pane exist?

    End If
            
    If Not NotificationToolbar Is Nothing Then
   
        'Create criteria to find the Close (X) button on the Notification pane
        'Name:          "Close"
        'ControlType:   UIA_ButtonControlTypeId
       
        Set ControlName = UIAutomation.CreatePropertyCondition(UIA_NamePropertyId, sControlName_Close)
        Set ControlType = UIAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
        Set NameAndType = UIAutomation.CreateAndCondition(ControlName, ControlType)
           
        'Find the Close button in the IE Download Notification Bar
       
        Set Button = NotificationToolbar.FindFirst(TreeScope_Children, NameAndType)
       
        'Click the Close button
       
        Button.SetFocus
        Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        InvokePattern.Invoke
   
    End If
                
End Function


#If VBA7 Then
Public Sub IE_Click_Tab_Like(IEhwnd As LongPtr, findTabName As String)
#Else
Public Sub IE_Click_Tab_Like(IEhwnd As Long, findTabName As String)
#End If
  
    'This finds all TabItemControls of the IE window and loops through them looking for the tab item whose CurrentName property matches the specified tab name.
    'If found, it activates that tab.  Uses the Like operator, so wildcards can be used (see - https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/like-operator)
    'to specify the tab name to be found and activated.
    'This is useful because sometimes the visible tab name - shown when hovering over the tab - may be "xxxxx", the actual tab name
    'according to UIAutomation is "xxxxx Tab Group 1".
  
    Dim UIauto As IUIAutomation
    Dim IEwindow As IUIAutomationElement, IEtab As IUIAutomationElement
    Dim IEtabs As IUIAutomationElementArray
    Dim tabItemCondition As IUIAutomationCondition
    Dim IEtabPattern As IUIAutomationLegacyIAccessiblePattern
    Dim i As Long
   
    'Create UIAutomation object
   
    Set UIauto = New CUIAutomation
   
    'Get Internet Explorer UIAutomation element
   
    Set IEwindow = UIauto.ElementFromHandle(ByVal IEhwnd)
   
    'Create condition to find a TabItemControl
   
    Set tabItemCondition = UIauto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TabItemControlTypeId)
   
    'Find all tabs
   
    Set IEtabs = IEwindow.FindAll(TreeScope_Descendants, tabItemCondition)
   
    'Look for the tab which matches the specified tab name
   
    Set IEtab = Nothing
    i = 0
    While i < IEtabs.Length And IEtab Is Nothing
        'Debug.Print i; IEtabs.GetElement(i).CurrentName
        If LCase(IEtabs.GetElement(i).CurrentName) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
        i = i + 1
    Wend
       
    '==== OR look for the tab which matches the specified tab name and its URL.  The tab name AND URL is shown when you hover the mouse on the IE tab
   
    'LegacyIAccessible.Description:  "Balance Sheet for Intel Corp (INTC) from Morningstar.com
    '                                http://financials.morningstar.com/balance-sheet/bs.html?t=INTC&region=usa&culture=en-US"
   
'    Set IEtab = Nothing
'    i = 0
'    While i < IEtabs.Length And IEtab Is Nothing
'        Debug.Print i; IEtabs.GetElement(i).GetCurrentPropertyValue(UIA_LegacyIAccessibleDescriptionPropertyId)
'        If LCase(IEtabs.GetElement(i).GetCurrentPropertyValue(UIA_LegacyIAccessibleDescriptionPropertyId)) Like LCase(findTabName) Then Set IEtab = IEtabs.GetElement(i)
'        i = i + 1
'    Wend
   
    If Not IEtab Is Nothing Then
   
        'Access the legacy pattern of the IE tab, which has the DoDefaultAction method (Click)  (Press, 27-Oct-2021)
       
        Set IEtabPattern = IEtab.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
        IEwindow.SetFocus   'optional - brings the IE window to the foreground
        IEtabPattern.DoDefaultAction
        'Another way of clicking the tab
        'IEtabPattern.Select 2
       
    Else
   
        MsgBox "IE tab with name '" & findTabName & "' not found"
       
    End If
       
    Set IEtabPattern = Nothing
    Set IEtab = Nothing
    Set IEwindow = Nothing
    Set UIauto = Nothing
   
End Sub
```

*Test routine *which downloads a .xls file from tools.morningstar.co.uk using the two public functions in the above code.

```
'References required:
'Microsoft Internet Controls
'Microsoft HTML Object Library

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
#End If


Public Sub Test_IE_Download()

    Dim URL As String
    Dim IE As InternetExplorer
    Dim HTMLdoc As HTMLDocument
    Dim exportLink As HTMLLinkElement
    Dim saveAsFullName As String
    Dim replaceExistingFile As Boolean
    Dim downloadResult As String, downloadStatus As Boolean
   
    saveAsFullName = "C:\Temp\"                         'Save in this folder with the file name provided by web site.  Folder must end with "\"
    replaceExistingFile = True
   
    'URL for Record PLC (REC); Financials and Ratios; Balance Sheet
   
    URL = "https://tools.morningstar.co.uk/uk/stockreport/default.aspx?tab=10&vw=bs&SecurityToken=0P0000APA1%5D3%5D0%5DE0WWE%24%24ALL&Id=0P0000APA1&ClientFund=0&CurrencyId=BAS"
   
    'See if an IE window is already open at the site
   
    Set IE = Get_IE_Window2(URL)
    If IE Is Nothing Then
        Set IE = New InternetExplorer
        With IE
            .Visible = True
            SetForegroundWindow .hwnd
            If .LocationURL <> URL Then
                .navigate URL
                While .Busy Or .readyState <> READYSTATE_COMPLETE: DoEvents: Sleep 100: Wend
                While .document.readyState <> "complete": DoEvents: Sleep 100: Wend
            End If
        End With
    End If
    Set HTMLdoc = IE.document
      
    'Find and click the Export to Excel link to start the IE file download dialogue
   
    Set exportLink = HTMLdoc.getElementById("ExportExcel")
    exportLink.Click
   
    'Activate the morningstar IE tab and download the .xls file using Save As
   
    IE_Click_Tab_Like IE.hwnd, "*morningstar*"
    downloadStatus = IE_Download_File_Using_UIAutomation(IE.hwnd, saveAsFullName, replaceExistingFile, downloadResult)
    Debug.Print "Download result = " & downloadResult
    Debug.Print "Download saveAsFullName = " & saveAsFullName
    Debug.Print "Download status = " & downloadStatus
   
End Sub


Private Function Get_IE_Window2(URL As String) As InternetExplorer

    'Look for an IE browser window or tab already open at the domain of the specified URL (which can start with http://, https://
    'or nothing) and, if found, return that browser as an InternetExplorer object.  Otherwise return Nothing

    Dim Shell As Object
    Dim IE As InternetExplorer
    Dim i As Variant 'Must be a Variant to index Shell.Windows.Item() array
   
    Set Shell = CreateObject("Shell.Application")
   
    i = 0
    Set Get_IE_Window2 = Nothing
    While i < Shell.Windows.count And Get_IE_Window2 Is Nothing
        Set IE = Shell.Windows.Item(i)
        If Not IE Is Nothing Then
            If TypeOf IE Is InternetExplorer Then
                'Debug.Print IE.LocationURL, IE.LocationName
                If InStr(IE.LocationURL, URL) = 1 Then Set Get_IE_Window2 = IE
            End If
        End If
        i = i + 1
    Wend
   
End Function
```


----------

