xref: /reactos/dll/win32/msxml3/mxwriter.c (revision 58588b76)
1 /*
2  *    MXWriter implementation
3  *
4  * Copyright 2011-2014, 2016 Nikolay Sivov for CodeWeavers
5  * Copyright 2011 Thomas Mullaly
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #define COBJMACROS
23 #include "config.h"
24 
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
29 
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
33 
34 #include "msxml6.h"
35 
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 
39 #include "msxml_private.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
42 
43 static const WCHAR emptyW[] = {0};
44 static const WCHAR spaceW[] = {' '};
45 static const WCHAR quotW[]  = {'\"'};
46 static const WCHAR closetagW[] = {'>','\r','\n'};
47 static const WCHAR crlfW[] = {'\r','\n'};
48 static const WCHAR entityW[] = {'<','!','E','N','T','I','T','Y',' '};
49 static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
50 static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
51 
52 /* should be ordered as encoding names are sorted */
53 typedef enum
54 {
55     XmlEncoding_ISO_8859_1 = 0,
56     XmlEncoding_ISO_8859_13,
57     XmlEncoding_ISO_8859_15,
58     XmlEncoding_ISO_8859_2,
59     XmlEncoding_ISO_8859_3,
60     XmlEncoding_ISO_8859_4,
61     XmlEncoding_ISO_8859_5,
62     XmlEncoding_ISO_8859_7,
63     XmlEncoding_ISO_8859_9,
64     XmlEncoding_UTF16,
65     XmlEncoding_UTF8,
66     XmlEncoding_Unknown
67 } xml_encoding;
68 
69 struct xml_encoding_data
70 {
71     const WCHAR *encoding;
72     xml_encoding enc;
73     UINT cp;
74 };
75 
76 static const WCHAR iso_8859_1W[] = {'i','s','o','-','8','8','5','9','-','1',0};
77 static const WCHAR iso_8859_2W[] = {'i','s','o','-','8','8','5','9','-','2',0};
78 static const WCHAR iso_8859_3W[] = {'i','s','o','-','8','8','5','9','-','3',0};
79 static const WCHAR iso_8859_4W[] = {'i','s','o','-','8','8','5','9','-','4',0};
80 static const WCHAR iso_8859_5W[] = {'i','s','o','-','8','8','5','9','-','5',0};
81 static const WCHAR iso_8859_7W[] = {'i','s','o','-','8','8','5','9','-','7',0};
82 static const WCHAR iso_8859_9W[] = {'i','s','o','-','8','8','5','9','-','9',0};
83 static const WCHAR iso_8859_13W[] = {'i','s','o','-','8','8','5','9','-','1','3',0};
84 static const WCHAR iso_8859_15W[] = {'i','s','o','-','8','8','5','9','-','1','5',0};
85 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
86 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
87 
88 static const struct xml_encoding_data xml_encoding_map[] = {
89     { iso_8859_1W,  XmlEncoding_ISO_8859_1,  28591 },
90     { iso_8859_13W, XmlEncoding_ISO_8859_13, 28603 },
91     { iso_8859_15W, XmlEncoding_ISO_8859_15, 28605 },
92     { iso_8859_2W,  XmlEncoding_ISO_8859_2,  28592 },
93     { iso_8859_3W,  XmlEncoding_ISO_8859_3,  28593 },
94     { iso_8859_4W,  XmlEncoding_ISO_8859_4,  28594 },
95     { iso_8859_5W,  XmlEncoding_ISO_8859_5,  28595 },
96     { iso_8859_7W,  XmlEncoding_ISO_8859_7,  28597 },
97     { iso_8859_9W,  XmlEncoding_ISO_8859_9,  28599 },
98     { utf16W,       XmlEncoding_UTF16,          ~0 },
99     { utf8W,        XmlEncoding_UTF8,      CP_UTF8 }
100 };
101 
102 typedef enum
103 {
104     MXWriter_BOM = 0,
105     MXWriter_DisableEscaping,
106     MXWriter_Indent,
107     MXWriter_OmitXmlDecl,
108     MXWriter_Standalone,
109     MXWriter_LastProp
110 } mxwriter_prop;
111 
112 typedef enum
113 {
114     EscapeValue,
115     EscapeText
116 } escape_mode;
117 
118 typedef struct
119 {
120     struct list entry;
121     char *data;
122     unsigned int allocated;
123     unsigned int written;
124 } encoded_buffer;
125 
126 typedef struct
127 {
128     encoded_buffer encoded;
129     UINT code_page;
130     UINT utf16_total;   /* total number of bytes written since last buffer reinitialization */
131     struct list blocks; /* only used when output was not set, for BSTR case */
132 } output_buffer;
133 
134 typedef struct
135 {
136     DispatchEx dispex;
137     IMXWriter IMXWriter_iface;
138     ISAXContentHandler ISAXContentHandler_iface;
139     ISAXLexicalHandler ISAXLexicalHandler_iface;
140     ISAXDeclHandler    ISAXDeclHandler_iface;
141     ISAXDTDHandler     ISAXDTDHandler_iface;
142     ISAXErrorHandler   ISAXErrorHandler_iface;
143     IVBSAXDeclHandler  IVBSAXDeclHandler_iface;
144     IVBSAXLexicalHandler IVBSAXLexicalHandler_iface;
145     IVBSAXContentHandler IVBSAXContentHandler_iface;
146     IVBSAXDTDHandler     IVBSAXDTDHandler_iface;
147     IVBSAXErrorHandler   IVBSAXErrorHandler_iface;
148 
149     LONG ref;
150     MSXML_VERSION class_version;
151 
152     VARIANT_BOOL props[MXWriter_LastProp];
153     BOOL prop_changed;
154     BOOL cdata;
155 
156     BOOL text; /* last node was text node, so we shouldn't indent next node */
157     BOOL newline; /* newline was already added as a part of previous call */
158     UINT indent; /* indentation level for next node */
159 
160     BSTR version;
161 
162     BSTR encoding; /* exact property value */
163     xml_encoding xml_enc;
164 
165     /* contains a pending (or not closed yet) element name or NULL if
166        we don't have to close */
167     BSTR element;
168 
169     IStream *dest;
170 
171     output_buffer buffer;
172 } mxwriter;
173 
174 typedef struct
175 {
176     BSTR qname;
177     BSTR local;
178     BSTR uri;
179     BSTR type;
180     BSTR value;
181 } mxattribute;
182 
183 typedef struct
184 {
185     DispatchEx dispex;
186     IMXAttributes IMXAttributes_iface;
187     ISAXAttributes ISAXAttributes_iface;
188     IVBSAXAttributes IVBSAXAttributes_iface;
189     LONG ref;
190 
191     MSXML_VERSION class_version;
192 
193     mxattribute *attr;
194     int length;
195     int allocated;
196 } mxattributes;
197 
198 static inline mxattributes *impl_from_IMXAttributes( IMXAttributes *iface )
199 {
200     return CONTAINING_RECORD(iface, mxattributes, IMXAttributes_iface);
201 }
202 
203 static inline mxattributes *impl_from_ISAXAttributes( ISAXAttributes *iface )
204 {
205     return CONTAINING_RECORD(iface, mxattributes, ISAXAttributes_iface);
206 }
207 
208 static inline mxattributes *impl_from_IVBSAXAttributes( IVBSAXAttributes *iface )
209 {
210     return CONTAINING_RECORD(iface, mxattributes, IVBSAXAttributes_iface);
211 }
212 
213 static HRESULT mxattributes_grow(mxattributes *This)
214 {
215     if (This->length < This->allocated) return S_OK;
216 
217     This->allocated *= 2;
218     This->attr = heap_realloc(This->attr, This->allocated*sizeof(mxattribute));
219 
220     return This->attr ? S_OK : E_OUTOFMEMORY;
221 }
222 
223 static xml_encoding parse_encoding_name(const WCHAR *encoding)
224 {
225     int min, max, n, c;
226 
227     min = 0;
228     max = ARRAY_SIZE(xml_encoding_map) - 1;
229 
230     while (min <= max)
231     {
232         n = (min+max)/2;
233 
234         c = strcmpiW(xml_encoding_map[n].encoding, encoding);
235         if (!c)
236             return xml_encoding_map[n].enc;
237 
238         if (c > 0)
239             max = n-1;
240         else
241             min = n+1;
242     }
243 
244     return XmlEncoding_Unknown;
245 }
246 
247 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
248 {
249     const int initial_len = 0x1000;
250     buffer->data = heap_alloc(initial_len);
251     if (!buffer->data) return E_OUTOFMEMORY;
252 
253     memset(buffer->data, 0, 4);
254     buffer->allocated = initial_len;
255     buffer->written = 0;
256 
257     return S_OK;
258 }
259 
260 static void free_encoded_buffer(encoded_buffer *buffer)
261 {
262     heap_free(buffer->data);
263 }
264 
265 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
266 {
267     const struct xml_encoding_data *data;
268 
269     if (encoding == XmlEncoding_Unknown)
270     {
271         FIXME("unsupported encoding %d\n", encoding);
272         return E_NOTIMPL;
273     }
274 
275     data = &xml_encoding_map[encoding];
276     *cp = data->cp;
277 
278     return S_OK;
279 }
280 
281 static HRESULT init_output_buffer(xml_encoding encoding, output_buffer *buffer)
282 {
283     HRESULT hr;
284 
285     hr = get_code_page(encoding, &buffer->code_page);
286     if (hr != S_OK)
287         return hr;
288 
289     hr = init_encoded_buffer(&buffer->encoded);
290     if (hr != S_OK)
291         return hr;
292 
293     list_init(&buffer->blocks);
294     buffer->utf16_total = 0;
295 
296     return S_OK;
297 }
298 
299 static void free_output_buffer(output_buffer *buffer)
300 {
301     encoded_buffer *cur, *cur2;
302 
303     free_encoded_buffer(&buffer->encoded);
304 
305     LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &buffer->blocks, encoded_buffer, entry)
306     {
307         list_remove(&cur->entry);
308         free_encoded_buffer(cur);
309         heap_free(cur);
310     }
311 }
312 
313 static HRESULT write_output_buffer(mxwriter *writer, const WCHAR *data, int len)
314 {
315     output_buffer *buffer = &writer->buffer;
316     encoded_buffer *buff;
317     unsigned int written;
318     int src_len;
319 
320     if (!len || !*data)
321         return S_OK;
322 
323     src_len = len == -1 ? strlenW(data) : len;
324     if (writer->dest)
325     {
326         buff = &buffer->encoded;
327 
328         if (buffer->code_page == ~0)
329         {
330             unsigned int avail = buff->allocated - buff->written;
331 
332             src_len *= sizeof(WCHAR);
333             written = min(avail, src_len);
334 
335             /* fill internal buffer first */
336             if (avail)
337             {
338                 memcpy(buff->data + buff->written, data, written);
339                 data += written / sizeof(WCHAR);
340                 buff->written += written;
341                 avail -= written;
342                 src_len -= written;
343             }
344 
345             if (!avail)
346             {
347                 IStream_Write(writer->dest, buff->data, buff->written, &written);
348                 buff->written = 0;
349                 if (src_len >= buff->allocated)
350                     IStream_Write(writer->dest, data, src_len, &written);
351                 else if (src_len)
352                 {
353                     memcpy(buff->data, data, src_len);
354                     buff->written += src_len;
355                 }
356             }
357         }
358         else
359         {
360             unsigned int avail = buff->allocated - buff->written;
361             int length;
362 
363             length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, NULL, 0, NULL, NULL);
364             if (avail >= length)
365             {
366                 length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL);
367                 buff->written += length;
368             }
369             else
370             {
371                 /* drain what we go so far */
372                 if (buff->written)
373                 {
374                     IStream_Write(writer->dest, buff->data, buff->written, &written);
375                     buff->written = 0;
376                     avail = buff->allocated;
377                 }
378 
379                 if (avail >= length)
380                 {
381                     length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL);
382                     buff->written += length;
383                 }
384                 else
385                 {
386                     char *mb;
387 
388                     /* if current chunk is larger than total buffer size, convert it at once using temporary allocated buffer */
389                     mb = heap_alloc(length);
390                     if (!mb)
391                         return E_OUTOFMEMORY;
392 
393                     length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, mb, length, NULL, NULL);
394                     IStream_Write(writer->dest, mb, length, &written);
395                     heap_free(mb);
396                 }
397             }
398         }
399     }
400     /* When writer has no output set we have to accumulate everything to return it later in a form of BSTR.
401        To achieve that:
402 
403        - fill a buffer already allocated as part of output buffer;
404        - when current buffer is full, allocate another one and switch to it; buffers themselves never grow,
405          but are linked together, with head pointing to first allocated buffer after initial one got filled;
406        - later during get_output() contents are concatenated by copying one after another to destination BSTR buffer,
407          that's returned to the client. */
408     else
409     {
410         /* select last used block */
411         if (list_empty(&buffer->blocks))
412             buff = &buffer->encoded;
413         else
414             buff = LIST_ENTRY(list_tail(&buffer->blocks), encoded_buffer, entry);
415 
416         src_len *= sizeof(WCHAR);
417         while (src_len)
418         {
419             unsigned int avail = buff->allocated - buff->written;
420             unsigned int written = min(avail, src_len);
421 
422             if (avail)
423             {
424                 memcpy(buff->data + buff->written, data, written);
425                 buff->written += written;
426                 buffer->utf16_total += written;
427                 src_len -= written;
428             }
429 
430             /* alloc new block if needed and retry */
431             if (src_len)
432             {
433                 encoded_buffer *next = heap_alloc(sizeof(*next));
434                 HRESULT hr;
435 
436                 if (FAILED(hr = init_encoded_buffer(next))) {
437                     heap_free(next);
438                     return hr;
439                 }
440 
441                 list_add_tail(&buffer->blocks, &next->entry);
442                 buff = next;
443             }
444         }
445     }
446 
447     return S_OK;
448 }
449 
450 static HRESULT write_output_buffer_quoted(mxwriter *writer, const WCHAR *data, int len)
451 {
452     write_output_buffer(writer, quotW, 1);
453     write_output_buffer(writer, data, len);
454     write_output_buffer(writer, quotW, 1);
455 
456     return S_OK;
457 }
458 
459 /* frees buffer data, reallocates with a default lengths */
460 static void close_output_buffer(mxwriter *writer)
461 {
462     encoded_buffer *cur, *cur2;
463 
464     heap_free(writer->buffer.encoded.data);
465 
466     LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &writer->buffer.blocks, encoded_buffer, entry)
467     {
468         list_remove(&cur->entry);
469         free_encoded_buffer(cur);
470         heap_free(cur);
471     }
472 
473     init_encoded_buffer(&writer->buffer.encoded);
474     get_code_page(writer->xml_enc, &writer->buffer.code_page);
475     writer->buffer.utf16_total = 0;
476     list_init(&writer->buffer.blocks);
477 }
478 
479 /* Escapes special characters like:
480    '<' -> "&lt;"
481    '&' -> "&amp;"
482    '"' -> "&quot;"
483    '>' -> "&gt;"
484 
485    On call 'len' contains a length of 'str' in chars or -1 if it's null terminated.
486    After a call it's updated with actual new length if it wasn't -1 initially.
487 */
488 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
489 {
490     static const WCHAR ltW[]    = {'&','l','t',';'};
491     static const WCHAR ampW[]   = {'&','a','m','p',';'};
492     static const WCHAR equotW[] = {'&','q','u','o','t',';'};
493     static const WCHAR gtW[]    = {'&','g','t',';'};
494 
495     const int default_alloc = 100;
496     const int grow_thresh = 10;
497     int p = *len, conv_len;
498     WCHAR *ptr, *ret;
499 
500     /* default buffer size to something if length is unknown */
501     conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
502     ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
503 
504     while (*str && p)
505     {
506         if (ptr - ret > conv_len - grow_thresh)
507         {
508             int written = ptr - ret;
509             conv_len *= 2;
510             ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
511             ptr += written;
512         }
513 
514         switch (*str)
515         {
516         case '<':
517             memcpy(ptr, ltW, sizeof(ltW));
518             ptr += ARRAY_SIZE(ltW);
519             break;
520         case '&':
521             memcpy(ptr, ampW, sizeof(ampW));
522             ptr += ARRAY_SIZE(ampW);
523             break;
524         case '>':
525             memcpy(ptr, gtW, sizeof(gtW));
526             ptr += ARRAY_SIZE(gtW);
527             break;
528         case '"':
529             if (mode == EscapeValue)
530             {
531                 memcpy(ptr, equotW, sizeof(equotW));
532                 ptr += ARRAY_SIZE(equotW);
533                 break;
534             }
535             /* fallthrough for text mode */
536         default:
537             *ptr++ = *str;
538             break;
539         }
540 
541         str++;
542         if (*len != -1) p--;
543     }
544 
545     if (*len != -1) *len = ptr-ret;
546     *++ptr = 0;
547 
548     return ret;
549 }
550 
551 static void write_prolog_buffer(mxwriter *writer)
552 {
553     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
554     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
555     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
556     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
557     static const WCHAR noW[] = {'n','o','\"','?','>'};
558 
559     /* version */
560     write_output_buffer(writer, versionW, ARRAY_SIZE(versionW));
561     write_output_buffer_quoted(writer, writer->version, -1);
562 
563     /* encoding */
564     write_output_buffer(writer, encodingW, ARRAY_SIZE(encodingW));
565 
566     if (writer->dest)
567         write_output_buffer(writer, writer->encoding, -1);
568     else
569         write_output_buffer(writer, utf16W, ARRAY_SIZE(utf16W) - 1);
570     write_output_buffer(writer, quotW, 1);
571 
572     /* standalone */
573     write_output_buffer(writer, standaloneW, ARRAY_SIZE(standaloneW));
574     if (writer->props[MXWriter_Standalone] == VARIANT_TRUE)
575         write_output_buffer(writer, yesW, ARRAY_SIZE(yesW));
576     else
577         write_output_buffer(writer, noW, ARRAY_SIZE(noW));
578 
579     write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
580     writer->newline = TRUE;
581 }
582 
583 /* Attempts to the write data from the mxwriter's buffer to
584  * the destination stream (if there is one).
585  */
586 static HRESULT write_data_to_stream(mxwriter *writer)
587 {
588     encoded_buffer *buffer = &writer->buffer.encoded;
589     ULONG written = 0;
590 
591     if (!writer->dest)
592         return S_OK;
593 
594     if (buffer->written == 0)
595     {
596         if (writer->xml_enc == XmlEncoding_UTF8)
597             IStream_Write(writer->dest, buffer->data, 0, &written);
598     }
599     else
600     {
601         IStream_Write(writer->dest, buffer->data, buffer->written, &written);
602         buffer->written = 0;
603     }
604 
605     return S_OK;
606 }
607 
608 /* Newly added element start tag left unclosed cause for empty elements
609    we have to close it differently. */
610 static void close_element_starttag(mxwriter *writer)
611 {
612     static const WCHAR gtW[] = {'>'};
613     if (!writer->element) return;
614     write_output_buffer(writer, gtW, 1);
615 }
616 
617 static void write_node_indent(mxwriter *writer)
618 {
619     static const WCHAR tabW[] = {'\t'};
620     int indent = writer->indent;
621 
622     if (!writer->props[MXWriter_Indent] || writer->text)
623     {
624         writer->text = FALSE;
625         return;
626     }
627 
628     /* This is to workaround PI output logic that always puts newline chars,
629        document prolog PI does that too. */
630     if (!writer->newline)
631         write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
632     while (indent--)
633         write_output_buffer(writer, tabW, 1);
634 
635     writer->newline = FALSE;
636     writer->text = FALSE;
637 }
638 
639 static inline void writer_inc_indent(mxwriter *This)
640 {
641     This->indent++;
642 }
643 
644 static inline void writer_dec_indent(mxwriter *This)
645 {
646     if (This->indent) This->indent--;
647     /* depth is decreased only when element is closed, meaning it's not a text node
648        at this point */
649     This->text = FALSE;
650 }
651 
652 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
653 {
654     SysFreeString(This->element);
655     if (name)
656         This->element = len != -1 ? SysAllocStringLen(name, len) : SysAllocString(name);
657     else
658         This->element = NULL;
659 }
660 
661 static inline HRESULT flush_output_buffer(mxwriter *This)
662 {
663     close_element_starttag(This);
664     set_element_name(This, NULL, 0);
665     This->cdata = FALSE;
666     return write_data_to_stream(This);
667 }
668 
669 /* Resets the mxwriter's output buffer by closing it, then creating a new
670  * output buffer using the given encoding.
671  */
672 static inline void reset_output_buffer(mxwriter *This)
673 {
674     close_output_buffer(This);
675 }
676 
677 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
678 {
679     writer->props[property] = value;
680     writer->prop_changed = TRUE;
681     return S_OK;
682 }
683 
684 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
685 {
686     if (!value) return E_POINTER;
687     *value = writer->props[property];
688     return S_OK;
689 }
690 
691 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
692 {
693     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
694 }
695 
696 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
697 {
698     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
699 }
700 
701 static inline mxwriter *impl_from_IVBSAXContentHandler(IVBSAXContentHandler *iface)
702 {
703     return CONTAINING_RECORD(iface, mxwriter, IVBSAXContentHandler_iface);
704 }
705 
706 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
707 {
708     return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
709 }
710 
711 static inline mxwriter *impl_from_IVBSAXLexicalHandler(IVBSAXLexicalHandler *iface)
712 {
713     return CONTAINING_RECORD(iface, mxwriter, IVBSAXLexicalHandler_iface);
714 }
715 
716 static inline mxwriter *impl_from_ISAXDeclHandler(ISAXDeclHandler *iface)
717 {
718     return CONTAINING_RECORD(iface, mxwriter, ISAXDeclHandler_iface);
719 }
720 
721 static inline mxwriter *impl_from_IVBSAXDeclHandler(IVBSAXDeclHandler *iface)
722 {
723     return CONTAINING_RECORD(iface, mxwriter, IVBSAXDeclHandler_iface);
724 }
725 
726 static inline mxwriter *impl_from_ISAXDTDHandler(ISAXDTDHandler *iface)
727 {
728     return CONTAINING_RECORD(iface, mxwriter, ISAXDTDHandler_iface);
729 }
730 
731 static inline mxwriter *impl_from_IVBSAXDTDHandler(IVBSAXDTDHandler *iface)
732 {
733     return CONTAINING_RECORD(iface, mxwriter, IVBSAXDTDHandler_iface);
734 }
735 
736 static inline mxwriter *impl_from_ISAXErrorHandler(ISAXErrorHandler *iface)
737 {
738     return CONTAINING_RECORD(iface, mxwriter, ISAXErrorHandler_iface);
739 }
740 
741 static inline mxwriter *impl_from_IVBSAXErrorHandler(IVBSAXErrorHandler *iface)
742 {
743     return CONTAINING_RECORD(iface, mxwriter, IVBSAXErrorHandler_iface);
744 }
745 
746 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
747 {
748     mxwriter *This = impl_from_IMXWriter( iface );
749 
750     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
751 
752     *obj = NULL;
753 
754     if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
755          IsEqualGUID( riid, &IID_IDispatch ) ||
756          IsEqualGUID( riid, &IID_IUnknown ) )
757     {
758         *obj = &This->IMXWriter_iface;
759     }
760     else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
761     {
762         *obj = &This->ISAXContentHandler_iface;
763     }
764     else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
765     {
766         *obj = &This->ISAXLexicalHandler_iface;
767     }
768     else if ( IsEqualGUID( riid, &IID_ISAXDeclHandler ) )
769     {
770         *obj = &This->ISAXDeclHandler_iface;
771     }
772     else if ( IsEqualGUID( riid, &IID_ISAXDTDHandler ) )
773     {
774         *obj = &This->ISAXDTDHandler_iface;
775     }
776     else if ( IsEqualGUID( riid, &IID_ISAXErrorHandler ) )
777     {
778         *obj = &This->ISAXErrorHandler_iface;
779     }
780     else if ( IsEqualGUID( riid, &IID_IVBSAXDeclHandler ) )
781     {
782         *obj = &This->IVBSAXDeclHandler_iface;
783     }
784     else if ( IsEqualGUID( riid, &IID_IVBSAXLexicalHandler ) )
785     {
786         *obj = &This->IVBSAXLexicalHandler_iface;
787     }
788     else if ( IsEqualGUID( riid, &IID_IVBSAXContentHandler ) )
789     {
790         *obj = &This->IVBSAXContentHandler_iface;
791     }
792     else if ( IsEqualGUID( riid, &IID_IVBSAXDTDHandler ) )
793     {
794         *obj = &This->IVBSAXDTDHandler_iface;
795     }
796     else if ( IsEqualGUID( riid, &IID_IVBSAXErrorHandler ) )
797     {
798         *obj = &This->IVBSAXErrorHandler_iface;
799     }
800     else if (dispex_query_interface(&This->dispex, riid, obj))
801     {
802         return *obj ? S_OK : E_NOINTERFACE;
803     }
804     else
805     {
806         ERR("interface %s not implemented\n", debugstr_guid(riid));
807         *obj = NULL;
808         return E_NOINTERFACE;
809     }
810 
811     IMXWriter_AddRef(iface);
812     return S_OK;
813 }
814 
815 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
816 {
817     mxwriter *This = impl_from_IMXWriter( iface );
818     LONG ref = InterlockedIncrement(&This->ref);
819 
820     TRACE("(%p)->(%d)\n", This, ref);
821 
822     return ref;
823 }
824 
825 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
826 {
827     mxwriter *This = impl_from_IMXWriter( iface );
828     ULONG ref = InterlockedDecrement(&This->ref);
829 
830     TRACE("(%p)->(%d)\n", This, ref);
831 
832     if(!ref)
833     {
834         /* Windows flushes the buffer when the interface is destroyed. */
835         flush_output_buffer(This);
836         free_output_buffer(&This->buffer);
837 
838         if (This->dest) IStream_Release(This->dest);
839         SysFreeString(This->version);
840         SysFreeString(This->encoding);
841 
842         SysFreeString(This->element);
843         heap_free(This);
844     }
845 
846     return ref;
847 }
848 
849 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
850 {
851     mxwriter *This = impl_from_IMXWriter( iface );
852     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
853 }
854 
855 static HRESULT WINAPI mxwriter_GetTypeInfo(
856     IMXWriter *iface,
857     UINT iTInfo, LCID lcid,
858     ITypeInfo** ppTInfo )
859 {
860     mxwriter *This = impl_from_IMXWriter( iface );
861     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
862         iTInfo, lcid, ppTInfo);
863 }
864 
865 static HRESULT WINAPI mxwriter_GetIDsOfNames(
866     IMXWriter *iface,
867     REFIID riid, LPOLESTR* rgszNames,
868     UINT cNames, LCID lcid, DISPID* rgDispId )
869 {
870     mxwriter *This = impl_from_IMXWriter( iface );
871     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
872         riid, rgszNames, cNames, lcid, rgDispId);
873 }
874 
875 static HRESULT WINAPI mxwriter_Invoke(
876     IMXWriter *iface,
877     DISPID dispIdMember, REFIID riid, LCID lcid,
878     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
879     EXCEPINFO* pExcepInfo, UINT* puArgErr )
880 {
881     mxwriter *This = impl_from_IMXWriter( iface );
882     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
883         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
884 }
885 
886 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
887 {
888     mxwriter *This = impl_from_IMXWriter( iface );
889     HRESULT hr;
890 
891     TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
892 
893     hr = flush_output_buffer(This);
894     if (FAILED(hr))
895         return hr;
896 
897     switch (V_VT(&dest))
898     {
899     case VT_EMPTY:
900     {
901         if (This->dest) IStream_Release(This->dest);
902         This->dest = NULL;
903         reset_output_buffer(This);
904         break;
905     }
906     case VT_UNKNOWN:
907     {
908         IStream *stream;
909 
910         hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
911         if (hr == S_OK)
912         {
913             /* Recreate the output buffer to make sure it's using the correct encoding. */
914             reset_output_buffer(This);
915 
916             if (This->dest) IStream_Release(This->dest);
917             This->dest = stream;
918             break;
919         }
920 
921         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
922         return E_NOTIMPL;
923     }
924     default:
925         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
926         return E_NOTIMPL;
927     }
928 
929     return S_OK;
930 }
931 
932 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
933 {
934     mxwriter *This = impl_from_IMXWriter( iface );
935 
936     TRACE("(%p)->(%p)\n", This, dest);
937 
938     if (!dest) return E_POINTER;
939 
940     if (This->dest)
941     {
942         /* we only support IStream output so far */
943         V_VT(dest) = VT_UNKNOWN;
944         V_UNKNOWN(dest) = (IUnknown*)This->dest;
945         IStream_AddRef(This->dest);
946     }
947     else
948     {
949         encoded_buffer *buff;
950         char *dest_ptr;
951         HRESULT hr;
952 
953         hr = flush_output_buffer(This);
954         if (FAILED(hr))
955             return hr;
956 
957         V_VT(dest)   = VT_BSTR;
958         V_BSTR(dest) = SysAllocStringLen(NULL, This->buffer.utf16_total / sizeof(WCHAR));
959         if (!V_BSTR(dest))
960             return E_OUTOFMEMORY;
961 
962         dest_ptr = (char*)V_BSTR(dest);
963         buff = &This->buffer.encoded;
964 
965         if (buff->written)
966         {
967             memcpy(dest_ptr, buff->data, buff->written);
968             dest_ptr += buff->written;
969         }
970 
971         LIST_FOR_EACH_ENTRY(buff, &This->buffer.blocks, encoded_buffer, entry)
972         {
973             memcpy(dest_ptr, buff->data, buff->written);
974             dest_ptr += buff->written;
975         }
976     }
977 
978     return S_OK;
979 }
980 
981 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
982 {
983     mxwriter *This = impl_from_IMXWriter( iface );
984     xml_encoding enc;
985     HRESULT hr;
986 
987     TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
988 
989     enc = parse_encoding_name(encoding);
990     if (enc == XmlEncoding_Unknown)
991     {
992         FIXME("unsupported encoding %s\n", debugstr_w(encoding));
993         return E_INVALIDARG;
994     }
995 
996     hr = flush_output_buffer(This);
997     if (FAILED(hr))
998         return hr;
999 
1000     SysReAllocString(&This->encoding, encoding);
1001     This->xml_enc = enc;
1002 
1003     TRACE("got encoding %d\n", This->xml_enc);
1004     reset_output_buffer(This);
1005     return S_OK;
1006 }
1007 
1008 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
1009 {
1010     mxwriter *This = impl_from_IMXWriter( iface );
1011 
1012     TRACE("(%p)->(%p)\n", This, encoding);
1013 
1014     if (!encoding) return E_POINTER;
1015 
1016     *encoding = SysAllocString(This->encoding);
1017     if (!*encoding) return E_OUTOFMEMORY;
1018 
1019     return S_OK;
1020 }
1021 
1022 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
1023 {
1024     mxwriter *This = impl_from_IMXWriter( iface );
1025 
1026     TRACE("(%p)->(%d)\n", This, value);
1027     return writer_set_property(This, MXWriter_BOM, value);
1028 }
1029 
1030 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
1031 {
1032     mxwriter *This = impl_from_IMXWriter( iface );
1033 
1034     TRACE("(%p)->(%p)\n", This, value);
1035     return writer_get_property(This, MXWriter_BOM, value);
1036 }
1037 
1038 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
1039 {
1040     mxwriter *This = impl_from_IMXWriter( iface );
1041 
1042     TRACE("(%p)->(%d)\n", This, value);
1043     return writer_set_property(This, MXWriter_Indent, value);
1044 }
1045 
1046 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
1047 {
1048     mxwriter *This = impl_from_IMXWriter( iface );
1049 
1050     TRACE("(%p)->(%p)\n", This, value);
1051     return writer_get_property(This, MXWriter_Indent, value);
1052 }
1053 
1054 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
1055 {
1056     mxwriter *This = impl_from_IMXWriter( iface );
1057 
1058     TRACE("(%p)->(%d)\n", This, value);
1059     return writer_set_property(This, MXWriter_Standalone, value);
1060 }
1061 
1062 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
1063 {
1064     mxwriter *This = impl_from_IMXWriter( iface );
1065 
1066     TRACE("(%p)->(%p)\n", This, value);
1067     return writer_get_property(This, MXWriter_Standalone, value);
1068 }
1069 
1070 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
1071 {
1072     mxwriter *This = impl_from_IMXWriter( iface );
1073 
1074     TRACE("(%p)->(%d)\n", This, value);
1075     return writer_set_property(This, MXWriter_OmitXmlDecl, value);
1076 }
1077 
1078 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
1079 {
1080     mxwriter *This = impl_from_IMXWriter( iface );
1081 
1082     TRACE("(%p)->(%p)\n", This, value);
1083     return writer_get_property(This, MXWriter_OmitXmlDecl, value);
1084 }
1085 
1086 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
1087 {
1088     mxwriter *This = impl_from_IMXWriter( iface );
1089 
1090     TRACE("(%p)->(%s)\n", This, debugstr_w(version));
1091 
1092     if (!version) return E_INVALIDARG;
1093 
1094     SysFreeString(This->version);
1095     This->version = SysAllocString(version);
1096 
1097     return S_OK;
1098 }
1099 
1100 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
1101 {
1102     mxwriter *This = impl_from_IMXWriter( iface );
1103 
1104     TRACE("(%p)->(%p)\n", This, version);
1105 
1106     if (!version) return E_POINTER;
1107 
1108     return return_bstr(This->version, version);
1109 }
1110 
1111 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
1112 {
1113     mxwriter *This = impl_from_IMXWriter( iface );
1114 
1115     TRACE("(%p)->(%d)\n", This, value);
1116     return writer_set_property(This, MXWriter_DisableEscaping, value);
1117 }
1118 
1119 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
1120 {
1121     mxwriter *This = impl_from_IMXWriter( iface );
1122 
1123     TRACE("(%p)->(%p)\n", This, value);
1124     return writer_get_property(This, MXWriter_DisableEscaping, value);
1125 }
1126 
1127 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
1128 {
1129     mxwriter *This = impl_from_IMXWriter( iface );
1130     TRACE("(%p)\n", This);
1131     return flush_output_buffer(This);
1132 }
1133 
1134 static const struct IMXWriterVtbl MXWriterVtbl =
1135 {
1136     mxwriter_QueryInterface,
1137     mxwriter_AddRef,
1138     mxwriter_Release,
1139     mxwriter_GetTypeInfoCount,
1140     mxwriter_GetTypeInfo,
1141     mxwriter_GetIDsOfNames,
1142     mxwriter_Invoke,
1143     mxwriter_put_output,
1144     mxwriter_get_output,
1145     mxwriter_put_encoding,
1146     mxwriter_get_encoding,
1147     mxwriter_put_byteOrderMark,
1148     mxwriter_get_byteOrderMark,
1149     mxwriter_put_indent,
1150     mxwriter_get_indent,
1151     mxwriter_put_standalone,
1152     mxwriter_get_standalone,
1153     mxwriter_put_omitXMLDeclaration,
1154     mxwriter_get_omitXMLDeclaration,
1155     mxwriter_put_version,
1156     mxwriter_get_version,
1157     mxwriter_put_disableOutputEscaping,
1158     mxwriter_get_disableOutputEscaping,
1159     mxwriter_flush
1160 };
1161 
1162 /*** ISAXContentHandler ***/
1163 static HRESULT WINAPI SAXContentHandler_QueryInterface(
1164     ISAXContentHandler *iface,
1165     REFIID riid,
1166     void **obj)
1167 {
1168     mxwriter *This = impl_from_ISAXContentHandler( iface );
1169     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1170 }
1171 
1172 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
1173 {
1174     mxwriter *This = impl_from_ISAXContentHandler( iface );
1175     return IMXWriter_AddRef(&This->IMXWriter_iface);
1176 }
1177 
1178 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
1179 {
1180     mxwriter *This = impl_from_ISAXContentHandler( iface );
1181     return IMXWriter_Release(&This->IMXWriter_iface);
1182 }
1183 
1184 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
1185     ISAXContentHandler *iface,
1186     ISAXLocator *locator)
1187 {
1188     mxwriter *This = impl_from_ISAXContentHandler( iface );
1189     FIXME("(%p)->(%p)\n", This, locator);
1190     return E_NOTIMPL;
1191 }
1192 
1193 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
1194 {
1195     mxwriter *This = impl_from_ISAXContentHandler( iface );
1196 
1197     TRACE("(%p)\n", This);
1198 
1199     /* If properties have been changed since the last "endDocument" call
1200      * we need to reset the output buffer. If we don't the output buffer
1201      * could end up with multiple XML documents in it, plus this seems to
1202      * be how Windows works.
1203      */
1204     if (This->prop_changed) {
1205         reset_output_buffer(This);
1206         This->prop_changed = FALSE;
1207     }
1208 
1209     if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
1210 
1211     write_prolog_buffer(This);
1212 
1213     if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
1214         static const char utf16BOM[] = {0xff,0xfe};
1215 
1216         if (This->props[MXWriter_BOM] == VARIANT_TRUE)
1217             /* Windows passes a NULL pointer as the pcbWritten parameter and
1218              * ignores any error codes returned from this Write call.
1219              */
1220             IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
1221     }
1222 
1223     return S_OK;
1224 }
1225 
1226 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
1227 {
1228     mxwriter *This = impl_from_ISAXContentHandler( iface );
1229     TRACE("(%p)\n", This);
1230     This->prop_changed = FALSE;
1231     return flush_output_buffer(This);
1232 }
1233 
1234 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
1235     ISAXContentHandler *iface,
1236     const WCHAR *prefix,
1237     int nprefix,
1238     const WCHAR *uri,
1239     int nuri)
1240 {
1241     mxwriter *This = impl_from_ISAXContentHandler( iface );
1242     TRACE("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
1243     return S_OK;
1244 }
1245 
1246 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
1247     ISAXContentHandler *iface,
1248     const WCHAR *prefix,
1249     int nprefix)
1250 {
1251     mxwriter *This = impl_from_ISAXContentHandler( iface );
1252     TRACE("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
1253     return S_OK;
1254 }
1255 
1256 static void mxwriter_write_attribute(mxwriter *writer, const WCHAR *qname, int qname_len,
1257     const WCHAR *value, int value_len, BOOL escape)
1258 {
1259     static const WCHAR eqW[] = {'='};
1260 
1261     /* space separator in front of every attribute */
1262     write_output_buffer(writer, spaceW, 1);
1263     write_output_buffer(writer, qname, qname_len);
1264     write_output_buffer(writer, eqW, 1);
1265 
1266     if (escape)
1267     {
1268         WCHAR *escaped = get_escaped_string(value, EscapeValue, &value_len);
1269         write_output_buffer_quoted(writer, escaped, value_len);
1270         heap_free(escaped);
1271     }
1272     else
1273         write_output_buffer_quoted(writer, value, value_len);
1274 }
1275 
1276 static void mxwriter_write_starttag(mxwriter *writer, const WCHAR *qname, int len)
1277 {
1278     static const WCHAR ltW[] = {'<'};
1279 
1280     close_element_starttag(writer);
1281     set_element_name(writer, qname ? qname : emptyW, qname ? len : 0);
1282 
1283     write_node_indent(writer);
1284 
1285     write_output_buffer(writer, ltW, 1);
1286     write_output_buffer(writer, qname ? qname : emptyW, qname ? len : 0);
1287     writer_inc_indent(writer);
1288 }
1289 
1290 static HRESULT WINAPI SAXContentHandler_startElement(
1291     ISAXContentHandler *iface,
1292     const WCHAR *namespaceUri,
1293     int nnamespaceUri,
1294     const WCHAR *local_name,
1295     int nlocal_name,
1296     const WCHAR *QName,
1297     int nQName,
1298     ISAXAttributes *attr)
1299 {
1300     mxwriter *This = impl_from_ISAXContentHandler( iface );
1301 
1302     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
1303         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
1304 
1305     if (((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6) ||
1306         (nQName == -1 && This->class_version == MSXML6))
1307         return E_INVALIDARG;
1308 
1309     mxwriter_write_starttag(This, QName, nQName);
1310 
1311     if (attr)
1312     {
1313         int length, i, escape;
1314         HRESULT hr;
1315 
1316         hr = ISAXAttributes_getLength(attr, &length);
1317         if (FAILED(hr)) return hr;
1318 
1319         escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
1320             (This->class_version == MSXML4 || This->class_version == MSXML6);
1321 
1322         for (i = 0; i < length; i++)
1323         {
1324             int qname_len = 0, value_len = 0;
1325             const WCHAR *qname, *value;
1326 
1327             hr = ISAXAttributes_getQName(attr, i, &qname, &qname_len);
1328             if (FAILED(hr)) return hr;
1329 
1330             hr = ISAXAttributes_getValue(attr, i, &value, &value_len);
1331             if (FAILED(hr)) return hr;
1332 
1333             mxwriter_write_attribute(This, qname, qname_len, value, value_len, escape);
1334         }
1335     }
1336 
1337     return S_OK;
1338 }
1339 
1340 static HRESULT WINAPI SAXContentHandler_endElement(
1341     ISAXContentHandler *iface,
1342     const WCHAR *namespaceUri,
1343     int nnamespaceUri,
1344     const WCHAR * local_name,
1345     int nlocal_name,
1346     const WCHAR *QName,
1347     int nQName)
1348 {
1349     mxwriter *This = impl_from_ISAXContentHandler( iface );
1350 
1351     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1352         debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1353 
1354     if (((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6) ||
1355          (nQName == -1 && This->class_version == MSXML6))
1356         return E_INVALIDARG;
1357 
1358     writer_dec_indent(This);
1359 
1360     if (This->element)
1361     {
1362         static const WCHAR closeW[] = {'/','>'};
1363         write_output_buffer(This, closeW, 2);
1364     }
1365     else
1366     {
1367         static const WCHAR closetagW[] = {'<','/'};
1368         static const WCHAR gtW[] = {'>'};
1369 
1370         write_node_indent(This);
1371         write_output_buffer(This, closetagW, 2);
1372         write_output_buffer(This, QName, nQName);
1373         write_output_buffer(This, gtW, 1);
1374     }
1375 
1376     set_element_name(This, NULL, 0);
1377 
1378     return S_OK;
1379 }
1380 
1381 static HRESULT WINAPI SAXContentHandler_characters(
1382     ISAXContentHandler *iface,
1383     const WCHAR *chars,
1384     int nchars)
1385 {
1386     mxwriter *This = impl_from_ISAXContentHandler( iface );
1387 
1388     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1389 
1390     if (!chars) return E_INVALIDARG;
1391 
1392     close_element_starttag(This);
1393     set_element_name(This, NULL, 0);
1394 
1395     if (!This->cdata)
1396         This->text = TRUE;
1397 
1398     if (nchars)
1399     {
1400         if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
1401             write_output_buffer(This, chars, nchars);
1402         else
1403         {
1404             int len = nchars;
1405             WCHAR *escaped;
1406 
1407             escaped = get_escaped_string(chars, EscapeText, &len);
1408             write_output_buffer(This, escaped, len);
1409             heap_free(escaped);
1410         }
1411     }
1412 
1413     return S_OK;
1414 }
1415 
1416 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1417     ISAXContentHandler *iface,
1418     const WCHAR *chars,
1419     int nchars)
1420 {
1421     mxwriter *This = impl_from_ISAXContentHandler( iface );
1422 
1423     TRACE("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1424 
1425     if (!chars) return E_INVALIDARG;
1426 
1427     write_output_buffer(This, chars, nchars);
1428 
1429     return S_OK;
1430 }
1431 
1432 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1433     ISAXContentHandler *iface,
1434     const WCHAR *target,
1435     int ntarget,
1436     const WCHAR *data,
1437     int ndata)
1438 {
1439     mxwriter *This = impl_from_ISAXContentHandler( iface );
1440     static const WCHAR openpiW[] = {'<','?'};
1441     static const WCHAR closepiW[] = {'?','>','\r','\n'};
1442 
1443     TRACE("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1444 
1445     if (!target) return E_INVALIDARG;
1446 
1447     write_node_indent(This);
1448     write_output_buffer(This, openpiW, ARRAY_SIZE(openpiW));
1449 
1450     if (*target)
1451         write_output_buffer(This, target, ntarget);
1452 
1453     if (data && *data && ndata)
1454     {
1455         write_output_buffer(This, spaceW, 1);
1456         write_output_buffer(This, data, ndata);
1457     }
1458 
1459     write_output_buffer(This, closepiW, ARRAY_SIZE(closepiW));
1460     This->newline = TRUE;
1461 
1462     return S_OK;
1463 }
1464 
1465 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1466     ISAXContentHandler *iface,
1467     const WCHAR *name,
1468     int nname)
1469 {
1470     mxwriter *This = impl_from_ISAXContentHandler( iface );
1471     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1472     return E_NOTIMPL;
1473 }
1474 
1475 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1476 {
1477     SAXContentHandler_QueryInterface,
1478     SAXContentHandler_AddRef,
1479     SAXContentHandler_Release,
1480     SAXContentHandler_putDocumentLocator,
1481     SAXContentHandler_startDocument,
1482     SAXContentHandler_endDocument,
1483     SAXContentHandler_startPrefixMapping,
1484     SAXContentHandler_endPrefixMapping,
1485     SAXContentHandler_startElement,
1486     SAXContentHandler_endElement,
1487     SAXContentHandler_characters,
1488     SAXContentHandler_ignorableWhitespace,
1489     SAXContentHandler_processingInstruction,
1490     SAXContentHandler_skippedEntity
1491 };
1492 
1493 /*** ISAXLexicalHandler ***/
1494 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1495     REFIID riid, void **obj)
1496 {
1497     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1498     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1499 }
1500 
1501 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1502 {
1503     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1504     return IMXWriter_AddRef(&This->IMXWriter_iface);
1505 }
1506 
1507 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1508 {
1509     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1510     return IMXWriter_Release(&This->IMXWriter_iface);
1511 }
1512 
1513 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1514     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1515     const WCHAR *systemId, int systemId_len)
1516 {
1517     static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1518     static const WCHAR openintW[] = {'[','\r','\n'};
1519 
1520     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1521 
1522     TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1523         debugstr_wn(systemId, systemId_len));
1524 
1525     if (!name) return E_INVALIDARG;
1526 
1527     write_output_buffer(This, doctypeW, ARRAY_SIZE(doctypeW));
1528 
1529     if (*name)
1530     {
1531         write_output_buffer(This, name, name_len);
1532         write_output_buffer(This, spaceW, 1);
1533     }
1534 
1535     if (publicId)
1536     {
1537         write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
1538         write_output_buffer_quoted(This, publicId, publicId_len);
1539 
1540         if (!systemId) return E_INVALIDARG;
1541 
1542         if (*publicId)
1543             write_output_buffer(This, spaceW, 1);
1544 
1545         write_output_buffer_quoted(This, systemId, systemId_len);
1546 
1547         if (*systemId)
1548             write_output_buffer(This, spaceW, 1);
1549     }
1550     else if (systemId)
1551     {
1552         write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
1553         write_output_buffer_quoted(This, systemId, systemId_len);
1554         if (*systemId)
1555             write_output_buffer(This, spaceW, 1);
1556     }
1557 
1558     write_output_buffer(This, openintW, ARRAY_SIZE(openintW));
1559 
1560     return S_OK;
1561 }
1562 
1563 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1564 {
1565     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1566     static const WCHAR closedtdW[] = {']','>','\r','\n'};
1567 
1568     TRACE("(%p)\n", This);
1569 
1570     write_output_buffer(This, closedtdW, ARRAY_SIZE(closedtdW));
1571 
1572     return S_OK;
1573 }
1574 
1575 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1576 {
1577     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1578     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1579     return E_NOTIMPL;
1580 }
1581 
1582 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1583 {
1584     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1585     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1586     return E_NOTIMPL;
1587 }
1588 
1589 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1590 {
1591     static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1592     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1593 
1594     TRACE("(%p)\n", This);
1595 
1596     write_node_indent(This);
1597     write_output_buffer(This, scdataW, ARRAY_SIZE(scdataW));
1598     This->cdata = TRUE;
1599 
1600     return S_OK;
1601 }
1602 
1603 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1604 {
1605     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1606     static const WCHAR ecdataW[] = {']',']','>'};
1607 
1608     TRACE("(%p)\n", This);
1609 
1610     write_output_buffer(This, ecdataW, ARRAY_SIZE(ecdataW));
1611     This->cdata = FALSE;
1612 
1613     return S_OK;
1614 }
1615 
1616 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1617 {
1618     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1619     static const WCHAR copenW[] = {'<','!','-','-'};
1620     static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1621 
1622     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1623 
1624     if (!chars) return E_INVALIDARG;
1625 
1626     close_element_starttag(This);
1627     write_node_indent(This);
1628 
1629     write_output_buffer(This, copenW, ARRAY_SIZE(copenW));
1630     if (nchars)
1631         write_output_buffer(This, chars, nchars);
1632     write_output_buffer(This, ccloseW, ARRAY_SIZE(ccloseW));
1633 
1634     return S_OK;
1635 }
1636 
1637 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1638 {
1639     SAXLexicalHandler_QueryInterface,
1640     SAXLexicalHandler_AddRef,
1641     SAXLexicalHandler_Release,
1642     SAXLexicalHandler_startDTD,
1643     SAXLexicalHandler_endDTD,
1644     SAXLexicalHandler_startEntity,
1645     SAXLexicalHandler_endEntity,
1646     SAXLexicalHandler_startCDATA,
1647     SAXLexicalHandler_endCDATA,
1648     SAXLexicalHandler_comment
1649 };
1650 
1651 /*** ISAXDeclHandler ***/
1652 static HRESULT WINAPI SAXDeclHandler_QueryInterface(ISAXDeclHandler *iface,
1653     REFIID riid, void **obj)
1654 {
1655     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1656     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1657 }
1658 
1659 static ULONG WINAPI SAXDeclHandler_AddRef(ISAXDeclHandler *iface)
1660 {
1661     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1662     return IMXWriter_AddRef(&This->IMXWriter_iface);
1663 }
1664 
1665 static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
1666 {
1667     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1668     return IMXWriter_Release(&This->IMXWriter_iface);
1669 }
1670 
1671 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
1672     const WCHAR *name, int n_name, const WCHAR *model, int n_model)
1673 {
1674     static const WCHAR elementW[] = {'<','!','E','L','E','M','E','N','T',' '};
1675     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1676 
1677     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1678         debugstr_wn(model, n_model), n_model);
1679 
1680     if (!name || !model) return E_INVALIDARG;
1681 
1682     write_output_buffer(This, elementW, ARRAY_SIZE(elementW));
1683     if (n_name) {
1684         write_output_buffer(This, name, n_name);
1685         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1686     }
1687     if (n_model)
1688         write_output_buffer(This, model, n_model);
1689     write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
1690 
1691     return S_OK;
1692 }
1693 
1694 static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
1695     const WCHAR *element, int n_element, const WCHAR *attr, int n_attr,
1696     const WCHAR *type, int n_type, const WCHAR *Default, int n_default,
1697     const WCHAR *value, int n_value)
1698 {
1699     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1700     static const WCHAR attlistW[] = {'<','!','A','T','T','L','I','S','T',' '};
1701     static const WCHAR closetagW[] = {'>','\r','\n'};
1702 
1703     TRACE("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d)\n", This, debugstr_wn(element, n_element), n_element,
1704         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
1705         debugstr_wn(value, n_value), n_value);
1706 
1707     write_output_buffer(This, attlistW, ARRAY_SIZE(attlistW));
1708     if (n_element) {
1709         write_output_buffer(This, element, n_element);
1710         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1711     }
1712 
1713     if (n_attr) {
1714         write_output_buffer(This, attr, n_attr);
1715         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1716     }
1717 
1718     if (n_type) {
1719         write_output_buffer(This, type, n_type);
1720         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1721     }
1722 
1723     if (n_default) {
1724         write_output_buffer(This, Default, n_default);
1725         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1726     }
1727 
1728     if (n_value)
1729         write_output_buffer_quoted(This, value, n_value);
1730 
1731     write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
1732 
1733     return S_OK;
1734 }
1735 
1736 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
1737     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
1738 {
1739     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1740 
1741     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1742         debugstr_wn(value, n_value), n_value);
1743 
1744     if (!name || !value) return E_INVALIDARG;
1745 
1746     write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
1747     if (n_name) {
1748         write_output_buffer(This, name, n_name);
1749         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1750     }
1751 
1752     if (n_value)
1753         write_output_buffer_quoted(This, value, n_value);
1754 
1755     write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
1756 
1757     return S_OK;
1758 }
1759 
1760 static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
1761     const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
1762     const WCHAR *systemId, int n_systemId)
1763 {
1764     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1765 
1766     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1767         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
1768 
1769     if (!name || !systemId) return E_INVALIDARG;
1770 
1771     write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
1772     if (n_name) {
1773         write_output_buffer(This, name, n_name);
1774         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1775     }
1776 
1777     if (publicId)
1778     {
1779         write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
1780         write_output_buffer_quoted(This, publicId, n_publicId);
1781         write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
1782         write_output_buffer_quoted(This, systemId, n_systemId);
1783     }
1784     else
1785     {
1786         write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
1787         write_output_buffer_quoted(This, systemId, n_systemId);
1788     }
1789 
1790     write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
1791 
1792     return S_OK;
1793 }
1794 
1795 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
1796     SAXDeclHandler_QueryInterface,
1797     SAXDeclHandler_AddRef,
1798     SAXDeclHandler_Release,
1799     SAXDeclHandler_elementDecl,
1800     SAXDeclHandler_attributeDecl,
1801     SAXDeclHandler_internalEntityDecl,
1802     SAXDeclHandler_externalEntityDecl
1803 };
1804 
1805 /*** IVBSAXDeclHandler ***/
1806 static HRESULT WINAPI VBSAXDeclHandler_QueryInterface(IVBSAXDeclHandler *iface,
1807     REFIID riid, void **obj)
1808 {
1809     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1810     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1811 }
1812 
1813 static ULONG WINAPI VBSAXDeclHandler_AddRef(IVBSAXDeclHandler *iface)
1814 {
1815     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1816     return IMXWriter_AddRef(&This->IMXWriter_iface);
1817 }
1818 
1819 static ULONG WINAPI VBSAXDeclHandler_Release(IVBSAXDeclHandler *iface)
1820 {
1821     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1822     return IMXWriter_Release(&This->IMXWriter_iface);
1823 }
1824 
1825 static HRESULT WINAPI VBSAXDeclHandler_GetTypeInfoCount(IVBSAXDeclHandler *iface, UINT* pctinfo)
1826 {
1827     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1828     return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
1829 }
1830 
1831 static HRESULT WINAPI VBSAXDeclHandler_GetTypeInfo(IVBSAXDeclHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1832 {
1833     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1834     return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
1835 }
1836 
1837 static HRESULT WINAPI VBSAXDeclHandler_GetIDsOfNames(IVBSAXDeclHandler *iface, REFIID riid, LPOLESTR* rgszNames,
1838     UINT cNames, LCID lcid, DISPID* rgDispId )
1839 {
1840     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1841     return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
1842 }
1843 
1844 static HRESULT WINAPI VBSAXDeclHandler_Invoke(IVBSAXDeclHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
1845     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
1846 {
1847     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1848     return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
1849         pExcepInfo, puArgErr);
1850 }
1851 
1852 static HRESULT WINAPI VBSAXDeclHandler_elementDecl(IVBSAXDeclHandler *iface, BSTR *name, BSTR *model)
1853 {
1854     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1855 
1856     TRACE("(%p)->(%p %p)\n", This, name, model);
1857 
1858     if (!name || !model)
1859         return E_POINTER;
1860 
1861     return ISAXDeclHandler_elementDecl(&This->ISAXDeclHandler_iface, *name, -1, *model, -1);
1862 }
1863 
1864 static HRESULT WINAPI VBSAXDeclHandler_attributeDecl(IVBSAXDeclHandler *iface,
1865     BSTR *element, BSTR *attr, BSTR *type, BSTR *default_value, BSTR *value)
1866 {
1867     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1868 
1869     TRACE("(%p)->(%p %p %p %p %p)\n", This, element, attr, type, default_value, value);
1870 
1871     if (!element || !attr || !type || !default_value || !value)
1872         return E_POINTER;
1873 
1874     return ISAXDeclHandler_attributeDecl(&This->ISAXDeclHandler_iface, *element, -1, *attr, -1, *type, -1,
1875         *default_value, -1, *value, -1);
1876 }
1877 
1878 static HRESULT WINAPI VBSAXDeclHandler_internalEntityDecl(IVBSAXDeclHandler *iface, BSTR *name, BSTR *value)
1879 {
1880     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1881 
1882     TRACE("(%p)->(%p %p)\n", This, name, value);
1883 
1884     if (!name || !value)
1885         return E_POINTER;
1886 
1887     return ISAXDeclHandler_internalEntityDecl(&This->ISAXDeclHandler_iface, *name, -1, *value, -1);
1888 }
1889 
1890 static HRESULT WINAPI VBSAXDeclHandler_externalEntityDecl(IVBSAXDeclHandler *iface,
1891     BSTR *name, BSTR *publicid, BSTR *systemid)
1892 {
1893     mxwriter *This = impl_from_IVBSAXDeclHandler( iface );
1894 
1895     TRACE("(%p)->(%p %p %p)\n", This, name, publicid, systemid);
1896 
1897     if (!name || !publicid || !systemid)
1898         return E_POINTER;
1899 
1900     return ISAXDeclHandler_externalEntityDecl(&This->ISAXDeclHandler_iface, *name, -1, *publicid, -1, *systemid, -1);
1901 }
1902 
1903 static const IVBSAXDeclHandlerVtbl VBSAXDeclHandlerVtbl = {
1904     VBSAXDeclHandler_QueryInterface,
1905     VBSAXDeclHandler_AddRef,
1906     VBSAXDeclHandler_Release,
1907     VBSAXDeclHandler_GetTypeInfoCount,
1908     VBSAXDeclHandler_GetTypeInfo,
1909     VBSAXDeclHandler_GetIDsOfNames,
1910     VBSAXDeclHandler_Invoke,
1911     VBSAXDeclHandler_elementDecl,
1912     VBSAXDeclHandler_attributeDecl,
1913     VBSAXDeclHandler_internalEntityDecl,
1914     VBSAXDeclHandler_externalEntityDecl
1915 };
1916 
1917 /*** IVBSAXLexicalHandler ***/
1918 static HRESULT WINAPI VBSAXLexicalHandler_QueryInterface(IVBSAXLexicalHandler *iface,
1919     REFIID riid, void **obj)
1920 {
1921     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1922     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1923 }
1924 
1925 static ULONG WINAPI VBSAXLexicalHandler_AddRef(IVBSAXLexicalHandler *iface)
1926 {
1927     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1928     return IMXWriter_AddRef(&This->IMXWriter_iface);
1929 }
1930 
1931 static ULONG WINAPI VBSAXLexicalHandler_Release(IVBSAXLexicalHandler *iface)
1932 {
1933     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1934     return IMXWriter_Release(&This->IMXWriter_iface);
1935 }
1936 
1937 static HRESULT WINAPI VBSAXLexicalHandler_GetTypeInfoCount(IVBSAXLexicalHandler *iface, UINT* pctinfo)
1938 {
1939     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1940     return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
1941 }
1942 
1943 static HRESULT WINAPI VBSAXLexicalHandler_GetTypeInfo(IVBSAXLexicalHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1944 {
1945     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1946     return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
1947 }
1948 
1949 static HRESULT WINAPI VBSAXLexicalHandler_GetIDsOfNames(IVBSAXLexicalHandler *iface, REFIID riid, LPOLESTR* rgszNames,
1950     UINT cNames, LCID lcid, DISPID* rgDispId )
1951 {
1952     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1953     return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
1954 }
1955 
1956 static HRESULT WINAPI VBSAXLexicalHandler_Invoke(IVBSAXLexicalHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
1957     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
1958 {
1959     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1960     return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
1961         pExcepInfo, puArgErr);
1962 }
1963 
1964 static HRESULT WINAPI VBSAXLexicalHandler_startDTD(IVBSAXLexicalHandler *iface, BSTR *name, BSTR *publicId, BSTR *systemId)
1965 {
1966     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1967 
1968     TRACE("(%p)->(%p %p %p)\n", This, name, publicId, systemId);
1969 
1970     if (!name || !publicId || !systemId)
1971         return E_POINTER;
1972 
1973     return ISAXLexicalHandler_startDTD(&This->ISAXLexicalHandler_iface, *name, -1, *publicId, -1, *systemId, -1);
1974 }
1975 
1976 static HRESULT WINAPI VBSAXLexicalHandler_endDTD(IVBSAXLexicalHandler *iface)
1977 {
1978     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1979     return ISAXLexicalHandler_endDTD(&This->ISAXLexicalHandler_iface);
1980 }
1981 
1982 static HRESULT WINAPI VBSAXLexicalHandler_startEntity(IVBSAXLexicalHandler *iface, BSTR *name)
1983 {
1984     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1985 
1986     TRACE("(%p)->(%p)\n", This, name);
1987 
1988     if (!name)
1989         return E_POINTER;
1990 
1991     return ISAXLexicalHandler_startEntity(&This->ISAXLexicalHandler_iface, *name, -1);
1992 }
1993 
1994 static HRESULT WINAPI VBSAXLexicalHandler_endEntity(IVBSAXLexicalHandler *iface, BSTR *name)
1995 {
1996     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
1997 
1998     TRACE("(%p)->(%p)\n", This, name);
1999 
2000     if (!name)
2001         return E_POINTER;
2002 
2003     return ISAXLexicalHandler_endEntity(&This->ISAXLexicalHandler_iface, *name, -1);
2004 }
2005 
2006 static HRESULT WINAPI VBSAXLexicalHandler_startCDATA(IVBSAXLexicalHandler *iface)
2007 {
2008     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
2009     return ISAXLexicalHandler_startCDATA(&This->ISAXLexicalHandler_iface);
2010 }
2011 
2012 static HRESULT WINAPI VBSAXLexicalHandler_endCDATA(IVBSAXLexicalHandler *iface)
2013 {
2014     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
2015     return ISAXLexicalHandler_endCDATA(&This->ISAXLexicalHandler_iface);
2016 }
2017 
2018 static HRESULT WINAPI VBSAXLexicalHandler_comment(IVBSAXLexicalHandler *iface, BSTR *chars)
2019 {
2020     mxwriter *This = impl_from_IVBSAXLexicalHandler( iface );
2021 
2022     TRACE("(%p)->(%p)\n", This, chars);
2023 
2024     if (!chars)
2025         return E_POINTER;
2026 
2027     return ISAXLexicalHandler_comment(&This->ISAXLexicalHandler_iface, *chars, -1);
2028 }
2029 
2030 static const IVBSAXLexicalHandlerVtbl VBSAXLexicalHandlerVtbl = {
2031     VBSAXLexicalHandler_QueryInterface,
2032     VBSAXLexicalHandler_AddRef,
2033     VBSAXLexicalHandler_Release,
2034     VBSAXLexicalHandler_GetTypeInfoCount,
2035     VBSAXLexicalHandler_GetTypeInfo,
2036     VBSAXLexicalHandler_GetIDsOfNames,
2037     VBSAXLexicalHandler_Invoke,
2038     VBSAXLexicalHandler_startDTD,
2039     VBSAXLexicalHandler_endDTD,
2040     VBSAXLexicalHandler_startEntity,
2041     VBSAXLexicalHandler_endEntity,
2042     VBSAXLexicalHandler_startCDATA,
2043     VBSAXLexicalHandler_endCDATA,
2044     VBSAXLexicalHandler_comment
2045 };
2046 
2047 /*** IVBSAXContentHandler ***/
2048 static HRESULT WINAPI VBSAXContentHandler_QueryInterface(IVBSAXContentHandler *iface, REFIID riid, void **obj)
2049 {
2050     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2051     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
2052 }
2053 
2054 static ULONG WINAPI VBSAXContentHandler_AddRef(IVBSAXContentHandler *iface)
2055 {
2056     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2057     return IMXWriter_AddRef(&This->IMXWriter_iface);
2058 }
2059 
2060 static ULONG WINAPI VBSAXContentHandler_Release(IVBSAXContentHandler *iface)
2061 {
2062     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2063     return IMXWriter_Release(&This->IMXWriter_iface);
2064 }
2065 
2066 static HRESULT WINAPI VBSAXContentHandler_GetTypeInfoCount(IVBSAXContentHandler *iface, UINT* pctinfo)
2067 {
2068     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2069     return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
2070 }
2071 
2072 static HRESULT WINAPI VBSAXContentHandler_GetTypeInfo(IVBSAXContentHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
2073 {
2074     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2075     return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
2076 }
2077 
2078 static HRESULT WINAPI VBSAXContentHandler_GetIDsOfNames(IVBSAXContentHandler *iface, REFIID riid, LPOLESTR* rgszNames,
2079     UINT cNames, LCID lcid, DISPID* rgDispId )
2080 {
2081     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2082     return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
2083 }
2084 
2085 static HRESULT WINAPI VBSAXContentHandler_Invoke(IVBSAXContentHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
2086     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
2087 {
2088     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2089     return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
2090         pExcepInfo, puArgErr);
2091 }
2092 
2093 static HRESULT WINAPI VBSAXContentHandler_putref_documentLocator(IVBSAXContentHandler *iface, IVBSAXLocator *locator)
2094 {
2095     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2096     TRACE("(%p)->(%p)\n", This, locator);
2097     return S_OK;
2098 }
2099 
2100 static HRESULT WINAPI VBSAXContentHandler_startDocument(IVBSAXContentHandler *iface)
2101 {
2102     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2103     return ISAXContentHandler_startDocument(&This->ISAXContentHandler_iface);
2104 }
2105 
2106 static HRESULT WINAPI VBSAXContentHandler_endDocument(IVBSAXContentHandler *iface)
2107 {
2108     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2109     return ISAXContentHandler_endDocument(&This->ISAXContentHandler_iface);
2110 }
2111 
2112 static HRESULT WINAPI VBSAXContentHandler_startPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix, BSTR *uri)
2113 {
2114     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2115 
2116     TRACE("(%p)->(%p %p)\n", This, prefix, uri);
2117 
2118     if (!prefix || !uri)
2119         return E_POINTER;
2120 
2121     return ISAXContentHandler_startPrefixMapping(&This->ISAXContentHandler_iface, *prefix, -1, *uri, -1);
2122 }
2123 
2124 static HRESULT WINAPI VBSAXContentHandler_endPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix)
2125 {
2126     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2127 
2128     TRACE("(%p)->(%p)\n", This, prefix);
2129 
2130     if (!prefix)
2131         return E_POINTER;
2132 
2133     return ISAXContentHandler_endPrefixMapping(&This->ISAXContentHandler_iface, *prefix, -1);
2134 }
2135 
2136 static HRESULT WINAPI VBSAXContentHandler_startElement(IVBSAXContentHandler *iface,
2137     BSTR *namespaceURI, BSTR *localName, BSTR *QName, IVBSAXAttributes *attrs)
2138 {
2139     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2140 
2141     TRACE("(%p)->(%p %p %p %p)\n", This, namespaceURI, localName, QName, attrs);
2142 
2143     if (!namespaceURI || !localName || !QName)
2144         return E_POINTER;
2145 
2146     TRACE("(%s %s %s)\n", debugstr_w(*namespaceURI), debugstr_w(*localName), debugstr_w(*QName));
2147 
2148     mxwriter_write_starttag(This, *QName, SysStringLen(*QName));
2149 
2150     if (attrs)
2151     {
2152         int length, i, escape;
2153         HRESULT hr;
2154 
2155         hr = IVBSAXAttributes_get_length(attrs, &length);
2156         if (FAILED(hr)) return hr;
2157 
2158         escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
2159             (This->class_version == MSXML4 || This->class_version == MSXML6);
2160 
2161         for (i = 0; i < length; i++)
2162         {
2163             BSTR qname, value;
2164 
2165             hr = IVBSAXAttributes_getQName(attrs, i, &qname);
2166             if (FAILED(hr)) return hr;
2167 
2168             hr = IVBSAXAttributes_getValue(attrs, i, &value);
2169             if (FAILED(hr))
2170             {
2171                 SysFreeString(qname);
2172                 return hr;
2173             }
2174 
2175             mxwriter_write_attribute(This, qname, SysStringLen(qname), value, SysStringLen(value), escape);
2176             SysFreeString(qname);
2177             SysFreeString(value);
2178         }
2179     }
2180 
2181     return S_OK;
2182 }
2183 
2184 static HRESULT WINAPI VBSAXContentHandler_endElement(IVBSAXContentHandler *iface, BSTR *namespaceURI,
2185     BSTR *localName, BSTR *QName)
2186 {
2187     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2188 
2189     TRACE("(%p)->(%p %p %p)\n", This, namespaceURI, localName, QName);
2190 
2191     if (!namespaceURI || !localName || !QName)
2192         return E_POINTER;
2193 
2194     return ISAXContentHandler_endElement(&This->ISAXContentHandler_iface,
2195         *namespaceURI, SysStringLen(*namespaceURI),
2196         *localName, SysStringLen(*localName),
2197         *QName, SysStringLen(*QName));
2198 }
2199 
2200 static HRESULT WINAPI VBSAXContentHandler_characters(IVBSAXContentHandler *iface, BSTR *chars)
2201 {
2202     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2203 
2204     TRACE("(%p)->(%p)\n", This, chars);
2205 
2206     if (!chars)
2207         return E_POINTER;
2208 
2209     return ISAXContentHandler_characters(&This->ISAXContentHandler_iface, *chars, -1);
2210 }
2211 
2212 static HRESULT WINAPI VBSAXContentHandler_ignorableWhitespace(IVBSAXContentHandler *iface, BSTR *chars)
2213 {
2214     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2215 
2216     TRACE("(%p)->(%p)\n", This, chars);
2217 
2218     if (!chars)
2219         return E_POINTER;
2220 
2221     return ISAXContentHandler_ignorableWhitespace(&This->ISAXContentHandler_iface, *chars, -1);
2222 }
2223 
2224 static HRESULT WINAPI VBSAXContentHandler_processingInstruction(IVBSAXContentHandler *iface,
2225     BSTR *target, BSTR *data)
2226 {
2227     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2228 
2229     TRACE("(%p)->(%p %p)\n", This, target, data);
2230 
2231     if (!target || !data)
2232         return E_POINTER;
2233 
2234     return ISAXContentHandler_processingInstruction(&This->ISAXContentHandler_iface, *target, -1, *data, -1);
2235 }
2236 
2237 static HRESULT WINAPI VBSAXContentHandler_skippedEntity(IVBSAXContentHandler *iface, BSTR *name)
2238 {
2239     mxwriter *This = impl_from_IVBSAXContentHandler( iface );
2240 
2241     TRACE("(%p)->(%p)\n", This, name);
2242 
2243     if (!name)
2244         return E_POINTER;
2245 
2246     return ISAXContentHandler_skippedEntity(&This->ISAXContentHandler_iface, *name, -1);
2247 }
2248 
2249 static const IVBSAXContentHandlerVtbl VBSAXContentHandlerVtbl = {
2250     VBSAXContentHandler_QueryInterface,
2251     VBSAXContentHandler_AddRef,
2252     VBSAXContentHandler_Release,
2253     VBSAXContentHandler_GetTypeInfoCount,
2254     VBSAXContentHandler_GetTypeInfo,
2255     VBSAXContentHandler_GetIDsOfNames,
2256     VBSAXContentHandler_Invoke,
2257     VBSAXContentHandler_putref_documentLocator,
2258     VBSAXContentHandler_startDocument,
2259     VBSAXContentHandler_endDocument,
2260     VBSAXContentHandler_startPrefixMapping,
2261     VBSAXContentHandler_endPrefixMapping,
2262     VBSAXContentHandler_startElement,
2263     VBSAXContentHandler_endElement,
2264     VBSAXContentHandler_characters,
2265     VBSAXContentHandler_ignorableWhitespace,
2266     VBSAXContentHandler_processingInstruction,
2267     VBSAXContentHandler_skippedEntity
2268 };
2269 
2270 static HRESULT WINAPI SAXDTDHandler_QueryInterface(ISAXDTDHandler *iface, REFIID riid, void **obj)
2271 {
2272     mxwriter *This = impl_from_ISAXDTDHandler( iface );
2273     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
2274 }
2275 
2276 static ULONG WINAPI SAXDTDHandler_AddRef(ISAXDTDHandler *iface)
2277 {
2278     mxwriter *This = impl_from_ISAXDTDHandler( iface );
2279     return IMXWriter_AddRef(&This->IMXWriter_iface);
2280 }
2281 
2282 static ULONG WINAPI SAXDTDHandler_Release(ISAXDTDHandler *iface)
2283 {
2284     mxwriter *This = impl_from_ISAXDTDHandler( iface );
2285     return IMXWriter_Release(&This->IMXWriter_iface);
2286 }
2287 
2288 static HRESULT WINAPI SAXDTDHandler_notationDecl(ISAXDTDHandler *iface,
2289     const WCHAR *name, INT n_name,
2290     const WCHAR *publicid, INT n_publicid,
2291     const WCHAR *systemid, INT n_systemid)
2292 {
2293     static const WCHAR notationW[] = {'<','!','N','O','T','A','T','I','O','N',' '};
2294     mxwriter *This = impl_from_ISAXDTDHandler( iface );
2295 
2296     TRACE("(%p)->(%s:%d, %s:%d, %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
2297         debugstr_wn(publicid, n_publicid), n_publicid, debugstr_wn(systemid, n_systemid), n_systemid);
2298 
2299     if (!name || !n_name)
2300         return E_INVALIDARG;
2301 
2302     write_output_buffer(This, notationW, ARRAY_SIZE(notationW));
2303     write_output_buffer(This, name, n_name);
2304 
2305     if (!publicid && !systemid)
2306         return E_INVALIDARG;
2307 
2308     write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
2309     if (publicid)
2310     {
2311         write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
2312         write_output_buffer_quoted(This, publicid, n_publicid);
2313         if (systemid)
2314         {
2315             write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
2316             write_output_buffer_quoted(This, systemid, n_systemid);
2317         }
2318     }
2319     else
2320     {
2321         write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
2322         write_output_buffer_quoted(This, systemid, n_systemid);
2323     }
2324 
2325     write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
2326 
2327     return S_OK;
2328 }
2329 
2330 static HRESULT WINAPI SAXDTDHandler_unparsedEntityDecl(ISAXDTDHandler *iface,
2331     const WCHAR *name, INT nname,
2332     const WCHAR *publicid, INT npublicid,
2333     const WCHAR *systemid, INT nsystemid,
2334     const WCHAR *notation, INT nnotation)
2335 {
2336     mxwriter *This = impl_from_ISAXDTDHandler( iface );
2337     FIXME("(%p)->(%s:%d, %s:%d, %s:%d, %s:%d): stub\n", This, debugstr_wn(name, nname), nname,
2338         debugstr_wn(publicid, npublicid), npublicid, debugstr_wn(systemid, nsystemid), nsystemid,
2339         debugstr_wn(notation, nnotation), nnotation);
2340     return E_NOTIMPL;
2341 }
2342 
2343 static const ISAXDTDHandlerVtbl SAXDTDHandlerVtbl = {
2344     SAXDTDHandler_QueryInterface,
2345     SAXDTDHandler_AddRef,
2346     SAXDTDHandler_Release,
2347     SAXDTDHandler_notationDecl,
2348     SAXDTDHandler_unparsedEntityDecl
2349 };
2350 
2351 /*** IVBSAXDTDHandler ***/
2352 static HRESULT WINAPI VBSAXDTDHandler_QueryInterface(IVBSAXDTDHandler *iface, REFIID riid, void **obj)
2353 {
2354     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2355     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
2356 }
2357 
2358 static ULONG WINAPI VBSAXDTDHandler_AddRef(IVBSAXDTDHandler *iface)
2359 {
2360     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2361     return IMXWriter_AddRef(&This->IMXWriter_iface);
2362 }
2363 
2364 static ULONG WINAPI VBSAXDTDHandler_Release(IVBSAXDTDHandler *iface)
2365 {
2366     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2367     return IMXWriter_Release(&This->IMXWriter_iface);
2368 }
2369 
2370 static HRESULT WINAPI VBSAXDTDHandler_GetTypeInfoCount(IVBSAXDTDHandler *iface, UINT* pctinfo)
2371 {
2372     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2373     return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
2374 }
2375 
2376 static HRESULT WINAPI VBSAXDTDHandler_GetTypeInfo(IVBSAXDTDHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
2377 {
2378     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2379     return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
2380 }
2381 
2382 static HRESULT WINAPI VBSAXDTDHandler_GetIDsOfNames(IVBSAXDTDHandler *iface, REFIID riid, LPOLESTR* rgszNames,
2383     UINT cNames, LCID lcid, DISPID* rgDispId )
2384 {
2385     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2386     return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
2387 }
2388 
2389 static HRESULT WINAPI VBSAXDTDHandler_Invoke(IVBSAXDTDHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
2390     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
2391 {
2392     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2393     return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
2394         pExcepInfo, puArgErr);
2395 }
2396 
2397 static HRESULT WINAPI VBSAXDTDHandler_notationDecl(IVBSAXDTDHandler *iface, BSTR *name, BSTR *publicId, BSTR *systemId)
2398 {
2399     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2400 
2401     TRACE("(%p)->(%p %p %p)\n", This, name, publicId, systemId);
2402 
2403     if (!name || !publicId || !systemId)
2404         return E_POINTER;
2405 
2406     return ISAXDTDHandler_notationDecl(&This->ISAXDTDHandler_iface, *name, -1, *publicId, -1, *systemId, -1);
2407 }
2408 
2409 static HRESULT WINAPI VBSAXDTDHandler_unparsedEntityDecl(IVBSAXDTDHandler *iface, BSTR *name, BSTR *publicId,
2410     BSTR *systemId, BSTR *notation)
2411 {
2412     mxwriter *This = impl_from_IVBSAXDTDHandler( iface );
2413 
2414     TRACE("(%p)->(%p %p %p %p)\n", This, name, publicId, systemId, notation);
2415 
2416     if (!name || !publicId || !systemId || !notation)
2417         return E_POINTER;
2418 
2419     return ISAXDTDHandler_unparsedEntityDecl(&This->ISAXDTDHandler_iface, *name, -1, *publicId, -1,
2420         *systemId, -1, *notation, -1);
2421 }
2422 
2423 static const IVBSAXDTDHandlerVtbl VBSAXDTDHandlerVtbl = {
2424     VBSAXDTDHandler_QueryInterface,
2425     VBSAXDTDHandler_AddRef,
2426     VBSAXDTDHandler_Release,
2427     VBSAXDTDHandler_GetTypeInfoCount,
2428     VBSAXDTDHandler_GetTypeInfo,
2429     VBSAXDTDHandler_GetIDsOfNames,
2430     VBSAXDTDHandler_Invoke,
2431     VBSAXDTDHandler_notationDecl,
2432     VBSAXDTDHandler_unparsedEntityDecl
2433 };
2434 
2435 /* ISAXErrorHandler */
2436 static HRESULT WINAPI SAXErrorHandler_QueryInterface(ISAXErrorHandler *iface, REFIID riid, void **obj)
2437 {
2438     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2439     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
2440 }
2441 
2442 static ULONG WINAPI SAXErrorHandler_AddRef(ISAXErrorHandler *iface)
2443 {
2444     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2445     return IMXWriter_AddRef(&This->IMXWriter_iface);
2446 }
2447 
2448 static ULONG WINAPI SAXErrorHandler_Release(ISAXErrorHandler *iface)
2449 {
2450     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2451     return IMXWriter_Release(&This->IMXWriter_iface);
2452 }
2453 
2454 static HRESULT WINAPI SAXErrorHandler_error(ISAXErrorHandler *iface,
2455     ISAXLocator *locator, const WCHAR *message, HRESULT hr)
2456 {
2457     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2458 
2459     FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
2460 
2461     return E_NOTIMPL;
2462 }
2463 
2464 static HRESULT WINAPI SAXErrorHandler_fatalError(ISAXErrorHandler *iface,
2465     ISAXLocator *locator, const WCHAR *message, HRESULT hr)
2466 {
2467     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2468 
2469     FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
2470 
2471     return E_NOTIMPL;
2472 }
2473 
2474 static HRESULT WINAPI SAXErrorHandler_ignorableWarning(ISAXErrorHandler *iface,
2475     ISAXLocator *locator, const WCHAR *message, HRESULT hr)
2476 {
2477     mxwriter *This = impl_from_ISAXErrorHandler( iface );
2478 
2479     FIXME("(%p)->(%p %s 0x%08x)\n", This, locator, debugstr_w(message), hr);
2480 
2481     return E_NOTIMPL;
2482 }
2483 
2484 static const ISAXErrorHandlerVtbl SAXErrorHandlerVtbl = {
2485     SAXErrorHandler_QueryInterface,
2486     SAXErrorHandler_AddRef,
2487     SAXErrorHandler_Release,
2488     SAXErrorHandler_error,
2489     SAXErrorHandler_fatalError,
2490     SAXErrorHandler_ignorableWarning
2491 };
2492 
2493 /*** IVBSAXErrorHandler ***/
2494 static HRESULT WINAPI VBSAXErrorHandler_QueryInterface(IVBSAXErrorHandler *iface, REFIID riid, void **obj)
2495 {
2496     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2497     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
2498 }
2499 
2500 static ULONG WINAPI VBSAXErrorHandler_AddRef(IVBSAXErrorHandler *iface)
2501 {
2502     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2503     return IMXWriter_AddRef(&This->IMXWriter_iface);
2504 }
2505 
2506 static ULONG WINAPI VBSAXErrorHandler_Release(IVBSAXErrorHandler *iface)
2507 {
2508     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2509     return IMXWriter_Release(&This->IMXWriter_iface);
2510 }
2511 
2512 static HRESULT WINAPI VBSAXErrorHandler_GetTypeInfoCount(IVBSAXErrorHandler *iface, UINT* pctinfo)
2513 {
2514     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2515     return IMXWriter_GetTypeInfoCount(&This->IMXWriter_iface, pctinfo);
2516 }
2517 
2518 static HRESULT WINAPI VBSAXErrorHandler_GetTypeInfo(IVBSAXErrorHandler *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
2519 {
2520     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2521     return IMXWriter_GetTypeInfo(&This->IMXWriter_iface, iTInfo, lcid, ppTInfo);
2522 }
2523 
2524 static HRESULT WINAPI VBSAXErrorHandler_GetIDsOfNames(IVBSAXErrorHandler *iface, REFIID riid, LPOLESTR* rgszNames,
2525     UINT cNames, LCID lcid, DISPID* rgDispId )
2526 {
2527     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2528     return IMXWriter_GetIDsOfNames(&This->IMXWriter_iface, riid, rgszNames, cNames, lcid, rgDispId);
2529 }
2530 
2531 static HRESULT WINAPI VBSAXErrorHandler_Invoke(IVBSAXErrorHandler *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
2532     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr )
2533 {
2534     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2535     return IMXWriter_Invoke(&This->IMXWriter_iface, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult,
2536         pExcepInfo, puArgErr);
2537 }
2538 
2539 static HRESULT WINAPI VBSAXErrorHandler_error(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
2540 {
2541     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2542     FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
2543     return E_NOTIMPL;
2544 }
2545 
2546 static HRESULT WINAPI VBSAXErrorHandler_fatalError(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
2547 {
2548     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2549     FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
2550     return E_NOTIMPL;
2551 }
2552 
2553 static HRESULT WINAPI VBSAXErrorHandler_ignorableWarning(IVBSAXErrorHandler *iface, IVBSAXLocator *locator, BSTR *message, LONG code)
2554 {
2555     mxwriter *This = impl_from_IVBSAXErrorHandler( iface );
2556     FIXME("(%p)->(%p %p %x): stub\n", This, locator, message, code);
2557     return E_NOTIMPL;
2558 }
2559 
2560 static const IVBSAXErrorHandlerVtbl VBSAXErrorHandlerVtbl = {
2561     VBSAXErrorHandler_QueryInterface,
2562     VBSAXErrorHandler_AddRef,
2563     VBSAXErrorHandler_Release,
2564     VBSAXErrorHandler_GetTypeInfoCount,
2565     VBSAXErrorHandler_GetTypeInfo,
2566     VBSAXErrorHandler_GetIDsOfNames,
2567     VBSAXErrorHandler_Invoke,
2568     VBSAXErrorHandler_error,
2569     VBSAXErrorHandler_fatalError,
2570     VBSAXErrorHandler_ignorableWarning
2571 };
2572 
2573 static const tid_t mxwriter_iface_tids[] = {
2574     IMXWriter_tid,
2575     0
2576 };
2577 
2578 static dispex_static_data_t mxwriter_dispex = {
2579     NULL,
2580     IMXWriter_tid,
2581     NULL,
2582     mxwriter_iface_tids
2583 };
2584 
2585 HRESULT MXWriter_create(MSXML_VERSION version, void **ppObj)
2586 {
2587     static const WCHAR version10W[] = {'1','.','0',0};
2588     mxwriter *This;
2589     HRESULT hr;
2590 
2591     TRACE("(%p)\n", ppObj);
2592 
2593     This = heap_alloc( sizeof (*This) );
2594     if(!This)
2595         return E_OUTOFMEMORY;
2596 
2597     This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
2598     This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
2599     This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
2600     This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
2601     This->ISAXDTDHandler_iface.lpVtbl = &SAXDTDHandlerVtbl;
2602     This->ISAXErrorHandler_iface.lpVtbl = &SAXErrorHandlerVtbl;
2603     This->IVBSAXDeclHandler_iface.lpVtbl = &VBSAXDeclHandlerVtbl;
2604     This->IVBSAXLexicalHandler_iface.lpVtbl = &VBSAXLexicalHandlerVtbl;
2605     This->IVBSAXContentHandler_iface.lpVtbl = &VBSAXContentHandlerVtbl;
2606     This->IVBSAXDTDHandler_iface.lpVtbl = &VBSAXDTDHandlerVtbl;
2607     This->IVBSAXErrorHandler_iface.lpVtbl = &VBSAXErrorHandlerVtbl;
2608     This->ref = 1;
2609     This->class_version = version;
2610 
2611     This->props[MXWriter_BOM] = VARIANT_TRUE;
2612     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
2613     This->props[MXWriter_Indent] = VARIANT_FALSE;
2614     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
2615     This->props[MXWriter_Standalone] = VARIANT_FALSE;
2616     This->prop_changed = FALSE;
2617     This->encoding = SysAllocString(utf16W);
2618     This->version  = SysAllocString(version10W);
2619     This->xml_enc  = XmlEncoding_UTF16;
2620 
2621     This->element = NULL;
2622     This->cdata = FALSE;
2623     This->indent = 0;
2624     This->text = FALSE;
2625     This->newline = FALSE;
2626 
2627     This->dest = NULL;
2628 
2629     hr = init_output_buffer(This->xml_enc, &This->buffer);
2630     if (hr != S_OK) {
2631         SysFreeString(This->encoding);
2632         SysFreeString(This->version);
2633         heap_free(This);
2634         return hr;
2635     }
2636 
2637     init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
2638 
2639     *ppObj = &This->IMXWriter_iface;
2640 
2641     TRACE("returning iface %p\n", *ppObj);
2642 
2643     return S_OK;
2644 }
2645 
2646 static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
2647 {
2648     mxattributes *This = impl_from_IMXAttributes( iface );
2649 
2650     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
2651 
2652     *ppObj = NULL;
2653 
2654     if ( IsEqualGUID( riid, &IID_IUnknown )  ||
2655          IsEqualGUID( riid, &IID_IDispatch ) ||
2656          IsEqualGUID( riid, &IID_IMXAttributes ))
2657     {
2658         *ppObj = iface;
2659     }
2660     else if ( IsEqualGUID( riid, &IID_ISAXAttributes ))
2661     {
2662         *ppObj = &This->ISAXAttributes_iface;
2663     }
2664     else if ( IsEqualGUID( riid, &IID_IVBSAXAttributes ))
2665     {
2666         *ppObj = &This->IVBSAXAttributes_iface;
2667     }
2668     else if (dispex_query_interface(&This->dispex, riid, ppObj))
2669     {
2670         return *ppObj ? S_OK : E_NOINTERFACE;
2671     }
2672     else
2673     {
2674         FIXME("interface %s not implemented\n", debugstr_guid(riid));
2675         return E_NOINTERFACE;
2676     }
2677 
2678     IMXAttributes_AddRef( iface );
2679 
2680     return S_OK;
2681 }
2682 
2683 static ULONG WINAPI MXAttributes_AddRef(IMXAttributes *iface)
2684 {
2685     mxattributes *This = impl_from_IMXAttributes( iface );
2686     ULONG ref = InterlockedIncrement( &This->ref );
2687     TRACE("(%p)->(%d)\n", This, ref );
2688     return ref;
2689 }
2690 
2691 static ULONG WINAPI MXAttributes_Release(IMXAttributes *iface)
2692 {
2693     mxattributes *This = impl_from_IMXAttributes( iface );
2694     LONG ref = InterlockedDecrement( &This->ref );
2695 
2696     TRACE("(%p)->(%d)\n", This, ref);
2697 
2698     if (ref == 0)
2699     {
2700         int i;
2701 
2702         for (i = 0; i < This->length; i++)
2703         {
2704             SysFreeString(This->attr[i].qname);
2705             SysFreeString(This->attr[i].local);
2706             SysFreeString(This->attr[i].uri);
2707             SysFreeString(This->attr[i].type);
2708             SysFreeString(This->attr[i].value);
2709         }
2710 
2711         heap_free(This->attr);
2712         heap_free(This);
2713     }
2714 
2715     return ref;
2716 }
2717 
2718 static HRESULT WINAPI MXAttributes_GetTypeInfoCount(IMXAttributes *iface, UINT* pctinfo)
2719 {
2720     mxattributes *This = impl_from_IMXAttributes( iface );
2721     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
2722 }
2723 
2724 static HRESULT WINAPI MXAttributes_GetTypeInfo(IMXAttributes *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
2725 {
2726     mxattributes *This = impl_from_IMXAttributes( iface );
2727     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
2728 }
2729 
2730 static HRESULT WINAPI MXAttributes_GetIDsOfNames(
2731     IMXAttributes *iface,
2732     REFIID riid,
2733     LPOLESTR* rgszNames,
2734     UINT cNames,
2735     LCID lcid,
2736     DISPID* rgDispId)
2737 {
2738     mxattributes *This = impl_from_IMXAttributes( iface );
2739     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
2740         riid, rgszNames, cNames, lcid, rgDispId);
2741 }
2742 
2743 static HRESULT WINAPI MXAttributes_Invoke(
2744     IMXAttributes *iface,
2745     DISPID dispIdMember,
2746     REFIID riid,
2747     LCID lcid,
2748     WORD wFlags,
2749     DISPPARAMS* pDispParams,
2750     VARIANT* pVarResult,
2751     EXCEPINFO* pExcepInfo,
2752     UINT* puArgErr)
2753 {
2754     mxattributes *This = impl_from_IMXAttributes( iface );
2755     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
2756         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2757 }
2758 
2759 static HRESULT WINAPI MXAttributes_addAttribute(IMXAttributes *iface,
2760     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
2761 {
2762     mxattributes *This = impl_from_IMXAttributes( iface );
2763     mxattribute *attr;
2764     HRESULT hr;
2765 
2766     TRACE("(%p)->(%s %s %s %s %s)\n", This, debugstr_w(uri), debugstr_w(localName),
2767         debugstr_w(QName), debugstr_w(type), debugstr_w(value));
2768 
2769     if ((!uri || !localName || !QName || !type || !value) && This->class_version != MSXML6)
2770         return E_INVALIDARG;
2771 
2772     /* ensure array is large enough */
2773     hr = mxattributes_grow(This);
2774     if (hr != S_OK) return hr;
2775 
2776     attr = &This->attr[This->length];
2777 
2778     attr->qname = SysAllocString(QName);
2779     attr->local = SysAllocString(localName);
2780     attr->uri   = SysAllocString(uri);
2781     attr->type  = SysAllocString(type ? type : emptyW);
2782     attr->value = SysAllocString(value);
2783     This->length++;
2784 
2785     return S_OK;
2786 }
2787 
2788 static HRESULT WINAPI MXAttributes_addAttributeFromIndex(IMXAttributes *iface,
2789     VARIANT atts, int index)
2790 {
2791     mxattributes *This = impl_from_IMXAttributes( iface );
2792     FIXME("(%p)->(%s %d): stub\n", This, debugstr_variant(&atts), index);
2793     return E_NOTIMPL;
2794 }
2795 
2796 static HRESULT WINAPI MXAttributes_clear(IMXAttributes *iface)
2797 {
2798     mxattributes *This = impl_from_IMXAttributes( iface );
2799     int i;
2800 
2801     TRACE("(%p)\n", This);
2802 
2803     for (i = 0; i < This->length; i++)
2804     {
2805         SysFreeString(This->attr[i].qname);
2806         SysFreeString(This->attr[i].local);
2807         SysFreeString(This->attr[i].uri);
2808         SysFreeString(This->attr[i].type);
2809         SysFreeString(This->attr[i].value);
2810         memset(&This->attr[i], 0, sizeof(mxattribute));
2811     }
2812 
2813     This->length = 0;
2814 
2815     return S_OK;
2816 }
2817 
2818 static mxattribute *get_attribute_byindex(mxattributes *attrs, int index)
2819 {
2820     if (index < 0 || index >= attrs->length) return NULL;
2821     return &attrs->attr[index];
2822 }
2823 
2824 static HRESULT WINAPI MXAttributes_removeAttribute(IMXAttributes *iface, int index)
2825 {
2826     mxattributes *This = impl_from_IMXAttributes( iface );
2827     mxattribute *dst;
2828 
2829     TRACE("(%p)->(%d)\n", This, index);
2830 
2831     if (!(dst = get_attribute_byindex(This, index))) return E_INVALIDARG;
2832 
2833     /* no need to remove last attribute, just make it inaccessible */
2834     if (index + 1 == This->length)
2835     {
2836         This->length--;
2837         return S_OK;
2838     }
2839 
2840     memmove(dst, dst + 1, (This->length-index-1)*sizeof(*dst));
2841     This->length--;
2842 
2843     return S_OK;
2844 }
2845 
2846 static HRESULT WINAPI MXAttributes_setAttribute(IMXAttributes *iface, int index,
2847     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
2848 {
2849     mxattributes *This = impl_from_IMXAttributes( iface );
2850     FIXME("(%p)->(%d %s %s %s %s %s): stub\n", This, index, debugstr_w(uri),
2851         debugstr_w(localName), debugstr_w(QName), debugstr_w(type), debugstr_w(value));
2852     return E_NOTIMPL;
2853 }
2854 
2855 static HRESULT WINAPI MXAttributes_setAttributes(IMXAttributes *iface, VARIANT atts)
2856 {
2857     mxattributes *This = impl_from_IMXAttributes( iface );
2858     FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&atts));
2859     return E_NOTIMPL;
2860 }
2861 
2862 static HRESULT WINAPI MXAttributes_setLocalName(IMXAttributes *iface, int index,
2863     BSTR localName)
2864 {
2865     mxattributes *This = impl_from_IMXAttributes( iface );
2866     mxattribute *attr;
2867 
2868     TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(localName));
2869 
2870     if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
2871 
2872     SysFreeString(attr->local);
2873     attr->local = SysAllocString(localName);
2874 
2875     return S_OK;
2876 }
2877 
2878 static HRESULT WINAPI MXAttributes_setQName(IMXAttributes *iface, int index, BSTR QName)
2879 {
2880     mxattributes *This = impl_from_IMXAttributes( iface );
2881     mxattribute *attr;
2882 
2883     TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(QName));
2884 
2885     if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
2886 
2887     SysFreeString(attr->qname);
2888     attr->qname = SysAllocString(QName);
2889 
2890     return S_OK;
2891 }
2892 
2893 static HRESULT WINAPI MXAttributes_setURI(IMXAttributes *iface, int index, BSTR uri)
2894 {
2895     mxattributes *This = impl_from_IMXAttributes( iface );
2896     mxattribute *attr;
2897 
2898     TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(uri));
2899 
2900     if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
2901 
2902     SysFreeString(attr->uri);
2903     attr->uri = SysAllocString(uri);
2904 
2905     return S_OK;
2906 }
2907 
2908 static HRESULT WINAPI MXAttributes_setValue(IMXAttributes *iface, int index, BSTR value)
2909 {
2910     mxattributes *This = impl_from_IMXAttributes( iface );
2911     mxattribute *attr;
2912 
2913     TRACE("(%p)->(%d %s)\n", This, index, debugstr_w(value));
2914 
2915     if (!(attr = get_attribute_byindex(This, index))) return E_INVALIDARG;
2916 
2917     SysFreeString(attr->value);
2918     attr->value = SysAllocString(value);
2919 
2920     return S_OK;
2921 }
2922 
2923 static const IMXAttributesVtbl MXAttributesVtbl = {
2924     MXAttributes_QueryInterface,
2925     MXAttributes_AddRef,
2926     MXAttributes_Release,
2927     MXAttributes_GetTypeInfoCount,
2928     MXAttributes_GetTypeInfo,
2929     MXAttributes_GetIDsOfNames,
2930     MXAttributes_Invoke,
2931     MXAttributes_addAttribute,
2932     MXAttributes_addAttributeFromIndex,
2933     MXAttributes_clear,
2934     MXAttributes_removeAttribute,
2935     MXAttributes_setAttribute,
2936     MXAttributes_setAttributes,
2937     MXAttributes_setLocalName,
2938     MXAttributes_setQName,
2939     MXAttributes_setURI,
2940     MXAttributes_setValue
2941 };
2942 
2943 static HRESULT WINAPI SAXAttributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **ppObj)
2944 {
2945     mxattributes *This = impl_from_ISAXAttributes( iface );
2946     return IMXAttributes_QueryInterface(&This->IMXAttributes_iface, riid, ppObj);
2947 }
2948 
2949 static ULONG WINAPI SAXAttributes_AddRef(ISAXAttributes *iface)
2950 {
2951     mxattributes *This = impl_from_ISAXAttributes( iface );
2952     return IMXAttributes_AddRef(&This->IMXAttributes_iface);
2953 }
2954 
2955 static ULONG WINAPI SAXAttributes_Release(ISAXAttributes *iface)
2956 {
2957     mxattributes *This = impl_from_ISAXAttributes( iface );
2958     return IMXAttributes_Release(&This->IMXAttributes_iface);
2959 }
2960 
2961 static HRESULT WINAPI SAXAttributes_getLength(ISAXAttributes *iface, int *length)
2962 {
2963     mxattributes *This = impl_from_ISAXAttributes( iface );
2964     TRACE("(%p)->(%p)\n", This, length);
2965 
2966     if (!length && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
2967        return E_POINTER;
2968 
2969     *length = This->length;
2970 
2971     return S_OK;
2972 }
2973 
2974 static HRESULT WINAPI SAXAttributes_getURI(ISAXAttributes *iface, int index, const WCHAR **uri,
2975     int *len)
2976 {
2977     mxattributes *This = impl_from_ISAXAttributes( iface );
2978 
2979     TRACE("(%p)->(%d %p %p)\n", This, index, uri, len);
2980 
2981     if (index >= This->length || index < 0) return E_INVALIDARG;
2982     if (!uri || !len) return E_POINTER;
2983 
2984     *len = SysStringLen(This->attr[index].uri);
2985     *uri = This->attr[index].uri;
2986 
2987     return S_OK;
2988 }
2989 
2990 static HRESULT WINAPI SAXAttributes_getLocalName(ISAXAttributes *iface, int index, const WCHAR **name,
2991     int *len)
2992 {
2993     mxattributes *This = impl_from_ISAXAttributes( iface );
2994 
2995     TRACE("(%p)->(%d %p %p)\n", This, index, name, len);
2996 
2997     if (index >= This->length || index < 0) return E_INVALIDARG;
2998     if (!name || !len) return E_POINTER;
2999 
3000     *len = SysStringLen(This->attr[index].local);
3001     *name = This->attr[index].local;
3002 
3003     return S_OK;
3004 }
3005 
3006 static HRESULT WINAPI SAXAttributes_getQName(ISAXAttributes *iface, int index, const WCHAR **qname, int *length)
3007 {
3008     mxattributes *This = impl_from_ISAXAttributes( iface );
3009 
3010     TRACE("(%p)->(%d %p %p)\n", This, index, qname, length);
3011 
3012     if (index >= This->length) return E_INVALIDARG;
3013     if (!qname || !length) return E_POINTER;
3014 
3015     *qname = This->attr[index].qname;
3016     *length = SysStringLen(This->attr[index].qname);
3017 
3018     return S_OK;
3019 }
3020 
3021 static HRESULT WINAPI SAXAttributes_getName(ISAXAttributes *iface, int index, const WCHAR **uri, int *uri_len,
3022     const WCHAR **local, int *local_len, const WCHAR **qname, int *qname_len)
3023 {
3024     mxattributes *This = impl_from_ISAXAttributes( iface );
3025 
3026     TRACE("(%p)->(%d %p %p %p %p %p %p)\n", This, index, uri, uri_len, local, local_len, qname, qname_len);
3027 
3028     if (index >= This->length || index < 0)
3029         return E_INVALIDARG;
3030 
3031     if (!uri || !uri_len || !local || !local_len || !qname || !qname_len)
3032         return E_POINTER;
3033 
3034     *uri_len = SysStringLen(This->attr[index].uri);
3035     *uri = This->attr[index].uri;
3036 
3037     *local_len = SysStringLen(This->attr[index].local);
3038     *local = This->attr[index].local;
3039 
3040     *qname_len = SysStringLen(This->attr[index].qname);
3041     *qname = This->attr[index].qname;
3042 
3043     TRACE("(%s, %s, %s)\n", debugstr_w(*uri), debugstr_w(*local), debugstr_w(*qname));
3044 
3045     return S_OK;
3046 }
3047 
3048 static HRESULT WINAPI SAXAttributes_getIndexFromName(ISAXAttributes *iface, const WCHAR *uri, int uri_len,
3049     const WCHAR *name, int len, int *index)
3050 {
3051     mxattributes *This = impl_from_ISAXAttributes( iface );
3052     int i;
3053 
3054     TRACE("(%p)->(%s:%d %s:%d %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
3055         debugstr_wn(name, len), len, index);
3056 
3057     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
3058         return E_POINTER;
3059 
3060     if (!uri || !name || !index) return E_INVALIDARG;
3061 
3062     for (i = 0; i < This->length; i++)
3063     {
3064         if (uri_len != SysStringLen(This->attr[i].uri)) continue;
3065         if (strncmpW(uri, This->attr[i].uri, uri_len)) continue;
3066 
3067         if (len != SysStringLen(This->attr[i].local)) continue;
3068         if (strncmpW(name, This->attr[i].local, len)) continue;
3069 
3070         *index = i;
3071         return S_OK;
3072     }
3073 
3074     return E_INVALIDARG;
3075 }
3076 
3077 static HRESULT WINAPI SAXAttributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR *qname,
3078     int len, int *index)
3079 {
3080     mxattributes *This = impl_from_ISAXAttributes( iface );
3081     int i;
3082 
3083     TRACE("(%p)->(%s:%d %p)\n", This, debugstr_wn(qname, len), len, index);
3084 
3085     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
3086         return E_POINTER;
3087 
3088     if (!qname || !index || !len) return E_INVALIDARG;
3089 
3090     for (i = 0; i < This->length; i++)
3091     {
3092         if (len != SysStringLen(This->attr[i].qname)) continue;
3093         if (strncmpW(qname, This->attr[i].qname, len)) continue;
3094 
3095         *index = i;
3096         return S_OK;
3097     }
3098 
3099     return E_INVALIDARG;
3100 }
3101 
3102 static HRESULT WINAPI SAXAttributes_getType(ISAXAttributes *iface, int index, const WCHAR **type,
3103     int *len)
3104 {
3105     mxattributes *This = impl_from_ISAXAttributes( iface );
3106 
3107     TRACE("(%p)->(%d %p %p)\n", This, index, type, len);
3108 
3109     if (index >= This->length) return E_INVALIDARG;
3110 
3111     if ((!type || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
3112        return E_POINTER;
3113 
3114     *type = This->attr[index].type;
3115     *len = SysStringLen(This->attr[index].type);
3116 
3117     return S_OK;
3118 }
3119 
3120 static HRESULT WINAPI SAXAttributes_getTypeFromName(ISAXAttributes *iface, const WCHAR * pUri, int nUri,
3121     const WCHAR * pLocalName, int nLocalName, const WCHAR ** pType, int * nType)
3122 {
3123     mxattributes *This = impl_from_ISAXAttributes( iface );
3124     FIXME("(%p)->(%s:%d %s:%d %p %p): stub\n", This, debugstr_wn(pUri, nUri), nUri,
3125         debugstr_wn(pLocalName, nLocalName), nLocalName, pType, nType);
3126     return E_NOTIMPL;
3127 }
3128 
3129 static HRESULT WINAPI SAXAttributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR * pQName,
3130     int nQName, const WCHAR ** pType, int * nType)
3131 {
3132     mxattributes *This = impl_from_ISAXAttributes( iface );
3133     FIXME("(%p)->(%s:%d %p %p): stub\n", This, debugstr_wn(pQName, nQName), nQName, pType, nType);
3134     return E_NOTIMPL;
3135 }
3136 
3137 static HRESULT WINAPI SAXAttributes_getValue(ISAXAttributes *iface, int index, const WCHAR **value,
3138     int *len)
3139 {
3140     mxattributes *This = impl_from_ISAXAttributes( iface );
3141 
3142     TRACE("(%p)->(%d %p %p)\n", This, index, value, len);
3143 
3144     if (index >= This->length) return E_INVALIDARG;
3145 
3146     if ((!value || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
3147        return E_POINTER;
3148 
3149     *value = This->attr[index].value;
3150     *len = SysStringLen(This->attr[index].value);
3151 
3152     return S_OK;
3153 }
3154 
3155 static HRESULT WINAPI SAXAttributes_getValueFromName(ISAXAttributes *iface, const WCHAR *uri,
3156     int uri_len, const WCHAR *name, int name_len, const WCHAR **value, int *value_len)
3157 {
3158     mxattributes *This = impl_from_ISAXAttributes( iface );
3159     HRESULT hr;
3160     int index;
3161 
3162     TRACE("(%p)->(%s:%d %s:%d %p %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
3163         debugstr_wn(name, name_len), name_len, value, value_len);
3164 
3165     if (!uri || !name || !value || !value_len)
3166         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
3167 
3168     hr = ISAXAttributes_getIndexFromName(iface, uri, uri_len, name, name_len, &index);
3169     if (hr == S_OK)
3170         hr = ISAXAttributes_getValue(iface, index, value, value_len);
3171 
3172     return hr;
3173 }
3174 
3175 static HRESULT WINAPI SAXAttributes_getValueFromQName(ISAXAttributes *iface, const WCHAR *qname,
3176     int qname_len, const WCHAR **value, int *value_len)
3177 {
3178     mxattributes *This = impl_from_ISAXAttributes( iface );
3179     HRESULT hr;
3180     int index;
3181 
3182     TRACE("(%p)->(%s:%d %p %p)\n", This, debugstr_wn(qname, qname_len), qname_len, value, value_len);
3183 
3184     if (!qname || !value || !value_len)
3185         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
3186 
3187     hr = ISAXAttributes_getIndexFromQName(iface, qname, qname_len, &index);
3188     if (hr == S_OK)
3189         hr = ISAXAttributes_getValue(iface, index, value, value_len);
3190 
3191     return hr;
3192 }
3193 
3194 static const ISAXAttributesVtbl SAXAttributesVtbl = {
3195     SAXAttributes_QueryInterface,
3196     SAXAttributes_AddRef,
3197     SAXAttributes_Release,
3198     SAXAttributes_getLength,
3199     SAXAttributes_getURI,
3200     SAXAttributes_getLocalName,
3201     SAXAttributes_getQName,
3202     SAXAttributes_getName,
3203     SAXAttributes_getIndexFromName,
3204     SAXAttributes_getIndexFromQName,
3205     SAXAttributes_getType,
3206     SAXAttributes_getTypeFromName,
3207     SAXAttributes_getTypeFromQName,
3208     SAXAttributes_getValue,
3209     SAXAttributes_getValueFromName,
3210     SAXAttributes_getValueFromQName
3211 };
3212 
3213 static HRESULT WINAPI VBSAXAttributes_QueryInterface(
3214         IVBSAXAttributes* iface,
3215         REFIID riid,
3216         void **ppvObject)
3217 {
3218     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3219     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
3220     return ISAXAttributes_QueryInterface(&This->ISAXAttributes_iface, riid, ppvObject);
3221 }
3222 
3223 static ULONG WINAPI VBSAXAttributes_AddRef(IVBSAXAttributes* iface)
3224 {
3225     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3226     return ISAXAttributes_AddRef(&This->ISAXAttributes_iface);
3227 }
3228 
3229 static ULONG WINAPI VBSAXAttributes_Release(IVBSAXAttributes* iface)
3230 {
3231     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3232     return ISAXAttributes_Release(&This->ISAXAttributes_iface);
3233 }
3234 
3235 static HRESULT WINAPI VBSAXAttributes_GetTypeInfoCount( IVBSAXAttributes *iface, UINT* pctinfo )
3236 {
3237     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3238 
3239     TRACE("(%p)->(%p)\n", This, pctinfo);
3240 
3241     *pctinfo = 1;
3242 
3243     return S_OK;
3244 }
3245 
3246 static HRESULT WINAPI VBSAXAttributes_GetTypeInfo(
3247     IVBSAXAttributes *iface,
3248     UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
3249 {
3250     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3251     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
3252     return get_typeinfo(IVBSAXAttributes_tid, ppTInfo);
3253 }
3254 
3255 static HRESULT WINAPI VBSAXAttributes_GetIDsOfNames(
3256     IVBSAXAttributes *iface,
3257     REFIID riid,
3258     LPOLESTR* rgszNames,
3259     UINT cNames,
3260     LCID lcid,
3261     DISPID* rgDispId)
3262 {
3263     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3264     ITypeInfo *typeinfo;
3265     HRESULT hr;
3266 
3267     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
3268           lcid, rgDispId);
3269 
3270     if(!rgszNames || cNames == 0 || !rgDispId)
3271         return E_INVALIDARG;
3272 
3273     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
3274     if(SUCCEEDED(hr))
3275     {
3276         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
3277         ITypeInfo_Release(typeinfo);
3278     }
3279 
3280     return hr;
3281 }
3282 
3283 static HRESULT WINAPI VBSAXAttributes_Invoke(
3284     IVBSAXAttributes *iface,
3285     DISPID dispIdMember,
3286     REFIID riid,
3287     LCID lcid,
3288     WORD wFlags,
3289     DISPPARAMS* pDispParams,
3290     VARIANT* pVarResult,
3291     EXCEPINFO* pExcepInfo,
3292     UINT* puArgErr)
3293 {
3294     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3295     ITypeInfo *typeinfo;
3296     HRESULT hr;
3297 
3298     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
3299           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
3300 
3301     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
3302     if(SUCCEEDED(hr))
3303     {
3304         hr = ITypeInfo_Invoke(typeinfo, &This->IVBSAXAttributes_iface, dispIdMember, wFlags,
3305                 pDispParams, pVarResult, pExcepInfo, puArgErr);
3306         ITypeInfo_Release(typeinfo);
3307     }
3308 
3309     return hr;
3310 }
3311 
3312 static HRESULT WINAPI VBSAXAttributes_get_length(IVBSAXAttributes* iface, int *len)
3313 {
3314     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3315     return ISAXAttributes_getLength(&This->ISAXAttributes_iface, len);
3316 }
3317 
3318 static HRESULT WINAPI VBSAXAttributes_getURI(IVBSAXAttributes* iface, int index, BSTR *uri)
3319 {
3320     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3321     const WCHAR *uriW;
3322     HRESULT hr;
3323     int len;
3324 
3325     TRACE("(%p)->(%d %p)\n", This, index, uri);
3326 
3327     if (!uri)
3328         return E_POINTER;
3329 
3330     *uri = NULL;
3331     hr = ISAXAttributes_getURI(&This->ISAXAttributes_iface, index, &uriW, &len);
3332     if (FAILED(hr))
3333         return hr;
3334 
3335     return return_bstrn(uriW, len, uri);
3336 }
3337 
3338 static HRESULT WINAPI VBSAXAttributes_getLocalName(IVBSAXAttributes* iface, int index, BSTR *name)
3339 {
3340     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3341     const WCHAR *nameW;
3342     HRESULT hr;
3343     int len;
3344 
3345     TRACE("(%p)->(%d %p)\n", This, index, name);
3346 
3347     if (!name)
3348         return E_POINTER;
3349 
3350     *name = NULL;
3351     hr = ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, index, &nameW, &len);
3352     if (FAILED(hr))
3353         return hr;
3354 
3355     return return_bstrn(nameW, len, name);
3356 }
3357 
3358 static HRESULT WINAPI VBSAXAttributes_getQName(IVBSAXAttributes* iface, int index, BSTR *qname)
3359 {
3360     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3361     const WCHAR *qnameW;
3362     HRESULT hr;
3363     int len;
3364 
3365     TRACE("(%p)->(%d %p)\n", This, index, qname);
3366 
3367     if (!qname)
3368         return E_POINTER;
3369 
3370     *qname = NULL;
3371     hr = ISAXAttributes_getQName(&This->ISAXAttributes_iface, index, &qnameW, &len);
3372     if (FAILED(hr))
3373         return hr;
3374 
3375     return return_bstrn(qnameW, len, qname);
3376 }
3377 
3378 static HRESULT WINAPI VBSAXAttributes_getIndexFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name, int *index)
3379 {
3380     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3381     return ISAXAttributes_getIndexFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
3382             name, SysStringLen(name), index);
3383 }
3384 
3385 static HRESULT WINAPI VBSAXAttributes_getIndexFromQName(IVBSAXAttributes* iface, BSTR qname, int *index)
3386 {
3387     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3388     return ISAXAttributes_getIndexFromQName(&This->ISAXAttributes_iface, qname,
3389             SysStringLen(qname), index);
3390 }
3391 
3392 static HRESULT WINAPI VBSAXAttributes_getType(IVBSAXAttributes* iface, int index, BSTR *type)
3393 {
3394     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3395     const WCHAR *typeW;
3396     HRESULT hr;
3397     int len;
3398 
3399     TRACE("(%p)->(%d %p)\n", This, index, type);
3400 
3401     if (!type)
3402         return E_POINTER;
3403 
3404     *type = NULL;
3405     hr = ISAXAttributes_getType(&This->ISAXAttributes_iface, index, &typeW, &len);
3406     if (FAILED(hr))
3407         return hr;
3408 
3409     return return_bstrn(typeW, len, type);
3410 }
3411 
3412 static HRESULT WINAPI VBSAXAttributes_getTypeFromName(IVBSAXAttributes* iface, BSTR uri,
3413     BSTR name, BSTR *type)
3414 {
3415     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3416     const WCHAR *typeW;
3417     HRESULT hr;
3418     int len;
3419 
3420     TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(name), type);
3421 
3422     if (!type)
3423         return E_POINTER;
3424 
3425     *type = NULL;
3426     hr = ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
3427             name, SysStringLen(name), &typeW, &len);
3428     if (FAILED(hr))
3429         return hr;
3430 
3431     return return_bstrn(typeW, len, type);
3432 }
3433 
3434 static HRESULT WINAPI VBSAXAttributes_getTypeFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *type)
3435 {
3436     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3437     const WCHAR *typeW;
3438     HRESULT hr;
3439     int len;
3440 
3441     TRACE("(%p)->(%s %p)\n", This, debugstr_w(qname), type);
3442 
3443     if (!type)
3444         return E_POINTER;
3445 
3446     *type = NULL;
3447     hr = ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
3448             &typeW, &len);
3449     if (FAILED(hr))
3450         return hr;
3451 
3452     return return_bstrn(typeW, len, type);
3453 }
3454 
3455 static HRESULT WINAPI VBSAXAttributes_getValue(IVBSAXAttributes* iface, int index, BSTR *value)
3456 {
3457     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3458     const WCHAR *valueW;
3459     HRESULT hr;
3460     int len;
3461 
3462     TRACE("(%p)->(%d %p)\n", This, index, value);
3463 
3464     if (!value)
3465         return E_POINTER;
3466 
3467     *value = NULL;
3468     hr = ISAXAttributes_getValue(&This->ISAXAttributes_iface, index, &valueW, &len);
3469     if (FAILED(hr))
3470         return hr;
3471 
3472     return return_bstrn(valueW, len, value);
3473 }
3474 
3475 static HRESULT WINAPI VBSAXAttributes_getValueFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name,
3476     BSTR *value)
3477 {
3478     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3479     const WCHAR *valueW;
3480     HRESULT hr;
3481     int len;
3482 
3483     TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(name), value);
3484 
3485     if (!value)
3486         return E_POINTER;
3487 
3488     *value = NULL;
3489     hr = ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
3490             name, SysStringLen(name), &valueW, &len);
3491     if (FAILED(hr))
3492         return hr;
3493 
3494     return return_bstrn(valueW, len, value);
3495 }
3496 
3497 static HRESULT WINAPI VBSAXAttributes_getValueFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *value)
3498 {
3499     mxattributes *This = impl_from_IVBSAXAttributes( iface );
3500     const WCHAR *valueW;
3501     HRESULT hr;
3502     int len;
3503 
3504     TRACE("(%p)->(%s %p)\n", This, debugstr_w(qname), value);
3505 
3506     if (!value)
3507         return E_POINTER;
3508 
3509     *value = NULL;
3510     hr = ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
3511         &valueW, &len);
3512     if (FAILED(hr))
3513         return hr;
3514 
3515     return return_bstrn(valueW, len, value);
3516 }
3517 
3518 static const struct IVBSAXAttributesVtbl VBSAXAttributesVtbl =
3519 {
3520     VBSAXAttributes_QueryInterface,
3521     VBSAXAttributes_AddRef,
3522     VBSAXAttributes_Release,
3523     VBSAXAttributes_GetTypeInfoCount,
3524     VBSAXAttributes_GetTypeInfo,
3525     VBSAXAttributes_GetIDsOfNames,
3526     VBSAXAttributes_Invoke,
3527     VBSAXAttributes_get_length,
3528     VBSAXAttributes_getURI,
3529     VBSAXAttributes_getLocalName,
3530     VBSAXAttributes_getQName,
3531     VBSAXAttributes_getIndexFromName,
3532     VBSAXAttributes_getIndexFromQName,
3533     VBSAXAttributes_getType,
3534     VBSAXAttributes_getTypeFromName,
3535     VBSAXAttributes_getTypeFromQName,
3536     VBSAXAttributes_getValue,
3537     VBSAXAttributes_getValueFromName,
3538     VBSAXAttributes_getValueFromQName
3539 };
3540 
3541 static const tid_t mxattrs_iface_tids[] = {
3542     IMXAttributes_tid,
3543     0
3544 };
3545 
3546 static dispex_static_data_t mxattrs_dispex = {
3547     NULL,
3548     IMXAttributes_tid,
3549     NULL,
3550     mxattrs_iface_tids
3551 };
3552 
3553 HRESULT SAXAttributes_create(MSXML_VERSION version, void **ppObj)
3554 {
3555     static const int default_count = 10;
3556     mxattributes *This;
3557 
3558     TRACE("(%p)\n", ppObj);
3559 
3560     This = heap_alloc( sizeof (*This) );
3561     if( !This )
3562         return E_OUTOFMEMORY;
3563 
3564     This->IMXAttributes_iface.lpVtbl = &MXAttributesVtbl;
3565     This->ISAXAttributes_iface.lpVtbl = &SAXAttributesVtbl;
3566     This->IVBSAXAttributes_iface.lpVtbl = &VBSAXAttributesVtbl;
3567     This->ref = 1;
3568 
3569     This->class_version = version;
3570 
3571     This->attr = heap_alloc(default_count*sizeof(mxattribute));
3572     This->length = 0;
3573     This->allocated = default_count;
3574 
3575     *ppObj = &This->IMXAttributes_iface;
3576 
3577     init_dispex(&This->dispex, (IUnknown*)&This->IMXAttributes_iface, &mxattrs_dispex);
3578 
3579     TRACE("returning iface %p\n", *ppObj);
3580 
3581     return S_OK;
3582 }
3583