Click here to Skip to main content
Click here to Skip to main content

Downloading files with VBScript

, 12 Dec 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How to download files with VBScript using COM (WinHTTP and MSXML) or WGET

Introduction

With this article, we pretend to show you how to download files in VBScript with COM access (WinHTTP, MSXML.XMLHTTP) and with command line (WGET). You can choose what option will be more useful to you, and this post is here to clarify it.

Background

Downloading files is something really basic today, because it is really hard to find someone that does not have internet access. You may want to do an automatic update for your program, or to make download of a file only if a user need a specific function from your code, decreasing download of you script.

Options available  

The COM technology allows another program access resources from another program. It is a Microsoft Technology and a really useful one. We can access Active Directory with it and add users and groups without opening the Management Console. We can download and install updates automatically with Windows Update API without accessing Microsoft Update site. We can write and save a document in Microsoft Word in command line.  

And the better of all: It does not matter the programming language that i am using. I can do that with PHP, VBScript, Javascript, C#... The programming language only needs to support COM access and running in a Windows environment.  

Some COM functions are bult-in with Microsoft Windows, another you need to download and install it. COM functions is better because, in general, we do not need to redistribute any 3rd party code because Windows already have it.      

  WinHTTP  MSXML.XMLHTTP  WGET
COM access   Yes  Yes  No 
Minimum Requirements  Windows 2000 (with SP3)   Windows 95 and Internet Explorer 4.0  Windows 95 and Internet Explorer 4.0 
Get header without download  No   No*  Yes 
Can show download progress in real-time     No*  No*  Yes** 
Check local file version and download new file only if necessary.     No  No   Yes   
HTTPS support.     Yes  No   Yes   
FTP support.     No  No   Yes   
Support authentication (User/Password).     Yes     Yes    Yes   

 
* It is available, but we can't access it from VBScript (We need to use .NET instead).  

** VBScript stops if we try assync command-line read with WGET. We need to show the command line prompt to show progress (It can't be viewed on the same window from script).    

 

I hope the table below helps you choose the method you will use to download files. 

WinHTTP and MSXML does not need you to redistribute anything with your code, but you lost some interesting functions from WGET for Win32 (it have much more functions than it! Read WGET documentation!). If you need to read the header from page or "Check local file version [...]" may be useful for you, choose it. WGET support this function natively, but we can implement it in .NET reading header and getting Content-Length and comparing it with Local file.   

If WGET is discarded, you can choose WinHTTP or MSXML.    

If you do not need to support Windows 9x, choose the 1st one. It is because MSXML, to download internet files in Windows Server versions of Windows, need manual change from Internet Explorer Security Zone, allowing local programs to access external resources (Security > Trusted Sites > Access data sources across domains). If we do not do that, our script will return "Access is denied." and will be closed.   

 

I really recommends WinHTTP, only if you do not care that your script will not run in Windows Server versions  without you ask for user to change Security Settings, making the server less secure.   

Using WGET 

WGET does not have COM access, but we can call command-line from our script and use it. 

strScriptFile = Wscript.ScriptFullName ' C:\download.vbs
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strScriptFile)
strFolder = objFSO.GetParentFolderName(objFile) ' C:

Set objShell = CreateObject("WScript.Shell")
 
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
' Use strFolder to save on the same location of this script.
strSaveTo = "C:\"
 
' WGet saves file always on the actual folder. So, change the actual folder for C:\, where we want to save file
objShell.CurrentDirectory = strSaveTo
 
' "C:\wget.exe" "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab" -N
objShell.Run Quotes(strFolder & "\wget.exe") & " " & Quotes(strLink) & " -N",1,True
' -N: Continue download only if the local version is outdated.

objShell.CurrentDirectory = strFolder
 
' Add Quotes to string
' http://stackoverflow.com/questions/2942554/vbscript-adding-quotes-to-a-string
Function Quotes(strQuotes)
	Quotes = chr(34) & strQuotes & chr(34)
End Function

In the first 4 lines, we get the folder that our script is saved (and wget.exe is located).

Then, we define two strings: strLink, that is the link that we will download, and strSaveTo, where file will be saved.  

After that, we change the actual folder to the folder where we want to save the file. WGet saves the file on the actual folder that we are using, than it will save the file there.  

We called WGet with objShell.Run  . It have 3 args:

1. Command to Execute.   

2. Window mode. Use 0 to hide, 1 to show (in another Window).  More options: http://ss64.com/vb/run.html

3. Wait for Finish. Use True to wait and only continue processing our script after finish download, False to continue processing script while download is made.   

We have sent args for WGet:

-N  Download file only if local version is outdated. 
  

With our code, it will open a 2nd window, showing download progress. 

But there is some cases that we can not open a second prompt. (Silent script). We have an alternative: Hide prompt and show progress on the same Window. There is a problem: We lose the real-time view of our download. We have to wait download finish to show output.    

We need to use the code below for this case. 

strScriptFile = Wscript.ScriptFullName ' C:\download.vbs
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strScriptFile) ' Get file information from our Script.
strFolder = objFSO.GetParentFolderName(objFile) ' Get only folder from our script (C:)

Set objShell = CreateObject("WScript.Shell") ' Expand variables from command prompt
systemroot = objShell.ExpandEnvironmentStrings("%systemroot%") 
 
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
strSaveTo = "C:\"
 
' Use strFolder to save on the same location of this script
' WGet saves file always on the actual folder. So, change the actual folder for C:\, where we want to save file
objShell.CurrentDirectory = strSaveTo
 
' "C:\wget.exe" "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab" -N -o C:\Windows\Temp\output.txt
objShell.Run Quotes(strFolder & "\wget.exe") & " " & Quotes(strLink) & " -N -o " & systemroot & "\temp\output.txt",0,True
' -N: Continue download only if the local version is outdated.

objShell.CurrentDirectory = strFolder
 
Set objFile = objFSO.OpenTextFile(systemroot & "\temp\output.txt")
			
Do
	line = objFile.ReadLine
	WScript.Echo line			
Loop While Not objFile.AtEndOfStream
 
objFile.Close
	
' http://stackoverflow.com/questions/2942554/vbscript-adding-quotes-to-a-string
Function Quotes(strQuotes)
	Quotes = chr(34) & strQuotes & chr(34)
End Function 

 

We will only comment what is new on this code.

We called objShell to expand %systemroot% variable from prompt. We will use it to call Command Line in the next lines.   

We called WGet with objShell.Run  with 2 args: 

-N  Download file only if local version is outdated. 

-o   Send all messages from WGet to %systemroot%\temp\output.txt  

We back to strFolder location, and used objFSO to read the file %systemroot%\temp\output.txt. It will read all lines of file until the end of file, and then exit from file.  

If you want to read Header, you need to replace objShell.Run with: 

objShell.Run Quotes(strFolder & "\wget.exe") & " --server-response --spider " & Quotes(strLink) & " -o " & systemroot & "\temp\output.txt",0,True

You need to replace the content inside Do Loop with: 

Do
	line = objFile.ReadLine
	WScript.Echo line
			
	If InStr(line,"Content-Length") Then
		strContentLength = Mid(line,19,Len(line))
		' WScript.Echo strContentLength
        End If
			
Loop While Not objFile.AtEndOfStream
 
 

 

Explaining: We use InStr function to check if a string have some text. If the actual line have "Content-Length", it will get the value.     

We use Mid to read the string from the character 19 to the final of the string, removing "Content-Length" and maintaining only the size. We can use it to compare the size of the actual file with the server, for example. (Use -N to let WGet make this automatically).  You can read another information from header data using this method, just use the right values on Mid.   

If you need to use Authentication, add --user=username --password='password' to WGet call on objShell.Run  

Using WinHTTP  

WinHTTP have COM access and is a native component from Windows 2000 with SP3 and above. It means that you do not need to redistribute anything with your code if you just want to download a small file. 

 strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
 	 ' Get file name from URL.
 	 ' http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab -> wsusscn2.cab
 	 strSaveName = Mid(strLink, InStrRev(strLink,"/") + 1, Len(strLink))
 	 strSaveTo = "C:\" & strSaveName
 	 
 	 WScript.Echo "HTTPDownload"
 	 WScript.Echo "-------------"
 	 WScript.Echo "Download: " & strLink
 	 WScript.Echo "Save to:  " & strSaveTo
	
     ' Create an HTTP object
     Set objHTTP = CreateObject( "WinHttp.WinHttpRequest.5.1" )
 
     ' Download the specified URL
     objHTTP.Open "GET", strLink, False
     ' Use HTTPREQUEST_SETCREDENTIALS_FOR_PROXY if user and password is for proxy, not for download the file.
     ' objHTTP.SetCredentials "User", "Password", HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
     objHTTP.Send
     
          Set objFSO = CreateObject("Scripting.FileSystemObject")
	  If objFSO.FileExists(strSaveTo) Then
	  	objFSO.DeleteFile(strSaveTo)
	  End If
 
      If objHTTP.Status = 200 Then
    	Dim objStream
	    Set objStream = CreateObject("ADODB.Stream")
	    With objStream
		    .Type = 1 'adTypeBinary
		    .Open
		    .Write objHTTP.ResponseBody
		    .SaveToFile strSaveTo
		    .Close
	    End With
	    set objStream = Nothing
	  End If
	  
	  If objFSO.FileExists(strSaveTo) Then
	  	WScript.Echo "Download `" & strSaveName & "` completed successfuly."
	  End If 

I don't think that there is much things to explain here. You need to inform Download URL, then we get the file name from URL (with Mid, it is necessary for ADODB call (.SaveToFile)), call WinHTTP, download file and save it with objStream.

Using MSXML.XMLHTTP  

MSXML has two major versions: 3.0 and 6.0.

Version 6.0 is the most recent, and is built-in in Windows Vista+ and Server 2008+. It is compatible with Windows XP and Server 2003, but requires manual installation in this OS. You can redistribute the executable and install it (Recommended). If you do not do that, it will use Version 3.0, that is built-in in IE6.   

Version 3.0 will be used for clients with Windows 2000 and 98 with IE6 installed.  

Version 2.x will be used for clients with Windows 95 or 98 with IE lower than 6 installed. 

Remember to warn Windows Server users that they need to change Internet Security Zone to allow scripts have internet access! (It decrease security if a malicious script be executed!)

strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
' Get file name from URL.
' http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab -> wsusscn2.cab
strSaveName = Mid(strLink, InStrRev(strLink,"/") + 1, Len(strLink))
strSaveTo = "C:\" & strSaveName

WScript.Echo "HTTPDownload"
WScript.Echo "-------------"
WScript.Echo "Download: " & strLink
WScript.Echo "Save to:  " & strSaveTo

' Create an HTTP object
Set objHTTP = CreateObject("MSXML2.XMLHTTP")

' Download the specified URL
'xmlhttp.Open "GET", strURL, false, "User", "Password"
objHTTP.open "GET", strLink, False
objHTTP.send
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(strSaveTo) Then
  objFSO.DeleteFile(strSaveTo)
End If

If objHTTP.Status = 200 Then
  Dim objStream
  Set objStream = CreateObject("ADODB.Stream")
  With objStream
    .Type = 1 'adTypeBinary
    .Open
    .Write objHTTP.responseBody
    .SaveToFile strSaveTo
    .Close
  End With
  set objStream = Nothing
End If

If objFSO.FileExists(strSaveTo) Then
  WScript.Echo "Download `" & strSaveName & "` completed successfuly."
End If

Points of Interest

With this alternatives, you can download a file using VBScript. I recommend you using WGet for large files, then the user won't think that the program stopped. If you can choose WinHTTP or MSXML, i recommend the first one, because it is more compatible (even without Windows 9x support, does not require attention in Windows Server edition).  

You can use X-HTTP, curl or Internet Explorer API to download files, but i didn't tested then. 

References

I do not own all knowledge of the world, and this article only was written because I want to help another people, like the people bellow does. I really recommend you read these references: 

VBScript Scripting Techniques: Download Files by Rob van der Woude 

HTTP-Download-vbs by Frank4DD 

History

12 Dec 2012 Tip published.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 12 Dec 2012
Article Copyright 2012 by Eduardo Mozart de Oliveira
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid