0

First off, this is the sort of website I am trying to download files from (via clicking on "Download Data" with the CSV option on). The problematic code (I think; it's hard to tell, partially because it only occurs when running the code at full speed, not stepping through, and partially because the problem is inconsistent, in that it doesn't occur all the time), is this:

Option Explicit 'this stuff at the beginning, of course

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

Sub ContactWeb(ByVal URL As String)

Dim IE As InternetExplorer
'leaving these here so you can see what it is I'm working with right now
Dim Doc As Object, Elmt As Object
Dim HTMLColl As MSHTML.IHTMLElementCollection

Dim Handle As LongPtr
Dim CUI As IUIAutomation
Dim HandleElement As IUIAutomationElement
Dim Condition As IUIAutomationCondition
Dim Button As IUIAutomationElement
Dim InvokePattern As IUIAutomationInvokePattern

Set IE = New InternetExplorer

With IE
    'code that loops through elements/clicks on csv and download data goes here

    Set CUI = New CUIAutomation
    'this point on just clicks "save" on the "do you want to open or save" bar

SetHandle:
    Do
        Handle = FindWindowEx(.Hwnd, 0, "Frame Notification Bar", vbNullString)
    Loop While Handle = 0

    If Handle = 0 Then  'just in case it somehow breaks out of that loop
        .Visible = True
        MsgBox "Could not download file; please do so manually."
        Stop
        GoTo SetHandle 'I really really *really* hate GoTo
            'but I wasn't sure how to eliminate it in this case
    End If

    'this is the spot I think where it starts failing sometimes
    'or at least, the above seems to work a lot more consistently

        DoEvents    'if you try to go through this full tilt
            'it will return "object variable or with block variable not set"
            'so this slows it down a bit
    Set HandleElement = CUI.ElementFromHandle(ByVal Handle)
        DoEvents
    Set Condition = CUI.CreatePropertyCondition(UIA_NamePropertyId, "Save")
        DoEvents
    Set Button = HandleElement.FindFirst(TreeScope_Subtree, Condition)
        'this is the only part that actually requires IE to be visible
            '(for reasons unknown to me)
        DoEvents
    Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
        DoEvents
    InvokePattern.Invoke

    .Quit

End With

End Sub

So as mentioned, this code always runs, but the file doesn't always appear...if I run it automatically. When I run it step by step, it always works like a charm (except for that one time it got stuck in an eternal loop - where I have a comment wondering if complete or interactive makes more sense). Can someone help me out here? Why is this happening?

kumquatwhat
  • 315
  • 1
  • 4
  • 12
  • 1
    Would you mind to reformat your post, so the horizontal scollbar will disappear? – Reporter Apr 25 '17 at 15:29
  • I have now done this. Sorry! – kumquatwhat Apr 25 '17 at 15:54
  • Two Remarks: 1) You can make a function ("FindWindowHandle") and put your window-finding loop in there. This would rid you of the GoTo. 2) Is it necessary to click buttons this way? Can't you just either download the file with an XmlHttpRequest, or, if that's not an option, why not use IE automation ("InternetExplorer.Application") instead of dealing with window handles? – Tomalak Apr 25 '17 at 15:55
  • Referencing [the `Sleep` function](http://stackoverflow.com/a/2737726/18771) and putting that into the window finding loop, along with a DoEvents call, might also be a good idea in order to not clog up the gears while waiting for the IE window. – Tomalak Apr 25 '17 at 16:03
  • I'm sure there a bunch of other ways to do what I'm trying to accomplish, but I'm a novice at html, and I don't *really* understand how vba interacts with html. This is the first solution I managed to cobble together that more or less worked. I am totally open to better methods. – kumquatwhat Apr 25 '17 at 16:05
  • Well, that levels the playing field a bit. Is the URL you get the CSV file from predictable? Does it require a log-in cookie being present or is it freely available? Are you, by any chance, on Excel and just want to retrieve a data file for display in a worksheet? – Tomalak Apr 25 '17 at 16:10
  • It's always the same format, so yeah, and it is freely available. And yeah, I am on excel, pretty much just trying to get the file so I can format it/save it/get data from it. – kumquatwhat Apr 25 '17 at 16:12
  • Sooooo unless you really want something super special, there is no code to write for this. No code is always the best solution. Read http://www.kimgentes.com/worshiptech-web-tools-page/2010/8/18/web-connecting-csv-files-as-external-data-to-excel-spreadshe.html and try that first. – Tomalak Apr 25 '17 at 16:16
  • Hmm...that just seems to bring up the html code, with each new line in a new cell. I assume that's because it links the website, not the file, but that button doesn't seem to connect to anything - there's no website behind it (that I can see) whose URL I can copy and use in that method. – kumquatwhat Apr 25 '17 at 16:59
  • Of course you need to use the URL of the CSV file, not the URL of the parent page. I thought that would be obvious. *"Is the URL you get the CSV file from predictable?"*, I asked. ;) – Tomalak Apr 25 '17 at 17:03
  • Ooooh, I misunderstood - sorry. I have no idea what the URL for the CSV is, so I do not know. – kumquatwhat Apr 25 '17 at 17:14
  • Yeah, then find out. If it's not copyable from link/button you click, open the browser's developer tools (via F12), goto the network tab, click the button, figure out the URL that belongs the the CSV file. Try to download that URL directly. If that works, try to determine the URL parameters and if you can predict them, which would effectively let you circumvent the HTML page and get to the file directly. (preferred approach) If the file is the response to a POST request, figure out which parameters are posted, exactly. (next best approach) – Tomalak Apr 25 '17 at 17:23
  • Oh, wow, that is so much easier than what I was doing. It's like a twentieth the steps, and easier to understand to boot. Thank you so much. – kumquatwhat Apr 25 '17 at 17:45
  • Is it working? That was quick! – Tomalak Apr 25 '17 at 17:54
  • Yeah, I hit record macro, did what that site said, and it imported perfectly. Just what I needed. – kumquatwhat Apr 25 '17 at 17:58
  • All-right, not quite what I had in mind, but if it works for you, that's fine. Recorded macros have a tendency to be comparatively brittle and not easy to maintain, but they are a good place to start and then flesh out a dedicated function later, if need be. – Tomalak Apr 25 '17 at 18:01
  • Ah, I'm not going to import it regularly, but to get the baseline code, I usually import and adapt. So to find out functions, arguments that are important, etc, and then I dissect the auto-generated macro to see what I need out of it. – kumquatwhat Apr 25 '17 at 18:32

0 Answers0