You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Excel VBA非STA线程中调用InternetGetProxyInfo

解决Excel VBA中调用InternetGetProxyInfo获取PAC代理的问题

我来帮你搞定这个问题——这个坑我之前也踩过,核心原因在于JSProxy.dll的InternetGetProxyInfo函数对调用环境和参数格式有严格要求,尤其是线程模型和PAC路径的处理,这就是你VBA调用返回ERROR_CAN_NOT_COMPLETE (1003L)的关键所在。下面分步骤给你讲清楚可行的解决方案:

1. 先排查VBA的API声明正确性

首先要确保你对Win32 API的Declare声明完全匹配函数的__stdcall调用约定和参数类型,尤其是指针类参数的处理。这里给你一套经过验证的声明:

Option Explicit

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hModule As Long) As Long
Private Declare Function CoInitializeEx Lib "ole32.dll" (ByVal pvReserved As Long, ByVal dwCoInit As Long) As Long
Private Declare Function CoUninitialize Lib "ole32.dll" () As Long
Private Declare Function LocalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _
    ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long

Private Const COINIT_APARTMENTTHREADED As Long = &H2
Private Const ERROR_SUCCESS As Long = 0
Private Const ERROR_INSUFFICIENT_BUFFER As Long = &H7A

2. 解决线程模型不兼容问题

JSProxy.dll依赖IE的脚本引擎,而Excel VBA的默认线程是非单线程单元(STA),这会直接导致脚本引擎初始化失败。解决办法是显式初始化COM环境为STA模式,这是解决ERROR_CAN_NOT_COMPLETE的核心步骤。

另外,PAC文件路径要使用file:///协议格式(比如file:///D:/Users/SC5071/Desktop/proxy.pac),而不是本地路径——C++里能直接用本地路径是因为API做了隐式处理,但VBA里必须明确用URL格式。

3. 完整的VBA调用流程(含资源清理)

下面是经过验证的完整代码,包含初始化、调用、资源释放的全流程:

Sub GetProxyFromPAC()
    Dim hModJS As Long
    Dim pIIAPD As Long, pIGPI As Long, pIDAPD As Long
    Dim bInit As Boolean
    Dim dwLastError As Long
    Dim strPACUrl As String, strURL As String, strHost As String
    Dim lpszProxies As Long, lpdwProxyLen As Long
    Dim strProxies As String
    
    ' 1. 初始化COM为STA模式,这一步必须有
    If CoInitializeEx(0, COINIT_APARTMENTTHREADED) <> ERROR_SUCCESS Then
        MsgBox "COM环境初始化失败"
        Exit Sub
    End If
    
    ' 2. 加载JSProxy.dll
    hModJS = LoadLibrary("jsproxy.dll")
    If hModJS = 0 Then
        dwLastError = Err.LastDllError
        MsgBox "加载jsproxy.dll失败,错误码:" & dwLastError
        CoUninitialize
        Exit Sub
    End If
    
    ' 3. 获取三个关键函数的指针
    pIIAPD = GetProcAddress(hModJS, "InternetInitializeAutoProxyDll")
    pIGPI = GetProcAddress(hModJS, "InternetGetProxyInfo")
    pIDAPD = GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll")
    If pIIAPD = 0 Or pIGPI = 0 Then
        MsgBox "获取函数指针失败"
        FreeLibrary hModJS
        CoUninitialize
        Exit Sub
    End If
    
    ' 4. 初始化PAC(用file://协议路径)
    strPACUrl = "file:///D:/Users/SC5071/Desktop/proxy.pac"
    bInit = CallWindowProc(pIIAPD, 0, StrPtr(strPACUrl), 0, 0, 0)
    If Not bInit Then
        dwLastError = Err.LastDllError
        MsgBox "PAC初始化失败,错误码:" & dwLastError
        GoTo Cleanup
    End If
    
    ' 5. 获取目标URL的代理信息
    strURL = "https://www.google.fr/"
    strHost = "www.google.fr"
    
    ' 先调用一次获取所需缓冲区长度
    If Not CallWindowProc(pIGPI, StrPtr(strURL), Len(strURL), StrPtr(strHost), Len(strHost), lpszProxies, lpdwProxyLen) Then
        dwLastError = Err.LastDllError
        If dwLastError <> ERROR_INSUFFICIENT_BUFFER Then
            MsgBox "获取代理缓冲区长度失败,错误码:" & dwLastError
            GoTo Cleanup
        End If
    End If
    
    ' 分配缓冲区并获取代理字符串
    strProxies = String(lpdwProxyLen, vbNullChar)
    If CallWindowProc(pIGPI, StrPtr(strURL), Len(strURL), StrPtr(strHost), Len(strHost), StrPtr(strProxies), lpdwProxyLen) Then
        strProxies = Left(strProxies, InStr(strProxies, vbNullChar) - 1)
        MsgBox "获取到的代理信息:" & vbCrLf & strProxies
    Else
        dwLastError = Err.LastDllError
        MsgBox "获取代理信息失败,错误码:" & dwLastError
    End If

Cleanup:
    ' 6. 清理资源(必须做,避免内存泄漏)
    If pIDAPD <> 0 Then Call CallWindowProc(pIDAPD, 0)
    If lpszProxies <> 0 Then LocalFree lpszProxies
    If hModJS <> 0 Then FreeLibrary hModJS
    CoUninitialize
End Sub

4. 更简便的替代方案:用WinHTTP

如果你不需要严格依赖WinINet,WinHTTP提供了封装好的API,完全不需要手动处理JSProxy.dll的线程和初始化问题,更适合VBA环境:

Option Explicit

Private Declare Function WinHttpOpen Lib "winhttp.dll" Alias "WinHttpOpenA" ( _
    ByVal pwszUserAgent As String, ByVal dwAccessType As Long, _
    ByVal pwszProxyName As String, ByVal pwszProxyBypass As String, _
    ByVal dwFlags As Long) As Long

Private Declare Function WinHttpGetProxyForURL Lib "winhttp.dll" Alias "WinHttpGetProxyForURLA" ( _
    ByVal hSession As Long, ByVal lpszURL As String, _
    ByVal pAutoProxyOptions As Long, ByVal lpszProxy As String, _
    ByRef lpdwProxyLength As Long, ByVal lpszProxyBypass As String, _
    ByRef lpdwProxyBypassLength As Long) As Boolean

Private Declare Function WinHttpCloseHandle Lib "winhttp.dll" (ByVal hInternet As Long) As Boolean

Private Const WINHTTP_ACCESS_TYPE_NO_PROXY As Long = 1
Private Const WINHTTP_AUTOPROXY_CONFIG_URL As Long = &H2
Private Const WINHTTP_AUTOPROXY_RUN_INPROCESS As Long = &H10

Type WINHTTP_AUTOPROXY_OPTIONS
    dwFlags As Long
    dwAutoDetectFlags As Long
    lpszAutoConfigUrl As String
    lpvReserved As Long
    dwReserved As Long
    fAutoLogonIfChallenged As Boolean
End Type

Sub GetProxyWithWinHTTP()
    Dim hSession As Long
    Dim autoProxyOpts As WINHTTP_AUTOPROXY_OPTIONS
    Dim strProxy As String
    Dim dwProxyLen As Long
    Dim strPACUrl As String
    
    strPACUrl = "file:///D:/Users/SC5071/Desktop/proxy.pac"
    
    ' 初始化WinHTTP会话
    hSession = WinHttpOpen("Excel VBA Proxy Client", WINHTTP_ACCESS_TYPE_NO_PROXY, vbNullString, vbNullString, 0)
    If hSession = 0 Then
        MsgBox "WinHTTP会话初始化失败"
        Exit Sub
    End If
    
    ' 设置自动代理选项
    autoProxyOpts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL Or WINHTTP_AUTOPROXY_RUN_INPROCESS
    autoProxyOpts.lpszAutoConfigUrl = strPACUrl
    
    ' 获取缓冲区长度
    WinHttpGetProxyForURL hSession, "https://www.google.fr/", VarPtr(autoProxyOpts), vbNullString, dwProxyLen, vbNullString, 0
    
    ' 分配缓冲区并获取代理
    strProxy = String(dwProxyLen, vbNullChar)
    If WinHttpGetProxyForURL(hSession, "https://www.google.fr/", VarPtr(autoProxyOpts), strProxy, dwProxyLen, vbNullString, 0) Then
        strProxy = Left(strProxy, InStr(strProxy, vbNullChar) - 1)
        MsgBox "WinHTTP获取到的代理:" & vbCrLf & strProxy
    Else
        MsgBox "WinHTTP获取代理失败,错误码:" & Err.LastDllError
    End If
    
    ' 关闭会话
    WinHttpCloseHandle hSession
End Sub

内容的提问来源于stack exchange,提问作者hymced

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:11:18