[PATCH] msxml3/tests: Test VBSAXXMLReader.

Jefferson Carpenter jeffersoncarpenter2 at gmail.com
Fri Oct 8 03:05:48 CDT 2021


Summary:

* Generalized ContentHandler, ErrorHandler, and LexicalHandler 
implementations in test to not care about which interface is used.
* Implemented SAX.*Handler interfaces by calling into these generalized 
functions.
* Implemented VBSAX.*Handler interfaces the same way.
* Added vb_reader_support_data using VB interface IID_IVBSAXXMLReader.
* Added a data structure containing both reader_support_data and 
vb_reader_support_data.  This is iterated over first in test_saxreader().

The tests generally run.


thanks,
Jefferson
-------------- next part --------------
From 7c830b8da92328401b3f85012f53319188f55d69 Mon Sep 17 00:00:00 2001
From: Jefferson Carpenter <jeffersoncarpenter2 at gmail.com>
Date: Fri, 8 Oct 2021 08:50:07 +0000
Subject: [PATCH] msxml3/tests: Test VBSAXXMLReader.

Signed-off-by: Jefferson Carpenter <jeffersoncarpenter2 at gmail.com>
---
 dlls/msxml3/tests/saxreader.c | 2022 ++++++++++++++++++++++++---------
 1 file changed, 1488 insertions(+), 534 deletions(-)

diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index e401aafc87d..b7d64db2222 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -157,6 +157,32 @@ static void test_saxstr(const char *file, unsigned line, BSTR str, const char *e
                              wine_dbgstr_wn(str, len), expected);
 }
 
+enum sax_union_type {
+    SAXObjectType,
+    VB_SAXObjectType
+};
+struct any_reader {
+    enum sax_union_type type;
+    union {
+        ISAXXMLReader *reader;
+        IVBSAXXMLReader *vb_reader;
+    } pointer;
+};
+struct any_locator {
+    enum sax_union_type type;
+    union {
+        ISAXLocator *locator;
+        IVBSAXLocator *vb_locator;
+    } pointer;
+};
+struct any_attributes {
+    enum sax_union_type type;
+    union {
+        ISAXAttributes *attributes;
+        IVBSAXAttributes *vb_attributes;
+    } pointer;
+};
+
 typedef enum _CH {
     CH_ENDTEST,
     CH_PUTDOCUMENTLOCATOR,
@@ -241,11 +267,19 @@ struct call_sequence
 #define NUM_CALL_SEQUENCES    1
 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
 
-static void init_call_entry(ISAXLocator *locator, struct call_entry *call)
+static void init_call_entry(struct any_locator locator, struct call_entry *call)
 {
     memset(call, 0, sizeof(*call));
-    ISAXLocator_getLineNumber(locator, &call->line);
-    ISAXLocator_getColumnNumber(locator, &call->column);
+    switch (locator.type) {
+    case SAXObjectType:
+        ISAXLocator_getLineNumber(locator.pointer.locator, &call->line);
+        ISAXLocator_getColumnNumber(locator.pointer.locator, &call->column);
+        break;
+    case VB_SAXObjectType:
+        IVBSAXLocator_get_lineNumber(locator.pointer.vb_locator, &call->line);
+        IVBSAXLocator_get_columnNumber(locator.pointer.vb_locator, &call->column);
+        break;
+    }
 }
 
 static void add_call(struct call_sequence **seq, int sequence_index,
@@ -1027,8 +1061,8 @@ static const char xmlspace_attr[] =
     "<a xml:space=\"preserve\"> Some text data </a>";
 
 static struct call_entry *expectCall;
-static ISAXLocator *locator;
-static ISAXXMLReader *g_reader;
+static struct any_locator locator;
+static struct any_reader g_reader;
 int msxml_version;
 
 static void set_expected_seq(struct call_entry *expected)
@@ -1044,40 +1078,18 @@ static HRESULT get_expected_ret(void)
     return hr;
 }
 
-static HRESULT WINAPI contentHandler_QueryInterface(
-        ISAXContentHandler* iface,
-        REFIID riid,
-        void **ppvObject)
-{
-    *ppvObject = NULL;
-
-    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
-    {
-        *ppvObject = iface;
-    }
-    else
-    {
-        return E_NOINTERFACE;
-    }
-
-    return S_OK;
-}
-
-static ULONG WINAPI contentHandler_AddRef(
-        ISAXContentHandler* iface)
+static ULONG contentHandler_AddRef(void)
 {
     return 2;
 }
 
-static ULONG WINAPI contentHandler_Release(
-        ISAXContentHandler* iface)
+static ULONG contentHandler_Release(void)
 {
     return 1;
 }
 
-static HRESULT WINAPI contentHandler_putDocumentLocator(
-        ISAXContentHandler* iface,
-        ISAXLocator *pLocator)
+static HRESULT contentHandler_putDocumentLocator(
+        struct any_locator pLocator)
 {
     struct call_entry call;
     IUnknown *unk;
@@ -1089,41 +1101,92 @@ static HRESULT WINAPI contentHandler_putDocumentLocator(
     call.id = CH_PUTDOCUMENTLOCATOR;
     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
 
-    hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXLocator, (void**)&unk);
-    EXPECT_HR(hr, E_NOINTERFACE);
+    switch (pLocator.type) {
+    case SAXObjectType:
+        hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_IVBSAXLocator, (void**)&unk);
+        EXPECT_HR(hr, E_NOINTERFACE);
+        break;
+    case VB_SAXObjectType:
+        if (msxml_version < 4) {
+            hr = IVBSAXLocator_QueryInterface(pLocator.pointer.vb_locator, &IID_ISAXLocator, (void**)&unk);
+            EXPECT_HR(hr, E_NOINTERFACE);
+        }
+        else {
+            hr = IVBSAXLocator_QueryInterface(pLocator.pointer.vb_locator, &IID_ISAXLocator, (void**)&unk);
+            todo_wine EXPECT_HR(hr, S_OK);
+            if (unk) IUnknown_Release(unk);
+        }
+        break;
+    }
 
     if (msxml_version >= 6) {
         ISAXAttributes *attr, *attr1;
         IMXAttributes *mxattr;
 
-        EXPECT_REF(pLocator, 1);
-        hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr);
-        EXPECT_HR(hr, S_OK);
-        EXPECT_REF(pLocator, 2);
-        hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1);
-        EXPECT_HR(hr, S_OK);
-        EXPECT_REF(pLocator, 3);
-        ok(attr == attr1, "got %p, %p\n", attr, attr1);
+        switch (pLocator.type) {
+        case SAXObjectType:
+            EXPECT_REF(pLocator.pointer.locator, 1);
+            hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_ISAXAttributes, (void**)&attr);
+            EXPECT_HR(hr, S_OK);
+            EXPECT_REF(pLocator.pointer.locator, 2);
+            hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_ISAXAttributes, (void**)&attr1);
+            EXPECT_HR(hr, S_OK);
+            EXPECT_REF(pLocator.pointer.locator, 3);
+            ok(attr == attr1, "got %p, %p\n", attr, attr1);
 
-        hr = ISAXAttributes_QueryInterface(attr, &IID_IVBSAXAttributes, (void**)&unk);
-        EXPECT_HR(hr, E_NOINTERFACE);
+            hr = ISAXAttributes_QueryInterface(attr, &IID_IVBSAXAttributes, (void**)&unk);
+            EXPECT_HR(hr, E_NOINTERFACE);
 
-        hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXAttributes, (void**)&unk);
-        EXPECT_HR(hr, E_NOINTERFACE);
+            hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_IVBSAXAttributes, (void**)&unk);
+            EXPECT_HR(hr, E_NOINTERFACE);
 
-        hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
-        EXPECT_HR(hr, E_NOINTERFACE);
+            hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
+            EXPECT_HR(hr, E_NOINTERFACE);
+
+            ISAXAttributes_Release(attr);
+            ISAXAttributes_Release(attr1);
+            break;
+        case VB_SAXObjectType:
+            EXPECT_REF(pLocator.pointer.vb_locator, 1);
+
+            if (msxml_version < 6) {
+                hr = IVBSAXLocator_QueryInterface(pLocator.pointer.vb_locator, &IID_ISAXAttributes, (void**)&unk);
+                EXPECT_HR(hr, E_NOINTERFACE);
+            }
+            else {
+                hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_ISAXAttributes, (void**)&attr);
+                todo_wine EXPECT_HR(hr, S_OK);
+                EXPECT_REF(pLocator.pointer.locator, 1);
+
+                hr = ISAXLocator_QueryInterface(pLocator.pointer.locator, &IID_ISAXAttributes, (void**)&attr1);
+                todo_wine EXPECT_HR(hr, S_OK);
+
+                ok(attr == attr1, "got %p, %p\n", attr, attr1);
+
+                if (attr) {
+                    hr = ISAXAttributes_QueryInterface(attr, &IID_IVBSAXAttributes, (void**)&unk);
+                    EXPECT_HR(hr, E_NOINTERFACE);
+
+                    hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
+                    EXPECT_HR(hr, E_NOINTERFACE);
+                }
 
-        ISAXAttributes_Release(attr);
-        ISAXAttributes_Release(attr1);
+                if (attr) ISAXAttributes_Release(attr);
+                if (attr1) ISAXAttributes_Release(attr1);
+            }
+
+            hr = IVBSAXLocator_QueryInterface(pLocator.pointer.vb_locator, &IID_IVBSAXAttributes, (void**)&unk);
+            todo_wine EXPECT_HR(hr, E_NOINTERFACE);
+            break;
+        }
     }
 
     return get_expected_ret();
 }
 
 static ISAXAttributes *test_attr_ptr;
-static HRESULT WINAPI contentHandler_startDocument(
-        ISAXContentHandler* iface)
+static IVBSAXAttributes *test_vb_attr_ptr;
+static HRESULT contentHandler_startDocument(void)
 {
     struct call_entry call;
 
@@ -1132,12 +1195,12 @@ static HRESULT WINAPI contentHandler_startDocument(
     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
 
     test_attr_ptr = NULL;
+    test_vb_attr_ptr = NULL;
 
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_endDocument(
-        ISAXContentHandler* iface)
+static HRESULT contentHandler_endDocument(void)
 {
     struct call_entry call;
 
@@ -1148,8 +1211,7 @@ static HRESULT WINAPI contentHandler_endDocument(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_startPrefixMapping(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_startPrefixMapping(
         const WCHAR *prefix, int prefix_len,
         const WCHAR *uri, int uri_len)
 {
@@ -1167,8 +1229,7 @@ static HRESULT WINAPI contentHandler_startPrefixMapping(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_endPrefixMapping(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_endPrefixMapping(
         const WCHAR *prefix, int len)
 {
     struct call_entry call;
@@ -1183,24 +1244,27 @@ static HRESULT WINAPI contentHandler_endPrefixMapping(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_startElement(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_startElement(
         const WCHAR *uri, int uri_len,
         const WCHAR *localname, int local_len,
         const WCHAR *qname, int qname_len,
-        ISAXAttributes *saxattr)
+        struct any_attributes saxattr)
 {
     struct call_entry call;
     IMXAttributes *mxattr;
     HRESULT hr;
     int len;
 
-    ok(uri != NULL, "uri == NULL\n");
-    ok(localname != NULL, "localname == NULL\n");
-    ok(qname != NULL, "qname == NULL\n");
-
-    hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
-    EXPECT_HR(hr, E_NOINTERFACE);
+    switch (saxattr.type) {
+    case SAXObjectType:
+        hr = ISAXAttributes_QueryInterface(saxattr.pointer.attributes, &IID_IMXAttributes, (void**)&mxattr);
+        EXPECT_HR(hr, E_NOINTERFACE);
+        break;
+    case VB_SAXObjectType:
+        hr = IVBSAXAttributes_QueryInterface(saxattr.pointer.vb_attributes, &IID_IMXAttributes, (void**)&mxattr);
+        EXPECT_HR(hr, E_NOINTERFACE);
+        break;
+    }
 
     init_call_entry(locator, &call);
     call.id = CH_STARTELEMENT;
@@ -1208,38 +1272,92 @@ static HRESULT WINAPI contentHandler_startElement(
     call.arg2W = SysAllocStringLen(localname, local_len);
     call.arg3W = SysAllocStringLen(qname, qname_len);
 
-    if(!test_attr_ptr)
-        test_attr_ptr = saxattr;
-    ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
+    switch (saxattr.type) {
+    case SAXObjectType:
+        if(!test_attr_ptr)
+            test_attr_ptr = saxattr.pointer.attributes;
+        ok(test_attr_ptr == saxattr.pointer.attributes, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr.pointer.attributes);
+        break;
+    case VB_SAXObjectType:
+        if(!test_vb_attr_ptr)
+            test_vb_attr_ptr = saxattr.pointer.vb_attributes;
+        ok(test_vb_attr_ptr == saxattr.pointer.vb_attributes, "Multiple ISAXAttributes instances are used (%p %p)\n", test_vb_attr_ptr, saxattr.pointer.vb_attributes);
+        break;
+    }
 
     /* store actual attributes */
     len = 0;
-    hr = ISAXAttributes_getLength(saxattr, &len);
-    EXPECT_HR(hr, S_OK);
+    switch (saxattr.type) {
+    case SAXObjectType:
+        hr = ISAXAttributes_getLength(saxattr.pointer.attributes, &len);
+        EXPECT_HR(hr, S_OK);
+        break;
+    case VB_SAXObjectType:
+        hr = IVBSAXAttributes_get_length(saxattr.pointer.vb_attributes, &len);
+        EXPECT_HR(hr, S_OK);
+        break;
+    }
 
     if (len)
     {
         VARIANT_BOOL v;
         int i;
+        BSTR uri_bstr = NULL;
+        BSTR localname_bstr = NULL;
+        BSTR qname_bstr = NULL;
+        BSTR value_bstr = NULL;
 
         struct attribute_entry *attr;
         attr = heap_alloc_zero(len * sizeof(*attr));
 
         v = VARIANT_TRUE;
-        hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
-        EXPECT_HR(hr, S_OK);
+        switch (g_reader.type) {
+        case SAXObjectType:
+            hr = ISAXXMLReader_getFeature(g_reader.pointer.reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
+            EXPECT_HR(hr, S_OK);
+            break;
+        case VB_SAXObjectType:
+            hr = IVBSAXXMLReader_getFeature(g_reader.pointer.vb_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
+            EXPECT_HR(hr, S_OK);
+            break;
+        }
 
         for (i = 0; i < len; i++)
         {
             const WCHAR *value;
             int value_len;
 
-            hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
-                &localname, &local_len, &qname, &qname_len);
-            EXPECT_HR(hr, S_OK);
+            switch (saxattr.type) {
+            case SAXObjectType:
+                hr = ISAXAttributes_getName(saxattr.pointer.attributes, i, &uri, &uri_len,
+                    &localname, &local_len, &qname, &qname_len);
+                EXPECT_HR(hr, S_OK);
 
-            hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
-            EXPECT_HR(hr, S_OK);
+                hr = ISAXAttributes_getValue(saxattr.pointer.attributes, i, &value, &value_len);
+                EXPECT_HR(hr, S_OK);
+                break;
+            case VB_SAXObjectType:
+                hr = IVBSAXAttributes_getURI(saxattr.pointer.vb_attributes, i, &uri_bstr);
+                EXPECT_HR(hr, S_OK);
+                uri = uri_bstr;
+                uri_len = SysStringLen(uri_bstr);
+
+                hr = IVBSAXAttributes_getLocalName(saxattr.pointer.vb_attributes, i, &localname_bstr);
+                EXPECT_HR(hr, S_OK);
+                localname = localname_bstr;
+                local_len = SysStringLen(localname_bstr);
+
+                hr = IVBSAXAttributes_getQName(saxattr.pointer.vb_attributes, i, &qname_bstr);
+                EXPECT_HR(hr, S_OK);
+                qname = qname_bstr;
+                qname_len = SysStringLen(qname_bstr);
+
+                hr = IVBSAXAttributes_getValue(saxattr.pointer.vb_attributes, i, &value_bstr);
+                EXPECT_HR(hr, S_OK);
+                value = value_bstr;
+                value_len = SysStringLen(value_bstr);
+                break;
+            }
 
             /* if 'namespaces' switched off uri and local name contains garbage */
             if (v == VARIANT_FALSE && msxml_version > 0)
@@ -1255,6 +1373,11 @@ static HRESULT WINAPI contentHandler_startElement(
 
             attr[i].qnameW = SysAllocStringLen(qname, qname_len);
             attr[i].valueW = SysAllocStringLen(value, value_len);
+
+            SysFreeString(uri_bstr);
+            SysFreeString(localname_bstr);
+            SysFreeString(qname_bstr);
+            SysFreeString(value_bstr);
         }
 
         call.attributes = attr;
@@ -1266,18 +1389,13 @@ static HRESULT WINAPI contentHandler_startElement(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_endElement(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_endElement(
         const WCHAR *uri, int uri_len,
         const WCHAR *localname, int local_len,
         const WCHAR *qname, int qname_len)
 {
     struct call_entry call;
 
-    ok(uri != NULL, "uri == NULL\n");
-    ok(localname != NULL, "localname == NULL\n");
-    ok(qname != NULL, "qname == NULL\n");
-
     init_call_entry(locator, &call);
     call.id = CH_ENDELEMENT;
     call.arg1W = SysAllocStringLen(uri, uri_len);
@@ -1288,8 +1406,7 @@ static HRESULT WINAPI contentHandler_endElement(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_characters(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_characters(
         const WCHAR *chars,
         int len)
 {
@@ -1305,8 +1422,7 @@ static HRESULT WINAPI contentHandler_characters(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_ignorableWhitespace(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_ignorableWhitespace(
         const WCHAR *chars, int len)
 {
     struct call_entry call;
@@ -1321,8 +1437,7 @@ static HRESULT WINAPI contentHandler_ignorableWhitespace(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_processingInstruction(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_processingInstruction(
         const WCHAR *target, int target_len,
         const WCHAR *data, int data_len)
 {
@@ -1340,8 +1455,7 @@ static HRESULT WINAPI contentHandler_processingInstruction(
     return get_expected_ret();
 }
 
-static HRESULT WINAPI contentHandler_skippedEntity(
-        ISAXContentHandler* iface,
+static HRESULT contentHandler_skippedEntity(
         const WCHAR *name, int len)
 {
     struct call_entry call;
@@ -1356,34 +1470,14 @@ static HRESULT WINAPI contentHandler_skippedEntity(
     return get_expected_ret();
 }
 
-static const ISAXContentHandlerVtbl contentHandlerVtbl =
-{
-    contentHandler_QueryInterface,
-    contentHandler_AddRef,
-    contentHandler_Release,
-    contentHandler_putDocumentLocator,
-    contentHandler_startDocument,
-    contentHandler_endDocument,
-    contentHandler_startPrefixMapping,
-    contentHandler_endPrefixMapping,
-    contentHandler_startElement,
-    contentHandler_endElement,
-    contentHandler_characters,
-    contentHandler_ignorableWhitespace,
-    contentHandler_processingInstruction,
-    contentHandler_skippedEntity
-};
-
-static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
-
-static HRESULT WINAPI isaxerrorHandler_QueryInterface(
-        ISAXErrorHandler* iface,
+static HRESULT WINAPI saxContentHandler_QueryInterface(
+        ISAXContentHandler* iface,
         REFIID riid,
         void **ppvObject)
 {
     *ppvObject = NULL;
 
-    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
+    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
     {
         *ppvObject = iface;
     }
@@ -1395,76 +1489,149 @@ static HRESULT WINAPI isaxerrorHandler_QueryInterface(
     return S_OK;
 }
 
-static ULONG WINAPI isaxerrorHandler_AddRef(
-        ISAXErrorHandler* iface)
+static ULONG WINAPI saxContentHandler_AddRef(
+        ISAXContentHandler* iface)
 {
-    return 2;
+    return contentHandler_AddRef();
 }
 
-static ULONG WINAPI isaxerrorHandler_Release(
-        ISAXErrorHandler* iface)
+static ULONG WINAPI saxContentHandler_Release(
+        ISAXContentHandler* iface)
 {
-    return 1;
+    return contentHandler_Release();
 }
 
-static HRESULT WINAPI isaxerrorHandler_error(
-        ISAXErrorHandler* iface,
-        ISAXLocator *pLocator,
-        const WCHAR *pErrorMessage,
-        HRESULT hrErrorCode)
+static HRESULT WINAPI saxContentHandler_putDocumentLocator(
+        ISAXContentHandler* iface,
+        ISAXLocator *pLocator)
 {
-    ok(0, "unexpected call\n");
-    return S_OK;
+    struct any_locator put_locator;
+    put_locator.type = SAXObjectType;
+    put_locator.pointer.locator = pLocator;
+
+    return contentHandler_putDocumentLocator(put_locator);
 }
 
-static HRESULT WINAPI isaxerrorHandler_fatalError(
-        ISAXErrorHandler* iface,
-        ISAXLocator *pLocator,
-        const WCHAR *message,
-        HRESULT hr)
+static HRESULT WINAPI saxContentHandler_startDocument(
+        ISAXContentHandler* iface)
 {
-    struct call_entry call;
+    return contentHandler_startDocument();
+}
 
-    init_call_entry(locator, &call);
-    call.id  = EH_FATALERROR;
-    call.ret = hr;
+static HRESULT WINAPI saxContentHandler_endDocument(
+        ISAXContentHandler* iface)
+{
+    return contentHandler_endDocument();
+}
 
-    add_call(sequences, CONTENT_HANDLER_INDEX, &call);
+static HRESULT WINAPI saxContentHandler_startPrefixMapping(
+        ISAXContentHandler* iface,
+        const WCHAR *prefix, int prefix_len,
+        const WCHAR *uri, int uri_len)
+{
+    return contentHandler_startPrefixMapping(prefix, prefix_len, uri, uri_len);
+}
 
-    get_expected_ret();
-    return S_OK;
+static HRESULT WINAPI saxContentHandler_endPrefixMapping(
+        ISAXContentHandler* iface,
+        const WCHAR *prefix, int len)
+{
+    return contentHandler_endPrefixMapping(prefix, len);
 }
 
-static HRESULT WINAPI isaxerrorHandler_ignorableWarning(
-        ISAXErrorHandler* iface,
-        ISAXLocator *pLocator,
-        const WCHAR *pErrorMessage,
-        HRESULT hrErrorCode)
+static HRESULT WINAPI saxContentHandler_startElement(
+        ISAXContentHandler* iface,
+        const WCHAR *uri, int uri_len,
+        const WCHAR *localname, int local_len,
+        const WCHAR *qname, int qname_len,
+        ISAXAttributes *saxattr)
 {
-    ok(0, "unexpected call\n");
-    return S_OK;
+    struct any_attributes attributes;
+    attributes.type = SAXObjectType;
+    attributes.pointer.attributes = saxattr;
+
+    ok(uri != NULL, "uri == NULL\n");
+    ok(localname != NULL, "localname == NULL\n");
+    ok(qname != NULL, "qname == NULL\n");
+
+    return contentHandler_startElement(
+        uri, uri_len, localname, local_len, qname, qname_len,
+        attributes);
 }
 
-static const ISAXErrorHandlerVtbl errorHandlerVtbl =
+static HRESULT WINAPI saxContentHandler_endElement(
+        ISAXContentHandler* iface,
+        const WCHAR *uri, int uri_len,
+        const WCHAR *localname, int local_len,
+        const WCHAR *qname, int qname_len)
 {
-    isaxerrorHandler_QueryInterface,
-    isaxerrorHandler_AddRef,
-    isaxerrorHandler_Release,
-    isaxerrorHandler_error,
-    isaxerrorHandler_fatalError,
-    isaxerrorHandler_ignorableWarning
+    ok(uri != NULL, "uri == NULL\n");
+    ok(localname != NULL, "localname == NULL\n");
+    ok(qname != NULL, "qname == NULL\n");
+
+    return contentHandler_endElement(
+        uri, uri_len, localname, local_len, qname, qname_len);
+}
+
+static HRESULT WINAPI saxContentHandler_characters(
+        ISAXContentHandler* iface,
+        const WCHAR *chars,
+        int len)
+{
+    return contentHandler_characters(chars, len);
+}
+
+static HRESULT WINAPI saxContentHandler_ignorableWhitespace(
+        ISAXContentHandler* iface,
+        const WCHAR *chars, int len)
+{
+    return contentHandler_ignorableWhitespace(chars, len);
+}
+
+static HRESULT WINAPI saxContentHandler_processingInstruction(
+        ISAXContentHandler* iface,
+        const WCHAR *target, int target_len,
+        const WCHAR *data, int data_len)
+{
+    return contentHandler_processingInstruction(target, target_len, data, data_len);
+}
+
+static HRESULT WINAPI saxContentHandler_skippedEntity(
+        ISAXContentHandler* iface,
+        const WCHAR *name, int len)
+{
+    return contentHandler_skippedEntity(name, len);
+}
+
+static const ISAXContentHandlerVtbl contentHandlerVtbl =
+{
+    saxContentHandler_QueryInterface,
+    saxContentHandler_AddRef,
+    saxContentHandler_Release,
+    saxContentHandler_putDocumentLocator,
+    saxContentHandler_startDocument,
+    saxContentHandler_endDocument,
+    saxContentHandler_startPrefixMapping,
+    saxContentHandler_endPrefixMapping,
+    saxContentHandler_startElement,
+    saxContentHandler_endElement,
+    saxContentHandler_characters,
+    saxContentHandler_ignorableWhitespace,
+    saxContentHandler_processingInstruction,
+    saxContentHandler_skippedEntity
 };
 
-static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
+static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
 
-static HRESULT WINAPI isaxattributes_QueryInterface(
-        ISAXAttributes* iface,
+
+static HRESULT WINAPI vbSaxContentHandler_QueryInterface(
+        IVBSAXContentHandler* iface,
         REFIID riid,
         void **ppvObject)
 {
     *ppvObject = NULL;
 
-    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
+    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IVBSAXContentHandler))
     {
         *ppvObject = iface;
     }
@@ -1476,62 +1643,485 @@ static HRESULT WINAPI isaxattributes_QueryInterface(
     return S_OK;
 }
 
-static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
+static ULONG WINAPI vbSaxContentHandler_AddRef(
+        IVBSAXContentHandler* iface)
 {
-    return 2;
+    return contentHandler_AddRef();
 }
 
-static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
+static ULONG WINAPI vbSaxContentHandler_Release(
+        IVBSAXContentHandler* iface)
 {
-    return 1;
+    return contentHandler_Release();
 }
 
-static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
+static HRESULT WINAPI vbSaxContentHandler_GetTypeInfoCount( IVBSAXContentHandler* iface, UINT* pctinfo )
 {
-    *length = 3;
-    return S_OK;
+    return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxattributes_getURI(
-    ISAXAttributes* iface,
-    int nIndex,
-    const WCHAR **pUrl,
-    int *pUriSize)
+static HRESULT WINAPI vbSaxContentHandler_GetTypeInfo(
+        IVBSAXContentHandler* iface,
+        UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
 {
-    ok(0, "unexpected call\n");
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxattributes_getLocalName(
-    ISAXAttributes* iface,
-    int nIndex,
-    const WCHAR **pLocalName,
-    int *pLocalNameLength)
+static HRESULT WINAPI vbSaxContentHandler_GetIDsOfNames(
+        IVBSAXContentHandler* iface,
+        REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+        LCID lcid, DISPID* rgDispId)
 {
-    ok(0, "unexpected call\n");
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxattributes_getQName(
-    ISAXAttributes* iface,
-    int index,
-    const WCHAR **QName,
-    int *QNameLength)
+static HRESULT WINAPI vbSaxContentHandler_Invoke(
+        IVBSAXContentHandler* iface,
+        DISPID dispIdMember, REFIID riid, LCID lcid,
+        WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo, UINT* puArgErr)
 {
-    static const WCHAR attrqnamesW[][15] = {L"a:attr1junk",
-                                            L"attr2junk",
-                                            L"attr3"};
-    static const int attrqnamelen[] = {7, 5, 5};
+    return E_NOTIMPL;
+}
 
-    ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
 
-    if (index >= 0 && index <= 2) {
-        *QName = attrqnamesW[index];
-        *QNameLength = attrqnamelen[index];
-    } else {
-        *QName = NULL;
-        *QNameLength = 0;
-    }
+static HRESULT WINAPI vbSaxContentHandler_putDocumentLocator(
+        IVBSAXContentHandler* iface,
+        IVBSAXLocator *oLocator)
+{
+    struct any_locator put_locator;
+    put_locator.type = VB_SAXObjectType;
+    put_locator.pointer.vb_locator = oLocator;
+
+    return contentHandler_putDocumentLocator(put_locator);
+}
+
+static HRESULT WINAPI vbSaxContentHandler_startDocument(
+        IVBSAXContentHandler* iface)
+{
+    return contentHandler_startDocument();
+}
+
+static HRESULT WINAPI vbSaxContentHandler_endDocument(
+        IVBSAXContentHandler* iface)
+{
+    return contentHandler_endDocument();
+}
+
+static HRESULT WINAPI vbSaxContentHandler_startPrefixMapping(
+        IVBSAXContentHandler* iface,
+        BSTR *prefix, BSTR *uri)
+{
+    return contentHandler_startPrefixMapping(*prefix, SysStringLen(*prefix),
+        *uri, SysStringLen(*uri));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_endPrefixMapping(
+        IVBSAXContentHandler* iface,
+        BSTR *prefix)
+{
+    return contentHandler_endPrefixMapping(*prefix, SysStringLen(*prefix));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_startElement(
+        IVBSAXContentHandler* iface,
+        BSTR *uri, BSTR *localname, BSTR *qname,
+        IVBSAXAttributes *saxattr)
+{
+    struct any_attributes attributes;
+    attributes.type = VB_SAXObjectType;
+    attributes.pointer.vb_attributes = saxattr;
+
+    if (*uri) ok(*uri != NULL, "*uri == NULL\n");
+    else todo_wine ok(*uri != NULL, "*uri == NULL\n");
+    if (*localname) ok(*localname != NULL, "*localname == NULL\n");
+    else todo_wine ok(*localname != NULL, "*localname == NULL\n");
+    ok(*qname != NULL, "*qname == NULL\n");
+
+    return contentHandler_startElement(
+        *uri, SysStringLen(*uri), *localname, SysStringLen(*localname),
+        *qname, SysStringLen(*qname), attributes);
+}
+
+static HRESULT WINAPI vbSaxContentHandler_endElement(
+        IVBSAXContentHandler* iface,
+        BSTR *uri, BSTR *localname, BSTR *qname)
+{
+    if (*uri) ok(*uri != NULL, "*uri == NULL\n");
+    else todo_wine ok(*uri != NULL, "*uri == NULL\n");
+    if (*localname) ok(*localname != NULL, "*localname == NULL\n");
+    else todo_wine ok(*localname != NULL, "*localname == NULL\n");
+    ok(*qname != NULL, "*qname == NULL\n");
+
+    return contentHandler_endElement(
+        *uri, SysStringLen(*uri), *localname, SysStringLen(*localname),
+        *qname, SysStringLen(*qname));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_characters(
+        IVBSAXContentHandler* iface,
+        BSTR *chars)
+{
+    return contentHandler_characters(*chars, SysStringLen(*chars));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_ignorableWhitespace(
+        IVBSAXContentHandler* iface,
+        BSTR *chars)
+{
+    return contentHandler_ignorableWhitespace(*chars, SysStringLen(*chars));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_processingInstruction(
+        IVBSAXContentHandler* iface,
+        BSTR *target, BSTR *data)
+{
+    return contentHandler_processingInstruction(
+        *target, SysStringLen(*target), *data, SysStringLen(*data));
+}
+
+static HRESULT WINAPI vbSaxContentHandler_skippedEntity(
+        IVBSAXContentHandler* iface,
+        BSTR *name)
+{
+    return contentHandler_skippedEntity(*name, SysStringLen(*name));
+}
+
+static const IVBSAXContentHandlerVtbl vbContentHandlerVtbl =
+{
+    vbSaxContentHandler_QueryInterface,
+    vbSaxContentHandler_AddRef,
+    vbSaxContentHandler_Release,
+    vbSaxContentHandler_GetTypeInfoCount,
+    vbSaxContentHandler_GetTypeInfo,
+    vbSaxContentHandler_GetIDsOfNames,
+    vbSaxContentHandler_Invoke,
+    vbSaxContentHandler_putDocumentLocator,
+    vbSaxContentHandler_startDocument,
+    vbSaxContentHandler_endDocument,
+    vbSaxContentHandler_startPrefixMapping,
+    vbSaxContentHandler_endPrefixMapping,
+    vbSaxContentHandler_startElement,
+    vbSaxContentHandler_endElement,
+    vbSaxContentHandler_characters,
+    vbSaxContentHandler_ignorableWhitespace,
+    vbSaxContentHandler_processingInstruction,
+    vbSaxContentHandler_skippedEntity
+};
+
+static IVBSAXContentHandler vbContentHandler = { &vbContentHandlerVtbl };
+
+static ULONG errorHandler_AddRef(void)
+{
+    return 2;
+}
+
+static ULONG errorHandler_Release(void)
+{
+    return 1;
+}
+
+static HRESULT errorHandler_error(
+        struct any_locator pLocator,
+        const WCHAR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT errorHandler_fatalError(
+        struct any_locator pLocator,
+        const WCHAR *message,
+        HRESULT hr)
+{
+    struct call_entry call;
+
+    init_call_entry(locator, &call);
+    call.id  = EH_FATALERROR;
+    call.ret = hr;
+
+    add_call(sequences, CONTENT_HANDLER_INDEX, &call);
+
+    get_expected_ret();
+    return S_OK;
+}
+
+static HRESULT errorHandler_ignorableWarning(
+        struct any_locator pLocator,
+        const WCHAR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    ok(0, "unexpected call\n");
+    return S_OK;
+}
+
+static HRESULT WINAPI isaxerrorHandler_QueryInterface(
+        ISAXErrorHandler* iface,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
+    {
+        *ppvObject = iface;
+    }
+    else
+    {
+        return E_NOINTERFACE;
+    }
+
+    return S_OK;
+}
+
+static ULONG WINAPI isaxerrorHandler_AddRef(
+        ISAXErrorHandler* iface)
+{
+    return errorHandler_AddRef();
+}
+
+static ULONG WINAPI isaxerrorHandler_Release(
+        ISAXErrorHandler* iface)
+{
+    return errorHandler_Release();
+}
+
+static HRESULT WINAPI isaxerrorHandler_error(
+        ISAXErrorHandler* iface,
+        ISAXLocator *pLocator,
+        const WCHAR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    struct any_locator loc;
+    loc.type = SAXObjectType;
+    loc.pointer.locator = pLocator;
+    return errorHandler_error(loc, pErrorMessage, hrErrorCode);
+}
+
+static HRESULT WINAPI isaxerrorHandler_fatalError(
+        ISAXErrorHandler* iface,
+        ISAXLocator *pLocator,
+        const WCHAR *message,
+        HRESULT hr)
+{
+    struct any_locator loc;
+    loc.type = SAXObjectType;
+    loc.pointer.locator = pLocator;
+    return errorHandler_fatalError(loc, message, hr);
+}
+
+static HRESULT WINAPI isaxerrorHandler_ignorableWarning(
+        ISAXErrorHandler* iface,
+        ISAXLocator *pLocator,
+        const WCHAR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    struct any_locator loc;
+    loc.type = SAXObjectType;
+    loc.pointer.locator = pLocator;
+    return errorHandler_ignorableWarning(loc, pErrorMessage, hrErrorCode);
+}
+
+static const ISAXErrorHandlerVtbl errorHandlerVtbl =
+{
+    isaxerrorHandler_QueryInterface,
+    isaxerrorHandler_AddRef,
+    isaxerrorHandler_Release,
+    isaxerrorHandler_error,
+    isaxerrorHandler_fatalError,
+    isaxerrorHandler_ignorableWarning
+};
+
+static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
+
+static HRESULT WINAPI ivbsaxerrorHandler_QueryInterface(
+        IVBSAXErrorHandler* iface,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IVBSAXErrorHandler))
+    {
+        *ppvObject = iface;
+    }
+    else
+    {
+        return E_NOINTERFACE;
+    }
+
+    return S_OK;
+}
+
+static ULONG WINAPI ivbsaxerrorHandler_AddRef(
+        IVBSAXErrorHandler* iface)
+{
+    return errorHandler_AddRef();
+}
+
+static ULONG WINAPI ivbsaxerrorHandler_Release(
+        IVBSAXErrorHandler* iface)
+{
+    return errorHandler_Release();
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_GetTypeInfoCount( IVBSAXErrorHandler* iface, UINT* pctinfo )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_GetTypeInfo(
+        IVBSAXErrorHandler* iface,
+        UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_GetIDsOfNames(
+        IVBSAXErrorHandler* iface,
+        REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+        LCID lcid, DISPID* rgDispId)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_Invoke(
+        IVBSAXErrorHandler* iface,
+        DISPID dispIdMember, REFIID riid, LCID lcid,
+        WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo, UINT* puArgErr)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_error(
+        IVBSAXErrorHandler* iface,
+        IVBSAXLocator *pLocator,
+        BSTR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    struct any_locator loc;
+    loc.type = VB_SAXObjectType;
+    loc.pointer.vb_locator = pLocator;
+    return errorHandler_error(loc, *pErrorMessage, hrErrorCode);
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_fatalError(
+        IVBSAXErrorHandler* iface,
+        IVBSAXLocator *pLocator,
+        BSTR *message,
+        HRESULT hr)
+{
+    struct any_locator loc;
+    loc.type = VB_SAXObjectType;
+    loc.pointer.vb_locator = pLocator;
+    return errorHandler_fatalError(loc, *message, hr);
+}
+
+static HRESULT WINAPI ivbsaxerrorHandler_ignorableWarning(
+        IVBSAXErrorHandler* iface,
+        IVBSAXLocator *pLocator,
+        BSTR *pErrorMessage,
+        HRESULT hrErrorCode)
+{
+    struct any_locator loc;
+    loc.type = VB_SAXObjectType;
+    loc.pointer.vb_locator = pLocator;
+    return errorHandler_ignorableWarning(loc, *pErrorMessage, hrErrorCode);
+}
+
+static const IVBSAXErrorHandlerVtbl vbErrorHandlerVtbl =
+{
+    ivbsaxerrorHandler_QueryInterface,
+    ivbsaxerrorHandler_AddRef,
+    ivbsaxerrorHandler_Release,
+    ivbsaxerrorHandler_GetTypeInfoCount,
+    ivbsaxerrorHandler_GetTypeInfo,
+    ivbsaxerrorHandler_GetIDsOfNames,
+    ivbsaxerrorHandler_Invoke,
+    ivbsaxerrorHandler_error,
+    ivbsaxerrorHandler_fatalError,
+    ivbsaxerrorHandler_ignorableWarning
+};
+
+static IVBSAXErrorHandler vbErrorHandler = { &vbErrorHandlerVtbl };
+
+static HRESULT WINAPI isaxattributes_QueryInterface(
+        ISAXAttributes* iface,
+        REFIID riid,
+        void **ppvObject)
+{
+    *ppvObject = NULL;
+
+    if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
+    {
+        *ppvObject = iface;
+    }
+    else
+    {
+        return E_NOINTERFACE;
+    }
+
+    return S_OK;
+}
+
+static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
+{
+    *length = 3;
+    return S_OK;
+}
+
+static HRESULT WINAPI isaxattributes_getURI(
+    ISAXAttributes* iface,
+    int nIndex,
+    const WCHAR **pUrl,
+    int *pUriSize)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI isaxattributes_getLocalName(
+    ISAXAttributes* iface,
+    int nIndex,
+    const WCHAR **pLocalName,
+    int *pLocalNameLength)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI isaxattributes_getQName(
+    ISAXAttributes* iface,
+    int index,
+    const WCHAR **QName,
+    int *QNameLength)
+{
+    static const WCHAR attrqnamesW[][15] = {L"a:attr1junk",
+                                            L"attr2junk",
+                                            L"attr3"};
+    static const int attrqnamelen[] = {7, 5, 5};
+
+    ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
+
+    if (index >= 0 && index <= 2) {
+        *QName = attrqnamesW[index];
+        *QNameLength = attrqnamelen[index];
+    } else {
+        *QName = NULL;
+        *QNameLength = 0;
+    }
 
     return S_OK;
 }
@@ -1676,6 +2266,7 @@ static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
 struct saxlexicalhandler
 {
     ISAXLexicalHandler ISAXLexicalHandler_iface;
+    IVBSAXLexicalHandler IVBSAXLexicalHandler_iface;
     LONG ref;
 
     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
@@ -1686,44 +2277,50 @@ static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexica
     return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
 }
 
-static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
+static inline struct saxlexicalhandler *impl_from_IVBSAXLexicalHandler( IVBSAXLexicalHandler *iface )
 {
-    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
+    return CONTAINING_RECORD(iface, struct saxlexicalhandler, IVBSAXLexicalHandler_iface);
+}
 
+static HRESULT saxlexical_QueryInterface(struct saxlexicalhandler *handler, REFIID riid, void **out)
+{
     *out = NULL;
 
     if (IsEqualGUID(riid, &IID_IUnknown))
     {
-        *out = iface;
+        *out = &handler->ISAXLexicalHandler_iface;
         ok(0, "got unexpected IID_IUnknown query\n");
     }
     else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
     {
         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
-        *out = iface;
+        *out = &handler->ISAXLexicalHandler_iface;
+    }
+    else if (IsEqualGUID(riid, &IID_IVBSAXLexicalHandler))
+    {
+        if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
+        *out = &handler->IVBSAXLexicalHandler_iface;
     }
 
     if (*out)
-        ISAXLexicalHandler_AddRef(iface);
+        ISAXLexicalHandler_AddRef(&handler->ISAXLexicalHandler_iface);
     else
         return E_NOINTERFACE;
 
     return S_OK;
 }
 
-static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
+static ULONG saxlexical_AddRef(struct saxlexicalhandler *handler)
 {
-    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
     return InterlockedIncrement(&handler->ref);
 }
 
-static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
+static ULONG saxlexical_Release(struct saxlexicalhandler *handler)
 {
-    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
     return InterlockedDecrement(&handler->ref);
 }
 
-static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
+static HRESULT saxlexical_startDTD(
     const WCHAR * pName, int nName, const WCHAR * pPublicId,
     int nPublicId, const WCHAR * pSystemId, int nSystemId)
 {
@@ -1731,27 +2328,25 @@ static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
+static HRESULT saxlexical_endDTD(void)
 {
     ok(0, "call not expected\n");
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
-    const WCHAR * pName, int nName)
+static HRESULT saxlexical_startEntity(const WCHAR * pName, int nName)
 {
     ok(0, "call not expected\n");
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
-    const WCHAR * pName, int nName)
+static HRESULT saxlexical_endEntity(const WCHAR * pName, int nName)
 {
     ok(0, "call not expected\n");
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
+static HRESULT saxlexical_startCDATA(void)
 {
     struct call_entry call;
 
@@ -1762,7 +2357,7 @@ static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
     return get_expected_ret();
 }
 
-static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
+static HRESULT saxlexical_endCDATA(void)
 {
     struct call_entry call;
 
@@ -1773,13 +2368,70 @@ static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
     return get_expected_ret();
 }
 
-static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
-    const WCHAR * pChars, int nChars)
+static HRESULT saxlexical_comment(const WCHAR * pChars, int nChars)
 {
     ok(0, "call not expected\n");
     return E_NOTIMPL;
 }
 
+static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
+{
+    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
+    return saxlexical_QueryInterface(handler, riid, out);
+}
+
+static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
+{
+    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
+    return saxlexical_AddRef(handler);
+}
+
+static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
+{
+    struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
+    return saxlexical_Release(handler);
+}
+
+static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
+    const WCHAR * pName, int nName, const WCHAR * pPublicId,
+    int nPublicId, const WCHAR * pSystemId, int nSystemId)
+{
+    return saxlexical_startDTD(pName, nName, pPublicId, nPublicId, pSystemId, nSystemId);
+}
+
+static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
+{
+    return saxlexical_endDTD();
+}
+
+static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
+    const WCHAR * pName, int nName)
+{
+    return saxlexical_startEntity(pName, nName);
+}
+
+static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
+    const WCHAR * pName, int nName)
+{
+    return saxlexical_endEntity(pName, nName);
+}
+
+static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
+{
+    return saxlexical_startCDATA();
+}
+
+static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
+{
+    return saxlexical_endCDATA();
+}
+
+static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
+    const WCHAR * pChars, int nChars)
+{
+    return saxlexical_comment(pChars, nChars);
+}
+
 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
 {
    isaxlexical_QueryInterface,
@@ -1794,9 +2446,115 @@ static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
    isaxlexical_comment
 };
 
+static HRESULT WINAPI ivbsaxlexical_QueryInterface(IVBSAXLexicalHandler* iface, REFIID riid, void **out)
+{
+    struct saxlexicalhandler *handler = impl_from_IVBSAXLexicalHandler(iface);
+    return saxlexical_QueryInterface(handler, riid, out);
+}
+
+static ULONG WINAPI ivbsaxlexical_AddRef(IVBSAXLexicalHandler* iface)
+{
+    struct saxlexicalhandler *handler = impl_from_IVBSAXLexicalHandler(iface);
+    return saxlexical_AddRef(handler);
+}
+
+static ULONG WINAPI ivbsaxlexical_Release(IVBSAXLexicalHandler* iface)
+{
+    struct saxlexicalhandler *handler = impl_from_IVBSAXLexicalHandler(iface);
+    return saxlexical_Release(handler);
+}
+
+static HRESULT WINAPI ivbsaxlexical_GetTypeInfoCount( IVBSAXLexicalHandler* iface, UINT* pctinfo )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxlexical_GetTypeInfo(
+        IVBSAXLexicalHandler* iface,
+        UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxlexical_GetIDsOfNames(
+        IVBSAXLexicalHandler* iface,
+        REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+        LCID lcid, DISPID* rgDispId)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxlexical_Invoke(
+        IVBSAXLexicalHandler* iface,
+        DISPID dispIdMember, REFIID riid, LCID lcid,
+        WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo, UINT* puArgErr)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ivbsaxlexical_startDTD(IVBSAXLexicalHandler* iface,
+        BSTR *pName, BSTR *pPublicId, BSTR *pSystemId)
+{
+    return saxlexical_startDTD(*pName, SysStringLen(*pName),
+        *pPublicId, SysStringLen(*pPublicId), *pSystemId, SysStringLen(*pSystemId));
+}
+
+static HRESULT WINAPI ivbsaxlexical_endDTD(IVBSAXLexicalHandler* iface)
+{
+    return saxlexical_endDTD();
+}
+
+static HRESULT WINAPI ivbsaxlexical_startEntity(IVBSAXLexicalHandler *iface,
+    BSTR *pName)
+{
+    return saxlexical_startEntity(*pName, SysStringLen(*pName));
+}
+
+static HRESULT WINAPI ivbsaxlexical_endEntity(IVBSAXLexicalHandler *iface,
+    BSTR *pName)
+{
+    return saxlexical_endEntity(*pName, SysStringLen(*pName));
+}
+
+static HRESULT WINAPI ivbsaxlexical_startCDATA(IVBSAXLexicalHandler *iface)
+{
+    return saxlexical_startCDATA();
+}
+
+static HRESULT WINAPI ivbsaxlexical_endCDATA(IVBSAXLexicalHandler *iface)
+{
+    return saxlexical_endCDATA();
+}
+
+static HRESULT WINAPI ivbsaxlexical_comment(IVBSAXLexicalHandler *iface,
+    BSTR *pChars)
+{
+    return saxlexical_comment(*pChars, SysStringLen(*pChars));
+}
+
+static const IVBSAXLexicalHandlerVtbl VBSAXLexicalHandlerVtbl =
+{
+   ivbsaxlexical_QueryInterface,
+   ivbsaxlexical_AddRef,
+   ivbsaxlexical_Release,
+   ivbsaxlexical_GetTypeInfoCount,
+   ivbsaxlexical_GetTypeInfo,
+   ivbsaxlexical_GetIDsOfNames,
+   ivbsaxlexical_Invoke,
+   ivbsaxlexical_startDTD,
+   ivbsaxlexical_endDTD,
+   ivbsaxlexical_startEntity,
+   ivbsaxlexical_endEntity,
+   ivbsaxlexical_startCDATA,
+   ivbsaxlexical_endCDATA,
+   ivbsaxlexical_comment
+};
+
 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
 {
     handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
+    handler->IVBSAXLexicalHandler_iface.lpVtbl = &VBSAXLexicalHandlerVtbl;
     handler->ref = 1;
     handler->qi_hr = hr;
 }
@@ -2098,7 +2856,16 @@ static const IStreamVtbl instreamVtbl = {
 
 static IStream instream = { &instreamVtbl };
 
-static struct msxmlsupported_data_t reader_support_data[] =
+static struct msxmlsupported_data_t reader_support_data[] =
+{
+    { &CLSID_SAXXMLReader,   "SAXReader"   },
+    { &CLSID_SAXXMLReader30, "SAXReader30" },
+    { &CLSID_SAXXMLReader40, "SAXReader40" },
+    { &CLSID_SAXXMLReader60, "SAXReader60" },
+    { NULL }
+};
+
+static struct msxmlsupported_data_t vb_reader_support_data[] =
 {
     { &CLSID_SAXXMLReader,   "SAXReader"   },
     { &CLSID_SAXXMLReader30, "SAXReader30" },
@@ -2107,6 +2874,27 @@ static struct msxmlsupported_data_t reader_support_data[] =
     { NULL }
 };
 
+struct typed_msxmlsupported_data_t {
+    enum sax_union_type type;
+    const struct msxmlsupported_data_t* table;
+};
+
+static struct typed_msxmlsupported_data_t typed_reader_support_data[] =
+{
+    {
+        SAXObjectType,
+        reader_support_data,
+    },
+    {
+        VB_SAXObjectType,
+        vb_reader_support_data,
+    },
+    {
+        0,
+        NULL,
+    },
+};
+
 static struct saxlexicalhandler lexicalhandler;
 static struct saxdeclhandler declhandler;
 
@@ -2128,14 +2916,133 @@ static IStream *create_test_stream(const char *data, int len)
      return stream;
 }
 
+static ULONG any_reader_Release(struct any_reader reader) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_Release(reader.pointer.reader);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_Release(reader.pointer.vb_reader);
+    }
+    return 0;
+}
+
+static HRESULT any_reader_getContentHandler(struct any_reader reader,
+        ISAXContentHandler **pContentHandler, IVBSAXContentHandler **pVBContentHandler) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_getContentHandler(reader.pointer.reader, pContentHandler);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_get_contentHandler(reader.pointer.vb_reader, pVBContentHandler);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_putContentHandler(struct any_reader reader,
+        ISAXContentHandler *pContentHandler, IVBSAXContentHandler *pVBContentHandler) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_putContentHandler(reader.pointer.reader, pContentHandler);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_putref_contentHandler(reader.pointer.vb_reader, pVBContentHandler);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_getErrorHandler(struct any_reader reader,
+        ISAXErrorHandler **pErrorHandler, IVBSAXErrorHandler **pVBErrorHandler) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_getErrorHandler(reader.pointer.reader, pErrorHandler);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_get_errorHandler(reader.pointer.vb_reader, pVBErrorHandler);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_putErrorHandler(struct any_reader reader,
+        ISAXErrorHandler *pErrorHandler, IVBSAXErrorHandler *pVBErrorHandler) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_putErrorHandler(reader.pointer.reader, pErrorHandler);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_putref_errorHandler(reader.pointer.vb_reader, pVBErrorHandler);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_getEntityResolver(struct any_reader reader,
+        ISAXEntityResolver **pEntityResolver, IVBSAXEntityResolver **pVBEntityResolver) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_getEntityResolver(reader.pointer.reader, pEntityResolver);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_get_entityResolver(reader.pointer.vb_reader, pVBEntityResolver);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_putEntityResolver(struct any_reader reader,
+        ISAXEntityResolver *pEntityResolver, IVBSAXEntityResolver *pVBEntityResolver) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_putEntityResolver(reader.pointer.reader, pEntityResolver);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_putref_entityResolver(reader.pointer.vb_reader, pVBEntityResolver);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_putProperty(struct any_reader reader, BSTR pProp, VARIANT value) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_putProperty(reader.pointer.reader, pProp, value);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_putProperty(reader.pointer.vb_reader, pProp, value);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_parse(struct any_reader reader, VARIANT varInput) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_parse(reader.pointer.reader, varInput);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_parse(reader.pointer.vb_reader, varInput);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_parseURL(struct any_reader reader, const WCHAR *url, BSTR vb_url) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_parseURL(reader.pointer.reader, url);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_parseURL(reader.pointer.vb_reader, vb_url);
+    }
+    return E_NOTIMPL;
+}
+
+static HRESULT any_reader_putFeature(struct any_reader reader, BSTR pFeature, VARIANT_BOOL vfValue) {
+    switch (reader.type) {
+    case SAXObjectType:
+        return ISAXXMLReader_putFeature(reader.pointer.reader, pFeature, vfValue);
+    case VB_SAXObjectType:
+        return IVBSAXXMLReader_putFeature(reader.pointer.vb_reader, pFeature, vfValue);
+    }
+    return E_NOTIMPL;
+}
+
 static void test_saxreader(void)
 {
-    const struct msxmlsupported_data_t *table = reader_support_data;
+    const struct typed_msxmlsupported_data_t *tables = typed_reader_support_data;
+    const struct msxmlsupported_data_t *table;
     HRESULT hr;
-    ISAXXMLReader *reader = NULL;
+    struct any_reader reader;
     VARIANT var;
     ISAXContentHandler *content;
-    ISAXErrorHandler *lpErrorHandler;
+    IVBSAXContentHandler *vb_content;
+    ISAXErrorHandler *error;
+    IVBSAXErrorHandler *vb_error;
     SAFEARRAY *sa;
     SAFEARRAYBOUND SADim[1];
     char *ptr = NULL;
@@ -2143,404 +3050,450 @@ static void test_saxreader(void)
     ULONG written;
     HANDLE file;
     static const CHAR testXmlA[] = "test.xml";
+    static const WCHAR testXmlW[] = L"test.xml";
+    BSTR testXmlBstr = _bstr_(testXmlA);
     IXMLDOMDocument *doc;
     char seqname[50];
     VARIANT_BOOL v;
 
-    while (table->clsid)
-    {
-        struct call_entry *test_seq;
-        ISAXEntityResolver *resolver;
-        BSTR str;
-
-        if (!is_clsid_supported(table->clsid, reader_support_data))
+    while (tables->table) {
+        table = tables->table;
+        while (table->clsid)
         {
-            table++;
-            continue;
-        }
-
-        hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
-        EXPECT_HR(hr, S_OK);
-        g_reader = reader;
+            struct call_entry *test_seq;
+            ISAXEntityResolver *resolver;
+            IVBSAXEntityResolver *vb_resolver;
+            BSTR str;
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
-            msxml_version = 4;
-        else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            msxml_version = 6;
-        else
-            msxml_version = 0;
+            if (!is_clsid_supported(table->clsid, tables->table))
+            {
+                table++;
+                continue;
+            }
 
-        /* crashes on old versions */
-        if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
-            !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            hr = ISAXXMLReader_getContentHandler(reader, NULL);
-            EXPECT_HR(hr, E_POINTER);
+            switch (tables->type) {
+            case SAXObjectType:
+                reader.type = tables->type;
+                hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader.pointer.reader);
+                EXPECT_HR(hr, S_OK);
+                break;
+            case VB_SAXObjectType:
+                reader.type = tables->type;
+                hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IVBSAXXMLReader, (void**)&reader.pointer.vb_reader);
+                EXPECT_HR(hr, S_OK);
+                break;
+            }
+            g_reader = reader;
 
-            hr = ISAXXMLReader_getErrorHandler(reader, NULL);
-            EXPECT_HR(hr, E_POINTER);
-        }
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
+                msxml_version = 4;
+            else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                msxml_version = 6;
+            else
+                msxml_version = 0;
 
-        hr = ISAXXMLReader_getContentHandler(reader, &content);
-        EXPECT_HR(hr, S_OK);
-        ok(content == NULL, "Expected %p, got %p\n", NULL, content);
+            /* crashes on old versions */
+            if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
+                !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                hr = any_reader_getContentHandler(reader, NULL, NULL);
+                EXPECT_HR(hr, E_POINTER);
 
-        hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
-        EXPECT_HR(hr, S_OK);
-        ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
+                hr = any_reader_getErrorHandler(reader, NULL, NULL);
+                EXPECT_HR(hr, E_POINTER);
+            }
 
-        hr = ISAXXMLReader_putContentHandler(reader, NULL);
-        EXPECT_HR(hr, S_OK);
+            hr = any_reader_getContentHandler(reader, &content, &vb_content);
+            EXPECT_HR(hr, S_OK);
+            switch (reader.type) {
+            case SAXObjectType:
+                ok(content == NULL, "Expected %p, got %p\n", NULL, content);
+                break;
+            case VB_SAXObjectType:
+                ok(vb_content == NULL, "Expected %p, got %p\n", NULL, vb_content);
+                break;
+            }
 
-        hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
-        EXPECT_HR(hr, S_OK);
+            hr = any_reader_getErrorHandler(reader, &error, &vb_error);
+            EXPECT_HR(hr, S_OK);
+            switch (reader.type) {
+            case SAXObjectType:
+                ok(error == NULL, "Expected %p, got %p\n", NULL, error);
+                break;
+            case VB_SAXObjectType:
+                ok(vb_error == NULL, "Expected %p, got %p\n", NULL, vb_error);
+                break;
+            }
 
-        hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
-        EXPECT_HR(hr, S_OK);
+            hr = any_reader_putContentHandler(reader, NULL, NULL);
+            EXPECT_HR(hr, S_OK);
 
-        hr = ISAXXMLReader_getContentHandler(reader, &content);
-        EXPECT_HR(hr, S_OK);
-        ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
+            hr = any_reader_putContentHandler(reader, &contentHandler, &vbContentHandler);
+            EXPECT_HR(hr, S_OK);
 
-        V_VT(&var) = VT_BSTR;
-        V_BSTR(&var) = SysAllocString(szSimpleXML);
+            hr = any_reader_putErrorHandler(reader, &errorHandler, &vbErrorHandler);
+            EXPECT_HR(hr, S_OK);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_test1_alternate;
-        else
-            test_seq = content_handler_test1;
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
+            hr = any_reader_getContentHandler(reader, &content, &vb_content);
+            EXPECT_HR(hr, S_OK);
+            switch (reader.type) {
+            case SAXObjectType:
+                ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
+                break;
+            case VB_SAXObjectType:
+                ok(vb_content == &vbContentHandler, "Expected %p, got %p\n", &vbContentHandler, vb_content);
+                break;
+            }
 
-        VariantClear(&var);
+            V_VT(&var) = VT_BSTR;
+            V_BSTR(&var) = SysAllocString(szSimpleXML);
 
-        SADim[0].lLbound = 0;
-        SADim[0].cElements = sizeof(testXML)-1;
-        sa = SafeArrayCreate(VT_UI1, 1, SADim);
-        SafeArrayAccessData(sa, (void**)&ptr);
-        memcpy(ptr, testXML, sizeof(testXML)-1);
-        SafeArrayUnaccessData(sa);
-        V_VT(&var) = VT_ARRAY|VT_UI1;
-        V_ARRAY(&var) = sa;
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_test1_alternate;
+            else
+                test_seq = content_handler_test1;
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
+            VariantClear(&var);
 
-        SafeArrayDestroy(sa);
+            SADim[0].lLbound = 0;
+            SADim[0].cElements = sizeof(testXML)-1;
+            sa = SafeArrayCreate(VT_UI1, 1, SADim);
+            SafeArrayAccessData(sa, (void**)&ptr);
+            memcpy(ptr, testXML, sizeof(testXML)-1);
+            SafeArrayUnaccessData(sa);
+            V_VT(&var) = VT_ARRAY|VT_UI1;
+            V_ARRAY(&var) = sa;
 
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = NULL;
-        hr = ISAXXMLReader_parse(reader, var);
-        ok(hr == E_INVALIDARG, "got %#x\n", hr);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
 
-        V_VT(&var) = VT_DISPATCH;
-        V_DISPATCH(&var) = NULL;
-        hr = ISAXXMLReader_parse(reader, var);
-        ok(hr == E_INVALIDARG, "got %#x\n", hr);
+            SafeArrayDestroy(sa);
 
-        stream = create_test_stream(testXML, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = NULL;
+            hr = any_reader_parse(reader, var);
+            ok(hr == E_INVALIDARG, "got %#x\n", hr);
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
+            V_VT(&var) = VT_DISPATCH;
+            V_DISPATCH(&var) = NULL;
+            hr = any_reader_parse(reader, var);
+            ok(hr == E_INVALIDARG, "got %#x\n", hr);
 
-        IStream_Release(stream);
+            stream = create_test_stream(testXML, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        stream = create_test_stream(test_attributes, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
-            test_seq = content_handler_test_attributes_alternate_4;
-        else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_test_attributes_alternate_6;
-        else
-            test_seq = content_handler_test_attributes;
+            IStream_Release(stream);
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
+            stream = create_test_stream(test_attributes, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
-        else
-            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
+                test_seq = content_handler_test_attributes_alternate_4;
+            else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_test_attributes_alternate_6;
+            else
+                test_seq = content_handler_test_attributes;
 
-        IStream_Release(stream);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
 
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)&instream;
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
+            else
+                ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
 
-        test_seq = read_test_seq;
-        read_cnt = 0;
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok(read_cnt == 7, "read_cnt = %d\n", read_cnt);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE);
+            IStream_Release(stream);
 
-        V_VT(&var) = VT_BSTR;
-        V_BSTR(&var) = SysAllocString(carriage_ret_test);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)&instream;
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_test2_alternate;
-        else
-            test_seq = content_handler_test2;
+            test_seq = read_test_seq;
+            read_cnt = 0;
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok(read_cnt == 7, "read_cnt = %d\n", read_cnt);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE);
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
+            V_VT(&var) = VT_BSTR;
+            V_BSTR(&var) = SysAllocString(carriage_ret_test);
 
-        VariantClear(&var);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_test2_alternate;
+            else
+                test_seq = content_handler_test2;
 
-        /* from file url */
-        file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-        ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
-        WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
-        CloseHandle(file);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_test1_alternate;
-        else
-            test_seq = content_handler_test1;
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parseURL(reader, L"test.xml");
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
+            VariantClear(&var);
 
-        /* error handler */
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_testerror_alternate;
-        else
-            test_seq = content_handler_testerror;
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parseURL(reader, L"test.xml");
-        EXPECT_HR(hr, E_FAIL);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
+            /* from file url */
+            file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+            ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
+            WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
+            CloseHandle(file);
 
-        /* callback ret values */
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            test_seq = content_handler_test_callback_rets_alt;
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_test1_alternate;
+            else
+                test_seq = content_handler_test1;
             set_expected_seq(test_seq);
-            hr = ISAXXMLReader_parseURL(reader, L"test.xml");
+            hr = any_reader_parseURL(reader, testXmlW, testXmlBstr);
             EXPECT_HR(hr, S_OK);
-        }
-        else
-        {
-            test_seq = content_handler_test_callback_rets;
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
+
+            /* error handler */
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_testerror_alternate;
+            else
+                test_seq = content_handler_testerror;
             set_expected_seq(test_seq);
-            hr = ISAXXMLReader_parseURL(reader, L"test.xml");
-            EXPECT_HR(hr, S_FALSE);
-        }
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
+            hr = any_reader_parseURL(reader, testXmlW, testXmlBstr);
+            EXPECT_HR(hr, E_FAIL);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
 
-        DeleteFileA(testXmlA);
+            /* callback ret values */
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                test_seq = content_handler_test_callback_rets_alt;
+                set_expected_seq(test_seq);
+                hr = any_reader_parseURL(reader, testXmlW, testXmlBstr);
+                EXPECT_HR(hr, S_OK);
+            }
+            else
+            {
+                test_seq = content_handler_test_callback_rets;
+                set_expected_seq(test_seq);
+                hr = any_reader_parseURL(reader, testXmlW, testXmlBstr);
+                EXPECT_HR(hr, S_FALSE);
+            }
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
 
-        /* parse from IXMLDOMDocument */
-        hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
-                &IID_IXMLDOMDocument, (void**)&doc);
-        EXPECT_HR(hr, S_OK);
+            DeleteFileA(testXmlA);
 
-        str = SysAllocString(szSimpleXML);
-        hr = IXMLDOMDocument_loadXML(doc, str, &v);
-        EXPECT_HR(hr, S_OK);
-        SysFreeString(str);
+            /* parse from IXMLDOMDocument */
+            hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
+                                  &IID_IXMLDOMDocument, (void**)&doc);
+            EXPECT_HR(hr, S_OK);
 
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)doc;
+            str = SysAllocString(szSimpleXML);
+            hr = IXMLDOMDocument_loadXML(doc, str, &v);
+            EXPECT_HR(hr, S_OK);
+            SysFreeString(str);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-            test_seq = content_handler_test2_alternate;
-        else
-            test_seq = content_handler_test2;
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)doc;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
-        IXMLDOMDocument_Release(doc);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+                test_seq = content_handler_test2_alternate;
+            else
+                test_seq = content_handler_test2;
 
-        /* xml:space test */
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            test_seq = xmlspaceattr_test_alternate;
-        }
-        else
-            test_seq = xmlspaceattr_test;
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
+            IXMLDOMDocument_Release(doc);
 
-        set_expected_seq(test_seq);
-        V_VT(&var) = VT_BSTR;
-        V_BSTR(&var) = _bstr_(xmlspace_attr);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
+            /* xml:space test */
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                test_seq = xmlspaceattr_test_alternate;
+            }
+            else
+                test_seq = xmlspaceattr_test;
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
-        }
-        else
-            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
+            set_expected_seq(test_seq);
+            V_VT(&var) = VT_BSTR;
+            V_BSTR(&var) = _bstr_(xmlspace_attr);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
 
-        /* switch off 'namespaces' feature */
-        hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
-        EXPECT_HR(hr, S_OK);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
+            }
+            else
+                ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
 
-        stream = create_test_stream(test_attributes, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            /* switch off 'namespaces' feature */
+            hr = any_reader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
+            EXPECT_HR(hr, S_OK);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            test_seq = content_handler_test_attributes_alt_no_ns;
-        }
-        else
-            test_seq = content_handler_test_attributes;
+            stream = create_test_stream(test_attributes, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
-        IStream_Release(stream);
-        hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
-        EXPECT_HR(hr, S_OK);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                test_seq = content_handler_test_attributes_alt_no_ns;
+            }
+            else
+                test_seq = content_handler_test_attributes;
 
-        /* switch off 'namespace-prefixes' feature */
-        hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
-        EXPECT_HR(hr, S_OK);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
+            IStream_Release(stream);
+            hr = any_reader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
+            EXPECT_HR(hr, S_OK);
 
-        stream = create_test_stream(test_attributes, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            /* switch off 'namespace-prefixes' feature */
+            hr = any_reader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
+            EXPECT_HR(hr, S_OK);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            test_seq = content_handler_test_attributes_alt_no_prefix;
-        }
-        else
-            test_seq = content_handler_test_attributes_no_prefix;
+            stream = create_test_stream(test_attributes, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
-        IStream_Release(stream);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                test_seq = content_handler_test_attributes_alt_no_prefix;
+            }
+            else
+                test_seq = content_handler_test_attributes_no_prefix;
 
-        hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
-        EXPECT_HR(hr, S_OK);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
+            IStream_Release(stream);
 
-        /* attribute normalization */
-        stream = create_test_stream(attribute_normalize, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            hr = any_reader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
+            EXPECT_HR(hr, S_OK);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
-        {
-            test_seq = attribute_norm_alt;
-        }
-        else
-            test_seq = attribute_norm;
+            /* attribute normalization */
+            stream = create_test_stream(attribute_normalize, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        EXPECT_HR(hr, S_OK);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
-        IStream_Release(stream);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
+            {
+                test_seq = attribute_norm_alt;
+            }
+            else
+                test_seq = attribute_norm;
 
-        resolver = (void*)0xdeadbeef;
-        hr = ISAXXMLReader_getEntityResolver(reader, &resolver);
-        ok(hr == S_OK, "got 0x%08x\n", hr);
-        ok(resolver == NULL, "got %p\n", resolver);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            EXPECT_HR(hr, S_OK);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
+            IStream_Release(stream);
 
-        hr = ISAXXMLReader_putEntityResolver(reader, NULL);
-        ok(hr == S_OK || broken(hr == E_FAIL), "got 0x%08x\n", hr);
+            resolver = (void*)0xdeadbeef;
+            vb_resolver = (void*)0xdeadbeef;
+            hr = any_reader_getEntityResolver(reader, &resolver, &vb_resolver);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
+            switch (reader.type) {
+            case SAXObjectType:
+                ok(resolver == NULL, "got %p\n", resolver);
+                break;
+            case VB_SAXObjectType:
+                ok(vb_resolver == NULL, "got %p\n", vb_resolver);
+                break;
+            }
 
-        /* CDATA sections */
-        init_saxlexicalhandler(&lexicalhandler, S_OK);
+            hr = any_reader_putEntityResolver(reader, NULL, NULL);
+            ok(hr == S_OK || broken(hr == E_FAIL), "got 0x%08x\n", hr);
 
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface;
-        hr = ISAXXMLReader_putProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), var);
-        ok(hr == S_OK, "got 0x%08x\n", hr);
+            /* CDATA sections */
+            init_saxlexicalhandler(&lexicalhandler, S_OK);
 
-        stream = create_test_stream(test_cdata_xml, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface;
+            hr = any_reader_putProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), var);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
-            test_seq = cdata_test_alt;
-        else
-            test_seq = cdata_test;
+            stream = create_test_stream(test_cdata_xml, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        ok(hr == S_OK, "got 0x%08x\n", hr);
-        sprintf(seqname, "%s: cdata test", table->name);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
+                test_seq = cdata_test_alt;
+            else
+                test_seq = cdata_test;
 
-        IStream_Release(stream);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
+            sprintf(seqname, "%s: cdata test", table->name);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
 
-        /* 2. CDATA sections */
-        stream = create_test_stream(test2_cdata_xml, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            IStream_Release(stream);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
-            test_seq = cdata_test2_alt;
-        else
-            test_seq = cdata_test2;
+            /* 2. CDATA sections */
+            stream = create_test_stream(test2_cdata_xml, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        ok(hr == S_OK, "got 0x%08x\n", hr);
-        sprintf(seqname, "%s: cdata test 2", table->name);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
+                test_seq = cdata_test2_alt;
+            else
+                test_seq = cdata_test2;
 
-        IStream_Release(stream);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
+            sprintf(seqname, "%s: cdata test 2", table->name);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
 
-        /* 3. CDATA sections */
-        stream = create_test_stream(test3_cdata_xml, -1);
-        V_VT(&var) = VT_UNKNOWN;
-        V_UNKNOWN(&var) = (IUnknown*)stream;
+            IStream_Release(stream);
 
-        if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
-            IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
-            test_seq = cdata_test3_alt;
-        else
-            test_seq = cdata_test3;
+            /* 3. CDATA sections */
+            stream = create_test_stream(test3_cdata_xml, -1);
+            V_VT(&var) = VT_UNKNOWN;
+            V_UNKNOWN(&var) = (IUnknown*)stream;
 
-        set_expected_seq(test_seq);
-        hr = ISAXXMLReader_parse(reader, var);
-        ok(hr == S_OK, "got 0x%08x\n", hr);
-        sprintf(seqname, "%s: cdata test 3", table->name);
-        ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
+            if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
+                IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
+                test_seq = cdata_test3_alt;
+            else
+                test_seq = cdata_test3;
 
-        IStream_Release(stream);
+            set_expected_seq(test_seq);
+            hr = any_reader_parse(reader, var);
+            ok(hr == S_OK, "got 0x%08x\n", hr);
+            sprintf(seqname, "%s: cdata test 3", table->name);
+            ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
 
-        ISAXXMLReader_Release(reader);
-        table++;
+            IStream_Release(stream);
+
+            any_reader_Release(reader);
+            table++;
+        }
+        tables++;
     }
 
     free_bstrs();
@@ -6017,6 +6970,7 @@ START_TEST(saxreader)
     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
 
     get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
+    get_class_support_data(vb_reader_support_data, &IID_IVBSAXXMLReader);
 
     test_saxreader();
     test_saxreader_properties();
-- 
2.26.2



More information about the wine-devel mailing list