urlmon: Use wine lists to store namespaces and MIME filters.
[wine] / dlls / msxml3 / mxwriter.c
1 /*
2  *    MXWriter implementation
3  *
4  * Copyright 2011-2012 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
38 #include "msxml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
41
42 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
43 static const WCHAR emptyW[] = {0};
44 static const WCHAR spaceW[] = {' '};
45 static const WCHAR quotW[]  = {'\"'};
46
47 typedef enum
48 {
49     XmlEncoding_UTF8,
50     XmlEncoding_UTF16,
51     XmlEncoding_Unknown
52 } xml_encoding;
53
54 typedef enum
55 {
56     OutputBuffer_Native  = 0x001,
57     OutputBuffer_Encoded = 0x010,
58     OutputBuffer_Both    = 0x100
59 } output_mode;
60
61 typedef enum
62 {
63     MXWriter_BOM = 0,
64     MXWriter_DisableEscaping,
65     MXWriter_Indent,
66     MXWriter_OmitXmlDecl,
67     MXWriter_Standalone,
68     MXWriter_LastProp
69 } mxwriter_prop;
70
71 typedef enum
72 {
73     EscapeValue,
74     EscapeText
75 } escape_mode;
76
77 typedef struct
78 {
79     char *data;
80     unsigned int allocated;
81     unsigned int written;
82 } encoded_buffer;
83
84 typedef struct
85 {
86     encoded_buffer utf16;
87     encoded_buffer encoded;
88     UINT code_page;
89 } output_buffer;
90
91 typedef struct
92 {
93     DispatchEx dispex;
94     IMXWriter IMXWriter_iface;
95     ISAXContentHandler ISAXContentHandler_iface;
96     ISAXLexicalHandler ISAXLexicalHandler_iface;
97     ISAXDeclHandler    ISAXDeclHandler_iface;
98
99     LONG ref;
100     MSXML_VERSION class_version;
101
102     VARIANT_BOOL props[MXWriter_LastProp];
103     BOOL prop_changed;
104     BOOL cdata;
105
106     BSTR version;
107
108     BSTR encoding; /* exact property value */
109     xml_encoding xml_enc;
110
111     /* contains a pending (or not closed yet) element name or NULL if
112        we don't have to close */
113     BSTR element;
114
115     IStream *dest;
116     ULONG dest_written;
117
118     output_buffer *buffer;
119 } mxwriter;
120
121 typedef struct
122 {
123     BSTR qname;
124     BSTR local;
125     BSTR uri;
126     BSTR type;
127     BSTR value;
128 } mxattribute;
129
130 typedef struct
131 {
132     DispatchEx dispex;
133     IMXAttributes IMXAttributes_iface;
134     ISAXAttributes ISAXAttributes_iface;
135     LONG ref;
136
137     MSXML_VERSION class_version;
138
139     mxattribute *attr;
140     int length;
141     int allocated;
142 } mxattributes;
143
144 static inline mxattributes *impl_from_IMXAttributes( IMXAttributes *iface )
145 {
146     return CONTAINING_RECORD(iface, mxattributes, IMXAttributes_iface);
147 }
148
149 static inline mxattributes *impl_from_ISAXAttributes( ISAXAttributes *iface )
150 {
151     return CONTAINING_RECORD(iface, mxattributes, ISAXAttributes_iface);
152 }
153
154 static HRESULT mxattributes_grow(mxattributes *This)
155 {
156     if (This->length < This->allocated) return S_OK;
157
158     This->allocated *= 2;
159     This->attr = heap_realloc(This->attr, This->allocated*sizeof(mxattribute));
160
161     return This->attr ? S_OK : E_OUTOFMEMORY;
162 }
163
164 static xml_encoding parse_encoding_name(const WCHAR *encoding)
165 {
166     static const WCHAR utf8W[]  = {'U','T','F','-','8',0};
167     if (!strcmpiW(encoding, utf8W))  return XmlEncoding_UTF8;
168     if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
169     return XmlEncoding_Unknown;
170 }
171
172 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
173 {
174     const int initial_len = 0x2000;
175     buffer->data = heap_alloc(initial_len);
176     if (!buffer->data) return E_OUTOFMEMORY;
177
178     memset(buffer->data, 0, 4);
179     buffer->allocated = initial_len;
180     buffer->written = 0;
181
182     return S_OK;
183 }
184
185 static void free_encoded_buffer(encoded_buffer *buffer)
186 {
187     heap_free(buffer->data);
188 }
189
190 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
191 {
192     switch (encoding)
193     {
194     case XmlEncoding_UTF8:
195         *cp = CP_UTF8;
196         break;
197     case XmlEncoding_UTF16:
198         *cp = ~0;
199         break;
200     default:
201         FIXME("unsupported encoding %d\n", encoding);
202         return E_NOTIMPL;
203     }
204
205     return S_OK;
206 }
207
208 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
209 {
210     output_buffer *ret;
211     HRESULT hr;
212
213     ret = heap_alloc(sizeof(*ret));
214     if (!ret) return E_OUTOFMEMORY;
215
216     hr = get_code_page(encoding, &ret->code_page);
217     if (hr != S_OK) {
218         heap_free(ret);
219         return hr;
220     }
221
222     hr = init_encoded_buffer(&ret->utf16);
223     if (hr != S_OK) {
224         heap_free(ret);
225         return hr;
226     }
227
228     if (ret->code_page == CP_UTF8) {
229         hr = init_encoded_buffer(&ret->encoded);
230         if (hr != S_OK) {
231             free_encoded_buffer(&ret->utf16);
232             heap_free(ret);
233             return hr;
234         }
235     }
236     else
237         memset(&ret->encoded, 0, sizeof(ret->encoded));
238
239     *buffer = ret;
240
241     return S_OK;
242 }
243
244 static void free_output_buffer(output_buffer *buffer)
245 {
246     free_encoded_buffer(&buffer->encoded);
247     free_encoded_buffer(&buffer->utf16);
248     heap_free(buffer);
249 }
250
251 static void grow_buffer(encoded_buffer *buffer, int length)
252 {
253     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
254     if (buffer->allocated < buffer->written + length + 4)
255     {
256         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
257         buffer->data = heap_realloc(buffer->data, grown_size);
258         buffer->allocated = grown_size;
259     }
260 }
261
262 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
263 {
264     int length;
265     char *ptr;
266
267     if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
268         if (buffer->code_page == CP_UTF8)
269         {
270             length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
271             grow_buffer(&buffer->encoded, length);
272             ptr = buffer->encoded.data + buffer->encoded.written;
273             length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
274             buffer->encoded.written += len == -1 ? length-1 : length;
275         }
276     }
277
278     if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
279         /* WCHAR data just copied */
280         length = len == -1 ? strlenW(data) : len;
281         if (length)
282         {
283             length *= sizeof(WCHAR);
284
285             grow_buffer(&buffer->utf16, length);
286             ptr = buffer->utf16.data + buffer->utf16.written;
287
288             memcpy(ptr, data, length);
289             buffer->utf16.written += length;
290             ptr += length;
291             /* null termination */
292             memset(ptr, 0, sizeof(WCHAR));
293         }
294     }
295
296     return S_OK;
297 }
298
299 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
300 {
301     return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
302 }
303
304 static HRESULT write_output_buffer_quoted(output_buffer *buffer, const WCHAR *data, int len)
305 {
306     write_output_buffer(buffer, quotW, 1);
307     write_output_buffer(buffer, data, len);
308     write_output_buffer(buffer, quotW, 1);
309
310     return S_OK;
311 }
312
313 /* frees buffer data, reallocates with a default lengths */
314 static void close_output_buffer(mxwriter *This)
315 {
316     heap_free(This->buffer->utf16.data);
317     heap_free(This->buffer->encoded.data);
318     init_encoded_buffer(&This->buffer->utf16);
319     init_encoded_buffer(&This->buffer->encoded);
320     get_code_page(This->xml_enc, &This->buffer->code_page);
321 }
322
323 /* escapes special characters like:
324    '<' -> "&lt;"
325    '&' -> "&amp;"
326    '"' -> "&quot;"
327    '>' -> "&gt;"
328 */
329 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
330 {
331     static const WCHAR ltW[]    = {'&','l','t',';'};
332     static const WCHAR ampW[]   = {'&','a','m','p',';'};
333     static const WCHAR equotW[] = {'&','q','u','o','t',';'};
334     static const WCHAR gtW[]    = {'&','g','t',';'};
335
336     const int default_alloc = 100;
337     const int grow_thresh = 10;
338     int p = *len, conv_len;
339     WCHAR *ptr, *ret;
340
341     /* default buffer size to something if length is unknown */
342     conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
343     ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
344
345     while (*str && p)
346     {
347         if (ptr - ret > conv_len - grow_thresh)
348         {
349             int written = ptr - ret;
350             conv_len *= 2;
351             ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
352             ptr += written;
353         }
354
355         switch (*str)
356         {
357         case '<':
358             memcpy(ptr, ltW, sizeof(ltW));
359             ptr += sizeof(ltW)/sizeof(WCHAR);
360             break;
361         case '&':
362             memcpy(ptr, ampW, sizeof(ampW));
363             ptr += sizeof(ampW)/sizeof(WCHAR);
364             break;
365         case '>':
366             memcpy(ptr, gtW, sizeof(gtW));
367             ptr += sizeof(gtW)/sizeof(WCHAR);
368             break;
369         case '"':
370             if (mode == EscapeValue)
371             {
372                 memcpy(ptr, equotW, sizeof(equotW));
373                 ptr += sizeof(equotW)/sizeof(WCHAR);
374                 break;
375             }
376             /* fallthrough for text mode */
377         default:
378             *ptr++ = *str;
379             break;
380         }
381
382         str++;
383         if (*len != -1) p--;
384     }
385
386     if (*len != -1) *len = ptr-ret;
387     *++ptr = 0;
388
389     return ret;
390 }
391
392 static void write_prolog_buffer(const mxwriter *This)
393 {
394     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
395     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
396     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
397     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
398     static const WCHAR noW[] = {'n','o','\"','?','>'};
399     static const WCHAR crlfW[] = {'\r','\n'};
400
401     /* version */
402     write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
403     write_output_buffer_quoted(This->buffer, This->version, -1);
404
405     /* encoding */
406     write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
407
408     /* always write UTF-16 to WCHAR buffer */
409     write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
410     write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
411     write_output_buffer(This->buffer, quotW, 1);
412
413     /* standalone */
414     write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
415     if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
416         write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
417     else
418         write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
419
420     write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
421 }
422
423 /* Attempts to the write data from the mxwriter's buffer to
424  * the destination stream (if there is one).
425  */
426 static HRESULT write_data_to_stream(mxwriter *This)
427 {
428     encoded_buffer *buffer;
429     ULONG written = 0;
430     HRESULT hr;
431
432     if (!This->dest)
433         return S_OK;
434
435     if (This->xml_enc != XmlEncoding_UTF16)
436         buffer = &This->buffer->encoded;
437     else
438         buffer = &This->buffer->utf16;
439
440     if (This->dest_written > buffer->written) {
441         ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
442         return E_FAIL;
443     } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
444         /* Windows seems to make an empty write call when the encoding is UTF-8 and
445          * all the data has been written to the stream. It doesn't seem make this call
446          * for any other encodings.
447          */
448         return S_OK;
449
450     /* Write the current content from the output buffer into 'dest'.
451      * TODO: Check what Windows does if the IStream doesn't write all of
452      *       the data we give it at once.
453      */
454     hr = IStream_Write(This->dest, buffer->data+This->dest_written,
455                          buffer->written-This->dest_written, &written);
456     if (FAILED(hr)) {
457         WARN("Failed to write data to IStream (0x%08x)\n", hr);
458         return hr;
459     }
460
461     This->dest_written += written;
462     return hr;
463 }
464
465 /* Newly added element start tag left unclosed cause for empty elements
466    we have to close it differently. */
467 static void close_element_starttag(const mxwriter *This)
468 {
469     static const WCHAR gtW[] = {'>'};
470     if (!This->element) return;
471     write_output_buffer(This->buffer, gtW, 1);
472 }
473
474 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
475 {
476     SysFreeString(This->element);
477     This->element = name ? SysAllocStringLen(name, len) : NULL;
478 }
479
480 static inline HRESULT flush_output_buffer(mxwriter *This)
481 {
482     close_element_starttag(This);
483     set_element_name(This, NULL, 0);
484     This->cdata = FALSE;
485     return write_data_to_stream(This);
486 }
487
488 /* Resets the mxwriter's output buffer by closing it, then creating a new
489  * output buffer using the given encoding.
490  */
491 static inline void reset_output_buffer(mxwriter *This)
492 {
493     close_output_buffer(This);
494     This->dest_written = 0;
495 }
496
497 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
498 {
499     writer->props[property] = value;
500     writer->prop_changed = TRUE;
501     return S_OK;
502 }
503
504 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
505 {
506     if (!value) return E_POINTER;
507     *value = writer->props[property];
508     return S_OK;
509 }
510
511 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
512 {
513     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
514 }
515
516 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
517 {
518     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
519 }
520
521 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
522 {
523     return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
524 }
525
526 static inline mxwriter *impl_from_ISAXDeclHandler(ISAXDeclHandler *iface)
527 {
528     return CONTAINING_RECORD(iface, mxwriter, ISAXDeclHandler_iface);
529 }
530
531 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
532 {
533     mxwriter *This = impl_from_IMXWriter( iface );
534
535     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
536
537     *obj = NULL;
538
539     if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
540          IsEqualGUID( riid, &IID_IDispatch ) ||
541          IsEqualGUID( riid, &IID_IUnknown ) )
542     {
543         *obj = &This->IMXWriter_iface;
544     }
545     else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
546     {
547         *obj = &This->ISAXContentHandler_iface;
548     }
549     else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
550     {
551         *obj = &This->ISAXLexicalHandler_iface;
552     }
553     else if ( IsEqualGUID( riid, &IID_ISAXDeclHandler ) )
554     {
555         *obj = &This->ISAXDeclHandler_iface;
556     }
557     else if (dispex_query_interface(&This->dispex, riid, obj))
558     {
559         return *obj ? S_OK : E_NOINTERFACE;
560     }
561     else
562     {
563         ERR("interface %s not implemented\n", debugstr_guid(riid));
564         *obj = NULL;
565         return E_NOINTERFACE;
566     }
567
568     IMXWriter_AddRef(iface);
569     return S_OK;
570 }
571
572 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
573 {
574     mxwriter *This = impl_from_IMXWriter( iface );
575     LONG ref = InterlockedIncrement(&This->ref);
576
577     TRACE("(%p)->(%d)\n", This, ref);
578
579     return ref;
580 }
581
582 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
583 {
584     mxwriter *This = impl_from_IMXWriter( iface );
585     ULONG ref = InterlockedDecrement(&This->ref);
586
587     TRACE("(%p)->(%d)\n", This, ref);
588
589     if(!ref)
590     {
591         /* Windows flushes the buffer when the interface is destroyed. */
592         flush_output_buffer(This);
593         free_output_buffer(This->buffer);
594
595         if (This->dest) IStream_Release(This->dest);
596         SysFreeString(This->version);
597         SysFreeString(This->encoding);
598
599         SysFreeString(This->element);
600         release_dispex(&This->dispex);
601         heap_free(This);
602     }
603
604     return ref;
605 }
606
607 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
608 {
609     mxwriter *This = impl_from_IMXWriter( iface );
610     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
611 }
612
613 static HRESULT WINAPI mxwriter_GetTypeInfo(
614     IMXWriter *iface,
615     UINT iTInfo, LCID lcid,
616     ITypeInfo** ppTInfo )
617 {
618     mxwriter *This = impl_from_IMXWriter( iface );
619     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
620         iTInfo, lcid, ppTInfo);
621 }
622
623 static HRESULT WINAPI mxwriter_GetIDsOfNames(
624     IMXWriter *iface,
625     REFIID riid, LPOLESTR* rgszNames,
626     UINT cNames, LCID lcid, DISPID* rgDispId )
627 {
628     mxwriter *This = impl_from_IMXWriter( iface );
629     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
630         riid, rgszNames, cNames, lcid, rgDispId);
631 }
632
633 static HRESULT WINAPI mxwriter_Invoke(
634     IMXWriter *iface,
635     DISPID dispIdMember, REFIID riid, LCID lcid,
636     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
637     EXCEPINFO* pExcepInfo, UINT* puArgErr )
638 {
639     mxwriter *This = impl_from_IMXWriter( iface );
640     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
641         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
642 }
643
644 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
645 {
646     mxwriter *This = impl_from_IMXWriter( iface );
647     HRESULT hr;
648
649     TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
650
651     hr = flush_output_buffer(This);
652     if (FAILED(hr))
653         return hr;
654
655     switch (V_VT(&dest))
656     {
657     case VT_EMPTY:
658     {
659         if (This->dest) IStream_Release(This->dest);
660         This->dest = NULL;
661         reset_output_buffer(This);
662         break;
663     }
664     case VT_UNKNOWN:
665     {
666         IStream *stream;
667
668         hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
669         if (hr == S_OK)
670         {
671             /* Recreate the output buffer to make sure it's using the correct encoding. */
672             reset_output_buffer(This);
673
674             if (This->dest) IStream_Release(This->dest);
675             This->dest = stream;
676             break;
677         }
678
679         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
680         return E_NOTIMPL;
681     }
682     default:
683         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
684         return E_NOTIMPL;
685     }
686
687     return S_OK;
688 }
689
690 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
691 {
692     mxwriter *This = impl_from_IMXWriter( iface );
693
694     TRACE("(%p)->(%p)\n", This, dest);
695
696     if (!This->dest)
697     {
698         HRESULT hr = flush_output_buffer(This);
699         if (FAILED(hr))
700             return hr;
701
702         V_VT(dest)   = VT_BSTR;
703         V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
704
705         return S_OK;
706     }
707     else
708         FIXME("not implemented when stream is set up\n");
709
710     return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
714 {
715     mxwriter *This = impl_from_IMXWriter( iface );
716     xml_encoding enc;
717     HRESULT hr;
718
719     TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
720
721     enc = parse_encoding_name(encoding);
722     if (enc == XmlEncoding_Unknown)
723     {
724         FIXME("unsupported encoding %s\n", debugstr_w(encoding));
725         return E_INVALIDARG;
726     }
727
728     hr = flush_output_buffer(This);
729     if (FAILED(hr))
730         return hr;
731
732     SysReAllocString(&This->encoding, encoding);
733     This->xml_enc = enc;
734
735     TRACE("got encoding %d\n", This->xml_enc);
736     reset_output_buffer(This);
737     return S_OK;
738 }
739
740 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
741 {
742     mxwriter *This = impl_from_IMXWriter( iface );
743
744     TRACE("(%p)->(%p)\n", This, encoding);
745
746     if (!encoding) return E_POINTER;
747
748     *encoding = SysAllocString(This->encoding);
749     if (!*encoding) return E_OUTOFMEMORY;
750
751     return S_OK;
752 }
753
754 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
755 {
756     mxwriter *This = impl_from_IMXWriter( iface );
757
758     TRACE("(%p)->(%d)\n", This, value);
759     return writer_set_property(This, MXWriter_BOM, value);
760 }
761
762 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
763 {
764     mxwriter *This = impl_from_IMXWriter( iface );
765
766     TRACE("(%p)->(%p)\n", This, value);
767     return writer_get_property(This, MXWriter_BOM, value);
768 }
769
770 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
771 {
772     mxwriter *This = impl_from_IMXWriter( iface );
773
774     TRACE("(%p)->(%d)\n", This, value);
775     return writer_set_property(This, MXWriter_Indent, value);
776 }
777
778 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
779 {
780     mxwriter *This = impl_from_IMXWriter( iface );
781
782     TRACE("(%p)->(%p)\n", This, value);
783     return writer_get_property(This, MXWriter_Indent, value);
784 }
785
786 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
787 {
788     mxwriter *This = impl_from_IMXWriter( iface );
789
790     TRACE("(%p)->(%d)\n", This, value);
791     return writer_set_property(This, MXWriter_Standalone, value);
792 }
793
794 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
795 {
796     mxwriter *This = impl_from_IMXWriter( iface );
797
798     TRACE("(%p)->(%p)\n", This, value);
799     return writer_get_property(This, MXWriter_Standalone, value);
800 }
801
802 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
803 {
804     mxwriter *This = impl_from_IMXWriter( iface );
805
806     TRACE("(%p)->(%d)\n", This, value);
807     return writer_set_property(This, MXWriter_OmitXmlDecl, value);
808 }
809
810 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
811 {
812     mxwriter *This = impl_from_IMXWriter( iface );
813
814     TRACE("(%p)->(%p)\n", This, value);
815     return writer_get_property(This, MXWriter_OmitXmlDecl, value);
816 }
817
818 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
819 {
820     mxwriter *This = impl_from_IMXWriter( iface );
821
822     TRACE("(%p)->(%s)\n", This, debugstr_w(version));
823
824     if (!version) return E_INVALIDARG;
825
826     SysFreeString(This->version);
827     This->version = SysAllocString(version);
828
829     return S_OK;
830 }
831
832 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
833 {
834     mxwriter *This = impl_from_IMXWriter( iface );
835
836     TRACE("(%p)->(%p)\n", This, version);
837
838     if (!version) return E_POINTER;
839
840     return return_bstr(This->version, version);
841 }
842
843 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
844 {
845     mxwriter *This = impl_from_IMXWriter( iface );
846
847     TRACE("(%p)->(%d)\n", This, value);
848     return writer_set_property(This, MXWriter_DisableEscaping, value);
849 }
850
851 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
852 {
853     mxwriter *This = impl_from_IMXWriter( iface );
854
855     TRACE("(%p)->(%p)\n", This, value);
856     return writer_get_property(This, MXWriter_DisableEscaping, value);
857 }
858
859 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
860 {
861     mxwriter *This = impl_from_IMXWriter( iface );
862     TRACE("(%p)\n", This);
863     return flush_output_buffer(This);
864 }
865
866 static const struct IMXWriterVtbl MXWriterVtbl =
867 {
868     mxwriter_QueryInterface,
869     mxwriter_AddRef,
870     mxwriter_Release,
871     mxwriter_GetTypeInfoCount,
872     mxwriter_GetTypeInfo,
873     mxwriter_GetIDsOfNames,
874     mxwriter_Invoke,
875     mxwriter_put_output,
876     mxwriter_get_output,
877     mxwriter_put_encoding,
878     mxwriter_get_encoding,
879     mxwriter_put_byteOrderMark,
880     mxwriter_get_byteOrderMark,
881     mxwriter_put_indent,
882     mxwriter_get_indent,
883     mxwriter_put_standalone,
884     mxwriter_get_standalone,
885     mxwriter_put_omitXMLDeclaration,
886     mxwriter_get_omitXMLDeclaration,
887     mxwriter_put_version,
888     mxwriter_get_version,
889     mxwriter_put_disableOutputEscaping,
890     mxwriter_get_disableOutputEscaping,
891     mxwriter_flush
892 };
893
894 /*** ISAXContentHandler ***/
895 static HRESULT WINAPI SAXContentHandler_QueryInterface(
896     ISAXContentHandler *iface,
897     REFIID riid,
898     void **obj)
899 {
900     mxwriter *This = impl_from_ISAXContentHandler( iface );
901     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
902 }
903
904 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
905 {
906     mxwriter *This = impl_from_ISAXContentHandler( iface );
907     return IMXWriter_AddRef(&This->IMXWriter_iface);
908 }
909
910 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
911 {
912     mxwriter *This = impl_from_ISAXContentHandler( iface );
913     return IMXWriter_Release(&This->IMXWriter_iface);
914 }
915
916 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
917     ISAXContentHandler *iface,
918     ISAXLocator *locator)
919 {
920     mxwriter *This = impl_from_ISAXContentHandler( iface );
921     FIXME("(%p)->(%p)\n", This, locator);
922     return E_NOTIMPL;
923 }
924
925 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
926 {
927     mxwriter *This = impl_from_ISAXContentHandler( iface );
928
929     TRACE("(%p)\n", This);
930
931     /* If properties have been changed since the last "endDocument" call
932      * we need to reset the output buffer. If we don't the output buffer
933      * could end up with multiple XML documents in it, plus this seems to
934      * be how Windows works.
935      */
936     if (This->prop_changed) {
937         reset_output_buffer(This);
938         This->prop_changed = FALSE;
939     }
940
941     if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
942
943     write_prolog_buffer(This);
944
945     if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
946         static const char utf16BOM[] = {0xff,0xfe};
947
948         if (This->props[MXWriter_BOM] == VARIANT_TRUE)
949             /* Windows passes a NULL pointer as the pcbWritten parameter and
950              * ignores any error codes returned from this Write call.
951              */
952             IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
953     }
954
955     return S_OK;
956 }
957
958 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
959 {
960     mxwriter *This = impl_from_ISAXContentHandler( iface );
961     TRACE("(%p)\n", This);
962     This->prop_changed = FALSE;
963     return flush_output_buffer(This);
964 }
965
966 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
967     ISAXContentHandler *iface,
968     const WCHAR *prefix,
969     int nprefix,
970     const WCHAR *uri,
971     int nuri)
972 {
973     mxwriter *This = impl_from_ISAXContentHandler( iface );
974     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
975     return E_NOTIMPL;
976 }
977
978 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
979     ISAXContentHandler *iface,
980     const WCHAR *prefix,
981     int nprefix)
982 {
983     mxwriter *This = impl_from_ISAXContentHandler( iface );
984     FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
985     return E_NOTIMPL;
986 }
987
988 static HRESULT WINAPI SAXContentHandler_startElement(
989     ISAXContentHandler *iface,
990     const WCHAR *namespaceUri,
991     int nnamespaceUri,
992     const WCHAR *local_name,
993     int nlocal_name,
994     const WCHAR *QName,
995     int nQName,
996     ISAXAttributes *attr)
997 {
998     mxwriter *This = impl_from_ISAXContentHandler( iface );
999     static const WCHAR ltW[] = {'<'};
1000
1001     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
1002         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
1003
1004     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1005         return E_INVALIDARG;
1006
1007     close_element_starttag(This);
1008     set_element_name(This, QName ? QName  : emptyW,
1009                            QName ? nQName : 0);
1010
1011     write_output_buffer(This->buffer, ltW, 1);
1012     write_output_buffer(This->buffer, QName, nQName);
1013
1014     if (attr)
1015     {
1016         HRESULT hr;
1017         INT length;
1018         INT i;
1019
1020         hr = ISAXAttributes_getLength(attr, &length);
1021         if (FAILED(hr)) return hr;
1022
1023         for (i = 0; i < length; i++)
1024         {
1025             static const WCHAR eqW[] = {'='};
1026             const WCHAR *str;
1027             WCHAR *escaped;
1028             INT len = 0;
1029
1030             hr = ISAXAttributes_getQName(attr, i, &str, &len);
1031             if (FAILED(hr)) return hr;
1032
1033             /* space separator in front of every attribute */
1034             write_output_buffer(This->buffer, spaceW, 1);
1035             write_output_buffer(This->buffer, str, len);
1036
1037             write_output_buffer(This->buffer, eqW, 1);
1038
1039             len = 0;
1040             hr = ISAXAttributes_getValue(attr, i, &str, &len);
1041             if (FAILED(hr)) return hr;
1042
1043             escaped = get_escaped_string(str, EscapeValue, &len);
1044             write_output_buffer_quoted(This->buffer, escaped, len);
1045             heap_free(escaped);
1046         }
1047     }
1048
1049     return S_OK;
1050 }
1051
1052 static HRESULT WINAPI SAXContentHandler_endElement(
1053     ISAXContentHandler *iface,
1054     const WCHAR *namespaceUri,
1055     int nnamespaceUri,
1056     const WCHAR * local_name,
1057     int nlocal_name,
1058     const WCHAR *QName,
1059     int nQName)
1060 {
1061     mxwriter *This = impl_from_ISAXContentHandler( iface );
1062
1063     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1064         debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1065
1066     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1067         return E_INVALIDARG;
1068
1069     if (This->element && QName && !strncmpW(This->element, QName, nQName))
1070     {
1071         static const WCHAR closeW[] = {'/','>'};
1072
1073         write_output_buffer(This->buffer, closeW, 2);
1074     }
1075     else
1076     {
1077         static const WCHAR closetagW[] = {'<','/'};
1078         static const WCHAR gtW[] = {'>'};
1079
1080         write_output_buffer(This->buffer, closetagW, 2);
1081         write_output_buffer(This->buffer, QName, nQName);
1082         write_output_buffer(This->buffer, gtW, 1);
1083     }
1084
1085     set_element_name(This, NULL, 0);
1086
1087     return S_OK;
1088 }
1089
1090 static HRESULT WINAPI SAXContentHandler_characters(
1091     ISAXContentHandler *iface,
1092     const WCHAR *chars,
1093     int nchars)
1094 {
1095     mxwriter *This = impl_from_ISAXContentHandler( iface );
1096
1097     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1098
1099     if (!chars) return E_INVALIDARG;
1100
1101     close_element_starttag(This);
1102     set_element_name(This, NULL, 0);
1103
1104     if (nchars)
1105     {
1106         if (This->cdata)
1107             write_output_buffer(This->buffer, chars, nchars);
1108         else
1109         {
1110             int len = nchars;
1111             WCHAR *escaped;
1112
1113             escaped = get_escaped_string(chars, EscapeText, &len);
1114             write_output_buffer(This->buffer, escaped, len);
1115             heap_free(escaped);
1116         }
1117     }
1118
1119     return S_OK;
1120 }
1121
1122 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1123     ISAXContentHandler *iface,
1124     const WCHAR *chars,
1125     int nchars)
1126 {
1127     mxwriter *This = impl_from_ISAXContentHandler( iface );
1128     FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1129     return E_NOTIMPL;
1130 }
1131
1132 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1133     ISAXContentHandler *iface,
1134     const WCHAR *target,
1135     int ntarget,
1136     const WCHAR *data,
1137     int ndata)
1138 {
1139     mxwriter *This = impl_from_ISAXContentHandler( iface );
1140     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1141     return E_NOTIMPL;
1142 }
1143
1144 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1145     ISAXContentHandler *iface,
1146     const WCHAR *name,
1147     int nname)
1148 {
1149     mxwriter *This = impl_from_ISAXContentHandler( iface );
1150     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1151     return E_NOTIMPL;
1152 }
1153
1154 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1155 {
1156     SAXContentHandler_QueryInterface,
1157     SAXContentHandler_AddRef,
1158     SAXContentHandler_Release,
1159     SAXContentHandler_putDocumentLocator,
1160     SAXContentHandler_startDocument,
1161     SAXContentHandler_endDocument,
1162     SAXContentHandler_startPrefixMapping,
1163     SAXContentHandler_endPrefixMapping,
1164     SAXContentHandler_startElement,
1165     SAXContentHandler_endElement,
1166     SAXContentHandler_characters,
1167     SAXContentHandler_ignorableWhitespace,
1168     SAXContentHandler_processingInstruction,
1169     SAXContentHandler_skippedEntity
1170 };
1171
1172 /*** ISAXLexicalHandler ***/
1173 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1174     REFIID riid, void **obj)
1175 {
1176     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1177     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1178 }
1179
1180 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1181 {
1182     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1183     return IMXWriter_AddRef(&This->IMXWriter_iface);
1184 }
1185
1186 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1187 {
1188     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1189     return IMXWriter_Release(&This->IMXWriter_iface);
1190 }
1191
1192 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1193     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1194     const WCHAR *systemId, int systemId_len)
1195 {
1196     static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1197     static const WCHAR openintW[] = {'[','\r','\n'};
1198
1199     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1200
1201     TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1202         debugstr_wn(systemId, systemId_len));
1203
1204     if (!name) return E_INVALIDARG;
1205
1206     write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1207
1208     if (*name)
1209     {
1210         write_output_buffer(This->buffer, name, name_len);
1211         write_output_buffer(This->buffer, spaceW, 1);
1212     }
1213
1214     if (publicId)
1215     {
1216         static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1217
1218         write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1219         write_output_buffer_quoted(This->buffer, publicId, publicId_len);
1220
1221         if (!systemId) return E_INVALIDARG;
1222
1223         if (*publicId)
1224             write_output_buffer(This->buffer, spaceW, 1);
1225
1226         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1227
1228         if (*systemId)
1229             write_output_buffer(This->buffer, spaceW, 1);
1230     }
1231     else if (systemId)
1232     {
1233         static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1234
1235         write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1236         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1237         if (*systemId)
1238             write_output_buffer(This->buffer, spaceW, 1);
1239     }
1240
1241     write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1242
1243     return S_OK;
1244 }
1245
1246 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1247 {
1248     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1249     static const WCHAR closedtdW[] = {']','>','\r','\n'};
1250
1251     TRACE("(%p)\n", This);
1252
1253     write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1254
1255     return S_OK;
1256 }
1257
1258 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1259 {
1260     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1261     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1262     return E_NOTIMPL;
1263 }
1264
1265 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1266 {
1267     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1268     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1269     return E_NOTIMPL;
1270 }
1271
1272 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1273 {
1274     static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1275     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1276
1277     TRACE("(%p)\n", This);
1278
1279     write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1280     This->cdata = TRUE;
1281
1282     return S_OK;
1283 }
1284
1285 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1286 {
1287     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1288     static const WCHAR ecdataW[] = {']',']','>'};
1289
1290     TRACE("(%p)\n", This);
1291
1292     write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1293     This->cdata = FALSE;
1294
1295     return S_OK;
1296 }
1297
1298 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1299 {
1300     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1301     static const WCHAR copenW[] = {'<','!','-','-'};
1302     static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1303
1304     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1305
1306     if (!chars) return E_INVALIDARG;
1307
1308     close_element_starttag(This);
1309
1310     write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1311     if (nchars)
1312         write_output_buffer(This->buffer, chars, nchars);
1313     write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1314
1315     return S_OK;
1316 }
1317
1318 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1319 {
1320     SAXLexicalHandler_QueryInterface,
1321     SAXLexicalHandler_AddRef,
1322     SAXLexicalHandler_Release,
1323     SAXLexicalHandler_startDTD,
1324     SAXLexicalHandler_endDTD,
1325     SAXLexicalHandler_startEntity,
1326     SAXLexicalHandler_endEntity,
1327     SAXLexicalHandler_startCDATA,
1328     SAXLexicalHandler_endCDATA,
1329     SAXLexicalHandler_comment
1330 };
1331
1332 /*** ISAXDeclHandler ***/
1333 static HRESULT WINAPI SAXDeclHandler_QueryInterface(ISAXDeclHandler *iface,
1334     REFIID riid, void **obj)
1335 {
1336     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1337     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1338 }
1339
1340 static ULONG WINAPI SAXDeclHandler_AddRef(ISAXDeclHandler *iface)
1341 {
1342     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1343     return IMXWriter_AddRef(&This->IMXWriter_iface);
1344 }
1345
1346 static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
1347 {
1348     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1349     return IMXWriter_Release(&This->IMXWriter_iface);
1350 }
1351
1352 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
1353     const WCHAR *name, int n_name, const WCHAR *model, int n_model)
1354 {
1355     static const WCHAR elementW[] = {'<','!','E','L','E','M','E','N','T',' '};
1356     static const WCHAR closeelementW[] = {'>','\r','\n'};
1357     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1358
1359     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1360         debugstr_wn(model, n_model), n_model);
1361
1362     if (!name || !model) return E_INVALIDARG;
1363
1364     write_output_buffer(This->buffer, elementW, sizeof(elementW)/sizeof(WCHAR));
1365     if (n_name) {
1366         write_output_buffer(This->buffer, name, n_name);
1367         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1368     }
1369     if (n_model)
1370         write_output_buffer(This->buffer, model, n_model);
1371     write_output_buffer(This->buffer, closeelementW, sizeof(closeelementW)/sizeof(WCHAR));
1372
1373     return S_OK;
1374 }
1375
1376 static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
1377     const WCHAR *element, int n_element, const WCHAR *attr, int n_attr,
1378     const WCHAR *type, int n_type, const WCHAR *Default, int n_default,
1379     const WCHAR *value, int n_value)
1380 {
1381     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1382     FIXME("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(element, n_element), n_element,
1383         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
1384         debugstr_wn(value, n_value), n_value);
1385     return E_NOTIMPL;
1386 }
1387
1388 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
1389     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
1390 {
1391     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1392     FIXME("(%p)->(%s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1393         debugstr_wn(value, n_value), n_value);
1394     return E_NOTIMPL;
1395 }
1396
1397 static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
1398     const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
1399     const WCHAR *systemId, int n_systemId)
1400 {
1401     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1402     FIXME("(%p)->(%s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1403         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
1404     return E_NOTIMPL;
1405 }
1406
1407 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
1408     SAXDeclHandler_QueryInterface,
1409     SAXDeclHandler_AddRef,
1410     SAXDeclHandler_Release,
1411     SAXDeclHandler_elementDecl,
1412     SAXDeclHandler_attributeDecl,
1413     SAXDeclHandler_internalEntityDecl,
1414     SAXDeclHandler_externalEntityDecl
1415 };
1416
1417 static const tid_t mxwriter_iface_tids[] = {
1418     IMXWriter_tid,
1419     0
1420 };
1421
1422 static dispex_static_data_t mxwriter_dispex = {
1423     NULL,
1424     IMXWriter_tid,
1425     NULL,
1426     mxwriter_iface_tids
1427 };
1428
1429 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1430 {
1431     static const WCHAR version10W[] = {'1','.','0',0};
1432     mxwriter *This;
1433     HRESULT hr;
1434
1435     TRACE("(%p, %p)\n", outer, ppObj);
1436
1437     if (outer) FIXME("support aggregation, outer\n");
1438
1439     This = heap_alloc( sizeof (*This) );
1440     if(!This)
1441         return E_OUTOFMEMORY;
1442
1443     This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1444     This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1445     This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1446     This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1447     This->ref = 1;
1448     This->class_version = version;
1449
1450     This->props[MXWriter_BOM] = VARIANT_TRUE;
1451     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1452     This->props[MXWriter_Indent] = VARIANT_FALSE;
1453     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1454     This->props[MXWriter_Standalone] = VARIANT_FALSE;
1455     This->prop_changed = FALSE;
1456     This->encoding = SysAllocString(utf16W);
1457     This->version  = SysAllocString(version10W);
1458     This->xml_enc  = XmlEncoding_UTF16;
1459
1460     This->element = NULL;
1461     This->cdata = FALSE;
1462
1463     This->dest = NULL;
1464     This->dest_written = 0;
1465
1466     hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1467     if (hr != S_OK) {
1468         SysFreeString(This->encoding);
1469         SysFreeString(This->version);
1470         heap_free(This);
1471         return hr;
1472     }
1473
1474     init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1475
1476     *ppObj = &This->IMXWriter_iface;
1477
1478     TRACE("returning iface %p\n", *ppObj);
1479
1480     return S_OK;
1481 }
1482
1483 static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
1484 {
1485     mxattributes *This = impl_from_IMXAttributes( iface );
1486
1487     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
1488
1489     *ppObj = NULL;
1490
1491     if ( IsEqualGUID( riid, &IID_IUnknown )  ||
1492          IsEqualGUID( riid, &IID_IDispatch ) ||
1493          IsEqualGUID( riid, &IID_IMXAttributes ))
1494     {
1495         *ppObj = iface;
1496     }
1497     else if ( IsEqualGUID( riid, &IID_ISAXAttributes ))
1498     {
1499         *ppObj = &This->ISAXAttributes_iface;
1500     }
1501     else if (dispex_query_interface(&This->dispex, riid, ppObj))
1502     {
1503         return *ppObj ? S_OK : E_NOINTERFACE;
1504     }
1505     else
1506     {
1507         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1508         return E_NOINTERFACE;
1509     }
1510
1511     IMXAttributes_AddRef( iface );
1512
1513     return S_OK;
1514 }
1515
1516 static ULONG WINAPI MXAttributes_AddRef(IMXAttributes *iface)
1517 {
1518     mxattributes *This = impl_from_IMXAttributes( iface );
1519     ULONG ref = InterlockedIncrement( &This->ref );
1520     TRACE("(%p)->(%d)\n", This, ref );
1521     return ref;
1522 }
1523
1524 static ULONG WINAPI MXAttributes_Release(IMXAttributes *iface)
1525 {
1526     mxattributes *This = impl_from_IMXAttributes( iface );
1527     LONG ref = InterlockedDecrement( &This->ref );
1528
1529     TRACE("(%p)->(%d)\n", This, ref);
1530
1531     if (ref == 0)
1532     {
1533         int i;
1534
1535         for (i = 0; i < This->length; i++)
1536         {
1537             SysFreeString(This->attr[i].qname);
1538             SysFreeString(This->attr[i].local);
1539             SysFreeString(This->attr[i].uri);
1540             SysFreeString(This->attr[i].type);
1541             SysFreeString(This->attr[i].value);
1542         }
1543
1544         release_dispex(&This->dispex);
1545         heap_free(This->attr);
1546         heap_free(This);
1547     }
1548
1549     return ref;
1550 }
1551
1552 static HRESULT WINAPI MXAttributes_GetTypeInfoCount(IMXAttributes *iface, UINT* pctinfo)
1553 {
1554     mxattributes *This = impl_from_IMXAttributes( iface );
1555     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1556 }
1557
1558 static HRESULT WINAPI MXAttributes_GetTypeInfo(IMXAttributes *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1559 {
1560     mxattributes *This = impl_from_IMXAttributes( iface );
1561     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
1562 }
1563
1564 static HRESULT WINAPI MXAttributes_GetIDsOfNames(
1565     IMXAttributes *iface,
1566     REFIID riid,
1567     LPOLESTR* rgszNames,
1568     UINT cNames,
1569     LCID lcid,
1570     DISPID* rgDispId)
1571 {
1572     mxattributes *This = impl_from_IMXAttributes( iface );
1573     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1574         riid, rgszNames, cNames, lcid, rgDispId);
1575 }
1576
1577 static HRESULT WINAPI MXAttributes_Invoke(
1578     IMXAttributes *iface,
1579     DISPID dispIdMember,
1580     REFIID riid,
1581     LCID lcid,
1582     WORD wFlags,
1583     DISPPARAMS* pDispParams,
1584     VARIANT* pVarResult,
1585     EXCEPINFO* pExcepInfo,
1586     UINT* puArgErr)
1587 {
1588     mxattributes *This = impl_from_IMXAttributes( iface );
1589     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1590         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1591 }
1592
1593 static HRESULT WINAPI MXAttributes_addAttribute(IMXAttributes *iface,
1594     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1595 {
1596     mxattributes *This = impl_from_IMXAttributes( iface );
1597     mxattribute *attr;
1598     HRESULT hr;
1599
1600     TRACE("(%p)->(%s %s %s %s %s)\n", This, debugstr_w(uri), debugstr_w(localName),
1601         debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1602
1603     if ((!uri || !localName || !QName || !type || !value) && This->class_version != MSXML6)
1604         return E_INVALIDARG;
1605
1606     /* ensure array is large enough */
1607     hr = mxattributes_grow(This);
1608     if (hr != S_OK) return hr;
1609
1610     attr = &This->attr[This->length];
1611
1612     attr->qname = SysAllocString(QName);
1613     attr->local = SysAllocString(localName);
1614     attr->uri   = SysAllocString(uri);
1615     attr->type  = SysAllocString(type);
1616     attr->value = SysAllocString(value);
1617     This->length++;
1618
1619     return S_OK;
1620 }
1621
1622 static HRESULT WINAPI MXAttributes_addAttributeFromIndex(IMXAttributes *iface,
1623     VARIANT atts, int index)
1624 {
1625     mxattributes *This = impl_from_IMXAttributes( iface );
1626     FIXME("(%p)->(%s %d): stub\n", This, debugstr_variant(&atts), index);
1627     return E_NOTIMPL;
1628 }
1629
1630 static HRESULT WINAPI MXAttributes_clear(IMXAttributes *iface)
1631 {
1632     mxattributes *This = impl_from_IMXAttributes( iface );
1633     FIXME("(%p): stub\n", This);
1634     return E_NOTIMPL;
1635 }
1636
1637 static HRESULT WINAPI MXAttributes_removeAttribute(IMXAttributes *iface, int index)
1638 {
1639     mxattributes *This = impl_from_IMXAttributes( iface );
1640     FIXME("(%p)->(%d): stub\n", This, index);
1641     return E_NOTIMPL;
1642 }
1643
1644 static HRESULT WINAPI MXAttributes_setAttribute(IMXAttributes *iface, int index,
1645     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1646 {
1647     mxattributes *This = impl_from_IMXAttributes( iface );
1648     FIXME("(%p)->(%d %s %s %s %s %s): stub\n", This, index, debugstr_w(uri),
1649         debugstr_w(localName), debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1650     return E_NOTIMPL;
1651 }
1652
1653 static HRESULT WINAPI MXAttributes_setAttributes(IMXAttributes *iface, VARIANT atts)
1654 {
1655     mxattributes *This = impl_from_IMXAttributes( iface );
1656     FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&atts));
1657     return E_NOTIMPL;
1658 }
1659
1660 static HRESULT WINAPI MXAttributes_setLocalName(IMXAttributes *iface, int index,
1661     BSTR localName)
1662 {
1663     mxattributes *This = impl_from_IMXAttributes( iface );
1664     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(localName));
1665     return E_NOTIMPL;
1666 }
1667
1668 static HRESULT WINAPI MXAttributes_setQName(IMXAttributes *iface, int index, BSTR QName)
1669 {
1670     mxattributes *This = impl_from_IMXAttributes( iface );
1671     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(QName));
1672     return E_NOTIMPL;
1673 }
1674
1675 static HRESULT WINAPI MXAttributes_setURI(IMXAttributes *iface, int index, BSTR uri)
1676 {
1677     mxattributes *This = impl_from_IMXAttributes( iface );
1678     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(uri));
1679     return E_NOTIMPL;
1680 }
1681
1682 static HRESULT WINAPI MXAttributes_setValue(IMXAttributes *iface, int index, BSTR value)
1683 {
1684     mxattributes *This = impl_from_IMXAttributes( iface );
1685     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(value));
1686     return E_NOTIMPL;
1687 }
1688
1689 static const IMXAttributesVtbl MXAttributesVtbl = {
1690     MXAttributes_QueryInterface,
1691     MXAttributes_AddRef,
1692     MXAttributes_Release,
1693     MXAttributes_GetTypeInfoCount,
1694     MXAttributes_GetTypeInfo,
1695     MXAttributes_GetIDsOfNames,
1696     MXAttributes_Invoke,
1697     MXAttributes_addAttribute,
1698     MXAttributes_addAttributeFromIndex,
1699     MXAttributes_clear,
1700     MXAttributes_removeAttribute,
1701     MXAttributes_setAttribute,
1702     MXAttributes_setAttributes,
1703     MXAttributes_setLocalName,
1704     MXAttributes_setQName,
1705     MXAttributes_setURI,
1706     MXAttributes_setValue
1707 };
1708
1709 static HRESULT WINAPI SAXAttributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **ppObj)
1710 {
1711     mxattributes *This = impl_from_ISAXAttributes( iface );
1712     return IMXAttributes_QueryInterface(&This->IMXAttributes_iface, riid, ppObj);
1713 }
1714
1715 static ULONG WINAPI SAXAttributes_AddRef(ISAXAttributes *iface)
1716 {
1717     mxattributes *This = impl_from_ISAXAttributes( iface );
1718     return IMXAttributes_AddRef(&This->IMXAttributes_iface);
1719 }
1720
1721 static ULONG WINAPI SAXAttributes_Release(ISAXAttributes *iface)
1722 {
1723     mxattributes *This = impl_from_ISAXAttributes( iface );
1724     return IMXAttributes_Release(&This->IMXAttributes_iface);
1725 }
1726
1727 static HRESULT WINAPI SAXAttributes_getLength(ISAXAttributes *iface, int *length)
1728 {
1729     mxattributes *This = impl_from_ISAXAttributes( iface );
1730     TRACE("(%p)->(%p)\n", This, length);
1731
1732     if (!length && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1733        return E_POINTER;
1734
1735     *length = This->length;
1736
1737     return S_OK;
1738 }
1739
1740 static HRESULT WINAPI SAXAttributes_getURI(ISAXAttributes *iface, int nIndex, const WCHAR **pUrl,
1741     int *pUriSize)
1742 {
1743     mxattributes *This = impl_from_ISAXAttributes( iface );
1744     FIXME("(%p)->(%d %p %p): stub\n", This, nIndex, pUrl, pUriSize);
1745     return E_NOTIMPL;
1746 }
1747
1748 static HRESULT WINAPI SAXAttributes_getLocalName(ISAXAttributes *iface, int nIndex, const WCHAR **localName,
1749     int *length)
1750 {
1751     mxattributes *This = impl_from_ISAXAttributes( iface );
1752     FIXME("(%p)->(%d %p %p): stub\n", This, nIndex, localName, length);
1753     return E_NOTIMPL;
1754 }
1755
1756 static HRESULT WINAPI SAXAttributes_getQName(ISAXAttributes *iface, int index, const WCHAR **qname, int *length)
1757 {
1758     mxattributes *This = impl_from_ISAXAttributes( iface );
1759
1760     TRACE("(%p)->(%d %p %p)\n", This, index, qname, length);
1761
1762     if (index >= This->length) return E_INVALIDARG;
1763     if (!qname || !length) return E_POINTER;
1764
1765     *qname = This->attr[index].qname;
1766     *length = SysStringLen(This->attr[index].qname);
1767
1768     return S_OK;
1769 }
1770
1771 static HRESULT WINAPI SAXAttributes_getName(ISAXAttributes *iface, int nIndex, const WCHAR **pUri, int *pUriLength,
1772     const WCHAR ** pLocalName, int * pLocalNameSize, const WCHAR ** pQName, int * pQNameLength)
1773 {
1774     mxattributes *This = impl_from_ISAXAttributes( iface );
1775     FIXME("(%p)->(%d %p %p %p %p %p %p): stub\n", This, nIndex, pUri, pUriLength, pLocalName, pLocalNameSize,
1776         pQName, pQNameLength);
1777     return E_NOTIMPL;
1778 }
1779
1780 static HRESULT WINAPI SAXAttributes_getIndexFromName(ISAXAttributes *iface, const WCHAR * pUri, int cUriLength,
1781     const WCHAR * pLocalName, int cocalNameLength, int * index)
1782 {
1783     mxattributes *This = impl_from_ISAXAttributes( iface );
1784     FIXME("(%p)->(%s:%d %s:%d %p): stub\n", This, debugstr_wn(pUri, cUriLength), cUriLength,
1785         debugstr_wn(pLocalName, cocalNameLength), cocalNameLength, index);
1786     return E_NOTIMPL;
1787 }
1788
1789 static HRESULT WINAPI SAXAttributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR * pQName,
1790     int nQNameLength, int * index)
1791 {
1792     mxattributes *This = impl_from_ISAXAttributes( iface );
1793     FIXME("(%p)->(%s:%d %p): stub\n", This, debugstr_wn(pQName, nQNameLength), nQNameLength, index);
1794     return E_NOTIMPL;
1795 }
1796
1797 static HRESULT WINAPI SAXAttributes_getType(ISAXAttributes *iface, int nIndex, const WCHAR ** pType,
1798     int * pTypeLength)
1799 {
1800     mxattributes *This = impl_from_ISAXAttributes( iface );
1801     FIXME("(%p)->(%d %p %p): stub\n", This, nIndex, pType, pTypeLength);
1802     return E_NOTIMPL;
1803 }
1804
1805 static HRESULT WINAPI SAXAttributes_getTypeFromName(ISAXAttributes *iface, const WCHAR * pUri, int nUri,
1806     const WCHAR * pLocalName, int nLocalName, const WCHAR ** pType, int * nType)
1807 {
1808     mxattributes *This = impl_from_ISAXAttributes( iface );
1809     FIXME("(%p)->(%s:%d %s:%d %p %p): stub\n", This, debugstr_wn(pUri, nUri), nUri,
1810         debugstr_wn(pLocalName, nLocalName), nLocalName, pType, nType);
1811     return E_NOTIMPL;
1812 }
1813
1814 static HRESULT WINAPI SAXAttributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR * pQName,
1815     int nQName, const WCHAR ** pType, int * nType)
1816 {
1817     mxattributes *This = impl_from_ISAXAttributes( iface );
1818     FIXME("(%p)->(%s:%d %p %p): stub\n", This, debugstr_wn(pQName, nQName), nQName, pType, nType);
1819     return E_NOTIMPL;
1820 }
1821
1822 static HRESULT WINAPI SAXAttributes_getValue(ISAXAttributes *iface, int nIndex, const WCHAR ** pValue,
1823     int * nValue)
1824 {
1825     mxattributes *This = impl_from_ISAXAttributes( iface );
1826     FIXME("(%p)->(%d %p %p): stub\n", This, nIndex, pValue, nValue);
1827     return E_NOTIMPL;
1828 }
1829
1830 static HRESULT WINAPI SAXAttributes_getValueFromName(ISAXAttributes *iface, const WCHAR * pUri,
1831     int nUri, const WCHAR * pLocalName, int nLocalName, const WCHAR ** pValue, int * nValue)
1832 {
1833     mxattributes *This = impl_from_ISAXAttributes( iface );
1834     FIXME("(%p)->(%s:%d %s:%d %p %p): stub\n", This, debugstr_wn(pUri, nUri), nUri,
1835         debugstr_wn(pLocalName, nLocalName), nLocalName, pValue, nValue);
1836     return E_NOTIMPL;
1837 }
1838
1839 static HRESULT WINAPI SAXAttributes_getValueFromQName(ISAXAttributes *iface, const WCHAR * pQName,
1840     int nQName, const WCHAR ** pValue, int * nValue)
1841 {
1842     mxattributes *This = impl_from_ISAXAttributes( iface );
1843     FIXME("(%p)->(%s:%d %p %p): stub\n", This, debugstr_wn(pQName, nQName), nQName, pValue, nValue);
1844     return E_NOTIMPL;
1845 }
1846
1847 static const ISAXAttributesVtbl SAXAttributesVtbl = {
1848     SAXAttributes_QueryInterface,
1849     SAXAttributes_AddRef,
1850     SAXAttributes_Release,
1851     SAXAttributes_getLength,
1852     SAXAttributes_getURI,
1853     SAXAttributes_getLocalName,
1854     SAXAttributes_getQName,
1855     SAXAttributes_getName,
1856     SAXAttributes_getIndexFromName,
1857     SAXAttributes_getIndexFromQName,
1858     SAXAttributes_getType,
1859     SAXAttributes_getTypeFromName,
1860     SAXAttributes_getTypeFromQName,
1861     SAXAttributes_getValue,
1862     SAXAttributes_getValueFromName,
1863     SAXAttributes_getValueFromQName
1864 };
1865
1866 static const tid_t mxattrs_iface_tids[] = {
1867     IMXAttributes_tid,
1868     0
1869 };
1870
1871 static dispex_static_data_t mxattrs_dispex = {
1872     NULL,
1873     IMXAttributes_tid,
1874     NULL,
1875     mxattrs_iface_tids
1876 };
1877
1878 HRESULT SAXAttributes_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1879 {
1880     static const int default_count = 10;
1881     mxattributes *This;
1882
1883     TRACE("(%p, %p)\n", outer, ppObj);
1884
1885     This = heap_alloc( sizeof (*This) );
1886     if( !This )
1887         return E_OUTOFMEMORY;
1888
1889     This->IMXAttributes_iface.lpVtbl = &MXAttributesVtbl;
1890     This->ISAXAttributes_iface.lpVtbl = &SAXAttributesVtbl;
1891     This->ref = 1;
1892
1893     This->class_version = version;
1894
1895     This->attr = heap_alloc(default_count*sizeof(mxattribute));
1896     This->length = 0;
1897     This->allocated = default_count;
1898
1899     *ppObj = &This->IMXAttributes_iface;
1900
1901     init_dispex(&This->dispex, (IUnknown*)&This->IMXAttributes_iface, &mxattrs_dispex);
1902
1903     TRACE("returning iface %p\n", *ppObj);
1904
1905     return S_OK;
1906 }