Search code examples
vbainternet-explorerdownloadbrowser-automationie-automation

VBA IE automation - wait for the download to complete


I am trying to automate some tasks which are being done through Internet explorer, which include downloading a file and then copying it to a different directory and renaming it. I was more or less successful in finding the information on how to do this, the code is working, but it has exceptions, therefore I would be grateful if someone could help me to improve this code.

There are two things I would like to do:

  1. Insert a loop, so that the script would wait for certain elements to appear and only then would proceed with execution. I have found something on this page, however, I also would like to built in a maximum wait time, like it is suggested there.
  2. As the code is downloading a file, it should also wait for the download to be finished, and only then proceed. Currently I am using "wait" command, but the download times may vary and the script will stop in that case. I have also found a solution to this, by waiting till the button "Open folder" appears, but I am not sure how to implement it in my code. Here is the code that i have found: Link

Also, maybe there is another solution, not to save file in a default download location, but do a "Save as" instead and then defining the directory and file name in that way?

Thank you in advance!

Below is my source code, that I am using right now. As an example, I am using Microsoft page with sample file download.

    Option Explicit
#If VBA7 Then
    Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)

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

#Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

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

Sub MyIEauto()

    Dim ieApp As InternetExplorer
    Dim ieDoc As Object

    Set ieApp = New InternetExplorer

    ieApp.Visible = True
    ieApp.navigate "https://learn.microsoft.com/en-us/power-bi/sample-financial-download"
    Do While ieApp.Busy: DoEvents: Loop
    Do Until ieApp.readyState = READYSTATE_COMPLETE: DoEvents: Loop

    ieApp.navigate "http://go.microsoft.com/fwlink/?LinkID=521962"
    Do While ieApp.Busy: DoEvents: Loop
    Do Until ieApp.readyState = READYSTATE_COMPLETE: DoEvents: Loop

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

    Set AutomationObj = New CUIAutomation

    Do While ieApp.Busy Or ieApp.readyState <> 4: DoEvents: Loop
    Application.Wait (Now + TimeValue("0:00:05"))
    hWnd = ieApp.hWnd
    hWnd = FindWindowEx(hWnd, 0, "Frame Notification Bar", vbNullString)
    If hWnd = 0 Then Exit Sub

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

    Set Button = WindowElement.FindFirst(TreeScope_Subtree, iCnd)
    Dim InvokePattern As IUIAutomationInvokePattern
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    Application.Wait (Now + TimeValue("0:00:05"))

    FileCopy "C:\Users\Name\Downloads\Financial Sample.xlsx", "C:\Users\Name\Desktop\Financial Sample.xlsx"
    Name "C:\Users\Name\Desktop\Financial Sample.xlsx" As "C:\Users\Name\Desktop\Hello.xlsx"
    Application.Wait (Now + TimeValue("0:00:01"))

    Dim KillFile As String
    KillFile = "C:\Users\Name\Downloads\Financial Sample.xlsx"
    If Len(Dir$(KillFile)) > 0 Then
    SetAttr KillFile, vbNormal
     Kill KillFile
End If

End Sub

Solution

  • So, after spending some additional time I was able to solve my problem in the way I was expecting, and I am posting the solution below. I thank everyone for the suggestions, and I hope that all of the suggested solutions will be a great find for others in the future :)

    So what the code does, it is going to a website, pressing on the download link, then pressing "Save" button, and the download is starting. Then Script is waiting for the "Open folder" button to appear, which means that the download has finished. After downloading the file, script copies the file to desktop, renames it and then deleted the original from the Downloads folder.

      Option Explicit
    #If VBA7 Then
    Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
    
    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
    
    
     (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, _
      ByVal lpsz2 As String) As LongPtr
    
    #Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
    (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
    ByVal lpsz2 As String) As Long
    #End If
    
    Sub MyIEauto()
    
    Dim ieApp As InternetExplorer
    Dim ieDoc As Object
    Const DebugMode As Boolean = False
    
    Set ieApp = New InternetExplorer
    
    ieApp.Visible = True
    ieApp.navigate "https://learn.microsoft.com/en-us/power-bi/sample-financial-download"
    Do While ieApp.Busy: DoEvents: Loop
    Do Until ieApp.readyState = READYSTATE_COMPLETE: DoEvents: Loop
    
    ieApp.navigate "http://go.microsoft.com/fwlink/?LinkID=521962"
    Do While ieApp.Busy: DoEvents: Loop
    Do Until ieApp.readyState = READYSTATE_COMPLETE: DoEvents: Loop
    
    Dim AutomationObj As IUIAutomation
    Dim WindowElement As IUIAutomationElement
    Dim Button As IUIAutomationElement
    Dim hWnd As LongPtr
    
    Set AutomationObj = New CUIAutomation
    
    Do While ieApp.Busy Or ieApp.readyState <> 4: DoEvents: Loop
    Application.Wait (Now + TimeValue("0:00:05"))
    hWnd = ieApp.hWnd
    hWnd = FindWindowEx(hWnd, 0, "Frame Notification Bar", vbNullString)
    If hWnd = 0 Then Exit Sub
    
    Set WindowElement = AutomationObj.ElementFromHandle(ByVal hWnd)
    Dim iCnd As IUIAutomationCondition
    Set iCnd = AutomationObj.CreatePropertyCondition(UIA_NamePropertyId, "Save")
    
    Set Button = WindowElement.FindFirst(TreeScope_Subtree, iCnd)
    Dim InvokePattern As IUIAutomationInvokePattern
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
    InvokePattern.Invoke
    
    Do
    Set iCnd = AutomationObj.CreatePropertyCondition(UIA_NamePropertyId, "Open folder")
    Set Button = WindowElement.FindFirst(TreeScope_Subtree, iCnd)
        Sleep 200
        If DebugMode Then Debug.Print Format(Now, "hh:mm:ss"); "Open folder"
        DoEvents
    Loop While Button Is Nothing
    
    
      FileCopy "C:\Users\" & Environ("UserName") & "\Downloads\Financial Sample.xlsx", "C:\Users\" & Environ("UserName") & "\Desktop\Financial Sample.xlsx"
    Name "C:\Users\" & Environ("UserName") & "\Desktop\Financial Sample.xlsx" As "C:\Users\" & Environ("UserName") & "\Desktop\Hello.xlsx"
    Application.Wait (Now + TimeValue("0:00:01"))
    
    Dim KillFile As String
    KillFile = "C:\Users\" & Environ("UserName") & "\Downloads\Financial Sample.xlsx"
    If Len(Dir$(KillFile)) > 0 Then
    SetAttr KillFile, vbNormal
     Kill KillFile
    End If
    
    End Sub
    

    Additionally, if someone will be searching how to loop the code until an element appears, here is the code below. It loops the lines four times and then displays a message.

    intCounter = 0
    
    Do Until IsObject(objIE.document.getElementById("btnLogIn")) = True Or intCounter > 3
    DoEvents
    Application.Wait (Now + TimeValue("0:00:01"))
    intCounter = intCounter + 1
    If intCounter = 4 Then
    MsgBox "Time out."
    End If
    Loop