Is XMLHTTP's responsebody working asynchronously in VB6? - vb6

We have a SSRS integrated sharepoint server. I am trying to get a report exported as PDF from it.
(I had to do like this, because my url is too long. I would rather use Shell command to do like this, although this looks like a better solution.)
Here'is my code:
Dim request as New XMLHTTP
Dim oStream as New Stream
Dim aBunchOfMiliseconds as Long
Dim fileLocation as String
Dim reportUrl as String
aBunchOfMiliseconds = 500
reportUrl = "http://www.mySharepointDomain.com/_vti_bin/ReportServer?http://www.mySharepointDomain.com/sites/AFolderHere/OOAnotherFolder/BlaBlaReportFolder/FolderFolder/AhTheLastOne/AtLeastMyReportFile.rdl&rs:Command=Render&rc:Toolbar=true&rs:Format=PDF&Parameter1=ABC &Parameter2=1&Parameter3=1&TheLastParameter=IllBeDamned&rs%3aParameterLanguage=ln-LN"
request.Open "GET", reportUrl
request.setRequestHeader "WWW-Authenticate", "NTLM" 'For impersonation
request.Send 'Go get it Bruce
If request.Status = 200 Then 'Are you successful?
oStream.Type = adTypeBinary
oStream.Open
oStream.Write request.responseBody 'Here's the problematic part
Sleep(aBunchOfMiliseconds) 'If I don't do this, file will be corrupted
oStream.SaveToFile fileLocation, adSaveCreateOverWrite
oStream.Close
End If
Set oStream = Nothing
Set request = Nothing
If I comment out the "Sleep" line, I will have a corrupted file that can not open. This code works fine but I found it ridiculus to use "Sleep" there.
Is there any way me to understand that the data copy operation is completed?

Oh I get it.
I've missed to say that "this is not an async call".
request.Open "GET", reportUrl, False

Related

MSXML2.ServerXMLHTTP and national characters

This question is related to this one: Character encoding Microsoft.XmlHttp in Vbscript, but differs in one thing, the national characters are in the domain name, not only arguments.
The task is: download a page from the given URL.
I already solved problem of passing UTF8 string into VBScript by reading it from UTF8 encoded file through ADO.
But now when I try opening it MSXML2.ServerXMLHTTP returns error: The URL is invalid.
Here is VBScript code:
Set objStream = CreateObject("ADODB.Stream")
objStream.CharSet = "utf-8"
objStream.Open
objStream.LoadFromFile("fileWithURL.txt")
url = objStream.ReadText()
objStream.Close
Set XMLHttpReq = CreateObject("MSXML2.ServerXMLHTTP")
XMLHttpReq.Open "GET", url, False
XMLHttpReq.send
WEBPAGE = XMLHttpReq.responseText
If you put something like hxxp://россия.рф/main/page5.html into the UTF8 encoded fileWithURL.txt the script will raise an error while working ok with hxxp://google.com.
The workaround is to use ascii representation of the domain name - but I yet haven't found PunnyCode encoder for vbscript (apart from Chillkat which is an overkill for my task).
Will appreciate your help on the main problem or workaround.
I've made an amazing journey in to depth of my hard drive and found a code writen by / for Jesper Høy. This was the source code of SimpleDNS Plus' IDN Conversion Tool at that time.
Archive.org page snapshot: http://www.simpledns.com/idn-convert.asp
Archive.org file snapshot: idn-convert-asp.zip
You can also copy the whole code from this gist.
Create a function to convert URLs.
Function DummyPuny(ByVal url)
Dim rSegments : rSegments = Split(url, "/")
If UBound(rSegments) > 1 Then
rSegments(2) = DomainPunyEncode(rSegments(2))
End If
DummyPuny = Join(rSegments, "/")
End Function
Then convert your url before making the request.
XMLHttpReq.Open "GET", DummyPuny(url), False

easiest way to download a file

There are many examples on the Internet and the simplest/easiest for a beginner seems to be:
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", "http://bla.com/ea_csv_160126.csv", False
xHttp.Send
with bStrm
.type = 1 '//binary
.open
.write xHttp.responseBody
.savetofile "c:\myFolder\ea_csv_160126.csv", 2 '//overwrite
end with
This returns:
Write to file failed code: 800A0BBC source ADODB.Stream
I would like to debug this one, there are people complaining about this error but no fix.
Also, If possible, I would like to define a variable in order to hold the value for the date. So the script should be able to download the file from the website adapting to the changed value in the URL 160126 will be something else.
Note: on the same website, there is a file which has almost the same name ea_csv_update_160126.csv, the one with update shouldn't be taken.
EDIT:
If you know a simpler or equally easy solution which works, please post it.

Errors Loading a Network File into a Stream in Classic ASP

I have a Classic ASP website that currently loads PDF files from a local directory and writes them to the Response. I create a stream and use LoadFromFile to pull the data. This has worked well for years, but we now want to delete the local files and pull from a network "\" drive, where all of our .NET sites pull the files from.
We do this in our .NET sites with no problem, but I cannot figure out how to open a network file from Classic ASP and load it into a stream. Nothing I have tried seems to recognize the "\server\directory\file" as a valid path.
So, just to be as clear as possible, this is what we do:
Pull a DocID from the QueryString.
Pass the DocID as a parameter to a SQL proc to pull the path to the file (In the current ASP page it pulls a local path, in .NET it pulls a network path. In our new ASP page it will now pull a network path).
We create a stream and load the file into it (this is the part that does not work in Classic ASP if the path is "\" instead of "D:\").
We change the Response.ContentType to "application/pdf".
We Response.BinaryWrite and Flush the Response.
The result is the page displays the PDF with no indication of the file name or location.
Adding partial code:
' sets the Cache-Control HTTP header to prevent proxy caching
Response.CacheControl = "Private"
Response.Expires = 0
Response.Buffer = True
Response.Clear
sDocID = Request.QueryString("DocID")
sSQL = "exec Get_DocData '" & sDocID & "'"
Set rsDoc = Server.CreateObject("ADODB.RecordSet")
rsDoc.Open sSQL, conn, 3, 1
'
' sPath = \\server\directory\filename.ext
' sFileName = filename.ext
'
sPath = rsDoc.Fields("DocPath").Value
sFileName = rDoc.Fields("DocDescription").Value
rsDoc.Close
Set rsDoc = Nothing
'sPath = Replace(sPath,"\\<svr>\<dir>","Z:")
If sPath <> "" Then
Set oStream = Server.CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1 ' adTypeBinary
oStream.LoadFromFile sPath
Response.ContentType = "application/pdf"
Response.AddHeader "Content-Disposition", "inline; filename=""" & sFileName & """"
Response.Charset = "UTF-8"
Do while not oStream.EOS
Response.BinaryWrite oStream.read(3670016)
Response.Flush
Loop
oStream.Close
Set oStream = Nothing
Else
Response.ContentType = "text/html"
'Response.Write("Invalid Document ID.")
End If
Response.End
Well, I knew I would feel stupid when I figured this out. As Lankymart commented, the LoadFromFile() does support UNC formatted paths, which lead me to suspect the issue was permission-based and not code-based.
Although our ApplicationPool was running under a general account which had permission to the network drives, the site was not. I simply brought up the site in IIS7, clicked on Basic Settings and used the "Connect as..." button to run the site under the same general account. That corrected my issue. Palm-slapping my head.

Download and save a webpage using vbscript

I am trying to give users the option to download and save a webpage to where ever they want. I have been looking all over for a solution but nothing seems to be working. I am using vbscript in classic asp.
This is what I tried last
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", "" &Session("ServerURL") & "/idautomation/IDAutomationStreamingLinear.aspx?D=MAPS$"&request.QueryString("catcode")&"%25"&request.QueryString("typecode")&"&X=0.09&BH=3&S=0&CC=T&LM=5&TM=7.5&ST=F", False
xHttp.Send
with bStrm
.type = 1 '//binary
.open
.write xHttp.responseBody
.savetofile "d:\DownloladPdf.pdf", 2 '//overwrite
end with
but its throwing a "Write to file failed. " on the .savetofile line.
I want the user to be able to chose where to save it to...
You can't save to the users computer from server side code VBscript directly as this would be a major security issue allowing drive by compromises of people browsing a web page. The reason possibly that it is throwing an error is that it is trying to save it server side, but there is no D: on the server.
Instead you want to serve the PDF to the browser using Response and let the browser display or save the PDF.
The below example is using a bytes array rather than a stream, but it should be similar. If you want to force it to download rather than show in the browser, change the content-disposition to attachment. You can also change the filename to whatever you like.
if Len(pdfBytes) > 0 then
Response.Clear()
Response.ContentType = "application/pdf"
Response.Charset = ""
Response.AddHeader "Cache-Control", "public, max-age=1" ' Setting Cache-Control to max-age=1 rather than no-cache due to IE8 bug
Response.AddHeader "content-disposition","inline; filename=filename.pdf"
Response.Buffer = True
Response.Expires = 0
Response.BinaryWrite(pdfBytes)
Response.Flush
Response.End
Response.Close
end if

Handle Events from WinHttpRequest

A program I use runs .VBS scripts
So, in VBScript how can you handle the OnResponseFinished event for a WinHttpRequest object?
Set oHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")
oHTTP.Open "GET", "http://www.google.com", True
oHTTP.Send
I was trying to get some code executed when the winhttp response comes (using VBScript inside HTA file). You may consider putting your event code right after the send. Using the following code, the user interface does not hang when waiting for the response:
Set objHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")
objHTTP.Open "GET", "http://www.google.com", True
objHTTP.Send
objHTTP.WaitForResponse 'pauses execution, but does not hang UI
'from now on, execution only takes effect after completion of the response:
msgbox objHTTP.responseText 'an example of what can be done with the response
This seems to be the same as synchronous winhttp for script files, which can be what you are looking for. So, the only difference may be noticed when using an user interface.
Change the third paramater in the call to the Open method to false. Then place the code you would have in OnResponseFinished after the call to send.
Use WScript's CreateObject not the built in one for event handler.
Set oHTTP = WScript.CreateObject(
"WinHttp.WinHttpRequest.5.1",
"oHTTP_"
)
I'll admit his isn't a great answer, but the usual way to register VBScript events is to use the GetRef function to get a reference to the event handler, eg with an MSXML2.XMLHTTP object:
Set oHTTP = CreateObject("MSXML2.XMLHTTP")
oHTTP.Open "GET", "http://www.google.com", True
oHTTP.OnReadyStateChange = GetRef("oHTTP_OnReadyStateChange")
Sub oHTTP_OnReadyStateChange
' do something
End sub
oHTTP.Send
The trouble is, I tried it for your code, ie
Set oHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")
oHTTP.Open "GET", "http://www.google.com", True
oHTTP.OnResponseFinished = GetRef("oHTTP_OnResponseFinished")
Sub oHTTP_OnResponseFinished
' do something
End sub
oHTTP.Send
and it didn't work, getting the error
Object doesn't support this property or method: 'oHTTP.OnResponseFinished'
but perhaps this can give you a starting point , or maybe you can use the MSXML2 library instead?
Just updating this answer with the other way of handling COM events - use the second parameter for the CreateObject function which allows you to specify the function prefix which connects functions to objects, eg
Set oHTTP = CreateObject("WinHttp.WinHttpRequest.5.1", "oHTTP_")
oHTTP.Open "GET", "http://www.google.com", True
Sub oHTTP_OnResponseFinished
' do something
End sub
oHTTP.Send
unfortunately, this doesn't work either - it must be that the IWinHttpRequestEvents interface is inaccessible
I checked the Windows Registry and there appears to be a number of Microsoft objects that do nearly the same thing:
Microsoft.XMLHTTP {ED8C108E-4349-11D2-91A4-00C04F7969E8}
MSXML2.XMLHTTP {F6D90F16-9C73-11D3-B32E-00C04F990BB4}
WinHttp.WinHttpRequest.5.1 {2087c2f4-2cef-4953-a8ab-66779b670495}
MSXML2.ServerXMLHTTP {AFBA6B42-5692-48EA-8141-DC517DCF0EF1}
What works for me is Microsoft.ServerXMLHTTP which allows setting of the onreadystatechange in VBScript. The "MSXML2.ServerXMLHTTP" handles redirecting websites (e.g. google.com) which makes it a better choice over "Microsoft.XMLHTTP".
Dim xmlhttp ' global so can be accessed in OnStateChange
Sub OnStateChange
If xmlhttp.readystate = 4 Then
' React to xmlhttp.responseText
MsgBox xmlhttp.responseText
End If
End Sub
Set xmlhttp = CreateObject("Microsoft.XMLHTTP")
xmlhttp.open "GET", "http://www.google.com/", true
xmlhttp.onreadystatechange = GetRef("OnStateChange")
xmlhttp.send
' do something else whilst xmlhttp is running in the background
MsgBox "Pausing so that OnStateChange can fire!"
It does need seem to be possible according to this (go to remarks) you can only access the error state with Err. Microsoft's documentation is lousy.
I found that I can get this to work Asynchronously by using the 'waitForResponse' with parameter '0' for the timeout method as a flag.
IE:
oHTTP.Open "GET", "http://www.google.com", True
oHTTP.Send
Do While oHTTP.waitForResponse(0) = False
'do stuff while waiting for it to be done
WScript.Sleep 200 'sleep for 0.2 seconds between checks as not waste CPU
DoEvents
Loop
'Once the loop is exited, the response is finished
MsgBox oHTTP.ResponseText

Resources