windowsvbaexcel64-bitwinhttp

How to make WinHttpCrackUrl work in 64-bit


I have Visual Basic for Applications code that uses WinHttp and works flawlessly with 32-bit Office 2010 running on 32-bit Windows XP. The same code fails to run properly on 64-bit Office 2013 on 64-bit Windows 8, even though it compiles fine.

The problem is that WinHttpCrackUrl() returns an error 87 "The parameter is incorrect" on Windows 8.

I have double-checked and triple-checked that all pointers are declared as LongPtr in the code where appropriate. What am I doing wrong?

Here is the code that runs fine on 32-bit Excel/Windows, but fails to run on 64-bit Excel/Windows:

Private Type URL_COMPONENTS
    dwStructSize      As Long
    lpszScheme        As LongPtr
    dwSchemeLength    As Long
    nScheme           As Long
    lpszHostName      As LongPtr
    dwHostNameLength  As Long
    nPort             As Long
    lpszUserName      As LongPtr
    dwUserNameLength  As Long
    lpszPassword      As LongPtr
    dwPasswordLength  As Long
    lpszUrlPath       As LongPtr
    dwUrlPathLength   As Long
    lpszExtraInfo     As LongPtr
    dwExtraInfoLength As Long
End Type

Private Declare PtrSafe Function WinHttpCrackUrl Lib "WinHTTP" ( _
    ByVal pwszUrl As LongPtr, _
    ByVal dwUrlLength As Long, _
    ByVal dwFlags As Long, _
    ByRef lpUrlComponents As URL_COMPONENTS) As Long

Sub Test()
    Dim result as Long
    Dim URLComp As URL_COMPONENTS
    Dim mURL as String
    mURL = "http://www.stackoverflow.com" & vbNullChar

    With URLComp
        .dwStructSize = Len(URLComp)
        .dwHostNameLength = -1
        .dwSchemeLength = -1
        .dwUrlPathLength = -1
    End With

    result = WinHttpCrackUrl(StrPtr(mURL), 0, 0, URLComp)

    ' Prints 1 on 32-bit Excel/Windows (indicating success)
    ' Prints 0 on 64-bit Excel/Windows (indicating failure)
    Debug.Print result

    ' Prints 87 on 64-bit Excel/Windows ("The parameter is incorrect.")
    Debug.Print err.LastDllError
End Sub

Solution

  • The struct is aligned in the C++ code, but VBA structs are packed. In 32 bit, for your struct, it does not matter since all members have alignment 4. But in 64 bit the pointers need 8 byte alignment and the struct has some extra padding. Put it in like this:

    Private Type URL_COMPONENTS
        dwStructSize      As Long
        padding1          As Long
        lpszScheme        As LongPtr
        dwSchemeLength    As Long
        nScheme           As Long
        lpszHostName      As LongPtr
        dwHostNameLength  As Long
        nPort             As Long
        lpszUserName      As LongPtr
        dwUserNameLength  As Long
        padding2          As Long
        lpszPassword      As LongPtr
        dwPasswordLength  As Long
        padding3          As Long
        lpszUrlPath       As LongPtr
        dwUrlPathLength   As Long
        padding4          As Long
        lpszExtraInfo     As LongPtr
        dwExtraInfoLength As Long
        padding5          As Long
    End Type
    

    I guess you'll want some conditional compilation to switch better 32 and 64 bit versions but I must confess to having no idea how to do that with VBA.