Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

The official name is XMLHttpRequest, not HTTPXMLRequest. In classic Visual Basic 6, an MSXML IXMLHTTPRequest object does not expose onreadystatechange as an ordinary WithEvents COM event. Use a Timer to poll readyState, or use Microsoft’s wrapper-class callback technique. A DOMDocument event is suitable for asynchronous XML loading, while VBScript can assign a function with GetRef.

This article targets VB6 and MSXML. VBA has similar COM limitations but requires host-specific testing; modern VB.NET applications should normally use HttpClient with Async/Await.

What onreadystatechange means

onreadystatechange is a write-only callback property invoked when an MSXML request’s readyState changes. It can run several times, so the handler must check for state 4 before processing the final response. Microsoft documents the property for IXMLHTTPRequest and IServerXMLHTTPRequest.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Value Meaning
0 Uninitialized; Open has not been called.
1 Opened; Send has not been called.
2 Request sent; status and headers are available.
3 Interactive; some response data has arrived.
4 Complete; all response data has arrived.

These states are described in Microsoft’s IXMLHTTPRequest documentation. State 4 means completion, not a successful HTTP response. Read Status only after completion and treat transport failures separately.

#1 Best Overall

Why ordinary WithEvents does not solve XMLHTTP

This declaration is not the general solution:

Private WithEvents xhr As MSXML2.XMLHTTP60

Microsoft explains that the callback was designed primarily for scripting environments, many of which do not support COM events. Consequently, VB6 normally cannot consume IXMLHTTPRequest.onreadystate​change like a control event. Microsoft’s VB6 guidance lists three practical choices: Timer polling, a wrapper class, or an event-bearing DOMDocument when the task is XML loading (Microsoft implementation guidance).

First prepare the MSXML reference

In VB6, open Project → References and select Microsoft XML, v6.0 if that version is installed. Then use the version-qualified type:

Dim xhr As MSXML2.XMLHTTP60
Set xhr = New MSXML2.XMLHTTP60

Available versions and reference labels depend on the Windows installation. Late binding avoids a compile-time reference but moves failures to runtime:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Dim xhr As Object
Set xhr = CreateObject("MSXML2.XMLHTTP.6.0")

Option 1: poll with a VB6 Timer

Timer polling is usually the easiest implementation to debug. Keep the request at form or module scope, open it asynchronously, and disable the Timer before handling the completed response.

Option Explicit

Private xhr As MSXML2.XMLHTTP60

Private Sub cmdGet_Click()
    On Error GoTo RequestError

    Set xhr = New MSXML2.XMLHTTP60
    Timer1.Interval = 50
    Timer1.Enabled = True

    xhr.Open "GET", "https://example.com/data.xml", True
    xhr.Send
    Exit Sub

RequestError:
    Timer1.Enabled = False
    MsgBox Err.Number & ": " & Err.Description, vbExclamation
End Sub

Private Sub Timer1_Timer()
    On Error GoTo PollError

    If xhr Is Nothing Then Exit Sub
    If xhr.readyState <> 4 Then Exit Sub

    Timer1.Enabled = False
    If xhr.Status >= 200 And xhr.Status < 300 Then
        Debug.Print xhr.responseText
    Else
        MsgBox "HTTP error: " & CStr(xhr.Status), vbExclamation
    End If
    Set xhr = Nothing
    Exit Sub

PollError:
    Timer1.Enabled = False
    MsgBox Err.Number & ": " & Err.Description, vbExclamation
End Sub

The 50-millisecond interval is only an example; choose a cadence appropriate for the application rather than assuming one interval is universally correct. A Timer will not fire while the UI thread is blocked, and a synchronous request (Open ..., False) can make the interface appear frozen. The first tick must inspect the current state because intermediate states may have passed before polling began.

Option 2: use Microsoft’s VB6 wrapper-class callback

Use this approach when callback-style organization is more useful than polling, especially with multiple requests or forms. The crucial detail is that the handler procedure must be made the class module’s default procedure.

  1. Create a VB6 Standard EXE project and add the MSXML reference.
  2. Add a Class Module named ReadyStateHandler.
  3. Add a public OnReadyStateChange procedure.
  4. Choose Tools → Procedure Attributes → Advanced, set Procedure ID to (Default), and confirm.
  5. Retain the handler in a form- or module-level variable.
  6. Assign it to OnReadyStateChange before calling Open and Send.

The default-procedure attribute is functional, not cosmetic: it is how VB6 supplies the object to this callback property.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Class module: ReadyStateHandler

Option Explicit

Public Sub OnReadyStateChange()
    Dim request As MSXML2.XMLHTTP60
    Set request = Form1.XmlHttp

    Debug.Print "readyState = " & CStr(request.readyState)
    If request.readyState <> 4 Then Exit Sub

    On Error GoTo ResponseError
    If request.Status >= 200 And request.Status < 300 Then
        Form1.HandleSuccessfulResponse request.responseText
    Else
        Form1.HandleHttpError request.Status
    End If
    Exit Sub

ResponseError:
    Form1.HandleTransportError Err.Number, Err.Description
End Sub

Form code

Option Explicit

Public XmlHttp As MSXML2.XMLHTTP60
Private readyHandler As ReadyStateHandler

Private Sub cmdGet_Click()
    On Error GoTo RequestError

    Set XmlHttp = New MSXML2.XMLHTTP60
    Set readyHandler = New ReadyStateHandler
    XmlHttp.OnReadyStateChange = readyHandler

    XmlHttp.Open "GET", "https://example.com/data.xml", True
    XmlHttp.Send
    Exit Sub

RequestError:
    MsgBox Err.Number & ": " & Err.Description, vbExclamation
End Sub

Public Sub HandleSuccessfulResponse(ByVal body As String)
    Debug.Print body
End Sub

Public Sub HandleHttpError(ByVal httpStatus As Long)
    MsgBox "HTTP status: " & CStr(httpStatus), vbExclamation
End Sub

Public Sub HandleTransportError(ByVal number As Long, ByVal description As String)
    MsgBox "Transport error " & CStr(number) & ": " & description, vbExclamation
End Sub

Do not declare the handler only inside cmdGet_Click; releasing it when the procedure ends can remove the callback before the request completes. Also guard against repeated state changes with readyState <> 4. If users can start overlapping requests, disable the command, retain one handler per request, or associate each callback with a request identifier.

Option 3: DOMDocument with WithEvents

For asynchronous XML document loading, VB6 can use the documented DOMDocument event syntax:

Option Explicit

Private WithEvents XmlDoc As MSXML2.DOMDocument60

Private Sub cmdLoadXml_Click()
    On Error GoTo LoadError
    Set XmlDoc = New MSXML2.DOMDocument60
    XmlDoc.async = True
    XmlDoc.Load "https://example.com/data.xml"
    Exit Sub

LoadError:
    MsgBox Err.Number & ": " & Err.Description, vbExclamation
End Sub

Private Sub XmlDoc_onreadystatechange()
    If XmlDoc.readyState <> 4 Then Exit Sub

    If XmlDoc.parseError.ErrorCode <> 0 Then
        MsgBox XmlDoc.parseError.Reason, vbExclamation
    Else
        Debug.Print XmlDoc.XML
    End If
End Sub

See Microsoft’s Visual Basic DOMDocument event documentation. This is an XML-loading alternative, not a universal replacement for IXMLHTTPRequest: Microsoft specifically notes that it is unsuitable when the same operation must first post XML to a web server.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

VBScript uses a different binding syntax

VBScript can pass a function reference directly with GetRef:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Option Explicit

Dim xhr
Set xhr = CreateObject("MSXML2.XMLHTTP.6.0")
xhr.onreadystatechange = GetRef("HandleStateChange")
xhr.Open "GET", "https://example.com/data.xml", True
xhr.Send

Sub HandleStateChange()
    If xhr.readyState <> 4 Then Exit Sub
    If xhr.Status >= 200 And xhr.Status < 300 Then
        WScript.Echo xhr.ResponseText
    Else
        WScript.Echo "HTTP error: " & xhr.Status
    End If
End Sub

Microsoft documents this GetRef pattern. Do not copy it unchanged into VB6; the wrapper class addresses the different callback-binding rules there.

Request order and response checks

For an asynchronous VB6 request, the reliable sequence is:

  1. Create the MSXML request object.
  2. Create and retain the callback handler, when using the wrapper approach.
  3. Assign OnReadyStateChange.
  4. Call Open with its third argument set to True.
  5. Call Send.
  6. Wait for readyState = 4.
  7. Check the HTTP status.
  8. Read responseText, responseXML, or responseBody.
  9. Release the request and handler after completion.

Use False as the third argument only for a synchronous contrast; it blocks the calling thread:

xhr.Open "GET", url, False
xhr.Send

The Status property is an HTTP code. A 2xx range includes successful responses such as 200, 201, 202, and 204. A 204 response may legitimately have no body. Codes such as 400, 401, 403, 404, 500, and 503 are completed HTTP responses but not application success. Redirect, authentication, proxy, and TLS behavior can differ between XMLHTTP and ServerXMLHTTP.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Diagnose the common failures

  • Missing reference or ProgID: verify the installed MSXML version, reference name, and whether early or late binding is appropriate.
  • Callback never fires: confirm that the request is asynchronous, the handler is assigned before Open/Send, and the handler object remains in scope.
  • Timer never fires: enable the Timer and check that the UI thread is not blocked by synchronous work.
  • Repeated completion processing: disable the Timer or add the state-4 guard before handling the response.
  • Status raises an error: a DNS, connection, timeout, certificate, proxy, invalid-URL, or permission failure may have occurred before any HTTP response existed. Keep On Error around Send and final status access.
  • HTTP error mistaken for COM failure: a 404 or 500 can complete normally; inspect Status.
  • XML cannot be parsed: transport success does not guarantee valid XML. For DOMDocument, inspect parseError after completion; see Microsoft’s XML parsing documentation.
  • Stale result from an earlier request: prevent overlapping requests or attach an identifier to each retained request and handler.

Which approach should you choose?

Need Best fit
Simplest VB6 implementation Timer polling
Callback-style VB6 design Wrapper class with a default procedure
Asynchronous XML file loading DOMDocument with WithEvents
VBScript callback GetRef
Modern VB.NET application HttpClient with Async/Await

MSXML2.XMLHTTP60 and MSXML2.ServerXMLHTTP60 are related but distinct interfaces. Choose based on the client or service networking context, proxy and timeout requirements, and target environment rather than assuming one is universally faster or better. The techniques above come from archived MSXML/VB6 guidance and are legacy-platform patterns, not the normal design for current .NET applications.

Quick Recap

Bestseller No. 1
Programming Microsoft Visual Basic 6.0
Programming Microsoft Visual Basic 6.0
Used Book in Good Condition
$5.00
SaleBestseller No. 2
Bestseller No. 4

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.