1

I am writing code to automatically download a spreadsheet from a website, by simulating a series of button clicks. (Has to be done this way, winHTTP methods won't work for other reasons).

The code clicks a download button, then clicks the save and open buttons on a frame notification bar. However, the issue I am having is that occasionally, the download button will get stuck for whatever reason, or will be incredibly slow. In this situation the code I am using to wait for the download is being ignored, and the code continues to run up to the point where it should click save in the frame notification bar, where it inevitably throws an error.

My question is, how can I handle the situation where the button becomes stuck after having been clicked? Perhaps a timeout error message if the download button is stuck? Though in my current code I am not sure how this would work, as it seems to ignore my checks of whether the download has been completed or not.

     Sub StartIE()
Dim appIE As Object
Dim URLString As String
Dim HTMLdoc, btn As Object
Dim eBrowser As IUIAutomationElement
Set appIE = CreateObject("internetexplorer.application") ' create an instance of internet explorer


With appIE
    .Navigate "https://analytics.twitter.com/user/QinetiQ/tweets" 'go to this url
    .Visible = True ' and show the IE
End With
apiShowWindow appIE.hwnd, SW_SHOWMINIMIZED ' minimise IE (wouldn't work with IE hidden for some reason)
Do While appIE.Busy Or (appIE.READYSTATE <> 4) ' wait until IE has finished loading

    DoEvents
Loop

URLString = appIE.LocationURL
'Application.Wait (Now + TimeValue("0:00:04")) ' wait a little bit longer as waiting for IE.Busy to finish doesn't seem to work 100%
If InStr(URLString, "login") Then

    UseCredentials appIE 'inputs login details for the site
    Do While appIE.Busy Or (appIE.READYSTATE <> 4) ' wait until IE has finished loading
        DoEvents
    Loop

End If
'Application.Wait (Now + TimeValue("0:00:04"))

Set HTMLdoc = appIE.document
Set btn = HTMLdoc.getElementsByClassName("btn btn-default ladda-button")(0) 'finds the export data button
btn.Click                                                                   'clicks the export button
Do While appIE.Busy Or (appIE.READYSTATE <> 4)                              ' wait until IE has finished loading
        DoEvents
Loop

Application.Wait (Now + TimeValue("0:00:07"))



Dim hwnd As LongPtr, h As LongPtr
Dim o As IUIAutomation ' The following steps are used to download a csv file from a webpage
Dim e As IUIAutomationElement
Set o = New CUIAutomation

Do While h = 0 'loops untill the frame notification window is avaliable, though THIS DOESN'T WORK FOR SOME REASON
    h = appIE.hwnd ' handle of the current window
    Set eBrowser = o.ElementFromHandle(ByVal h)
    DoEvents
    h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString) ' replaces the handle of the window with the frame bar, using h in the function
    Set eFNB = FindFromAllElementsWithClassName(eBrowser, "Frame Notification Bar", 10)
    If eFNB Is Nothing Then
        h = 0
    End If
Loop
 ' we must find the first frame notification handle
If h = 0 Then Exit Sub
Set e = o.ElementFromHandle(ByVal h) ' it was important to set h as a LongPtr, and using Option Explicit at the top
Dim iCnd As IUIAutomationCondition
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Save") ' click the save button
Dim Button As IUIAutomationElement
Set Button = e.FindFirst(TreeScope_Subtree, iCnd) ' finds the button we need to click in the subtree on the notification bar
Dim InvokePattern As IUIAutomationInvokePattern
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId) 'clicks the button
InvokePattern.Invoke
Application.Wait (Now + TimeValue("0:00:07"))
h = appIE.hwnd
h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString) ' repeating the above for the second stage of open/save on the notification bar
If h = 0 Then Exit Sub
Set e = o.ElementFromHandle(ByVal h)
Dim iCnd2 As IUIAutomationCondition
Set iCnd2 = o.CreatePropertyCondition(UIA_NamePropertyId, "Open") ' similar to the above snippet, except for the second stage of the frame notification window
Dim Button2 As IUIAutomationElement
Set Button2 = e.FindFirst(TreeScope_Subtree, iCnd2)
Dim InvokePattern2 As IUIAutomationInvokePattern
Set InvokePattern2 = Button2.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern2.Invoke  
Application.Wait (Now + TimeValue("0:00:03"))

End Sub

I've included all code from the sub because I feel it gives good context as to the steps I am taking. The snippet from this which doesn't seem to work is:

  Do While h = 0 'loops untill the frame notification window is avaliable, though THIS DOESN'T WORK FOR SOME REASON
    h = appIE.hwnd ' handle of the current window
    Set eBrowser = o.ElementFromHandle(ByVal h)
    DoEvents
    h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString) ' replaces the handle of the window with the frame bar, using h in the function
    Set eFNB = FindFromAllElementsWithClassName(eBrowser, "Frame Notification Bar", 10)
    If eFNB Is Nothing Then
        h = 0
    End If
Loop

Thankyou, hope this is clear.

Christian.

Christian T
  • 128
  • 1
  • 12
  • automating IE is a very bad idea. its a memory hog, reports wrong status, unaesthetic and overly complicated. from my experience everything can be achieved with an httprequest. just open chrome's dev tools, go to network tab and locate the request that returns the file you need, and recreate that request in vba. – Banana Mar 19 '18 at 13:25
  • thanks @Banana I must admit I have not looked in to using chromes dev tools yet but will try that. Although, I believe I may face issues attempting a httprequest due to using a proxy? – Christian T Mar 19 '18 at 13:43
  • 1
    you are simply 'emulating' what your browser does, so if it can be done with your browser then it can be done with an http request. you might need to research a bit about using a proxy with http request but it definitely can be done. besides, like i wrote above - `While appIE.Busy Or (appIE.READYSTATE <> 4)` will not always work as IE object tends to occasionally report ready status too early (way before it is actually ready) and in my opinion it is way more bothersome than managing your own http request. – Banana Mar 20 '18 at 06:50
  • Thanks @Banana. I understand that using the internet READYSTATE is going to be prone to error. What I don't understand is why my code which should wait until the frame notification bar is identified before continuing doesn't work. I am looking into using the httprequest method alongside this however due to time constraints it would be hugely beneficial if I could produce something working using this technique. – Christian T Mar 20 '18 at 09:43

0 Answers0