Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

WinHttpGetIEProxyConfigForCurrentUser and VBA

7,124 views
Skip to first unread message

George Kontoravdis

unread,
Oct 11, 2004, 12:57:35 PM10/11/04
to

I'm trying to get WinHttpGetIEProxyConfigForCurrentUser to work in an
Excel-VBA macro and Excel keeps crashing. I'm guessing that I haven't
mapped correctly to VB types. The code I use is below. Any pointers
would be appreciated.

Thanks,

George

' My app proxy information type
Private Type proxyInfo
active As Boolean
proxy As String
proxyBypass As String
End Type

' Structure to receive IE proxy settings
Private Type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
fAutoDetect As Long
lpszAutoConfigUrl As String
lpszProxy As String
lpszProxyBypass As String
End Type

' WinHttpGetIEProxyConfigForCurrentUser declaration
Private Declare Function WinHttpGetIEProxyConfigForCurrentUser Lib "WinHTTP.dll"
(ByRef proxyConfig As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) As Long

'
' Retrieves proxy information
'
' @param proxyInfo structure to receive proxy information. If the function fails
' then this parameter is populated with vbNullString values.
'
' @return 0 if successful, or appropriate dll error code if it fails.
'
' @todo currently it relies on WinHttpGetIEProxyConfigForCurrentUser which
' may not work in a server environment.
'
Private Function getProxyInfo_WinHTTPversion(ByRef proxyInfo As proxyInfo) As
Integer
Dim proxyBuf As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
Dim error As Integer
Dim errorDesc As String

proxyBuf.fAutoDetect = 0
proxyBuf.lpszAutoConfigUrl = ""
proxyBuf.lpszProxy = ""
proxyBuf.lpszProxyBypass = ""

If (WinHttpGetIEProxyConfigForCurrentUser(proxyBuf) > 0) Then
error = 0
proxyInfo.proxy = proxyBuf.lpszProxy
proxyInfo.proxyBypass = proxyBuf.lpszProxyBypass
Else
error = Err.LastDllError
proxyInfo.proxy = vbNullString
proxyInfo.proxyBypass = vbNullString
End If

getProxyInfo = error
End Function

Stephen Sulzer

unread,
Oct 12, 2004, 2:09:16 AM10/12/04
to
Hi George,

You are running into an interoperability mismatch between Visual Basic and
the Win32 API involving UNICODE strings. The lpszAutoConfigUrl, lpszProxy
and lpszProxyBypass fields of the WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
structure cannot be declared as String in VB.

Strings in VB are UNICODE BSTRs. When a VB String is passed to a Win32 API
function, VB converts the String to ANSI. And when a string is returned from
a Win32 API function, VB assumes it is ANSI and converts it to a UNICODE
BSTR. VB assumes it is calling the ANSI version of the Win32 API function,
but this assumption breaks with WinHTTP which is a UNICODE-only API. The
strings that WinHttpGetIEProxyConfigForCurrentUser writes into the
CURRENT_USER_IE_PROXY_CONFIG struct are UNICODE strings, but they are not
BSTR's. The crash you see is likely occurring when VB botches the conversion
of the strings from WinHTTP, incorrectly assuming they are ANSI strings.

So instead your VB code will need to manually convert the UNICODE strings
from WinHTTP to UNICODE BSTRs for VB, using 'Long' variables to handle
string pointers. To accomplish this, we need the undocumented VB VarPtr
function, as well as the CopyMemory Win32 API and SysAllocString COM API. It
should go without saying that managing string pointers in this "raw" fashion
can easily lead to crashes and memory leaks if not done correctly.

Below, I have rewritten your WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
declaration and getProxyInfo_WinHTTPversion function to properly handle the
string conversions. I did not change your proxyInfo structure. I tested this
code using VBA6 with Word 2000.

[Notes:
1. This case of converting a UNICODE string from a Win32 API to a BSTR
string that VB can use is harder than going the other way: converting a VB
BSTR string to a UNICODE string for a Win32 API function is easier, for
details see this post:
http://groups.google.com/groups?hl=en&lr=&c2coff=1&selm=%23ZFxE5z%23CHA.1612%40TK2MSFTNGP11.phx.gbl

2. The string conversion problem can be completely avoided if a typelibrary
is available that describes the Win32 API. The typelib would let VB know
that the string parameters are indeed UNICODE strings, so VB would avoid the
conversion to ANSI. However, the typelibrary in WinHTTP.dll describes only
the WinHttpRequest COM component, it does not include declarations for the
WinHTTP Win32 API.

3. Finally, please note that your getProxyInfo_WinHTTPversion function does
not handle the case where IE is configured to automatically detect the proxy
configuration via a proxy auto-configuration script. If IE is configured to
use "autoproxy", your getProxyInfo function will not find the name of the
proxy server. Instead you will need to use WinHTTP's WinHttpGetProxyForUrl
API.]

Hope that helps.

Stephen

-------------------

' Structure to receive IE proxy settings
Private Type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
fAutoDetect As Long

lpszAutoConfigUrl As Long ' declare as Long to avoid VB string
conversion
lpszProxy As Long
lpszProxyBypass As Long
End Type

' Need CopyMemory to copy BSTR pointers around
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (ByVal lpDest As Long, _
ByVal lpSource As Long, ByVal cbCopy As Long)

' SysAllocString creates a UNICODE BSTR string based on a UNICODE string
Private Declare Function SysAllocString Lib "oleaut32" (ByVal pwsz As Long)
As Long

' Need GlobalFree to free the pointers in the CURRENT_USER_IE_PROXY_CONFIG
' structure returned from WinHttpGetIEProxyConfigForCurrentUser, per the
documentation
Private Declare Function GlobalFree Lib "kernel32" (ByVal p As Long) As Long

' WinHttpGetIEProxyConfigForCurrentUser declaration
Private Declare Function WinHttpGetIEProxyConfigForCurrentUser Lib

"WinHTTP.dll" _


(ByRef proxyConfig As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) As Long

'
' Retrieves proxy information
'
' @param proxyInfo structure to receive proxy information. If the function
fails
' then this parameter is populated with vbNullString values.
'
' @return 0 if successful, or appropriate dll error code if it fails.
'
' @todo currently it relies on WinHttpGetIEProxyConfigForCurrentUser which
' may not work in a server environment.
'
Private Function getProxyInfo_WinHTTPversion(ByRef proxyInfo As proxyInfo)
As Integer

Dim proxyBuf As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
Dim error As Integer
Dim errorDesc As String

Dim ptr As Long

proxyBuf.fAutoDetect = 0
proxyBuf.lpszAutoConfigUrl = 0
proxyBuf.lpszProxy = 0
proxyBuf.lpszProxyBypass = 0

If (WinHttpGetIEProxyConfigForCurrentUser(proxyBuf) > 0) Then
error = 0

If (proxyBuf.lpszProxy <> 0) Then
' Create a BSTR based on the string from WinHttp
' This does not handle the case if SysAllocString fails and
returns 0
ptr = SysAllocString(proxyBuf.lpszProxy)

' Transfer the 32-bit BSTR pointer into the proxyInfo struct
' All this does is subverts the VB type system to do this (in
C-syntax):
' proxyInfo.proxy = (BSTR) ptr;
CopyMemory VarPtr(proxyInfo.proxy), VarPtr(ptr), 4

' Free the string from WinHttp
GlobalFree (proxyBuf.lpszProxy)
Else
proxyInfo.proxy = vbNullString
End If

If (proxyBuf.lpszProxyBypass <> 0) Then
ptr = SysAllocString(proxyBuf.lpszProxyBypass)
CopyMemory VarPtr(proxyInfo.proxyBypass), VarPtr(ptr), 4
GlobalFree (proxyBuf.lpszProxyBypass)
Else


proxyInfo.proxyBypass = vbNullString
End If

' don't forget to free the lpszAutoConfigUrl string from WinHttp
If (proxyBuf.lpszAutoConfigUrl <> 0) Then
GlobalFree (proxyBuf.lpszAutoConfigUrl)
End If

nelis

unread,
Oct 31, 2004, 8:38:59 AM10/31/04
to
"Stephen Sulzer" <sasulzer_at_seanet.com> wrote in message news:<10mmt8h...@corp.supernews.com>...

Hi Stephen,

> Private Declare Function WinHttpGetIEProxyConfigForCurrentUser Lib
> "WinHTTP.dll" _
> (ByRef proxyConfig As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) As Long

is that possible to write similar declaration for function:
WinHttpGetProxyForUrl and use it from VB6? (it contains types
unavailable in VB6)

I tried your example with WinHttpGetIEProxyConfigForCurrentUser and it
works great in my VB6. that's just what I need. now I would like to be
able to discover proxy (if exists) using WPAD service - AFAIK to do
that - I need to use WinHttpGetProxyForUrl.

sorry for my ignorance - I have never developed anything in VB that
would reffer to external dll's this way.

any advice would be appreciated,
best regards,
rafal urbanelis

Stephen Sulzer

unread,
Nov 1, 2004, 11:06:41 PM11/1/04
to

Please see the code below: I have written up a comprehensive
GetProxyInfoForUrl function in Visual Basic that uses both
WinHttpGetIEProxyConfigForCurrentUser and WinHttpGetProxyForUrl.

GetProxyInfoForUrl takes a URL string parameter and returns a ProxyInfo
structure with a (hopefully) suitable proxy configuration to use. The
function first consults the IE proxy configuration, and then decides whether
or not it needs to call WinHttpGetProxyForUrl. Hopefully this VB function
will work for you.

Note that WinHttpGetProxyForUrl can be quite slow, taking several seconds to
attempt auto-detection. If you will be calling GetProxyInfoForUrl multiple
times during the application, the inline comments in the VB function suggest
a way to speed up the process.

Stephen

-----------------------------

Option Explicit

' My app proxy information type

Private Type ProxyInfo


active As Boolean
proxy As String
proxyBypass As String
End Type

' Structure to receive IE proxy settings


Private Type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
fAutoDetect As Long
lpszAutoConfigUrl As Long

lpszProxy As Long
lpszProxyBypass As Long
End Type

' Need CopyMemory to copy BSTR pointers around
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (ByVal lpDest As Long, _
ByVal lpSource As Long, ByVal cbCopy As Long)

' SysAllocString creates a UNICODE BSTR string based on a UNICODE string
Private Declare Function SysAllocString Lib "oleaut32" (ByVal pwsz As Long)

_
As Long

' Need GlobalFree to free the pointers in the CURRENT_USER_IE_PROXY_CONFIG
' structure returned from WinHttpGetIEProxyConfigForCurrentUser,
' per the documentation
Private Declare Function GlobalFree Lib "kernel32" (ByVal p As Long) As Long

' WinHttpGetIEProxyConfigForCurrentUser declaration
Private Declare Function WinHttpGetIEProxyConfigForCurrentUser _


Lib "WinHTTP.dll" _
(ByRef proxyConfig As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) As Long

Private Type WINHTTP_AUTOPROXY_OPTIONS
dwFlags As Long
dwAutoDetectFlags As Long
lpszAutoConfigUrl As Long
lpvReserved As Long
dwReserved As Long
fAutoLogonIfChallenged As Long
End Type

Private Type WINHTTP_PROXY_INFO
dwAccessType As Long


lpszProxy As Long
lpszProxyBypass As Long
End Type

' Constants for dwFlags of WINHTTP_AUTOPROXY_OPTIONS
Const WINHTTP_AUTOPROXY_AUTO_DETECT = 1
Const WINHTTP_AUTOPROXY_CONFIG_URL = 2

' Constants for dwAutoDetectFlags
Const WINHTTP_AUTO_DETECT_TYPE_DHCP = 1
Const WINHTTP_AUTO_DETECT_TYPE_DNS = 2

Private Declare Function WinHttpGetProxyForUrl Lib "WinHTTP.dll" _
(ByVal hSession As Long, _
ByVal pszUrl As Long, _
ByRef pAutoProxyOptions As WINHTTP_AUTOPROXY_OPTIONS, _
ByRef pProxyInfo As WINHTTP_PROXY_INFO) As Long

Private Declare Function WinHttpOpen Lib "WinHTTP.dll" _
(ByVal pszUserAgent As Long, _
ByVal dwAccessType As Long, _
ByVal pszProxyName As Long, _
ByVal pszProxyBypass As Long, _
ByVal dwFlags As Long) As Long

Private Declare Function WinHttpCloseHandle Lib "WinHTTP.dll" _
(ByVal hInternet As Long) As Long


Private Function GetProxyInfoForUrl(Url As String) As ProxyInfo

Dim IEProxyConfig As WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
Dim AutoProxyOptions As WINHTTP_AUTOPROXY_OPTIONS
Dim WinHttpProxyInfo As WINHTTP_PROXY_INFO
Dim ProxyInfo As ProxyInfo
Dim fDoAutoProxy As Boolean
Dim ProxyStringPtr As Long
Dim ptr As Long
Dim error As Long

AutoProxyOptions.dwFlags = 0
AutoProxyOptions.dwAutoDetectFlags = 0
AutoProxyOptions.lpszAutoConfigUrl = 0
AutoProxyOptions.dwReserved = 0
AutoProxyOptions.lpvReserved = 0
AutoProxyOptions.fAutoLogonIfChallenged = 1

IEProxyConfig.fAutoDetect = 0
IEProxyConfig.lpszAutoConfigUrl = 0
IEProxyConfig.lpszProxy = 0
IEProxyConfig.lpszProxyBypass = 0

WinHttpProxyInfo.dwAccessType = 0
WinHttpProxyInfo.lpszProxy = 0
WinHttpProxyInfo.lpszProxyBypass = 0

ProxyInfo.active = False
ProxyInfo.proxy = vbNullString
ProxyInfo.proxyBypass = vbNullString

fDoAutoProxy = False
ProxyStringPtr = 0
ptr = 0

' Check IE's proxy configuration
If (WinHttpGetIEProxyConfigForCurrentUser(IEProxyConfig) > 0) Then
' If IE is configured to auto-detect, then we will too.
If (IEProxyConfig.fAutoDetect <> 0) Then
AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT
AutoProxyOptions.dwAutoDetectFlags = _
WINHTTP_AUTO_DETECT_TYPE_DHCP + _
WINHTTP_AUTO_DETECT_TYPE_DNS
fDoAutoProxy = True
End If

' If IE is configured to use an auto-config script, then
' we will use it too
If (IEProxyConfig.lpszAutoConfigUrl <> 0) Then
AutoProxyOptions.dwFlags = AutoProxyOptions.dwFlags + _
WINHTTP_AUTOPROXY_CONFIG_URL
AutoProxyOptions.lpszAutoConfigUrl =
IEProxyConfig.lpszAutoConfigUrl
fDoAutoProxy = True
End If
Else
' if the IE proxy config is not available, then
' we will try auto-detection
AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT
AutoProxyOptions.dwAutoDetectFlags = _
WINHTTP_AUTO_DETECT_TYPE_DHCP + _
WINHTTP_AUTO_DETECT_TYPE_DNS
fDoAutoProxy = True
End If

If fDoAutoProxy Then
Dim hSession As Long

' Need to create a temporary WinHttp session handle
' Note: performance of this GetProxyInfoForUrl function can be
' improved by saving this hSession handle across calls
' instead of creating a new handle each time
hSession = WinHttpOpen(0, 1, 0, 0, 0)

If (WinHttpGetProxyForUrl(hSession, StrPtr(Url), AutoProxyOptions, _
WinHttpProxyInfo) > 0) Then
ProxyStringPtr = WinHttpProxyInfo.lpszProxy
' ignore WinHttpProxyInfo.lpszProxyBypass, it will not be set
Else
error = Err.LastDllError
' some possibly autoproxy errors:
' 12166 - error in proxy auto-config script code
' 12167 - unable to download proxy auto-config script
' 12180 - WPAD detection failed
End If

WinHttpCloseHandle (hSession)
End If

' If we don't have a proxy server from WinHttpGetProxyForUrl,
' then pick one up from the IE proxy config (if given)
If (ProxyStringPtr = 0) Then
ProxyStringPtr = IEProxyConfig.lpszProxy
End If

' If there's a proxy string, convert it to a Basic string
If (ProxyStringPtr <> 0) Then
ptr = SysAllocString(ProxyStringPtr)
CopyMemory VarPtr(ProxyInfo.proxy), VarPtr(ptr), 4
ProxyInfo.active = True
End If

' Pick up any bypass string from the IEProxyConfig
If (IEProxyConfig.lpszProxyBypass <> 0) Then
ptr = SysAllocString(IEProxyConfig.lpszProxyBypass)
CopyMemory VarPtr(ProxyInfo.proxyBypass), VarPtr(ptr), 4
End If

' Free any strings received from WinHttp APIs
If (IEProxyConfig.lpszAutoConfigUrl <> 0) Then
GlobalFree (IEProxyConfig.lpszAutoConfigUrl)
End If
If (IEProxyConfig.lpszProxy <> 0) Then
GlobalFree (IEProxyConfig.lpszProxy)
End If
If (IEProxyConfig.lpszProxyBypass <> 0) Then
GlobalFree (IEProxyConfig.lpszProxyBypass)
End If
If (WinHttpProxyInfo.lpszProxy <> 0) Then
GlobalFree (WinHttpProxyInfo.lpszProxy)
End If
If (WinHttpProxyInfo.lpszProxyBypass <> 0) Then
GlobalFree (WinHttpProxyInfo.lpszProxyBypass)
End If

' return the ProxyInfo struct
GetProxyInfoForUrl = ProxyInfo

End Function


nelis

unread,
Nov 2, 2004, 2:09:32 PM11/2/04
to
"Stephen Sulzer" <sasulzer_at_seanet.com> wrote in message news:<10oe1up...@corp.supernews.com>...

> Please see the code below: I have written up a comprehensive
> GetProxyInfoForUrl function in Visual Basic that uses both
> WinHttpGetIEProxyConfigForCurrentUser and WinHttpGetProxyForUrl.

Stephen,
I have seen already your other activity on this group. I am really
amazed and I can't find words to thank you. It is really so cool that
you found a time not only to give me detailed answer but also to write
me a piece of code.
Without your help I probably would not be able to move on further with
my issue.
Massive thanks. You are just absolutely huge.
Regards,
Rafal Urbanelis

0 new messages