File download in IE10 - vba

I'm working on an internet explorer automation project. The code downloads and saves a file from a website. Its possible to download and save file using 'SendKeys' but its not a reliable method as I cannot detect the download notification:
Is there a way to download and save the file without 'SendKeys'? or at least is there a way to detect the presence of this notification?
Ps - I have referred to these links, which are helpful for IE8 downloads:
http://www.siddharthrout.com/2011/10/23/vbavb-netvb6click-opensavecancel-button-on-ie-download-window/
http://www.siddharthrout.com/2012/02/02/vbavb-netvb6click-opensavecancel-button-on-ie-download-window-part-ii/
Any help?

Why to use SendKeys method to download a file? Sorry, this is wrong approach.
Use API!
Solution 1:
Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal _
szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
Sub DownloadFilefromWeb()
Dim strSavePath As String
Dim URL As String, ext As String
Dim buf, ret As Long
URL = Worksheets("References & Resources").Range("URLMSL").Value
buf = Split(URL, ".")
ext = buf(UBound(buf))
strSavePath = ThisWorkbook.Path & "\" & "DownloadedFile." & ext
ret = URLDownloadToFile(0, URL, strSavePath, 0, 0)
If ret = 0 Then
MsgBox "Download has been succeed!"
Else
MsgBox "Error"
End If
End Sub
Source: VBA - Save a file from a Website
Solution 2:
Option Explicit
Sub DownloadXLFileFromURL()
Dim myURL As String, sFilename As String
myURL = "http://img.chandoo.org/hw/max-change-problem.xlsx"
sFilename = Environ("SystemDrive") & Environ("HomePath") & _
Application.PathSeparator & "Desktop" & Application.PathSeparator & _
"file.xlsx"
Dim WinHttpReq As Object, oStream As Object
Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
WinHttpReq.Open "GET", myURL, False ', "username", "password"
WinHttpReq.Send
myURL = WinHttpReq.ResponseBody
If WinHttpReq.Status = 200 Then
Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write WinHttpReq.ResponseBody
oStream.SaveToFile sFilename, 2 ' 1 = no overwrite, 2 = overwrite
oStream.Close
End If
End Sub
Source: Download file from URL using VBA
Solution 3:
Finally, you can create custom class, which can create an instance of MS IE. Now, you are able to control/manage of IE object via using its properties and events, such as DownloadBegin, DownloadComplete, FileDownload
Please, see: Using the WebBrowser Control from Visual Basic
Note: Never before i tried it...

Related

Why does my Access database object variable lose connection to its source file midway through reading its Modules container?

We use a VBA tool to extract modules, forms and reports from our Access applications and create an executable for users. This tool has been working without any problems until very recently. However, when I use it to extract from a couple of applications, I keep on encountering an "Automation Error (The remote procedure call failed)" error. However, my colleague (running the same code on a virtually identical build) is able to run it ok.
This is running on Win10 Pro (v2004 - 19041.685), Office 2016 Pro Plus (16.0.4266.1001). I believe my colleague's machine should be the same, as we have only just moved across to these laptops.
This is the core code:
Public Sub ExportAll()
On Error GoTo ErrorProc
Dim oAccessApp As Access.Application
Dim oDoc As Document
Dim sFilePath As String
Dim oDb As Database
Dim fso As FileSystemObject
Dim strFile As String
Dim strFolder As String
strFile = "accdb path"
strFolder = oApp.GetFolder(strFile)
Set oAccessApp = oApp.OpenDatabase(strFile)
Set oDb = oAccessApp.CurrentDb
Set fso = New FileSystemObject
If Not fso.FolderExists((strFolder) & "\SCC") Then
fso.CreateFolder strFolder & "\SCC"
End If
If Not fso.FolderExists(strFolder & "\SCC\Modules") Then
fso.CreateFolder strFolder & "\SCC\Modules"
End If
If Not fso.FolderExists(strFolder & "\SCC\Forms") Then
fso.CreateFolder strFolder & "\SCC\Forms"
End If
If Not fso.FolderExists(strFolder & "\SCC\Reports") Then
fso.CreateFolder strFolder & "\SCC\Reports"
End If
For Each oDoc In oDb.Containers("Modules").Documents
DoEvents
sFilePath = strFolder & "\SCC\Modules\" & oDoc.Name & ".bas.txt"
oAccessApp.SaveAsText acModule, oDoc.Name, sFilePath
Next
For Each oDoc In oDb.Containers("Forms").Documents
DoEvents
sFilePath = strFolder & "\SCC\Forms\" & oDoc.Name & ".frm.txt"
oAccessApp.SaveAsText acForm, oDoc.Name, sFilePath
Next
For Each oDoc In oDb.Containers("Reports").Documents
DoEvents
sFilePath = strFolder & "\SCC\Reports\" & oDoc.Name & ".rpt.txt"
oAccessApp.SaveAsText acReport, oDoc.Name, sFilePath
Next
oDb.Close
Set oDb = Nothing
oAccessApp.Quit
Set oAccessApp = Nothing
Exit Sub
ErrorProc:
If Not (oAccessApp Is Nothing) Then
oAccessApp.Quit
End If
Set oAccessApp = Nothing
MsgBox Err.Description, vbExclamation, "Error " & Err.Number
End Sub
As the extraction takes place, the database being extracted should remain open throughout. Each failure occurs in the For Each oDoc In oDb.Containers("Modules").Documents loop, and happens when a particular module is referenced by the oDoc variable. When I step through and reach the offending module, all is fine until the line with oDoc.Name is hit, at which point the database closes and all the messages for the object oDb reads "".
The module causing the problem is below:
Option Compare Database
Option Explicit
'
' Opens file using default program
' (.xls files open in Excel, .doc files open in Word, etc)
'
'Code Courtesy of
'Dev Ashish
#If Win64 Then
Private Declare PtrSafe Function apiShellExecute Lib "shell32.dll" Alias "ShellExecuteA" ( _
ByVal hWnd As LongPtr, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr
#Else
Private Declare Function apiShellExecute Lib "shell32.dll" _
Alias "ShellExecuteA" _
(ByVal hwnd As Long, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long) _
As Long
#End If
Public Enum ShellExecuteWinStyle
WIN_NORMAL = 1 'Open Normal
WIN_MAX = 2 'Open Maximized
WIN_MIN = 3 'Open Minimized
End Enum
Private Const ERROR_SUCCESS = 32&
Private Const ERROR_NO_ASSOC = 31&
Private Const ERROR_OUT_OF_MEM = 0&
Private Const ERROR_FILE_NOT_FOUND = 2&
Private Const ERROR_PATH_NOT_FOUND = 3&
Private Const ERROR_BAD_FORMAT = 11&
'
' Opens file using default program
' (.xls files open in Excel, .doc files open in Word, etc)
'
Function ShellExecute(strFile As Variant, lShowHow As ShellExecuteWinStyle)
#If Win64 Then
Dim lRet As LongPtr
#Else
Dim lRet As Long
#End If
Dim varTaskID As Variant
Dim stRet As String
Dim stFile As String
If IsNull(strFile) Or strFile = "" Then Exit Function
stFile = strFile
'First try ShellExecute
lRet = apiShellExecute(hWndAccessApp, vbNullString, _
stFile, vbNullString, vbNullString, lShowHow)
If lRet > ERROR_SUCCESS Then
stRet = vbNullString
lRet = -1
Else
Select Case lRet
Case ERROR_NO_ASSOC:
'Try the OpenWith dialog
varTaskID = Shell("rundll32.exe shell32.dll,OpenAs_RunDLL " _
& stFile, WIN_NORMAL)
lRet = (varTaskID <> 0)
Case ERROR_OUT_OF_MEM:
stRet = "Error: Out of Memory/Resources. Couldn't Execute!"
Case ERROR_FILE_NOT_FOUND:
stRet = "Error: File not found. Couldn't Execute!"
Case ERROR_PATH_NOT_FOUND:
stRet = "Error: Path not found. Couldn't Execute!"
Case ERROR_BAD_FORMAT:
stRet = "Error: Bad File Format. Couldn't Execute!"
Case Else:
End Select
End If
ShellExecute = lRet & _
IIf(stRet = "", vbNullString, ", " & stRet)
End Function
I have tried the following:
removing the problem module (process completes successfully)
renaming the module
removing pro-compile conditional statements
moving extraction code to a new database
extracting from database with only problem module plus module with comments
repair MS Office
reinstall MS Office
downgrade Windows updates
Does anyone have any idea why this might be failing? Any suggestions on how to rectify would be really appreciated.

Downloading all files with the prefix from the website in VBA

I have a website where there are 100 links to csv files that are automatically downloaded after clicking. Each of the files has a prefix in the form aaa_.
The following standard code allows you to save a file based on the URL to the selected location on the disk:
Sub Download_from_website()
Dim myURL As String
myURL = "https://mysite/2500/csv/aaa_1.csv"
Dim WinHttpReq As Object
Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
WinHttpReq.Open "GET", myURL, False
WinHttpReq.Send
myURL = WinHttpReq.ResponseBody
If WinHttpReq.Status = 200 Then
Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write WinHttpReq.ResponseBody
oStream.SaveToFile ("C:\Users\tkp\Desktop\download_from_website\aaa_1.csv")
oStream.Close
End If
End Sub
How can you transform the above code so that you can automatically search the web page to find all links in which the string aaa_ appears and automatically save in the selected location? I will be very grateful for any tips.
The above code was a simplified example of what I would like to get. However, in fact I would like to save all the files that have the SEB_ prefix from the site
https://sebgroup.lu/private/luxembourg-based-funds/download-of-portfolio-holdings
This should manage them
Option Explicit
#If VBA7 And Win64 Then
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" ( _
ByVal pCaller As LongPtr, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As LongPtr, _
ByVal lpfnCB As LongPtr _
) As Long
#Else
Private Declare Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" ( _
ByVal pCaller As Long, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long _
) As Long
#End If
Public Const BINDF_GETNEWESTVERSION As Long = &H10
Public Sub GetLinks()
Dim sResponse As String, html As New HTMLDocument
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://sebgroup.lu/private/luxembourg-based-funds/download-of-portfolio-holdings", False
.send
sResponse = StrConv(.responseBody, vbUnicode)
End With
sResponse = Mid$(sResponse, InStr(1, sResponse, "<!DOCTYPE "))
With html
.body.innerHTML = sResponse
Dim list As Object, i As Long
Set list = html.getElementsByClassName("linklist")(0).getElementsByTagName("a")
For i = 0 To list.Length - 1
If instr(list(i).getAttribute("href"),"SEB_") > 0 Then
downloadfile list(i).getAttribute("href")
End If
Next i
End With
End Sub
Public Sub downloadfile(ByVal url As String)
Dim fileName As String, fileNames() As String, folderName As String
fileNames = Split(url, "/")
fileName = fileNames(UBound(fileNames))
folderName = "C:\Users\User\Desktop\CurrentDownloads\" & fileName '<==change as required
Dim ret As Long
ret = URLDownloadToFile(0, url, folderName, BINDF_GETNEWESTVERSION, 0)
End Sub
Give the following script a go. I suppose it will fix the issues you are having now. I've written this script considering the fact that you want all the csv files which have seb in their links.
Here you go:
Sub DownloadFilesFromWeb()
Const URL As String = "https://sebgroup.lu/private/luxembourg-based-funds/download-of-portfolio-holdings"
Dim Http As New WinHttp.WinHttpRequest, Html As New HTMLDocument, I&, tempArr As Variant
With Http
.Open "GET", URL, False
.send
Html.body.innerHTML = .responseText
End With
With Html.querySelectorAll(".linklist a[href*='seb']")
For I = 0 To .Length - 1
tempArr = Split(.item(I).getAttribute("href"), "/")
tempArr = tempArr(UBound(tempArr))
Http.Open "GET", .item(I).getAttribute("href"), False
Http.send
With CreateObject("ADODB.Stream")
.Open
.Type = 1
.write Http.responseBody
''notice the following line how the "tempArr" should be appended to the folder you have
.SaveToFile "C:\Users\WCS\Desktop\downloadfile\" & tempArr
.Close
End With
Next I
End With
End Sub
Reference to add to the library:
Microsoft HTML Object Library
Microsoft WinHTTP Services, version 5.1

Downloading a PDF

I am trying to download a .pdf file which opens up in an IE Web browser. Here is my code:
Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
(ByVal pCaller As Long, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long) As Long
Sub DownPDF()
Dim sUrl As String
Dim sPath As String
sPath = "C:\Users\adhil\Documents"
sUrl = "http://analytics.ncsu.edu/sesug/2010/HOW01.Waller.pdf"
Ret = URLDownloadToFile(0, sUrl, sPath, 0, 0)
If Ret = 0 Then
Debug.Print sUrl & " downloaded to " & sPath
Else
Debug.Print sUrl & " not downloaded"
End If
End Sub
However, I am unable to get the file downloaded although response says so.
Can anyone assist me?
The function expects the parameter szFileName to be a fullpath name of a file, not a folder.
Try with this:
sPath = "C:\Users\adhil\Documents\HOW01.Waller.pdf"
I worked with me, while it did not work when the destination file name was omitted.

VBA Macro to download multiple files from links in IE

I want to download multiple files from a list of links. The website where I find the links is protected. This is why I want to use IE (using the current session/cookie). The target of each link is a xml file. The files are too large to open and then save. So I need to save them directly (right-click, save target as).
The list of links looks like this:
<html>
<body>
<p> <a href="https://example.com/report?_hhhh=XML"Link A</a><br>> </p>
<p> <a href="https://example.com/report?_aaaa=XML"Link B</a><br>> </p>
...
</body>
</html>
I want to loop through all links and save each target. Currently I have problems with the "Save As". I don't really know how to do it. This is my code so far:
Sub DownloadAllLinks()
Dim IE As Object
Dim Document As Object
Dim List As Object
Dim Link As Object
' Before I logged in to the website
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.Navigate ("https:\\......\links.html")
Do While IE.Busy
DoEvents
Loop
' Detect all links on website
Set Document = IE.Document
Set List = Document.getElementsByTagName("a")
' Loop through all links to download them
For Each Link In List
' Now I need to automate "save target as" / right-click and then "save as"
...
Next Link
End Sub
Do you have any ideas to automate "Save As" for each link?
Any help is appreciated. Many thanks,
Uli
Private Declare PtrSafe Function Test Lib "urlmon" _
Alias "URLDownloadToFileA" (ByVal pCaller As Long, _
ByVal szURL As String, ByVal szFileName As String, _
ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
Sub AutoOpen()
Dim strFile As String
Dim strURL As String
Dim strPath As String
Dim ret As Long
Dim strFile1 As String
Dim strURL1 As String
Dim strPath1 As String
Dim ret1 As Long
Dim Shex As Object
Dim Test2 As String
strFile = "1st_file"
strURL = "first-url" & strFile
strPath = Environ("UserProfile") & "your-path" & strFile
ret = Test(0, strURL, strPath, 0, 0)
strFile1 = "something_else"
strURL1 = "your-url" & strFile1
strPath1 = Environ("UserProfile") & "your-path" & strFile1
re1t = Test(0, strURL1, strPath1, 0, 0)
If ret <> 0 Then MsgBox "Something went wrong!", vbInformation
End Sub
You can use this macro to download multiple files. To download even more just duplicate this part
Dim strFile As String
Dim strURL As String
Dim strPath As String
Dim ret As Long
and this part:
strFile = "1st_file"
strURL = "first-url" & strFile
strPath = Environ("UserProfile") & "your-path" & strFile
ret = Test(0, strURL, strPath, 0, 0)
Obviously just change the variables and then you are good to go.
Below is a quite common example I adapted for your case, it shows the usage of XHR and RegEx to retrieve webpage HTML content, extract all links from it, and download each link's target file:
Option Explicit
Sub Test()
' declare vars
Dim sUrl As String
Dim sReqProt As String
Dim sReqAddr As String
Dim sReqPath As String
Dim sContent As String
Dim oLinks As Object
Dim oMatch As Object
Dim sHref As String
Dim sHrefProt As String
Dim sHrefAddr As String
Dim sHrefPath As String
Dim sHrefFull As String
Dim n As Long
Dim aContent() As Byte
' set source URL
sUrl = "https:\\......\links.html"
' process source URL
SplitUrl sUrl, sReqProt, sReqAddr, sReqPath
If sReqProt = "" Then sReqProt = "http:"
sUrl = sReqProt & "//" & sReqAddr & "/" & sReqPath
' retrieve source page HTML content
With CreateObject("Microsoft.XMLHTTP")
.Open "GET", sUrl, False
.Send
sContent = .ResponseText
End With
' parse source page HTML content to extract all links
Set oLinks = CreateObject("Scripting.Dictionary")
With CreateObject("VBScript.RegExp")
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = "<a.*?href *= *(?:'|"")(.*?)(?:'|"").*?>"
For Each oMatch In .Execute(sContent)
sHref = oMatch.subMatches(0)
SplitUrl sHref, sHrefProt, sHrefAddr, sHrefPath
If sHrefProt = "" Then sHrefProt = sReqProt
If sHrefAddr = "" Then sHrefAddr = sReqAddr
sHrefFull = sHrefProt & "//" & sHrefAddr & "/" & sHrefPath
oLinks(oLinks.Count) = sHrefFull
Next
End With
' save each link target into file
For Each n In oLinks
sHref = oLinks(n)
With CreateObject("Microsoft.XMLHTTP")
.Open "GET", sHref, False
.Send
aContent = .ResponseBody
End With
With CreateObject("ADODB.Stream")
.Type = 1 ' adTypeBinary
.Open
.Write aContent
.SaveToFile "C:\Test\" & n & ".xml", 2 ' adSaveCreateOverWrite
.Close
End With
Next
End Sub
Sub SplitUrl(sUrl, sProt, sAddr, sPath)
' extract protocol, address and path from URL
Dim aSplit
aSplit = Split(sUrl, "//")
If UBound(aSplit) = 0 Then
sProt = ""
sAddr = sUrl
Else
sProt = aSplit(0)
sAddr = aSplit(1)
End If
aSplit = Split(sAddr, "/")
If UBound(aSplit) = 0 Then
sPath = sAddr
sAddr = ""
Else
sPath = Mid(sAddr, Len(aSplit(0)) + 2)
sAddr = aSplit(0)
End If
End Sub
This method doesn't employ IE automation. Usually the IE's cookies which Microsoft.XMLHTTP processes are sufficient to refer to the current session, so if your website doesn't use additional procedures for authentication and generation the list of the links then the method should work for you.

Download Files (PDF) from Web

I am fresh-starter at VBA.
How do I download PDF file using UrlDownloadToFile from http://cetatenie.just.ro/wp-content/uploads/?
Can anybody help with this? The code is searching the PDF files udner hyperlinks and matches them under some criteria, i.e. the current year under their name.
Function UrlDownloadToFile(lNum As Long, sUrl As String, sPath As String, _
lNum1 As Long, lNum2 As Long) As Long
UrlDownloadToFile = 0
End Function
Sub DownPDF()
' This macro downloads the pdf file from webpage
' Need to download MSXML2 and MSHTML parsers and install
Dim sUrl As String
Dim xHttp As MSXML2.XMLHTTP
Dim hDoc As MSHTML.HTMLDocument
Dim hAnchor As MSHTML.HTMLAnchorElement
Dim Ret As Long
Dim sPath As String
Dim i As Long
sPath = "C:\Documents and Settings\ee28118\Desktop\"
sUrl = "http://cetatenie.just.ro/wp-content/uploads/"
'Get the directory listing
Set xHttp = New MSXML2.XMLHTTP
xHttp.Open "GET", sUrl
xHttp.send
'Wait for the page to load
Do Until xHttp.readyState = 4
DoEvents
Loop
'Put the page in an HTML document
Set hDoc = New MSHTML.HTMLDocument
hDoc.body.innerHTML = xHttp.responseText
'Loop through the hyperlinks on the directory listing
For i = 0 To hDoc.getElementsByTagName("a").Length - 1
Set hAnchor = hDoc.getElementsByTagName("a").Item(i)
'test the pathname to see if it matches your pattern
If hAnchor.pathname Like "Ordin-*.2013.pdf" Then
Ret = UrlDownloadToFile(0, sUrl & hAnchor.pathname, sPath, 0, 0)
If Ret = 0 Then
Debug.Print sUrl & hAnchor.pathname & " downloaded to " & sPath
Else
Debug.Print sUrl & hAnchor.pathname & " not downloaded"
End If
End If
Next i
End Sub
Sorry - I should have guessed that URLDownloadToFile was an API call and could have answered the whole question at SQL "%" equivalent in VBA.
Remove the function named URLDownloadToFile completely. Paste this at the top of the module where your Sample procedure is
Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
(ByVal pCaller As Long, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long) As Long
Now change that one line in Sample to look like this
Ret = URLDownloadToFile(0, sUrl & hAnchor.pathname, sPath & hAnchor.pathname, 0, 0)
Then you should be good to go. If you want some different file name, then you'll have to code some logic to change it at each iteration.