[MSXML3] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / msxml3 / mxwriter.c
index 0069e41..a5bfdf1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *    MXWriter implementation
  *
- * Copyright 2011-2012 Nikolay Sivov for CodeWeavers
+ * Copyright 2011-2014, 2016 Nikolay Sivov for CodeWeavers
  * Copyright 2011 Thomas Mullaly
  *
  * This library is free software; you can redistribute it and/or
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-
 #define COBJMACROS
-#include <config.h>
+#include "config.h"
 
-//#include <stdarg.h>
+#include <stdarg.h>
 #ifdef HAVE_LIBXML2
 # include <libxml/parser.h>
 #endif
 
-#include <windef.h>
-#include <winbase.h>
-#include <ole2.h>
+#include "windef.h"
+#include "winbase.h"
+#include "ole2.h"
 
-#include <msxml6.h>
+#include "msxml6.h"
 
-#include <wine/debug.h>
+#include "wine/debug.h"
+#include "wine/list.h"
 
 #include "msxml_private.h"
 
@@ -46,6 +44,10 @@ static const WCHAR emptyW[] = {0};
 static const WCHAR spaceW[] = {' '};
 static const WCHAR quotW[]  = {'\"'};
 static const WCHAR closetagW[] = {'>','\r','\n'};
+static const WCHAR crlfW[] = {'\r','\n'};
+static const WCHAR entityW[] = {'<','!','E','N','T','I','T','Y',' '};
+static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
+static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
 
 /* should be ordered as encoding names are sorted */
 typedef enum
@@ -97,13 +99,6 @@ static const struct xml_encoding_data xml_encoding_map[] = {
     { utf8W,        XmlEncoding_UTF8,      CP_UTF8 }
 };
 
-typedef enum
-{
-    OutputBuffer_Native  = 0x001,
-    OutputBuffer_Encoded = 0x010,
-    OutputBuffer_Both    = 0x100
-} output_mode;
-
 typedef enum
 {
     MXWriter_BOM = 0,
@@ -122,6 +117,7 @@ typedef enum
 
 typedef struct
 {
+    struct list entry;
     char *data;
     unsigned int allocated;
     unsigned int written;
@@ -129,9 +125,10 @@ typedef struct
 
 typedef struct
 {
-    encoded_buffer utf16;
     encoded_buffer encoded;
     UINT code_page;
+    UINT utf16_total;   /* total number of bytes written since last buffer reinitialization */
+    struct list blocks; /* only used when output was not set, for BSTR case */
 } output_buffer;
 
 typedef struct
@@ -141,6 +138,13 @@ typedef struct
     ISAXContentHandler ISAXContentHandler_iface;
     ISAXLexicalHandler ISAXLexicalHandler_iface;
     ISAXDeclHandler    ISAXDeclHandler_iface;
+    ISAXDTDHandler     ISAXDTDHandler_iface;
+    ISAXErrorHandler   ISAXErrorHandler_iface;
+    IVBSAXDeclHandler  IVBSAXDeclHandler_iface;
+    IVBSAXLexicalHandler IVBSAXLexicalHandler_iface;
+    IVBSAXContentHandler IVBSAXContentHandler_iface;
+    IVBSAXDTDHandler     IVBSAXDTDHandler_iface;
+    IVBSAXErrorHandler   IVBSAXErrorHandler_iface;
 
     LONG ref;
     MSXML_VERSION class_version;
@@ -149,6 +153,10 @@ typedef struct
     BOOL prop_changed;
     BOOL cdata;
 
+    BOOL text; /* last node was text node, so we shouldn't indent next node */
+    BOOL newline; /* newline was already added as a part of previous call */
+    UINT indent; /* indentation level for next node */
+
     BSTR version;
 
     BSTR encoding; /* exact property value */
@@ -159,9 +167,8 @@ typedef struct
     BSTR element;
 
     IStream *dest;
-    ULONG dest_written;
 
-    output_buffer *buffer;
+    output_buffer buffer;
 } mxwriter;
 
 typedef struct
@@ -218,7 +225,7 @@ static xml_encoding parse_encoding_name(const WCHAR *encoding)
     int min, max, n, c;
 
     min = 0;
-    max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
+    max = ARRAY_SIZE(xml_encoding_map) - 1;
 
     while (min <= max)
     {
@@ -239,7 +246,7 @@ static xml_encoding parse_encoding_name(const WCHAR *encoding)
 
 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
 {
-    const int initial_len = 0x2000;
+    const int initial_len = 0x1000;
     buffer->data = heap_alloc(initial_len);
     if (!buffer->data) return E_OUTOFMEMORY;
 
@@ -271,126 +278,212 @@ static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
     return S_OK;
 }
 
-static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
+static HRESULT init_output_buffer(xml_encoding encoding, output_buffer *buffer)
 {
-    output_buffer *ret;
     HRESULT hr;
 
-    ret = heap_alloc(sizeof(*ret));
-    if (!ret) return E_OUTOFMEMORY;
-
-    hr = get_code_page(encoding, &ret->code_page);
-    if (hr != S_OK) {
-        heap_free(ret);
+    hr = get_code_page(encoding, &buffer->code_page);
+    if (hr != S_OK)
         return hr;
-    }
 
-    hr = init_encoded_buffer(&ret->utf16);
-    if (hr != S_OK) {
-        heap_free(ret);
+    hr = init_encoded_buffer(&buffer->encoded);
+    if (hr != S_OK)
         return hr;
-    }
-
-    if (ret->code_page == CP_UTF8) {
-        hr = init_encoded_buffer(&ret->encoded);
-        if (hr != S_OK) {
-            free_encoded_buffer(&ret->utf16);
-            heap_free(ret);
-            return hr;
-        }
-    }
-    else
-        memset(&ret->encoded, 0, sizeof(ret->encoded));
 
-    *buffer = ret;
+    list_init(&buffer->blocks);
+    buffer->utf16_total = 0;
 
     return S_OK;
 }
 
 static void free_output_buffer(output_buffer *buffer)
 {
+    encoded_buffer *cur, *cur2;
+
     free_encoded_buffer(&buffer->encoded);
-    free_encoded_buffer(&buffer->utf16);
-    heap_free(buffer);
-}
 
-static void grow_buffer(encoded_buffer *buffer, int length)
-{
-    /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
-    if (buffer->allocated < buffer->written + length + 4)
+    LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &buffer->blocks, encoded_buffer, entry)
     {
-        int grown_size = max(2*buffer->allocated, buffer->allocated + length);
-        buffer->data = heap_realloc(buffer->data, grown_size);
-        buffer->allocated = grown_size;
+        list_remove(&cur->entry);
+        free_encoded_buffer(cur);
+        heap_free(cur);
     }
 }
 
-static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
+static HRESULT write_output_buffer(mxwriter *writer, const WCHAR *data, int len)
 {
-    int length;
-    char *ptr;
+    output_buffer *buffer = &writer->buffer;
+    encoded_buffer *buff;
+    unsigned int written;
+    int src_len;
+
+    if (!len || !*data)
+        return S_OK;
+
+    src_len = len == -1 ? strlenW(data) : len;
+    if (writer->dest)
+    {
+        buff = &buffer->encoded;
 
-    if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
-        if (buffer->code_page != ~0)
+        if (buffer->code_page == ~0)
+        {
+            unsigned int avail = buff->allocated - buff->written;
+
+            src_len *= sizeof(WCHAR);
+            written = min(avail, src_len);
+
+            /* fill internal buffer first */
+            if (avail)
+            {
+                memcpy(buff->data + buff->written, data, written);
+                data += written / sizeof(WCHAR);
+                buff->written += written;
+                avail -= written;
+                src_len -= written;
+            }
+
+            if (!avail)
+            {
+                IStream_Write(writer->dest, buff->data, buff->written, &written);
+                buff->written = 0;
+                if (src_len >= buff->allocated)
+                    IStream_Write(writer->dest, data, src_len, &written);
+                else if (src_len)
+                {
+                    memcpy(buff->data, data, src_len);
+                    buff->written += src_len;
+                }
+            }
+        }
+        else
         {
-            length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
-            grow_buffer(&buffer->encoded, length);
-            ptr = buffer->encoded.data + buffer->encoded.written;
-            length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
-            buffer->encoded.written += len == -1 ? length-1 : length;
+            unsigned int avail = buff->allocated - buff->written;
+            int length;
+
+            length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, NULL, 0, NULL, NULL);
+            if (avail >= length)
+            {
+                length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL);
+                buff->written += length;
+            }
+            else
+            {
+                /* drain what we go so far */
+                if (buff->written)
+                {
+                    IStream_Write(writer->dest, buff->data, buff->written, &written);
+                    buff->written = 0;
+                    avail = buff->allocated;
+                }
+
+                if (avail >= length)
+                {
+                    length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL);
+                    buff->written += length;
+                }
+                else
+                {
+                    char *mb;
+
+                    /* if current chunk is larger than total buffer size, convert it at once using temporary allocated buffer */
+                    mb = heap_alloc(length);
+                    if (!mb)
+                        return E_OUTOFMEMORY;
+
+                    length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, mb, length, NULL, NULL);
+                    IStream_Write(writer->dest, mb, length, &written);
+                    heap_free(mb);
+                }
+            }
         }
     }
+    /* When writer has no output set we have to accumulate everything to return it later in a form of BSTR.
+       To achieve that:
+
+       - fill a buffer already allocated as part of output buffer;
+       - when current buffer is full, allocate another one and switch to it; buffers themselves never grow,
+         but are linked together, with head pointing to first allocated buffer after initial one got filled;
+       - later during get_output() contents are concatenated by copying one after another to destination BSTR buffer,
+         that's returned to the client. */
+    else
+    {
+        /* select last used block */
+        if (list_empty(&buffer->blocks))
+            buff = &buffer->encoded;
+        else
+            buff = LIST_ENTRY(list_tail(&buffer->blocks), encoded_buffer, entry);
 
-    if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
-        /* WCHAR data just copied */
-        length = len == -1 ? strlenW(data) : len;
-        if (length)
+        src_len *= sizeof(WCHAR);
+        while (src_len)
         {
-            length *= sizeof(WCHAR);
+            unsigned int avail = buff->allocated - buff->written;
+            unsigned int written = min(avail, src_len);
+
+            if (avail)
+            {
+                memcpy(buff->data + buff->written, data, written);
+                buff->written += written;
+                buffer->utf16_total += written;
+                src_len -= written;
+            }
+
+            /* alloc new block if needed and retry */
+            if (src_len)
+            {
+                encoded_buffer *next = heap_alloc(sizeof(*next));
+                HRESULT hr;
 
-            grow_buffer(&buffer->utf16, length);
-            ptr = buffer->utf16.data + buffer->utf16.written;
+                if (FAILED(hr = init_encoded_buffer(next))) {
+                    heap_free(next);
+                    return hr;
+                }
 
-            memcpy(ptr, data, length);
-            buffer->utf16.written += length;
-            ptr += length;
-            /* null termination */
-            memset(ptr, 0, sizeof(WCHAR));
+                list_add_tail(&buffer->blocks, &next->entry);
+                buff = next;
+            }
         }
     }
 
     return S_OK;
 }
 
-static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
-{
-    return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
-}
-
-static HRESULT write_output_buffer_quoted(output_buffer *buffer, const WCHAR *data, int len)
+static HRESULT write_output_buffer_quoted(mxwriter *writer, const WCHAR *data, int len)
 {
-    write_output_buffer(buffer, quotW, 1);
-    write_output_buffer(buffer, data, len);
-    write_output_buffer(buffer, quotW, 1);
+    write_output_buffer(writer, quotW, 1);
+    write_output_buffer(writer, data, len);
+    write_output_buffer(writer, quotW, 1);
 
     return S_OK;
 }
 
 /* frees buffer data, reallocates with a default lengths */
-static void close_output_buffer(mxwriter *This)
+static void close_output_buffer(mxwriter *writer)
 {
-    heap_free(This->buffer->utf16.data);
-    heap_free(This->buffer->encoded.data);
-    init_encoded_buffer(&This->buffer->utf16);
-    init_encoded_buffer(&This->buffer->encoded);
-    get_code_page(This->xml_enc, &This->buffer->code_page);
+    encoded_buffer *cur, *cur2;
+
+    heap_free(writer->buffer.encoded.data);
+
+    LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &writer->buffer.blocks, encoded_buffer, entry)
+    {
+        list_remove(&cur->entry);
+        free_encoded_buffer(cur);
+        heap_free(cur);
+    }
+
+    init_encoded_buffer(&writer->buffer.encoded);
+    get_code_page(writer->xml_enc, &writer->buffer.code_page);
+    writer->buffer.utf16_total = 0;
+    list_init(&writer->buffer.blocks);
 }
 
-/* escapes special characters like:
+/* Escapes special characters like:
    '<' -> "&lt;"
    '&' -> "&amp;"
    '"' -> "&quot;"
    '>' -> "&gt;"
+
+   On call 'len' contains a length of 'str' in chars or -1 if it's null terminated.
+   After a call it's updated with actual new length if it wasn't -1 initially.
 */
 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
 {
@@ -405,10 +498,10 @@ static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
     WCHAR *ptr, *ret;
 
     /* default buffer size to something if length is unknown */
-    conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
+    conv_len = max(2**len, default_alloc);
     ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
 
-    while (*str && p)
+    while (p)
     {
         if (ptr - ret > conv_len - grow_thresh)
         {
@@ -422,21 +515,21 @@ static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
         {
         case '<':
             memcpy(ptr, ltW, sizeof(ltW));
-            ptr += sizeof(ltW)/sizeof(WCHAR);
+            ptr += ARRAY_SIZE(ltW);
             break;
         case '&':
             memcpy(ptr, ampW, sizeof(ampW));
-            ptr += sizeof(ampW)/sizeof(WCHAR);
+            ptr += ARRAY_SIZE(ampW);
             break;
         case '>':
             memcpy(ptr, gtW, sizeof(gtW));
-            ptr += sizeof(gtW)/sizeof(WCHAR);
+            ptr += ARRAY_SIZE(gtW);
             break;
         case '"':
             if (mode == EscapeValue)
             {
                 memcpy(ptr, equotW, sizeof(equotW));
-                ptr += sizeof(equotW)/sizeof(WCHAR);
+                ptr += ARRAY_SIZE(equotW);
                 break;
             }
             /* fallthrough for text mode */
@@ -446,95 +539,114 @@ static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
         }
 
         str++;
-        if (*len != -1) p--;
+        p--;
     }
 
-    if (*len != -1) *len = ptr-ret;
+    *len = ptr-ret;
     *++ptr = 0;
 
     return ret;
 }
 
-static void write_prolog_buffer(const mxwriter *This)
+static void write_prolog_buffer(mxwriter *writer)
 {
     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
     static const WCHAR noW[] = {'n','o','\"','?','>'};
-    static const WCHAR crlfW[] = {'\r','\n'};
 
     /* version */
-    write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
-    write_output_buffer_quoted(This->buffer, This->version, -1);
+    write_output_buffer(writer, versionW, ARRAY_SIZE(versionW));
+    write_output_buffer_quoted(writer, writer->version, -1);
 
     /* encoding */
-    write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
+    write_output_buffer(writer, encodingW, ARRAY_SIZE(encodingW));
 
-    /* always write UTF-16 to WCHAR buffer */
-    write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
-    write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
-    write_output_buffer(This->buffer, quotW, 1);
+    if (writer->dest)
+        write_output_buffer(writer, writer->encoding, -1);
+    else
+        write_output_buffer(writer, utf16W, ARRAY_SIZE(utf16W) - 1);
+    write_output_buffer(writer, quotW, 1);
 
     /* standalone */
-    write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
-    if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
-        write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
+    write_output_buffer(writer, standaloneW, ARRAY_SIZE(standaloneW));
+    if (writer->props[MXWriter_Standalone] == VARIANT_TRUE)
+        write_output_buffer(writer, yesW, ARRAY_SIZE(yesW));
     else
-        write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
+        write_output_buffer(writer, noW, ARRAY_SIZE(noW));
 
-    write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
+    write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
+    writer->newline = TRUE;
 }
 
 /* Attempts to the write data from the mxwriter's buffer to
  * the destination stream (if there is one).
  */
-static HRESULT write_data_to_stream(mxwriter *This)
+static HRESULT write_data_to_stream(mxwriter *writer)
 {
-    encoded_buffer *buffer;
+    encoded_buffer *buffer = &writer->buffer.encoded;
     ULONG written = 0;
-    HRESULT hr;
 
-    if (!This->dest)
+    if (!writer->dest)
         return S_OK;
 
-    if (This->xml_enc != XmlEncoding_UTF16)
-        buffer = &This->buffer->encoded;
+    if (buffer->written == 0)
+    {
+        if (writer->xml_enc == XmlEncoding_UTF8)
+            IStream_Write(writer->dest, buffer->data, 0, &written);
+    }
     else
-        buffer = &This->buffer->utf16;
-
-    if (This->dest_written > buffer->written) {
-        ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
-        return E_FAIL;
-    } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
-        /* Windows seems to make an empty write call when the encoding is UTF-8 and
-         * all the data has been written to the stream. It doesn't seem make this call
-         * for any other encodings.
-         */
-        return S_OK;
-
-    /* Write the current content from the output buffer into 'dest'.
-     * TODO: Check what Windows does if the IStream doesn't write all of
-     *       the data we give it at once.
-     */
-    hr = IStream_Write(This->dest, buffer->data+This->dest_written,
-                         buffer->written-This->dest_written, &written);
-    if (FAILED(hr)) {
-        WARN("Failed to write data to IStream (0x%08x)\n", hr);
-        return hr;
+    {
+        IStream_Write(writer->dest, buffer->data, buffer->written, &written);
+        buffer->written = 0;
     }
 
-    This->dest_written += written;
-    return hr;
+    return S_OK;
 }
 
 /* Newly added element start tag left unclosed cause for empty elements
    we have to close it differently. */
-static void close_element_starttag(const mxwriter *This)
+static void close_element_starttag(mxwriter *writer)
 {
     static const WCHAR gtW[] = {'>'};
-    if (!This->element) return;
-    write_output_buffer(This->buffer, gtW, 1);
+    if (!writer->element) return;
+    write_output_buffer(writer, gtW, 1);
+}
+
+static void write_node_indent(mxwriter *writer)
+{
+    static const WCHAR tabW[] = {'\t'};
+    int indent = writer->indent;
+
+    if (!writer->props[MXWriter_Indent] || writer->text)
+    {
+        writer->text = FALSE;
+        return;
+    }
+
+    /* This is to workaround PI output logic that always puts newline chars,
+       document prolog PI does that too. */
+    if (!writer->newline)
+        write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
+    while (indent--)
+        write_output_buffer(writer, tabW, 1);
+
+    writer->newline = FALSE;
+    writer->text = FALSE;
+}
+
+static inline void writer_inc_indent(mxwriter *This)
+{
+    This->indent++;
+}
+
+static inline void writer_dec_indent(mxwriter *This)
+{
+    if (This->indent) This->indent--;
+    /* depth is decreased only when element is closed, meaning it's not a text node
+       at this point */
+    This->text = FALSE;
 }
 
 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
@@ -560,7 +672,6 @@ static inline HRESULT flush_output_buffer(mxwriter *This)
 static inline void reset_output_buffer(mxwriter *This)
 {
     close_output_buffer(This);
-    This->dest_written = 0;
 }
 
 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
@@ -587,16 +698,51 @@ static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
 }
 
+static inline mxwriter *impl_from_IVBSAXContentHandler(IVBSAXContentHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, IVBSAXContentHandler_iface);
+}
+
 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
 {
     return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
 }
 
+static inline mxwriter *impl_from_IVBSAXLexicalHandler(IVBSAXLexicalHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, IVBSAXLexicalHandler_iface);
+}
+
 static inline mxwriter *impl_from_ISAXDeclHandler(ISAXDeclHandler *iface)
 {
     return CONTAINING_RECORD(iface, mxwriter, ISAXDeclHandler_iface);
 }
 
+static inline mxwriter *impl_from_IVBSAXDeclHandler(IVBSAXDeclHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, IVBSAXDeclHandler_iface);
+}
+
+static inline mxwriter *impl_from_ISAXDTDHandler(ISAXDTDHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, ISAXDTDHandler_iface);
+}
+
+static inline mxwriter *impl_from_IVBSAXDTDHandler(IVBSAXDTDHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, IVBSAXDTDHandler_iface);
+}
+
+static inline mxwriter *impl_from_ISAXErrorHandler(ISAXErrorHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, ISAXErrorHandler_iface);
+}
+
+static inline mxwriter *impl_from_IVBSAXErrorHandler(IVBSAXErrorHandler *iface)
+{
+    return CONTAINING_RECORD(iface, mxwriter, IVBSAXErrorHandler_iface);
+}
+
 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
 {
     mxwriter *This = impl_from_IMXWriter( iface );
@@ -623,6 +769,34 @@ static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, voi
     {
         *obj = &This->ISAXDeclHandler_iface;
     }
+    else if ( IsEqualGUID( riid, &IID_ISAXDTDHandler ) )
+    {
+        *obj = &This->ISAXDTDHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_ISAXErrorHandler ) )
+    {
+        *obj = &This->ISAXErrorHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_IVBSAXDeclHandler ) )
+    {
+        *obj = &This->IVBSAXDeclHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_IVBSAXLexicalHandler ) )
+    {
+        *obj = &This->IVBSAXLexicalHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_IVBSAXContentHandler ) )
+    {
+        *obj = &This->IVBSAXContentHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_IVBSAXDTDHandler ) )
+    {
+        *obj = &This->IVBSAXDTDHandler_iface;
+    }
+    else if ( IsEqualGUID( riid, &IID_IVBSAXErrorHandler ) )
+    {
+        *obj = &This->IVBSAXErrorHandler_iface;
+    }
     else if (dispex_query_interface(&This->dispex, riid, obj))
     {
         return *obj ? S_OK : E_NOINTERFACE;
@@ -659,14 +833,13 @@ static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
     {
         /* Windows flushes the buffer when the interface is destroyed. */
         flush_output_buffer(This);
-        free_output_buffer(This->buffer);
+        free_output_buffer(&This->buffer);
 
         if (This->dest) IStream_Release(This->dest);
         SysFreeString(This->version);
         SysFreeString(This->encoding);
 
         SysFreeString(This->element);
-        release_dispex(&This->dispex);
         heap_free(This);
     }
 
@@ -764,22 +937,43 @@ static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
 
     if (!dest) return E_POINTER;
 
-    if (!This->dest)
+    if (This->dest)
+    {
+        /* we only support IStream output so far */
+        V_VT(dest) = VT_UNKNOWN;
+        V_UNKNOWN(dest) = (IUnknown*)This->dest;
+        IStream_AddRef(This->dest);
+    }
+    else
     {
-        HRESULT hr = flush_output_buffer(This);
+        encoded_buffer *buff;
+        char *dest_ptr;
+        HRESULT hr;
+
+        hr = flush_output_buffer(This);
         if (FAILED(hr))
             return hr;
 
         V_VT(dest)   = VT_BSTR;
-        V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
+        V_BSTR(dest) = SysAllocStringLen(NULL, This->buffer.utf16_total / sizeof(WCHAR));
+        if (!V_BSTR(dest))
+            return E_OUTOFMEMORY;
 
-        return S_OK;
-    }
+        dest_ptr = (char*)V_BSTR(dest);
+        buff = &This->buffer.encoded;
+
+        if (buff->written)
+        {
+            memcpy(dest_ptr, buff->data, buff->written);
+            dest_ptr += buff->written;
+        }
 
-    /* we only support IStream output so far */
-    V_VT(dest) = VT_UNKNOWN;
-    V_UNKNOWN(dest) = (IUnknown*)This->dest;
-    IStream_AddRef(This->dest);
+        LIST_FOR_EACH_ENTRY(buff, &This->buffer.blocks, encoded_buffer, entry)
+        {
+            memcpy(dest_ptr, buff->data, buff->written);
+            dest_ptr += buff->written;
+        }
+    }
 
     return S_OK;
 }
@@ -1045,8 +1239,8 @@ static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
     int nuri)
 {
     mxwriter *This = impl_from_ISAXContentHandler( iface );
-    FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
-    return E_NOTIMPL;
+    TRACE("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
+    return S_OK;
 }
 
 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
@@ -1055,8 +1249,42 @@ static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
     int nprefix)
 {
     mxwriter *This = impl_from_ISAXContentHandler( iface );
-    FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
-    return E_NOTIMPL;
+    TRACE("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
+    return S_OK;
+}
+
+static void mxwriter_write_attribute(mxwriter *writer, const WCHAR *qname, int qname_len,
+    const WCHAR *value, int value_len, BOOL escape)
+{
+    static const WCHAR eqW[] = {'='};
+
+    /* space separator in front of every attribute */
+    write_output_buffer(writer, spaceW, 1);
+    write_output_buffer(writer, qname, qname_len);
+    write_output_buffer(writer, eqW, 1);
+
+    if (escape)
+    {
+        WCHAR *escaped = get_escaped_string(value, EscapeValue, &value_len);
+        write_output_buffer_quoted(writer, escaped, value_len);
+        heap_free(escaped);
+    }
+    else
+        write_output_buffer_quoted(writer, value, value_len);
+}
+
+static void mxwriter_write_starttag(mxwriter *writer, const WCHAR *qname, int len)
+{
+    static const WCHAR ltW[] = {'<'};
+
+    close_element_starttag(writer);
+    set_element_name(writer, qname ? qname : emptyW, qname ? len : 0);
+
+    write_node_indent(writer);
+
+    write_output_buffer(writer, ltW, 1);
+    write_output_buffer(writer, qname ? qname : emptyW, qname ? len : 0);
+    writer_inc_indent(writer);
 }
 
 static HRESULT WINAPI SAXContentHandler_startElement(
@@ -1070,7 +1298,6 @@ static HRESULT WINAPI SAXContentHandler_startElement(
     ISAXAttributes *attr)
 {
     mxwriter *This = impl_from_ISAXContentHandler( iface );
-    static const WCHAR ltW[] = {'<'};
 
     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
@@ -1079,12 +1306,7 @@ static HRESULT WINAPI SAXContentHandler_startElement(
         (nQName == -1 && This->class_version == MSXML6))
         return E_INVALIDARG;
 
-    close_element_starttag(This);
-    set_element_name(This, QName ? QName  : emptyW,
-                           QName ? nQName : 0);
-
-    write_output_buffer(This->buffer, ltW, 1);
-    write_output_buffer(This->buffer, QName, nQName);
+    mxwriter_write_starttag(This, QName, nQName);
 
     if (attr)
     {
@@ -1099,31 +1321,16 @@ static HRESULT WINAPI SAXContentHandler_startElement(
 
         for (i = 0; i < length; i++)
         {
-            static const WCHAR eqW[] = {'='};
-            const WCHAR *str;
-            int len = 0;
+            int qname_len = 0, value_len = 0;
+            const WCHAR *qname, *value;
 
-            hr = ISAXAttributes_getQName(attr, i, &str, &len);
+            hr = ISAXAttributes_getQName(attr, i, &qname, &qname_len);
             if (FAILED(hr)) return hr;
 
-            /* space separator in front of every attribute */
-            write_output_buffer(This->buffer, spaceW, 1);
-            write_output_buffer(This->buffer, str, len);
-
-            write_output_buffer(This->buffer, eqW, 1);
-
-            len = 0;
-            hr = ISAXAttributes_getValue(attr, i, &str, &len);
+            hr = ISAXAttributes_getValue(attr, i, &value, &value_len);
             if (FAILED(hr)) return hr;
 
-            if (escape)
-            {
-                WCHAR *escaped = get_escaped_string(str, EscapeValue, &len);
-                write_output_buffer_quoted(This->buffer, escaped, len);
-                heap_free(escaped);
-            }
-            else
-                write_output_buffer_quoted(This->buffer, str, len);
+            mxwriter_write_attribute(This, qname, qname_len, value, value_len, escape);
         }
     }
 
@@ -1148,19 +1355,22 @@ static HRESULT WINAPI SAXContentHandler_endElement(
          (nQName == -1 && This->class_version == MSXML6))
         return E_INVALIDARG;
 
+    writer_dec_indent(This);
+
     if (This->element)
     {
         static const WCHAR closeW[] = {'/','>'};
-        write_output_buffer(This->buffer, closeW, 2);
+        write_output_buffer(This, closeW, 2);
     }
     else
     {
         static const WCHAR closetagW[] = {'<','/'};
         static const WCHAR gtW[] = {'>'};
 
-        write_output_buffer(This->buffer, closetagW, 2);
-        write_output_buffer(This->buffer, QName, nQName);
-        write_output_buffer(This->buffer, gtW, 1);
+        write_node_indent(This);
+        write_output_buffer(This, closetagW, 2);
+        write_output_buffer(This, QName, nQName);
+        write_output_buffer(This, gtW, 1);
     }
 
     set_element_name(This, NULL, 0);
@@ -1182,17 +1392,20 @@ static HRESULT WINAPI SAXContentHandler_characters(
     close_element_starttag(This);
     set_element_name(This, NULL, 0);
 
+    if (!This->cdata)
+        This->text = TRUE;
+
     if (nchars)
     {
         if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
-            write_output_buffer(This->buffer, chars, nchars);
+            write_output_buffer(This, chars, nchars);
         else
         {
             int len = nchars;
             WCHAR *escaped;
 
             escaped = get_escaped_string(chars, EscapeText, &len);
-            write_output_buffer(This->buffer, escaped, len);
+            write_output_buffer(This, escaped, len);
             heap_free(escaped);
         }
     }
@@ -1211,7 +1424,7 @@ static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
 
     if (!chars) return E_INVALIDARG;
 
-    write_output_buffer(This->buffer, chars, nchars);
+    write_output_buffer(This, chars, nchars);
 
     return S_OK;
 }
@@ -1231,18 +1444,20 @@ static HRESULT WINAPI SAXContentHandler_processingInstruction(
 
     if (!target) return E_INVALIDARG;
 
-    write_output_buffer(This->buffer, openpiW, sizeof(openpiW)/sizeof(WCHAR));
+    write_node_indent(This);
+    write_output_buffer(This, openpiW, ARRAY_SIZE(openpiW));
 
     if (*target)
-        write_output_buffer(This->buffer, target, ntarget);
+        write_output_buffer(This, target, ntarget);
 
     if (data && *data && ndata)
     {
-        write_output_buffer(This->buffer, spaceW, 1);
-        write_output_buffer(This->buffer, data, ndata);
+        write_output_buffer(This, spaceW, 1);
+        write_output_buffer(This, data, ndata);
     }
 
-    write_output_buffer(This->buffer, closepiW, sizeof(closepiW)/sizeof(WCHAR));
+    write_output_buffer(This, closepiW, ARRAY_SIZE(closepiW));
+    This->newline = TRUE;
 
     return S_OK;
 }
@@ -1309,42 +1524,38 @@ static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
 
     if (!name) return E_INVALIDARG;
 
-    write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
+    write_output_buffer(This, doctypeW, ARRAY_SIZE(doctypeW));
 
     if (*name)
     {
-        write_output_buffer(This->buffer, name, name_len);
-        write_output_buffer(This->buffer, spaceW, 1);
+        write_output_buffer(This, name, name_len);
+        write_output_buffer(This, spaceW, 1);
     }
 
     if (publicId)
     {
-        static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
-
-        write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
-        write_output_buffer_quoted(This->buffer, publicId, publicId_len);
+        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
+        write_output_buffer_quoted(This, publicId, publicId_len);
 
         if (!systemId) return E_INVALIDARG;
 
         if (*publicId)
-            write_output_buffer(This->buffer, spaceW, 1);
+            write_output_buffer(This, spaceW, 1);
 
-        write_output_buffer_quoted(This->buffer, systemId, systemId_len);
+        write_output_buffer_quoted(This, systemId, systemId_len);
 
         if (*systemId)
-            write_output_buffer(This->buffer, spaceW, 1);
+            write_output_buffer(This, spaceW, 1);
     }
     else if (systemId)
     {
-        static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
-
-        write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
-        write_output_buffer_quoted(This->buffer, systemId, systemId_len);
+        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
+        write_output_buffer_quoted(This, systemId, systemId_len);
         if (*systemId)
-            write_output_buffer(This->buffer, spaceW, 1);
+            write_output_buffer(This, spaceW, 1);
     }
 
-    write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
+    write_output_buffer(This, openintW, ARRAY_SIZE(openintW));
 
     return S_OK;
 }
@@ -1356,7 +1567,7 @@ static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
 
     TRACE("(%p)\n", This);
 
-    write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
+    write_output_buffer(This, closedtdW, ARRAY_SIZE(closedtdW));
 
     return S_OK;
 }
@@ -1382,7 +1593,8 @@ static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
 
     TRACE("(%p)\n", This);
 
-    write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
+    write_node_indent(This);
+    write_output_buffer(This, scdataW, ARRAY_SIZE(scdataW));
     This->cdata = TRUE;
 
     return S_OK;
@@ -1395,7 +1607,7 @@ static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
 
     TRACE("(%p)\n", This);
 
-    write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
+    write_output_buffer(This, ecdataW, ARRAY_SIZE(ecdataW));
     This->cdata = FALSE;
 
     return S_OK;
@@ -1412,11 +1624,12 @@ static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const
     if (!chars) return E_INVALIDARG;
 
     close_element_starttag(This);
+    write_node_indent(This);
 
-    write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
+    write_output_buffer(This, copenW, ARRAY_SIZE(copenW));
     if (nchars)
-        write_output_buffer(This->buffer, chars, nchars);
-    write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
+        write_output_buffer(This, chars, nchars);
+    write_output_buffer(This, ccloseW, ARRAY_SIZE(ccloseW));
 
     return S_OK;
 }
@@ -1466,14 +1679,14 @@ static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
 
     if (!name || !model) return E_INVALIDARG;
 
-    write_output_buffer(This->buffer, elementW, sizeof(elementW)/sizeof(WCHAR));
+    write_output_buffer(This, elementW, ARRAY_SIZE(elementW));
     if (n_name) {
-        write_output_buffer(This->buffer, name, n_name);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, name, n_name);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
     if (n_model)
-        write_output_buffer(This->buffer, model, n_model);
-    write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
+        write_output_buffer(This, model, n_model);
+    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
 
     return S_OK;
 }
@@ -1491,31 +1704,31 @@ static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
         debugstr_wn(value, n_value), n_value);
 
-    write_output_buffer(This->buffer, attlistW, sizeof(attlistW)/sizeof(WCHAR));
+    write_output_buffer(This, attlistW, ARRAY_SIZE(attlistW));
     if (n_element) {
-        write_output_buffer(This->buffer, element, n_element);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, element, n_element);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
 
     if (n_attr) {
-        write_output_buffer(This->buffer, attr, n_attr);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, attr, n_attr);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
 
     if (n_type) {
-        write_output_buffer(This->buffer, type, n_type);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, type, n_type);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
 
     if (n_default) {
-        write_output_buffer(This->buffer, Default, n_default);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, Default, n_default);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
 
     if (n_value)
-        write_output_buffer_quoted(This->buffer, value, n_value);
+        write_output_buffer_quoted(This, value, n_value);
 
-    write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
+    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
 
     return S_OK;
 }
@@ -1524,23 +1737,22 @@ static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
 {
     mxwriter *This = impl_from_ISAXDeclHandler( iface );
-    static const WCHAR entityW[] = {'<','!','E','N','T','I','T','Y',' '};
 
     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
         debugstr_wn(value, n_value), n_value);
 
     if (!name || !value) return E_INVALIDARG;
 
-    write_output_buffer(This->buffer, entityW, sizeof(entityW)/sizeof(WCHAR));
+    write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
     if (n_name) {
-        write_output_buffer(This->buffer, name, n_name);
-        write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
+        write_output_buffer(This, name, n_name);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
     }
 
     if (n_value)
-        write_output_buffer_quoted(This->buffer, value, n_value);
+        write_output_buffer_quoted(This, value, n_value);
 
-    write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
+    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
 
     return S_OK;
 }
@@ -1550,9 +1762,34 @@ static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
     const WCHAR *systemId, int n_systemId)
 {
     mxwriter *This = impl_from_ISAXDeclHandler( iface );
-    FIXME("(%p)->(%s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
+
+    TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
-    return E_NOTIMPL;
+
+    if (!name || !systemId) return E_INVALIDARG;
+
+    write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
+    if (n_name) {
+        write_output_buffer(This, name, n_name);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+    }
+
+    if (publicId)
+    {
+        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
+        write_output_buffer_quoted(This, publicId, n_publicId);
+        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer_quoted(This, systemId, n_systemId);
+    }
+    else
+    {
+        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
+        write_output_buffer_quoted(This, systemId, n_systemId);
+    }
+
+    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+
+    return S_OK;
 }
 
 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
@@ -1565,86 +1802,861 @@ static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
     SAXDeclHandler_externalEntityDecl
 };
 
-static const tid_t mxwriter_iface_tids[] = {
-    IMXWriter_tid,
-    0
-};
+/*** IVBSAXDeclHandler ***/
+static HRESULT WINAPI VBSAXDeclHandler_QueryInterface(IVBSAXDeclHandler *iface,
+    REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
 
-static dispex_static_data_t mxwriter_dispex = {
-    NULL,
-    IMXWriter_tid,
-    NULL,
-    mxwriter_iface_tids
-};
+static ULONG WINAPI VBSAXDeclHandler_AddRef(IVBSAXDeclHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
 
-HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
+static ULONG WINAPI VBSAXDeclHandler_Release(IVBSAXDeclHandler *iface)
 {
-    static const WCHAR version10W[] = {'1','.','0',0};
-    mxwriter *This;
-    HRESULT hr;
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
 
-    TRACE("(%p, %p)\n", outer, ppObj);
+static HRESULT WINAPI VBSAXDeclHandler_GetTypeInfoCount(IVBSAXDeclHandler *iface, UINT* pctinfo)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
+}
 
-    if (outer) FIXME("support aggregation, outer\n");
+static HRESULT WINAPI VBSAXDeclHandler_GetTypeInfo(IVBSAXDeclHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
+}
 
-    This = heap_alloc( sizeof (*This) );
-    if(!This)
-        return E_OUTOFMEMORY;
+static HRESULT WINAPI VBSAXDeclHandler_GetIDsOfNames(IVBSAXDeclHandler *iface, REFIID riid, LPOLESTR* rgszNames,
+    UINT cNames, LCID lcid, DISPID* rgDispId )
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
+}
 
-    This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
-    This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
-    This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
-    This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
-    This->ref = 1;
-    This->class_version = version;
+static HRESULT WINAPI VBSAXDeclHandler_Invoke(IVBSAXDeclHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+    return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+}
 
-    This->props[MXWriter_BOM] = VARIANT_TRUE;
-    This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
-    This->props[MXWriter_Indent] = VARIANT_FALSE;
-    This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
-    This->props[MXWriter_Standalone] = VARIANT_FALSE;
-    This->prop_changed = FALSE;
-    This->encoding = SysAllocString(utf16W);
-    This->version  = SysAllocString(version10W);
-    This->xml_enc  = XmlEncoding_UTF16;
+static HRESULT WINAPI VBSAXDeclHandler_elementDecl(IVBSAXDeclHandler *iface, BSTR *name, BSTR *model)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
 
-    This->element = NULL;
-    This->cdata = FALSE;
+    TRACE("(%p)->(%p %p)\n", This, name, model);
 
-    This->dest = NULL;
-    This->dest_written = 0;
+    if (!name || !model)
+        return E_POINTER;
 
-    hr = alloc_output_buffer(This->xml_enc, &This->buffer);
-    if (hr != S_OK) {
-        SysFreeString(This->encoding);
-        SysFreeString(This->version);
-        heap_free(This);
-        return hr;
-    }
+    return ISAXDeclHandler_elementDecl(&This->ISAXDeclHandler_iface, *name, -1, *model, -1);
+}
 
-    init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
+static HRESULT WINAPI VBSAXDeclHandler_attributeDecl(IVBSAXDeclHandler *iface,
+    BSTR *element, BSTR *attr, BSTR *type, BSTR *default_value, BSTR *value)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
 
-    *ppObj = &This->IMXWriter_iface;
+    TRACE("(%p)->(%p %p %p %p %p)\n", This, element, attr, type, default_value, value);
 
-    TRACE("returning iface %p\n", *ppObj);
+    if (!element || !attr || !type || !default_value || !value)
+        return E_POINTER;
 
-    return S_OK;
+    return ISAXDeclHandler_attributeDecl(&This->ISAXDeclHandler_iface, *element, -1, *attr, -1, *type, -1,
+        *default_value, -1, *value, -1);
 }
 
-static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
+static HRESULT WINAPI VBSAXDeclHandler_internalEntityDecl(IVBSAXDeclHandler *iface, BSTR *name, BSTR *value)
 {
-    mxattributes *This = impl_from_IMXAttributes( iface );
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
 
-    TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
+    TRACE("(%p)->(%p %p)\n", This, name, value);
 
-    *ppObj = NULL;
+    if (!name || !value)
+        return E_POINTER;
 
-    if ( IsEqualGUID( riid, &IID_IUnknown )  ||
-         IsEqualGUID( riid, &IID_IDispatch ) ||
-         IsEqualGUID( riid, &IID_IMXAttributes ))
-    {
-        *ppObj = iface;
-    }
+    return ISAXDeclHandler_internalEntityDecl(&This->ISAXDeclHandler_iface, *name, -1, *value, -1);
+}
+
+static HRESULT WINAPI VBSAXDeclHandler_externalEntityDecl(IVBSAXDeclHandler *iface,
+    BSTR *name, BSTR *publicid, BSTR *systemid)
+{
+    mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
+
+    TRACE("(%p)->(%p %p %p)\n", This, name, publicid, systemid);
+
+    if (!name || !publicid || !systemid)
+        return E_POINTER;
+
+    return ISAXDeclHandler_externalEntityDecl(&This->ISAXDeclHandler_iface, *name, -1, *publicid, -1, *systemid, -1);
+}
+
+static const IVBSAXDeclHandlerVtbl VBSAXDeclHandlerVtbl = {
+    VBSAXDeclHandler_QueryInterface,
+    VBSAXDeclHandler_AddRef,
+    VBSAXDeclHandler_Release,
+    VBSAXDeclHandler_GetTypeInfoCount,
+    VBSAXDeclHandler_GetTypeInfo,
+    VBSAXDeclHandler_GetIDsOfNames,
+    VBSAXDeclHandler_Invoke,
+    VBSAXDeclHandler_elementDecl,
+    VBSAXDeclHandler_attributeDecl,
+    VBSAXDeclHandler_internalEntityDecl,
+    VBSAXDeclHandler_externalEntityDecl
+};
+
+/*** IVBSAXLexicalHandler ***/
+static HRESULT WINAPI VBSAXLexicalHandler_QueryInterface(IVBSAXLexicalHandler *iface,
+    REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI VBSAXLexicalHandler_AddRef(IVBSAXLexicalHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI VBSAXLexicalHandler_Release(IVBSAXLexicalHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_GetTypeInfoCount(IVBSAXLexicalHandler *iface, UINT* pctinfo)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_GetTypeInfo(IVBSAXLexicalHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_GetIDsOfNames(IVBSAXLexicalHandler *iface, REFIID riid, LPOLESTR* rgszNames,
+    UINT cNames, LCID lcid, DISPID* rgDispId )
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_Invoke(IVBSAXLexicalHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_startDTD(IVBSAXLexicalHandler *iface, BSTR *name, BSTR *publicId, BSTR *systemId)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+
+    TRACE("(%p)->(%p %p %p)\n", This, name, publicId, systemId);
+
+    if (!name || !publicId || !systemId)
+        return E_POINTER;
+
+    return ISAXLexicalHandler_startDTD(&This->ISAXLexicalHandler_iface, *name, -1, *publicId, -1, *systemId, -1);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_endDTD(IVBSAXLexicalHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return ISAXLexicalHandler_endDTD(&This->ISAXLexicalHandler_iface);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_startEntity(IVBSAXLexicalHandler *iface, BSTR *name)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, name);
+
+    if (!name)
+        return E_POINTER;
+
+    return ISAXLexicalHandler_startEntity(&This->ISAXLexicalHandler_iface, *name, -1);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_endEntity(IVBSAXLexicalHandler *iface, BSTR *name)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, name);
+
+    if (!name)
+        return E_POINTER;
+
+    return ISAXLexicalHandler_endEntity(&This->ISAXLexicalHandler_iface, *name, -1);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_startCDATA(IVBSAXLexicalHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return ISAXLexicalHandler_startCDATA(&This->ISAXLexicalHandler_iface);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_endCDATA(IVBSAXLexicalHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+    return ISAXLexicalHandler_endCDATA(&This->ISAXLexicalHandler_iface);
+}
+
+static HRESULT WINAPI VBSAXLexicalHandler_comment(IVBSAXLexicalHandler *iface, BSTR *chars)
+{
+    mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, chars);
+
+    if (!chars)
+        return E_POINTER;
+
+    return ISAXLexicalHandler_comment(&This->ISAXLexicalHandler_iface, *chars, -1);
+}
+
+static const IVBSAXLexicalHandlerVtbl VBSAXLexicalHandlerVtbl = {
+    VBSAXLexicalHandler_QueryInterface,
+    VBSAXLexicalHandler_AddRef,
+    VBSAXLexicalHandler_Release,
+    VBSAXLexicalHandler_GetTypeInfoCount,
+    VBSAXLexicalHandler_GetTypeInfo,
+    VBSAXLexicalHandler_GetIDsOfNames,
+    VBSAXLexicalHandler_Invoke,
+    VBSAXLexicalHandler_startDTD,
+    VBSAXLexicalHandler_endDTD,
+    VBSAXLexicalHandler_startEntity,
+    VBSAXLexicalHandler_endEntity,
+    VBSAXLexicalHandler_startCDATA,
+    VBSAXLexicalHandler_endCDATA,
+    VBSAXLexicalHandler_comment
+};
+
+/*** IVBSAXContentHandler ***/
+static HRESULT WINAPI VBSAXContentHandler_QueryInterface(IVBSAXContentHandler *iface, REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI VBSAXContentHandler_AddRef(IVBSAXContentHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI VBSAXContentHandler_Release(IVBSAXContentHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_GetTypeInfoCount(IVBSAXContentHandler *iface, UINT* pctinfo)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_GetTypeInfo(IVBSAXContentHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_GetIDsOfNames(IVBSAXContentHandler *iface, REFIID riid, LPOLESTR* rgszNames,
+    UINT cNames, LCID lcid, DISPID* rgDispId )
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_Invoke(IVBSAXContentHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_putref_documentLocator(IVBSAXContentHandler *iface, IVBSAXLocator *locator)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    TRACE("(%p)->(%p)\n", This, locator);
+    return S_OK;
+}
+
+static HRESULT WINAPI VBSAXContentHandler_startDocument(IVBSAXContentHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return ISAXContentHandler_startDocument(&This->ISAXContentHandler_iface);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_endDocument(IVBSAXContentHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+    return ISAXContentHandler_endDocument(&This->ISAXContentHandler_iface);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_startPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix, BSTR *uri)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p %p)\n", This, prefix, uri);
+
+    if (!prefix || !uri)
+        return E_POINTER;
+
+    return ISAXContentHandler_startPrefixMapping(&This->ISAXContentHandler_iface, *prefix, -1, *uri, -1);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_endPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, prefix);
+
+    if (!prefix)
+        return E_POINTER;
+
+    return ISAXContentHandler_endPrefixMapping(&This->ISAXContentHandler_iface, *prefix, -1);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_startElement(IVBSAXContentHandler *iface,
+    BSTR *namespaceURI, BSTR *localName, BSTR *QName, IVBSAXAttributes *attrs)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p %p %p %p)\n", This, namespaceURI, localName, QName, attrs);
+
+    if (!namespaceURI || !localName || !QName)
+        return E_POINTER;
+
+    TRACE("(%s %s %s)\n", debugstr_w(*namespaceURI), debugstr_w(*localName), debugstr_w(*QName));
+
+    mxwriter_write_starttag(This, *QName, SysStringLen(*QName));
+
+    if (attrs)
+    {
+        int length, i, escape;
+        HRESULT hr;
+
+        hr = IVBSAXAttributes_get_length(attrs, &length);
+        if (FAILED(hr)) return hr;
+
+        escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
+            (This->class_version == MSXML4 || This->class_version == MSXML6);
+
+        for (i = 0; i < length; i++)
+        {
+            BSTR qname, value;
+
+            hr = IVBSAXAttributes_getQName(attrs, i, &qname);
+            if (FAILED(hr)) return hr;
+
+            hr = IVBSAXAttributes_getValue(attrs, i, &value);
+            if (FAILED(hr))
+            {
+                SysFreeString(qname);
+                return hr;
+            }
+
+            mxwriter_write_attribute(This, qname, SysStringLen(qname), value, SysStringLen(value), escape);
+            SysFreeString(qname);
+            SysFreeString(value);
+        }
+    }
+
+    return S_OK;
+}
+
+static HRESULT WINAPI VBSAXContentHandler_endElement(IVBSAXContentHandler *iface, BSTR *namespaceURI,
+    BSTR *localName, BSTR *QName)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p %p %p)\n", This, namespaceURI, localName, QName);
+
+    if (!namespaceURI || !localName || !QName)
+        return E_POINTER;
+
+    return ISAXContentHandler_endElement(&This->ISAXContentHandler_iface,
+        *namespaceURI, SysStringLen(*namespaceURI),
+        *localName, SysStringLen(*localName),
+        *QName, SysStringLen(*QName));
+}
+
+static HRESULT WINAPI VBSAXContentHandler_characters(IVBSAXContentHandler *iface, BSTR *chars)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, chars);
+
+    if (!chars)
+        return E_POINTER;
+
+    return ISAXContentHandler_characters(&This->ISAXContentHandler_iface, *chars, SysStringLen(*chars));
+}
+
+static HRESULT WINAPI VBSAXContentHandler_ignorableWhitespace(IVBSAXContentHandler *iface, BSTR *chars)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, chars);
+
+    if (!chars)
+        return E_POINTER;
+
+    return ISAXContentHandler_ignorableWhitespace(&This->ISAXContentHandler_iface, *chars, -1);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_processingInstruction(IVBSAXContentHandler *iface,
+    BSTR *target, BSTR *data)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p %p)\n", This, target, data);
+
+    if (!target || !data)
+        return E_POINTER;
+
+    return ISAXContentHandler_processingInstruction(&This->ISAXContentHandler_iface, *target, -1, *data, -1);
+}
+
+static HRESULT WINAPI VBSAXContentHandler_skippedEntity(IVBSAXContentHandler *iface, BSTR *name)
+{
+    mxwriter *This = impl_from_IVBSAXContentHandler( iface );
+
+    TRACE("(%p)->(%p)\n", This, name);
+
+    if (!name)
+        return E_POINTER;
+
+    return ISAXContentHandler_skippedEntity(&This->ISAXContentHandler_iface, *name, -1);
+}
+
+static const IVBSAXContentHandlerVtbl VBSAXContentHandlerVtbl = {
+    VBSAXContentHandler_QueryInterface,
+    VBSAXContentHandler_AddRef,
+    VBSAXContentHandler_Release,
+    VBSAXContentHandler_GetTypeInfoCount,
+    VBSAXContentHandler_GetTypeInfo,
+    VBSAXContentHandler_GetIDsOfNames,
+    VBSAXContentHandler_Invoke,
+    VBSAXContentHandler_putref_documentLocator,
+    VBSAXContentHandler_startDocument,
+    VBSAXContentHandler_endDocument,
+    VBSAXContentHandler_startPrefixMapping,
+    VBSAXContentHandler_endPrefixMapping,
+    VBSAXContentHandler_startElement,
+    VBSAXContentHandler_endElement,
+    VBSAXContentHandler_characters,
+    VBSAXContentHandler_ignorableWhitespace,
+    VBSAXContentHandler_processingInstruction,
+    VBSAXContentHandler_skippedEntity
+};
+
+static HRESULT WINAPI SAXDTDHandler_QueryInterface(ISAXDTDHandler *iface, REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_ISAXDTDHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI SAXDTDHandler_AddRef(ISAXDTDHandler *iface)
+{
+    mxwriter *This = impl_from_ISAXDTDHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI SAXDTDHandler_Release(ISAXDTDHandler *iface)
+{
+    mxwriter *This = impl_from_ISAXDTDHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI SAXDTDHandler_notationDecl(ISAXDTDHandler *iface,
+    const WCHAR *name, INT n_name,
+    const WCHAR *publicid, INT n_publicid,
+    const WCHAR *systemid, INT n_systemid)
+{
+    static const WCHAR notationW[] = {'<','!','N','O','T','A','T','I','O','N',' '};
+    mxwriter *This = impl_from_ISAXDTDHandler( iface );
+
+    TRACE("(%p)->(%s:%d, %s:%d, %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
+        debugstr_wn(publicid, n_publicid), n_publicid, debugstr_wn(systemid, n_systemid), n_systemid);
+
+    if (!name || !n_name)
+        return E_INVALIDARG;
+
+    write_output_buffer(This, notationW, ARRAY_SIZE(notationW));
+    write_output_buffer(This, name, n_name);
+
+    if (!publicid && !systemid)
+        return E_INVALIDARG;
+
+    write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+    if (publicid)
+    {
+        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
+        write_output_buffer_quoted(This, publicid, n_publicid);
+        if (systemid)
+        {
+            write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+            write_output_buffer_quoted(This, systemid, n_systemid);
+        }
+    }
+    else
+    {
+        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
+        write_output_buffer_quoted(This, systemid, n_systemid);
+    }
+
+    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+
+    return S_OK;
+}
+
+static HRESULT WINAPI SAXDTDHandler_unparsedEntityDecl(ISAXDTDHandler *iface,
+    const WCHAR *name, INT nname,
+    const WCHAR *publicid, INT npublicid,
+    const WCHAR *systemid, INT nsystemid,
+    const WCHAR *notation, INT nnotation)
+{
+    mxwriter *This = impl_from_ISAXDTDHandler( iface );
+    FIXME("(%p)->(%s:%d, %s:%d, %s:%d, %s:%d): stub\n", This, debugstr_wn(name, nname), nname,
+        debugstr_wn(publicid, npublicid), npublicid, debugstr_wn(systemid, nsystemid), nsystemid,
+        debugstr_wn(notation, nnotation), nnotation);
+    return E_NOTIMPL;
+}
+
+static const ISAXDTDHandlerVtbl SAXDTDHandlerVtbl = {
+    SAXDTDHandler_QueryInterface,
+    SAXDTDHandler_AddRef,
+    SAXDTDHandler_Release,
+    SAXDTDHandler_notationDecl,
+    SAXDTDHandler_unparsedEntityDecl
+};
+
+/*** IVBSAXDTDHandler ***/
+static HRESULT WINAPI VBSAXDTDHandler_QueryInterface(IVBSAXDTDHandler *iface, REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI VBSAXDTDHandler_AddRef(IVBSAXDTDHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI VBSAXDTDHandler_Release(IVBSAXDTDHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_GetTypeInfoCount(IVBSAXDTDHandler *iface, UINT* pctinfo)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_GetTypeInfo(IVBSAXDTDHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_GetIDsOfNames(IVBSAXDTDHandler *iface, REFIID riid, LPOLESTR* rgszNames,
+    UINT cNames, LCID lcid, DISPID* rgDispId )
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_Invoke(IVBSAXDTDHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+    return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_notationDecl(IVBSAXDTDHandler *iface, BSTR *name, BSTR *publicId, BSTR *systemId)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+
+    TRACE("(%p)->(%p %p %p)\n", This, name, publicId, systemId);
+
+    if (!name || !publicId || !systemId)
+        return E_POINTER;
+
+    return ISAXDTDHandler_notationDecl(&This->ISAXDTDHandler_iface, *name, -1, *publicId, -1, *systemId, -1);
+}
+
+static HRESULT WINAPI VBSAXDTDHandler_unparsedEntityDecl(IVBSAXDTDHandler *iface, BSTR *name, BSTR *publicId,
+    BSTR *systemId, BSTR *notation)
+{
+    mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
+
+    TRACE("(%p)->(%p %p %p %p)\n", This, name, publicId, systemId, notation);
+
+    if (!name || !publicId || !systemId || !notation)
+        return E_POINTER;
+
+    return ISAXDTDHandler_unparsedEntityDecl(&This->ISAXDTDHandler_iface, *name, -1, *publicId, -1,
+        *systemId, -1, *notation, -1);
+}
+
+static const IVBSAXDTDHandlerVtbl VBSAXDTDHandlerVtbl = {
+    VBSAXDTDHandler_QueryInterface,
+    VBSAXDTDHandler_AddRef,
+    VBSAXDTDHandler_Release,
+    VBSAXDTDHandler_GetTypeInfoCount,
+    VBSAXDTDHandler_GetTypeInfo,
+    VBSAXDTDHandler_GetIDsOfNames,
+    VBSAXDTDHandler_Invoke,
+    VBSAXDTDHandler_notationDecl,
+    VBSAXDTDHandler_unparsedEntityDecl
+};
+
+/* ISAXErrorHandler */
+static HRESULT WINAPI SAXErrorHandler_QueryInterface(ISAXErrorHandler *iface, REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI SAXErrorHandler_AddRef(ISAXErrorHandler *iface)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI SAXErrorHandler_Release(ISAXErrorHandler *iface)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI SAXErrorHandler_error(ISAXErrorHandler *iface,
+    ISAXLocator *locator, const WCHAR *message, HRESULT hr)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+
+    FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI SAXErrorHandler_fatalError(ISAXErrorHandler *iface,
+    ISAXLocator *locator, const WCHAR *message, HRESULT hr)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+
+    FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI SAXErrorHandler_ignorableWarning(ISAXErrorHandler *iface,
+    ISAXLocator *locator, const WCHAR *message, HRESULT hr)
+{
+    mxwriter *This = impl_from_ISAXErrorHandler( iface );
+
+    FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
+
+    return E_NOTIMPL;
+}
+
+static const ISAXErrorHandlerVtbl SAXErrorHandlerVtbl = {
+    SAXErrorHandler_QueryInterface,
+    SAXErrorHandler_AddRef,
+    SAXErrorHandler_Release,
+    SAXErrorHandler_error,
+    SAXErrorHandler_fatalError,
+    SAXErrorHandler_ignorableWarning
+};
+
+/*** IVBSAXErrorHandler ***/
+static HRESULT WINAPI VBSAXErrorHandler_QueryInterface(IVBSAXErrorHandler *iface, REFIID riid, void **obj)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
+}
+
+static ULONG WINAPI VBSAXErrorHandler_AddRef(IVBSAXErrorHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_AddRef(&This->IMXWriter_iface);
+}
+
+static ULONG WINAPI VBSAXErrorHandler_Release(IVBSAXErrorHandler *iface)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_Release(&This->IMXWriter_iface);
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_GetTypeInfoCount(IVBSAXErrorHandler *iface, UINT* pctinfo)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_GetTypeInfo(IVBSAXErrorHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_GetIDsOfNames(IVBSAXErrorHandler *iface, REFIID riid, LPOLESTR* rgszNames,
+    UINT cNames, LCID lcid, DISPID* rgDispId )
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_Invoke(IVBSAXErrorHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_error(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_fatalError(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI VBSAXErrorHandler_ignorableWarning(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
+{
+    mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
+    FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
+    return E_NOTIMPL;
+}
+
+static const IVBSAXErrorHandlerVtbl VBSAXErrorHandlerVtbl = {
+    VBSAXErrorHandler_QueryInterface,
+    VBSAXErrorHandler_AddRef,
+    VBSAXErrorHandler_Release,
+    VBSAXErrorHandler_GetTypeInfoCount,
+    VBSAXErrorHandler_GetTypeInfo,
+    VBSAXErrorHandler_GetIDsOfNames,
+    VBSAXErrorHandler_Invoke,
+    VBSAXErrorHandler_error,
+    VBSAXErrorHandler_fatalError,
+    VBSAXErrorHandler_ignorableWarning
+};
+
+static const tid_t mxwriter_iface_tids[] = {
+    IMXWriter_tid,
+    0
+};
+
+static dispex_static_data_t mxwriter_dispex = {
+    NULL,
+    IMXWriter_tid,
+    NULL,
+    mxwriter_iface_tids
+};
+
+HRESULT MXWriter_create(MSXML_VERSION version, void **ppObj)
+{
+    static const WCHAR version10W[] = {'1','.','0',0};
+    mxwriter *This;
+    HRESULT hr;
+
+    TRACE("(%p)\n", ppObj);
+
+    This = heap_alloc( sizeof (*This) );
+    if(!This)
+        return E_OUTOFMEMORY;
+
+    This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
+    This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
+    This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
+    This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
+    This->ISAXDTDHandler_iface.lpVtbl = &SAXDTDHandlerVtbl;
+    This->ISAXErrorHandler_iface.lpVtbl = &SAXErrorHandlerVtbl;
+    This->IVBSAXDeclHandler_iface.lpVtbl = &VBSAXDeclHandlerVtbl;
+    This->IVBSAXLexicalHandler_iface.lpVtbl = &VBSAXLexicalHandlerVtbl;
+    This->IVBSAXContentHandler_iface.lpVtbl = &VBSAXContentHandlerVtbl;
+    This->IVBSAXDTDHandler_iface.lpVtbl = &VBSAXDTDHandlerVtbl;
+    This->IVBSAXErrorHandler_iface.lpVtbl = &VBSAXErrorHandlerVtbl;
+    This->ref = 1;
+    This->class_version = version;
+
+    This->props[MXWriter_BOM] = VARIANT_TRUE;
+    This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
+    This->props[MXWriter_Indent] = VARIANT_FALSE;
+    This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
+    This->props[MXWriter_Standalone] = VARIANT_FALSE;
+    This->prop_changed = FALSE;
+    This->encoding = SysAllocString(utf16W);
+    This->version  = SysAllocString(version10W);
+    This->xml_enc  = XmlEncoding_UTF16;
+
+    This->element = NULL;
+    This->cdata = FALSE;
+    This->indent = 0;
+    This->text = FALSE;
+    This->newline = FALSE;
+
+    This->dest = NULL;
+
+    hr = init_output_buffer(This->xml_enc, &This->buffer);
+    if (hr != S_OK) {
+        SysFreeString(This->encoding);
+        SysFreeString(This->version);
+        heap_free(This);
+        return hr;
+    }
+
+    init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
+
+    *ppObj = &This->IMXWriter_iface;
+
+    TRACE("returning iface %p\n", *ppObj);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
+{
+    mxattributes *This = impl_from_IMXAttributes( iface );
+
+    TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
+
+    *ppObj = NULL;
+
+    if ( IsEqualGUID( riid, &IID_IUnknown )  ||
+         IsEqualGUID( riid, &IID_IDispatch ) ||
+         IsEqualGUID( riid, &IID_IMXAttributes ))
+    {
+        *ppObj = iface;
+    }
     else if ( IsEqualGUID( riid, &IID_ISAXAttributes ))
     {
         *ppObj = &This->ISAXAttributes_iface;
@@ -1696,7 +2708,6 @@ static ULONG WINAPI MXAttributes_Release(IMXAttributes *iface)
             SysFreeString(This->attr[i].value);
         }
 
-        release_dispex(&This->dispex);
         heap_free(This->attr);
         heap_free(This);
     }
@@ -1804,11 +2815,32 @@ static HRESULT WINAPI MXAttributes_clear(IMXAttributes *iface)
     return S_OK;
 }
 
+static mxattribute *get_attribute_byindex(mxattributes *attrs, int index)
+{
+    if (index < 0 || index >= attrs->length) return NULL;
+    return &attrs->attr[index];
+}
+
 static HRESULT WINAPI MXAttributes_removeAttribute(IMXAttributes *iface, int index)
 {
     mxattributes *This = impl_from_IMXAttributes( iface );
-    FIXME("(%p)->(%d): stub\n", This, index);
-    return E_NOTIMPL;
+    mxattribute *dst;
+
+    TRACE("(%p)->(%d)\n", This, index);
+
+    if (!(dst = get_attribute_byindex(This, index))) return E_INVALIDARG;
+
+    /* no need to remove last attribute, just make it inaccessible */
+    if (index + 1 == This->length)
+    {
+        This->length--;
+        return S_OK;
+    }
+
+    memmove(dst, dst + 1, (This->length-index-1)*sizeof(*dst));
+    This->length--;
+
+    return S_OK;
 }
 
 static HRESULT WINAPI MXAttributes_setAttribute(IMXAttributes *iface, int index,
@@ -1831,29 +2863,61 @@ static HRESULT WINAPI MXAttributes_setLocalName(IMXAttributes *iface, int index,
     BSTR localName)
 {
     mxattributes *This = impl_from_IMXAttributes( iface );
-    FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(localName));
-    return E_NOTIMPL;
+    mxattribute *attr;
+
+    TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(localName));
+
+    if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
+
+    SysFreeString(attr->local);
+    attr->local = SysAllocString(localName);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI MXAttributes_setQName(IMXAttributes *iface, int index, BSTR QName)
 {
     mxattributes *This = impl_from_IMXAttributes( iface );
-    FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(QName));
-    return E_NOTIMPL;
+    mxattribute *attr;
+
+    TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(QName));
+
+    if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
+
+    SysFreeString(attr->qname);
+    attr->qname = SysAllocString(QName);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI MXAttributes_setURI(IMXAttributes *iface, int index, BSTR uri)
 {
     mxattributes *This = impl_from_IMXAttributes( iface );
-    FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(uri));
-    return E_NOTIMPL;
+    mxattribute *attr;
+
+    TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(uri));
+
+    if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
+
+    SysFreeString(attr->uri);
+    attr->uri = SysAllocString(uri);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI MXAttributes_setValue(IMXAttributes *iface, int index, BSTR value)
 {
     mxattributes *This = impl_from_IMXAttributes( iface );
-    FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(value));
-    return E_NOTIMPL;
+    mxattribute *attr;
+
+    TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(value));
+
+    if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
+
+    SysFreeString(attr->value);
+    attr->value = SysAllocString(value);
+
+    return S_OK;
 }
 
 static const IMXAttributesVtbl MXAttributesVtbl = {
@@ -2254,25 +3318,61 @@ static HRESULT WINAPI VBSAXAttributes_get_length(IVBSAXAttributes* iface, int *l
 static HRESULT WINAPI VBSAXAttributes_getURI(IVBSAXAttributes* iface, int index, BSTR *uri)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *uriW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getURI(&This->ISAXAttributes_iface, index, (const WCHAR**)uri, &len);
+    TRACE("(%p)->(%d %p)\n", This, index, uri);
+
+    if (!uri)
+        return E_POINTER;
+
+    *uri = NULL;
+    hr = ISAXAttributes_getURI(&This->ISAXAttributes_iface, index, &uriW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(uriW, len, uri);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getLocalName(IVBSAXAttributes* iface, int index, BSTR *name)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *nameW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, index, (const WCHAR**)name, &len);
+    TRACE("(%p)->(%d %p)\n", This, index, name);
+
+    if (!name)
+        return E_POINTER;
+
+    *name = NULL;
+    hr = ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, index, &nameW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(nameW, len, name);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getQName(IVBSAXAttributes* iface, int index, BSTR *qname)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *qnameW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getQName(&This->ISAXAttributes_iface, index, (const WCHAR**)qname, &len);
+    TRACE("(%p)->(%d %p)\n", This, index, qname);
+
+    if (!qname)
+        return E_POINTER;
+
+    *qname = NULL;
+    hr = ISAXAttributes_getQName(&This->ISAXAttributes_iface, index, &qnameW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(qnameW, len, qname);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getIndexFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name, int *index)
@@ -2289,58 +3389,130 @@ static HRESULT WINAPI VBSAXAttributes_getIndexFromQName(IVBSAXAttributes* iface,
             SysStringLen(qname), index);
 }
 
-static HRESULT WINAPI VBSAXAttributes_getType(IVBSAXAttributes* iface, int index,BSTR *type)
+static HRESULT WINAPI VBSAXAttributes_getType(IVBSAXAttributes* iface, int index, BSTR *type)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *typeW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getType(&This->ISAXAttributes_iface, index, (const WCHAR**)type, &len);
+    TRACE("(%p)->(%d %p)\n", This, index, type);
+
+    if (!type)
+        return E_POINTER;
+
+    *type = NULL;
+    hr = ISAXAttributes_getType(&This->ISAXAttributes_iface, index, &typeW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(typeW, len, type);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getTypeFromName(IVBSAXAttributes* iface, BSTR uri,
     BSTR name, BSTR *type)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *typeW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
-            name, SysStringLen(name), (const WCHAR**)type, &len);
+    TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(name), type);
+
+    if (!type)
+        return E_POINTER;
+
+    *type = NULL;
+    hr = ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
+            name, SysStringLen(name), &typeW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(typeW, len, type);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getTypeFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *type)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *typeW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
-            (const WCHAR**)type, &len);
+    TRACE("(%p)->(%s %p)\n", This, debugstr_w(qname), type);
+
+    if (!type)
+        return E_POINTER;
+
+    *type = NULL;
+    hr = ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
+            &typeW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(typeW, len, type);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getValue(IVBSAXAttributes* iface, int index, BSTR *value)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *valueW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getValue(&This->ISAXAttributes_iface, index, (const WCHAR**)value, &len);
+    TRACE("(%p)->(%d %p)\n", This, index, value);
+
+    if (!value)
+        return E_POINTER;
+
+    *value = NULL;
+    hr = ISAXAttributes_getValue(&This->ISAXAttributes_iface, index, &valueW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(valueW, len, value);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getValueFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name,
     BSTR *value)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *valueW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
-            name, SysStringLen(name), (const WCHAR**)value, &len);
+    TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(name), value);
+
+    if (!value)
+        return E_POINTER;
+
+    *value = NULL;
+    hr = ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
+            name, SysStringLen(name), &valueW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(valueW, len, value);
 }
 
 static HRESULT WINAPI VBSAXAttributes_getValueFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *value)
 {
     mxattributes *This = impl_from_IVBSAXAttributes( iface );
+    const WCHAR *valueW;
+    HRESULT hr;
     int len;
 
-    return ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
-        (const WCHAR**)value, &len);
+    TRACE("(%p)->(%s %p)\n", This, debugstr_w(qname), value);
+
+    if (!value)
+        return E_POINTER;
+
+    *value = NULL;
+    hr = ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
+        &valueW, &len);
+    if (FAILED(hr))
+        return hr;
+
+    return return_bstrn(valueW, len, value);
 }
 
 static const struct IVBSAXAttributesVtbl VBSAXAttributesVtbl =
@@ -2378,12 +3550,12 @@ static dispex_static_data_t mxattrs_dispex = {
     mxattrs_iface_tids
 };
 
-HRESULT SAXAttributes_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
+HRESULT SAXAttributes_create(MSXML_VERSION version, void **ppObj)
 {
     static const int default_count = 10;
     mxattributes *This;
 
-    TRACE("(%p, %p)\n", outer, ppObj);
+    TRACE("(%p)\n", ppObj);
 
     This = heap_alloc( sizeof (*This) );
     if( !This )