msxml3: Add stub support of ISAXDeclHandler for MXWriter.
[wine/multimedia.git] / dlls / msxml3 / mxwriter.c
blob2519a75fcea222608347be7f651d0f2057fdb711
1 /*
2 * MXWriter implementation
4 * Copyright 2011-2012 Nikolay Sivov for CodeWeavers
5 * Copyright 2011 Thomas Mullaly
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.
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.
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
22 #define COBJMACROS
23 #include "config.h"
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
34 #include "msxml6.h"
36 #include "wine/debug.h"
38 #include "msxml_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
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[] = {'\"'};
47 typedef enum
49 XmlEncoding_UTF8,
50 XmlEncoding_UTF16,
51 XmlEncoding_Unknown
52 } xml_encoding;
54 typedef enum
56 OutputBuffer_Native = 0x001,
57 OutputBuffer_Encoded = 0x010,
58 OutputBuffer_Both = 0x100
59 } output_mode;
61 typedef enum
63 MXWriter_BOM = 0,
64 MXWriter_DisableEscaping,
65 MXWriter_Indent,
66 MXWriter_OmitXmlDecl,
67 MXWriter_Standalone,
68 MXWriter_LastProp
69 } mxwriter_prop;
71 typedef enum
73 EscapeValue,
74 EscapeText
75 } escape_mode;
77 typedef struct
79 char *data;
80 unsigned int allocated;
81 unsigned int written;
82 } encoded_buffer;
84 typedef struct
86 encoded_buffer utf16;
87 encoded_buffer encoded;
88 UINT code_page;
89 } output_buffer;
91 typedef struct
93 DispatchEx dispex;
94 IMXWriter IMXWriter_iface;
95 ISAXContentHandler ISAXContentHandler_iface;
96 ISAXLexicalHandler ISAXLexicalHandler_iface;
97 ISAXDeclHandler ISAXDeclHandler_iface;
99 LONG ref;
100 MSXML_VERSION class_version;
102 VARIANT_BOOL props[MXWriter_LastProp];
103 BOOL prop_changed;
104 BOOL cdata;
106 BSTR version;
108 BSTR encoding; /* exact property value */
109 xml_encoding xml_enc;
111 /* contains a pending (or not closed yet) element name or NULL if
112 we don't have to close */
113 BSTR element;
115 IStream *dest;
116 ULONG dest_written;
118 output_buffer *buffer;
119 } mxwriter;
121 static xml_encoding parse_encoding_name(const WCHAR *encoding)
123 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
124 if (!strcmpiW(encoding, utf8W)) return XmlEncoding_UTF8;
125 if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
126 return XmlEncoding_Unknown;
129 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
131 const int initial_len = 0x2000;
132 buffer->data = heap_alloc(initial_len);
133 if (!buffer->data) return E_OUTOFMEMORY;
135 memset(buffer->data, 0, 4);
136 buffer->allocated = initial_len;
137 buffer->written = 0;
139 return S_OK;
142 static void free_encoded_buffer(encoded_buffer *buffer)
144 heap_free(buffer->data);
147 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
149 switch (encoding)
151 case XmlEncoding_UTF8:
152 *cp = CP_UTF8;
153 break;
154 case XmlEncoding_UTF16:
155 *cp = ~0;
156 break;
157 default:
158 FIXME("unsupported encoding %d\n", encoding);
159 return E_NOTIMPL;
162 return S_OK;
165 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
167 output_buffer *ret;
168 HRESULT hr;
170 ret = heap_alloc(sizeof(*ret));
171 if (!ret) return E_OUTOFMEMORY;
173 hr = get_code_page(encoding, &ret->code_page);
174 if (hr != S_OK) {
175 heap_free(ret);
176 return hr;
179 hr = init_encoded_buffer(&ret->utf16);
180 if (hr != S_OK) {
181 heap_free(ret);
182 return hr;
185 if (ret->code_page == CP_UTF8) {
186 hr = init_encoded_buffer(&ret->encoded);
187 if (hr != S_OK) {
188 free_encoded_buffer(&ret->utf16);
189 heap_free(ret);
190 return hr;
193 else
194 memset(&ret->encoded, 0, sizeof(ret->encoded));
196 *buffer = ret;
198 return S_OK;
201 static void free_output_buffer(output_buffer *buffer)
203 free_encoded_buffer(&buffer->encoded);
204 free_encoded_buffer(&buffer->utf16);
205 heap_free(buffer);
208 static void grow_buffer(encoded_buffer *buffer, int length)
210 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
211 if (buffer->allocated < buffer->written + length + 4)
213 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
214 buffer->data = heap_realloc(buffer->data, grown_size);
215 buffer->allocated = grown_size;
219 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
221 int length;
222 char *ptr;
224 if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
225 if (buffer->code_page == CP_UTF8)
227 length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
228 grow_buffer(&buffer->encoded, length);
229 ptr = buffer->encoded.data + buffer->encoded.written;
230 length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
231 buffer->encoded.written += len == -1 ? length-1 : length;
235 if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
236 /* WCHAR data just copied */
237 length = len == -1 ? strlenW(data) : len;
238 if (length)
240 length *= sizeof(WCHAR);
242 grow_buffer(&buffer->utf16, length);
243 ptr = buffer->utf16.data + buffer->utf16.written;
245 memcpy(ptr, data, length);
246 buffer->utf16.written += length;
247 ptr += length;
248 /* null termination */
249 memset(ptr, 0, sizeof(WCHAR));
253 return S_OK;
256 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
258 return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
261 static HRESULT write_output_buffer_quoted(output_buffer *buffer, const WCHAR *data, int len)
263 write_output_buffer(buffer, quotW, 1);
264 write_output_buffer(buffer, data, len);
265 write_output_buffer(buffer, quotW, 1);
267 return S_OK;
270 /* frees buffer data, reallocates with a default lengths */
271 static void close_output_buffer(mxwriter *This)
273 heap_free(This->buffer->utf16.data);
274 heap_free(This->buffer->encoded.data);
275 init_encoded_buffer(&This->buffer->utf16);
276 init_encoded_buffer(&This->buffer->encoded);
277 get_code_page(This->xml_enc, &This->buffer->code_page);
280 /* escapes special characters like:
281 '<' -> "&lt;"
282 '&' -> "&amp;"
283 '"' -> "&quot;"
284 '>' -> "&gt;"
286 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
288 static const WCHAR ltW[] = {'&','l','t',';'};
289 static const WCHAR ampW[] = {'&','a','m','p',';'};
290 static const WCHAR equotW[] = {'&','q','u','o','t',';'};
291 static const WCHAR gtW[] = {'&','g','t',';'};
293 const int default_alloc = 100;
294 const int grow_thresh = 10;
295 int p = *len, conv_len;
296 WCHAR *ptr, *ret;
298 /* default buffer size to something if length is unknown */
299 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
300 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
302 while (*str && p)
304 if (ptr - ret > conv_len - grow_thresh)
306 int written = ptr - ret;
307 conv_len *= 2;
308 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
309 ptr += written;
312 switch (*str)
314 case '<':
315 memcpy(ptr, ltW, sizeof(ltW));
316 ptr += sizeof(ltW)/sizeof(WCHAR);
317 break;
318 case '&':
319 memcpy(ptr, ampW, sizeof(ampW));
320 ptr += sizeof(ampW)/sizeof(WCHAR);
321 break;
322 case '>':
323 memcpy(ptr, gtW, sizeof(gtW));
324 ptr += sizeof(gtW)/sizeof(WCHAR);
325 break;
326 case '"':
327 if (mode == EscapeValue)
329 memcpy(ptr, equotW, sizeof(equotW));
330 ptr += sizeof(equotW)/sizeof(WCHAR);
331 break;
333 /* fallthrough for text mode */
334 default:
335 *ptr++ = *str;
336 break;
339 str++;
340 if (*len != -1) p--;
343 if (*len != -1) *len = ptr-ret;
344 *++ptr = 0;
346 return ret;
349 static void write_prolog_buffer(const mxwriter *This)
351 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
352 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
353 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
354 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
355 static const WCHAR noW[] = {'n','o','\"','?','>'};
356 static const WCHAR crlfW[] = {'\r','\n'};
358 /* version */
359 write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
360 write_output_buffer_quoted(This->buffer, This->version, -1);
362 /* encoding */
363 write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
365 /* always write UTF-16 to WCHAR buffer */
366 write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
367 write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
368 write_output_buffer(This->buffer, quotW, 1);
370 /* standalone */
371 write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
372 if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
373 write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
374 else
375 write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
377 write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
380 /* Attempts to the write data from the mxwriter's buffer to
381 * the destination stream (if there is one).
383 static HRESULT write_data_to_stream(mxwriter *This)
385 encoded_buffer *buffer;
386 ULONG written = 0;
387 HRESULT hr;
389 if (!This->dest)
390 return S_OK;
392 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
393 * 'conv' buffer when UTF8 encoding is used.
395 if (This->xml_enc != XmlEncoding_UTF16)
396 buffer = &This->buffer->encoded;
397 else
398 buffer = &This->buffer->utf16;
400 if (This->dest_written > buffer->written) {
401 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
402 return E_FAIL;
403 } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
404 /* Windows seems to make an empty write call when the encoding is UTF-8 and
405 * all the data has been written to the stream. It doesn't seem make this call
406 * for any other encodings.
408 return S_OK;
410 /* Write the current content from the output buffer into 'dest'.
411 * TODO: Check what Windows does if the IStream doesn't write all of
412 * the data we give it at once.
414 hr = IStream_Write(This->dest, buffer->data+This->dest_written,
415 buffer->written-This->dest_written, &written);
416 if (FAILED(hr)) {
417 WARN("Failed to write data to IStream (0x%08x)\n", hr);
418 return hr;
421 This->dest_written += written;
422 return hr;
425 /* Newly added element start tag left unclosed cause for empty elements
426 we have to close it differently. */
427 static void close_element_starttag(const mxwriter *This)
429 static const WCHAR gtW[] = {'>'};
430 if (!This->element) return;
431 write_output_buffer(This->buffer, gtW, 1);
434 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
436 SysFreeString(This->element);
437 This->element = name ? SysAllocStringLen(name, len) : NULL;
440 static inline HRESULT flush_output_buffer(mxwriter *This)
442 close_element_starttag(This);
443 set_element_name(This, NULL, 0);
444 This->cdata = FALSE;
445 return write_data_to_stream(This);
448 /* Resets the mxwriter's output buffer by closing it, then creating a new
449 * output buffer using the given encoding.
451 static inline void reset_output_buffer(mxwriter *This)
453 close_output_buffer(This);
454 This->dest_written = 0;
457 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
459 writer->props[property] = value;
460 writer->prop_changed = TRUE;
461 return S_OK;
464 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
466 if (!value) return E_POINTER;
467 *value = writer->props[property];
468 return S_OK;
471 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
473 return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
476 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
478 return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
481 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
483 return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
486 static inline mxwriter *impl_from_ISAXDeclHandler(ISAXDeclHandler *iface)
488 return CONTAINING_RECORD(iface, mxwriter, ISAXDeclHandler_iface);
491 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
493 mxwriter *This = impl_from_IMXWriter( iface );
495 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
497 *obj = NULL;
499 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
500 IsEqualGUID( riid, &IID_IDispatch ) ||
501 IsEqualGUID( riid, &IID_IUnknown ) )
503 *obj = &This->IMXWriter_iface;
505 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
507 *obj = &This->ISAXContentHandler_iface;
509 else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
511 *obj = &This->ISAXLexicalHandler_iface;
513 else if ( IsEqualGUID( riid, &IID_ISAXDeclHandler ) )
515 *obj = &This->ISAXDeclHandler_iface;
517 else if (dispex_query_interface(&This->dispex, riid, obj))
519 return *obj ? S_OK : E_NOINTERFACE;
521 else
523 ERR("interface %s not implemented\n", debugstr_guid(riid));
524 *obj = NULL;
525 return E_NOINTERFACE;
528 IMXWriter_AddRef(iface);
529 return S_OK;
532 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
534 mxwriter *This = impl_from_IMXWriter( iface );
535 LONG ref = InterlockedIncrement(&This->ref);
537 TRACE("(%p)->(%d)\n", This, ref);
539 return ref;
542 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
544 mxwriter *This = impl_from_IMXWriter( iface );
545 ULONG ref = InterlockedDecrement(&This->ref);
547 TRACE("(%p)->(%d)\n", This, ref);
549 if(!ref)
551 /* Windows flushes the buffer when the interface is destroyed. */
552 flush_output_buffer(This);
553 free_output_buffer(This->buffer);
555 if (This->dest) IStream_Release(This->dest);
556 SysFreeString(This->version);
557 SysFreeString(This->encoding);
559 SysFreeString(This->element);
560 release_dispex(&This->dispex);
561 heap_free(This);
564 return ref;
567 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
569 mxwriter *This = impl_from_IMXWriter( iface );
570 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
573 static HRESULT WINAPI mxwriter_GetTypeInfo(
574 IMXWriter *iface,
575 UINT iTInfo, LCID lcid,
576 ITypeInfo** ppTInfo )
578 mxwriter *This = impl_from_IMXWriter( iface );
579 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
580 iTInfo, lcid, ppTInfo);
583 static HRESULT WINAPI mxwriter_GetIDsOfNames(
584 IMXWriter *iface,
585 REFIID riid, LPOLESTR* rgszNames,
586 UINT cNames, LCID lcid, DISPID* rgDispId )
588 mxwriter *This = impl_from_IMXWriter( iface );
589 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
590 riid, rgszNames, cNames, lcid, rgDispId);
593 static HRESULT WINAPI mxwriter_Invoke(
594 IMXWriter *iface,
595 DISPID dispIdMember, REFIID riid, LCID lcid,
596 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
597 EXCEPINFO* pExcepInfo, UINT* puArgErr )
599 mxwriter *This = impl_from_IMXWriter( iface );
600 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
601 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
604 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
606 mxwriter *This = impl_from_IMXWriter( iface );
607 HRESULT hr;
609 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
611 hr = flush_output_buffer(This);
612 if (FAILED(hr))
613 return hr;
615 switch (V_VT(&dest))
617 case VT_EMPTY:
619 if (This->dest) IStream_Release(This->dest);
620 This->dest = NULL;
621 reset_output_buffer(This);
622 break;
624 case VT_UNKNOWN:
626 IStream *stream;
628 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
629 if (hr == S_OK)
631 /* Recreate the output buffer to make sure it's using the correct encoding. */
632 reset_output_buffer(This);
634 if (This->dest) IStream_Release(This->dest);
635 This->dest = stream;
636 break;
639 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
640 return E_NOTIMPL;
642 default:
643 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
644 return E_NOTIMPL;
647 return S_OK;
650 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
652 mxwriter *This = impl_from_IMXWriter( iface );
654 TRACE("(%p)->(%p)\n", This, dest);
656 if (!This->dest)
658 HRESULT hr = flush_output_buffer(This);
659 if (FAILED(hr))
660 return hr;
662 V_VT(dest) = VT_BSTR;
663 V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
665 return S_OK;
667 else
668 FIXME("not implemented when stream is set up\n");
670 return E_NOTIMPL;
673 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
675 mxwriter *This = impl_from_IMXWriter( iface );
676 xml_encoding enc;
677 HRESULT hr;
679 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
681 enc = parse_encoding_name(encoding);
682 if (enc == XmlEncoding_Unknown)
684 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
685 return E_INVALIDARG;
688 hr = flush_output_buffer(This);
689 if (FAILED(hr))
690 return hr;
692 SysReAllocString(&This->encoding, encoding);
693 This->xml_enc = enc;
695 TRACE("got encoding %d\n", This->xml_enc);
696 reset_output_buffer(This);
697 return S_OK;
700 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
702 mxwriter *This = impl_from_IMXWriter( iface );
704 TRACE("(%p)->(%p)\n", This, encoding);
706 if (!encoding) return E_POINTER;
708 *encoding = SysAllocString(This->encoding);
709 if (!*encoding) return E_OUTOFMEMORY;
711 return S_OK;
714 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
716 mxwriter *This = impl_from_IMXWriter( iface );
718 TRACE("(%p)->(%d)\n", This, value);
719 return writer_set_property(This, MXWriter_BOM, value);
722 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
724 mxwriter *This = impl_from_IMXWriter( iface );
726 TRACE("(%p)->(%p)\n", This, value);
727 return writer_get_property(This, MXWriter_BOM, value);
730 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
732 mxwriter *This = impl_from_IMXWriter( iface );
734 TRACE("(%p)->(%d)\n", This, value);
735 return writer_set_property(This, MXWriter_Indent, value);
738 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
740 mxwriter *This = impl_from_IMXWriter( iface );
742 TRACE("(%p)->(%p)\n", This, value);
743 return writer_get_property(This, MXWriter_Indent, value);
746 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
748 mxwriter *This = impl_from_IMXWriter( iface );
750 TRACE("(%p)->(%d)\n", This, value);
751 return writer_set_property(This, MXWriter_Standalone, value);
754 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
756 mxwriter *This = impl_from_IMXWriter( iface );
758 TRACE("(%p)->(%p)\n", This, value);
759 return writer_get_property(This, MXWriter_Standalone, value);
762 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
764 mxwriter *This = impl_from_IMXWriter( iface );
766 TRACE("(%p)->(%d)\n", This, value);
767 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
770 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
772 mxwriter *This = impl_from_IMXWriter( iface );
774 TRACE("(%p)->(%p)\n", This, value);
775 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
778 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
780 mxwriter *This = impl_from_IMXWriter( iface );
782 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
784 if (!version) return E_INVALIDARG;
786 SysFreeString(This->version);
787 This->version = SysAllocString(version);
789 return S_OK;
792 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
794 mxwriter *This = impl_from_IMXWriter( iface );
796 TRACE("(%p)->(%p)\n", This, version);
798 if (!version) return E_POINTER;
800 return return_bstr(This->version, version);
803 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
805 mxwriter *This = impl_from_IMXWriter( iface );
807 TRACE("(%p)->(%d)\n", This, value);
808 return writer_set_property(This, MXWriter_DisableEscaping, value);
811 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
813 mxwriter *This = impl_from_IMXWriter( iface );
815 TRACE("(%p)->(%p)\n", This, value);
816 return writer_get_property(This, MXWriter_DisableEscaping, value);
819 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
821 mxwriter *This = impl_from_IMXWriter( iface );
822 TRACE("(%p)\n", This);
823 return flush_output_buffer(This);
826 static const struct IMXWriterVtbl MXWriterVtbl =
828 mxwriter_QueryInterface,
829 mxwriter_AddRef,
830 mxwriter_Release,
831 mxwriter_GetTypeInfoCount,
832 mxwriter_GetTypeInfo,
833 mxwriter_GetIDsOfNames,
834 mxwriter_Invoke,
835 mxwriter_put_output,
836 mxwriter_get_output,
837 mxwriter_put_encoding,
838 mxwriter_get_encoding,
839 mxwriter_put_byteOrderMark,
840 mxwriter_get_byteOrderMark,
841 mxwriter_put_indent,
842 mxwriter_get_indent,
843 mxwriter_put_standalone,
844 mxwriter_get_standalone,
845 mxwriter_put_omitXMLDeclaration,
846 mxwriter_get_omitXMLDeclaration,
847 mxwriter_put_version,
848 mxwriter_get_version,
849 mxwriter_put_disableOutputEscaping,
850 mxwriter_get_disableOutputEscaping,
851 mxwriter_flush
854 /*** ISAXContentHandler ***/
855 static HRESULT WINAPI SAXContentHandler_QueryInterface(
856 ISAXContentHandler *iface,
857 REFIID riid,
858 void **obj)
860 mxwriter *This = impl_from_ISAXContentHandler( iface );
861 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
864 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
866 mxwriter *This = impl_from_ISAXContentHandler( iface );
867 return IMXWriter_AddRef(&This->IMXWriter_iface);
870 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
872 mxwriter *This = impl_from_ISAXContentHandler( iface );
873 return IMXWriter_Release(&This->IMXWriter_iface);
876 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
877 ISAXContentHandler *iface,
878 ISAXLocator *locator)
880 mxwriter *This = impl_from_ISAXContentHandler( iface );
881 FIXME("(%p)->(%p)\n", This, locator);
882 return E_NOTIMPL;
885 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
887 mxwriter *This = impl_from_ISAXContentHandler( iface );
889 TRACE("(%p)\n", This);
891 /* If properties have been changed since the last "endDocument" call
892 * we need to reset the output buffer. If we don't the output buffer
893 * could end up with multiple XML documents in it, plus this seems to
894 * be how Windows works.
896 if (This->prop_changed) {
897 reset_output_buffer(This);
898 This->prop_changed = FALSE;
901 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
903 write_prolog_buffer(This);
905 if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
906 static const char utf16BOM[] = {0xff,0xfe};
908 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
909 /* Windows passes a NULL pointer as the pcbWritten parameter and
910 * ignores any error codes returned from this Write call.
912 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
915 return S_OK;
918 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
920 mxwriter *This = impl_from_ISAXContentHandler( iface );
921 TRACE("(%p)\n", This);
922 This->prop_changed = FALSE;
923 return flush_output_buffer(This);
926 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
927 ISAXContentHandler *iface,
928 const WCHAR *prefix,
929 int nprefix,
930 const WCHAR *uri,
931 int nuri)
933 mxwriter *This = impl_from_ISAXContentHandler( iface );
934 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
935 return E_NOTIMPL;
938 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
939 ISAXContentHandler *iface,
940 const WCHAR *prefix,
941 int nprefix)
943 mxwriter *This = impl_from_ISAXContentHandler( iface );
944 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
945 return E_NOTIMPL;
948 static HRESULT WINAPI SAXContentHandler_startElement(
949 ISAXContentHandler *iface,
950 const WCHAR *namespaceUri,
951 int nnamespaceUri,
952 const WCHAR *local_name,
953 int nlocal_name,
954 const WCHAR *QName,
955 int nQName,
956 ISAXAttributes *attr)
958 mxwriter *This = impl_from_ISAXContentHandler( iface );
959 static const WCHAR ltW[] = {'<'};
961 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
962 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
964 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
965 return E_INVALIDARG;
967 close_element_starttag(This);
968 set_element_name(This, QName ? QName : emptyW,
969 QName ? nQName : 0);
971 write_output_buffer(This->buffer, ltW, 1);
972 write_output_buffer(This->buffer, QName, nQName);
974 if (attr)
976 HRESULT hr;
977 INT length;
978 INT i;
980 hr = ISAXAttributes_getLength(attr, &length);
981 if (FAILED(hr)) return hr;
983 for (i = 0; i < length; i++)
985 static const WCHAR eqW[] = {'='};
986 const WCHAR *str;
987 WCHAR *escaped;
988 INT len = 0;
990 hr = ISAXAttributes_getQName(attr, i, &str, &len);
991 if (FAILED(hr)) return hr;
993 /* space separator in front of every attribute */
994 write_output_buffer(This->buffer, spaceW, 1);
995 write_output_buffer(This->buffer, str, len);
997 write_output_buffer(This->buffer, eqW, 1);
999 len = 0;
1000 hr = ISAXAttributes_getValue(attr, i, &str, &len);
1001 if (FAILED(hr)) return hr;
1003 escaped = get_escaped_string(str, EscapeValue, &len);
1004 write_output_buffer_quoted(This->buffer, escaped, len);
1005 heap_free(escaped);
1009 return S_OK;
1012 static HRESULT WINAPI SAXContentHandler_endElement(
1013 ISAXContentHandler *iface,
1014 const WCHAR *namespaceUri,
1015 int nnamespaceUri,
1016 const WCHAR * local_name,
1017 int nlocal_name,
1018 const WCHAR *QName,
1019 int nQName)
1021 mxwriter *This = impl_from_ISAXContentHandler( iface );
1023 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1024 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1026 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1027 return E_INVALIDARG;
1029 if (This->element && QName && !strncmpW(This->element, QName, nQName))
1031 static const WCHAR closeW[] = {'/','>'};
1033 write_output_buffer(This->buffer, closeW, 2);
1035 else
1037 static const WCHAR closetagW[] = {'<','/'};
1038 static const WCHAR gtW[] = {'>'};
1040 write_output_buffer(This->buffer, closetagW, 2);
1041 write_output_buffer(This->buffer, QName, nQName);
1042 write_output_buffer(This->buffer, gtW, 1);
1045 set_element_name(This, NULL, 0);
1047 return S_OK;
1050 static HRESULT WINAPI SAXContentHandler_characters(
1051 ISAXContentHandler *iface,
1052 const WCHAR *chars,
1053 int nchars)
1055 mxwriter *This = impl_from_ISAXContentHandler( iface );
1057 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1059 if (!chars) return E_INVALIDARG;
1061 close_element_starttag(This);
1062 set_element_name(This, NULL, 0);
1064 if (nchars)
1066 if (This->cdata)
1067 write_output_buffer(This->buffer, chars, nchars);
1068 else
1070 int len = nchars;
1071 WCHAR *escaped;
1073 escaped = get_escaped_string(chars, EscapeText, &len);
1074 write_output_buffer(This->buffer, escaped, len);
1075 heap_free(escaped);
1079 return S_OK;
1082 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1083 ISAXContentHandler *iface,
1084 const WCHAR *chars,
1085 int nchars)
1087 mxwriter *This = impl_from_ISAXContentHandler( iface );
1088 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1089 return E_NOTIMPL;
1092 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1093 ISAXContentHandler *iface,
1094 const WCHAR *target,
1095 int ntarget,
1096 const WCHAR *data,
1097 int ndata)
1099 mxwriter *This = impl_from_ISAXContentHandler( iface );
1100 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1101 return E_NOTIMPL;
1104 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1105 ISAXContentHandler *iface,
1106 const WCHAR *name,
1107 int nname)
1109 mxwriter *This = impl_from_ISAXContentHandler( iface );
1110 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1111 return E_NOTIMPL;
1114 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1116 SAXContentHandler_QueryInterface,
1117 SAXContentHandler_AddRef,
1118 SAXContentHandler_Release,
1119 SAXContentHandler_putDocumentLocator,
1120 SAXContentHandler_startDocument,
1121 SAXContentHandler_endDocument,
1122 SAXContentHandler_startPrefixMapping,
1123 SAXContentHandler_endPrefixMapping,
1124 SAXContentHandler_startElement,
1125 SAXContentHandler_endElement,
1126 SAXContentHandler_characters,
1127 SAXContentHandler_ignorableWhitespace,
1128 SAXContentHandler_processingInstruction,
1129 SAXContentHandler_skippedEntity
1132 /*** ISAXLexicalHandler ***/
1133 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1134 REFIID riid, void **obj)
1136 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1137 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1140 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1142 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1143 return IMXWriter_AddRef(&This->IMXWriter_iface);
1146 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1148 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1149 return IMXWriter_Release(&This->IMXWriter_iface);
1152 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1153 const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1154 const WCHAR *systemId, int systemId_len)
1156 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1157 static const WCHAR openintW[] = {'[','\r','\n'};
1159 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1161 TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1162 debugstr_wn(systemId, systemId_len));
1164 if (!name) return E_INVALIDARG;
1166 write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1168 if (*name)
1170 write_output_buffer(This->buffer, name, name_len);
1171 write_output_buffer(This->buffer, spaceW, 1);
1174 if (publicId)
1176 static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1178 write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1179 write_output_buffer_quoted(This->buffer, publicId, publicId_len);
1181 if (!systemId) return E_INVALIDARG;
1183 if (*publicId)
1184 write_output_buffer(This->buffer, spaceW, 1);
1186 write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1188 if (*systemId)
1189 write_output_buffer(This->buffer, spaceW, 1);
1191 else if (systemId)
1193 static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1195 write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1196 write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1197 if (*systemId)
1198 write_output_buffer(This->buffer, spaceW, 1);
1201 write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1203 return S_OK;
1206 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1208 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1209 static const WCHAR closedtdW[] = {']','>','\r','\n'};
1211 TRACE("(%p)\n", This);
1213 write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1215 return S_OK;
1218 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1220 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1221 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1222 return E_NOTIMPL;
1225 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1227 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1228 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1229 return E_NOTIMPL;
1232 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1234 static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1235 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1237 TRACE("(%p)\n", This);
1239 write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1240 This->cdata = TRUE;
1242 return S_OK;
1245 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1247 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1248 static const WCHAR ecdataW[] = {']',']','>'};
1250 TRACE("(%p)\n", This);
1252 write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1253 This->cdata = FALSE;
1255 return S_OK;
1258 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1260 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1261 static const WCHAR copenW[] = {'<','!','-','-'};
1262 static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1264 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1266 if (!chars) return E_INVALIDARG;
1268 close_element_starttag(This);
1270 write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1271 if (nchars)
1272 write_output_buffer(This->buffer, chars, nchars);
1273 write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1275 return S_OK;
1278 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1280 SAXLexicalHandler_QueryInterface,
1281 SAXLexicalHandler_AddRef,
1282 SAXLexicalHandler_Release,
1283 SAXLexicalHandler_startDTD,
1284 SAXLexicalHandler_endDTD,
1285 SAXLexicalHandler_startEntity,
1286 SAXLexicalHandler_endEntity,
1287 SAXLexicalHandler_startCDATA,
1288 SAXLexicalHandler_endCDATA,
1289 SAXLexicalHandler_comment
1292 /*** ISAXDeclHandler ***/
1293 static HRESULT WINAPI SAXDeclHandler_QueryInterface(ISAXDeclHandler *iface,
1294 REFIID riid, void **obj)
1296 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1297 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1300 static ULONG WINAPI SAXDeclHandler_AddRef(ISAXDeclHandler *iface)
1302 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1303 return IMXWriter_AddRef(&This->IMXWriter_iface);
1306 static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
1308 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1309 return IMXWriter_Release(&This->IMXWriter_iface);
1312 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
1313 const WCHAR *name, int n_name, const WCHAR *model, int n_model)
1315 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1316 FIXME("(%p)->(%s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1317 debugstr_wn(model, n_model), n_model);
1318 return E_NOTIMPL;
1321 static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
1322 const WCHAR *element, int n_element, const WCHAR *attr, int n_attr,
1323 const WCHAR *type, int n_type, const WCHAR *Default, int n_default,
1324 const WCHAR *value, int n_value)
1326 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1327 FIXME("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(element, n_element), n_element,
1328 debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
1329 debugstr_wn(value, n_value), n_value);
1330 return E_NOTIMPL;
1333 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
1334 const WCHAR *name, int n_name, const WCHAR *value, int n_value)
1336 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1337 FIXME("(%p)->(%s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1338 debugstr_wn(value, n_value), n_value);
1339 return E_NOTIMPL;
1342 static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
1343 const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
1344 const WCHAR *systemId, int n_systemId)
1346 mxwriter *This = impl_from_ISAXDeclHandler( iface );
1347 FIXME("(%p)->(%s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1348 debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
1349 return E_NOTIMPL;
1352 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
1353 SAXDeclHandler_QueryInterface,
1354 SAXDeclHandler_AddRef,
1355 SAXDeclHandler_Release,
1356 SAXDeclHandler_elementDecl,
1357 SAXDeclHandler_attributeDecl,
1358 SAXDeclHandler_internalEntityDecl,
1359 SAXDeclHandler_externalEntityDecl
1362 static const tid_t mxwriter_iface_tids[] = {
1363 IMXWriter_tid,
1367 static dispex_static_data_t mxwriter_dispex = {
1368 NULL,
1369 IMXWriter_tid,
1370 NULL,
1371 mxwriter_iface_tids
1374 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1376 static const WCHAR version10W[] = {'1','.','0',0};
1377 mxwriter *This;
1378 HRESULT hr;
1380 TRACE("(%p, %p)\n", outer, ppObj);
1382 if (outer) FIXME("support aggregation, outer\n");
1384 This = heap_alloc( sizeof (*This) );
1385 if(!This)
1386 return E_OUTOFMEMORY;
1388 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1389 This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1390 This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1391 This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1392 This->ref = 1;
1393 This->class_version = version;
1395 This->props[MXWriter_BOM] = VARIANT_TRUE;
1396 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1397 This->props[MXWriter_Indent] = VARIANT_FALSE;
1398 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1399 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1400 This->prop_changed = FALSE;
1401 This->encoding = SysAllocString(utf16W);
1402 This->version = SysAllocString(version10W);
1403 This->xml_enc = XmlEncoding_UTF16;
1405 This->element = NULL;
1406 This->cdata = FALSE;
1408 This->dest = NULL;
1409 This->dest_written = 0;
1411 hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1412 if (hr != S_OK) {
1413 SysFreeString(This->encoding);
1414 SysFreeString(This->version);
1415 heap_free(This);
1416 return hr;
1419 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1421 *ppObj = &This->IMXWriter_iface;
1423 TRACE("returning iface %p\n", *ppObj);
1425 return S_OK;