Garry,
See if this code makes sense. I had to pull it from my program
code in pieces, but I think it will be clear when you read it.
Note: I provide the option to accept gzip encoding. That
seems polite these days. I let the server know that in the header.
But that also means one has to be prepared to decode gzip.
Most of the error issues never happen in my use of winhttp,
but I wanted to write it to be as informative and flexible as
possible. So if the call fails to download a file I run the result
through my error parser and then may 1) show a message asking
how to proceed or 2) try the call again with lower security
restrictions.
Another note: XP can't handle TLS 1.1. Win7 can. If you
want to test OS version you can make that option more
flexible. But be careful. Vista/7 and later will lie about OS
version. If someone runs in XP mode you'll be told you're
running on XP. But that doesn't mean XP code will work
properly. I just settled for TLS 1.0 figuring that highest
security for Google maps is not critical.
At the end of the main function (which is in a UC) the
downloaded file is in a byte array and an event has been
raised to deal with it.
And without further ado, here's the code. Wordwrap is going
to be horrendous here. Sorry.....
'declares:
Private Const OptTLS1_0 As Long = &H80&
'-- these fail in XP -------------------
Private Const OptTLS1_1 As Long = &H200&
Private Const OptTLS1_2 As Long = &H800&
Private Const OptAnyTLS As Long = &HA80& 'OptTLS1_0 Or OptTLS1_1 Or
OptTLS1_2
Private Const OptErrIgnore As Long = &H3300&
Private Const OptErrDontIgnore As Long = &H0&
' code within function TLSGetImage to get a file.
' This doesn't need to be an image file. I just had
'reasons to deal with different files differently.
' I didn't include all of function here, but the gist
'of it should be clear.
If BooTLS = True Then
sFullURL = "
Loading Image..."
Else
sFullURL = "
Loading Image..."
End If
Set WHR = New WinHttpRequest
WHR.open "GET", sFullURL, False
TLSSetOptions True
Err.Clear
On Error Resume Next
WHR.Send
If Err.Number <> 0 Then
LRet = ProcessWNRError(Err.Number, ErrInt)
Select Case LRet
'-- return 0 if dealt with (and should quit)
'-- -1 to show winhttp error and quit,
'--1 for security options.
Case 0 'either didn't want to continue after cert error or already
showed error msg.
sErr = "Server error: " & CStr(ErrInt) & " - " &
Err.Description
Set WHR = Nothing
TLSGetImage = -1
GoTo FinishUp
Case -1
sErr = "Server error: " & CStr(ErrInt) & " - " &
Err.Description
MsgBox "Server error: " & CStr(ErrInt) & " - " &
Err.Description
Set WHR = Nothing
TLSGetImage = -1
GoTo FinishUp
Case Else ' 1
WHR.open "GET", sFullURL, False
TLSSetOptions False '--try again without security errors
blocking.
WHR.Send
End Select
End If
LServerCode = WHR.Status
sServerCode = CStr(LServerCode)
If LServerCode <> 200 Then
sErr = "Server error: " & sServerCode
If Err.Number <> 0 Then sErr = sErr & "Program error: " & Err.Number
& " - " & Err.Description
TLSGetImage = -2
Set WHR = Nothing
GoTo FinishUp
End If
sHeader1 = WHR.GetAllResponseHeaders
A1 = WHR.ResponseBody
Set WHR = Nothing
If InStr(sHeader1, "Content-Encoding") > 0 And InStr(sHeader1,
"gzip") > 0 Then
AFile = GUnzip2(A1, LRet)
If LRet <> 0 Then
sErr = "Unzip error: " & CStr(LRet)
TLSGetImage = -2
End If
Else
AFile = A1
End If
FinishUp:
RaiseEvent OnFinish(TLSGetImage, sErr)
'-function ends here----
Private Sub TLSSetOptions(AllowSecurityErrors As Boolean)
On Error Resume Next
With WHR
.SetRequestHeader "Accept-Encoding", "gzip"
.SetRequestHeader "Host", sDefServer '-- like www.somewhere.com
.SetRequestHeader "User-Agent", UAgent '-- like a browser UA, or allow
user to se it.
.Option(WinHttpRequestOption_UserAgentString) = UAgent 'useragent
If AllowSecurityErrors = False Then
.Option(WinHttpRequestOption_SslErrorIgnoreFlags) = OptErrIgnore
'ignore ssl errors
Else
.Option(WinHttpRequestOption_SslErrorIgnoreFlags) = OptErrDontIgnore
'don't ignore ssl errors
End If
.Option(WinHttpRequestOption_SecureProtocols) = OptTLS1_0 ' use TLS
1.0. 1.1 and 1.2 cause
error in XP.
.Option(WinHttpRequestOption_EnablePassportAuthentication) = False
'block passport auth
.Option(WinHttpRequestOption_EnableCertificateRevocationCheck) = False
' block check of
revoked certificate
End With
End Sub
'------------------------------------------------------
'error parser code:
'-- return 0 if dealt with (and should quit), -1 to show winhttp error and
quit, 1 for security options.
Public Function ProcessWNRError(LErr As Long, IntError As Integer) As Long
Dim i3 As Integer
Dim LRet As Long
On Error Resume Next
i3 = GetLowInt(LErr)
Select Case i3
Case 12029 'Returned if connection to the server failed.
MsgBox "Unable to connect. If your computer is otherwise connecting to
the Internet the problem
may be with a firewall or anti-virus program blocking GMap Kit.", 64, "GMap
Kit Network Error"
ProcessWNRError = 0
Case 12009 'A request to WinHttpQueryOption or WinHttpSetOption
specified an invalid option
value.
LRet = MsgBox("Error with WinHTTP options. If you are using TLS for
secure connection[ see
program settings] try not using it.", 64, "GMap Kit Network Error")
ProcessWNRError = 0
Case 12037 'cert outdated.
LRet = MsgBox("Certificate is expired. This could be a security risk.
Do you want to continue?",
36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case 12038 'Returned when a certificate CN name does not match the
passed value (equivalent to
a CERT_E_CN_NO_MATCH error).
LRet = MsgBox("Security certificate name does not match. This could
be a security risk. Do you
want to continue?", 36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case 12170 'Indicates that a certificate has been revoked (equivalent
to CRYPT_E_REVOKED).
LRet = MsgBox("Certificate has been revoked. This could be a security
risk. Do you want to
continue?", 36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case 12157 'Indicates that an error occurred having to do with a secure
channel (equivalent to
error codes that begin with "SEC_E_" and "SEC_I_" listed in the "winerror.h"
header file).
LRet = MsgBox("Error with WinHTTP options. If you are using TLS for
secure connection[ see
program settings] and this error continues, try not using it.", 64, "GMap
Kit Network Error")
ProcessWNRError = 0
Case 12175 'One or more errors were found in the Secure Sockets Layer
(SSL) certificate sent by
the server. To determine what type of error was encountered, check for a
WINHTTP_CALLBACK_STATUS_SECURE_FAILURE notification in a status callback
function. For more
information, see WINHTTP_STATUS_CALLBACK.
LRet = MsgBox("Errors in certificate sent by server. This could be a
security risk. Do you want to
continue?", 36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case 12045 'Indicates that a certificate chain was processed, but
terminated in a root certificate
that is not trusted by the trust provider (equivalent to
CERT_E_UNTRUSTEDROOT).
LRet = MsgBox("Certificate sent by server is not trusted. This could
be a security risk. Do you
want to continue?", 36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case 12169 'Indicates that a certificate is invalid (equivalent to
errors such as CERT_E_ROLE,
CERT_E_PATHLENCONST, CERT_E_CRITICAL, CERT_E_PURPOSE, CERT_E_ISSUERCHAINING,
CERT_E_MALFORMED and CERT_E_CHAINING).
LRet = MsgBox("Certificate sent by server is invalid. This could be a
security risk. Do you want to
continue?", 36, "GMap Kit Network Error")
If LRet = 6 Then ProcessWNRError = 1 Else ProcessWNRError = 0
Case Else
ProcessWNRError = -1 'show basic error info and quit.
End Select
End Function
Public Function GetLowInt(LongIn As Long) As Integer
On Error Resume Next
If (LongIn And &HFFFF&) > &H7FFF Then
GetLowInt = (LongIn And &HFFFF&) - &H10000
Else
GetLowInt = LongIn And &HFFFF&
End If
End Function
'------------- gzip code:
Public Type zStream
next_in As Long
avail_in As Long
total_in As Long
next_out As Long
avail_out As Long
total_out As Long
MSG As Long
state As Long
zalloc As Long
zfree As Long
opaque As Long
data_type As Long
adler As Long
reserved As Long
End Type
Public Type GZHead ' 10 bytes
ID1 As Byte '31
ID2 As Byte '139
CM As Byte '8 compression method. (8 is only option!)
FLG As Byte ' flags.
MTime As Long ' modified date. set to 0 on write. ignore on read.
XFL As Byte '0
OS As Byte '0
End Type
Private Const Z_FINISH As Long = 4
Private Const Z_STREAM_END = 1
Private Declare Function inflateInit2 Lib "zlibw125.dll" Alias
"inflateInit2_" (strm As zStream, ByVal
windowBits As Long, ByVal ZLibVersion As String, ByVal zStream_size As Long)
As Long
Private Declare Function inflate Lib "zlibw125.dll" (vStream As zStream,
ByVal vflush As Long) As
Long
Private Declare Function inflateEnd Lib "zlibw125.dll" (vStream As zStream)
As Long
Public Function GUnzip2(AIn() As Byte, LErr As Long) As Byte()
Dim A2() As Byte
Dim LRet As Long, LenDData As Long, LenCData As Long
Dim ZS As zStream
On Error Resume Next
LenCData = UBound(AIn) + 1
CopyMemory LenDData, AIn(UBound(AIn) - 3), 4 '--get total size.
ReDim A2(LenDData - 1) As Byte
With ZS
.zalloc = 0
.zfree = 0
.opaque = 0
.avail_in = LenCData
.avail_out = LenDData
.next_in = VarPtr(AIn(0))
.next_out = VarPtr(A2(0))
End With
LRet = inflateInit2(ZS, 47, "1.2.5.0", Len(ZS))
If LRet <> 0 Then
LErr = LRet 'negative number errors.
Exit Function
End If
LRet = inflate(ZS, Z_FINISH)
If LRet = 1 Then LRet = 0 '-- 0 is OK. 1 is returned when the whole
thing has been
decompressed.
LErr = LRet
LRet = inflateEnd(ZS)
If (LErr <> 0) Then Exit Function
'-- make sure transfer of array is last call to avoid duplication of
array.
GUnzip2 = A2
End Function