msxml3/tests: Added IStream tests for IMXWriter.
[wine] / dlls / msxml3 / xmldoc.c
1 /*
2  * XML Document implementation
3  *
4  * Copyright 2007 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 # include <libxml/xmlerror.h>
29 #endif
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winuser.h"
34 #include "ole2.h"
35 #include "msxml6.h"
36 #include "wininet.h"
37 #include "winreg.h"
38 #include "shlwapi.h"
39 #include "ocidl.h"
40
41 #include "wine/debug.h"
42
43 #include "msxml_private.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
46
47 #ifdef HAVE_LIBXML2
48
49 /* FIXME: IXMLDocument needs to implement
50  *   - IXMLError
51  *   - IPersistMoniker
52  */
53
54 typedef struct _xmldoc
55 {
56     IXMLDocument IXMLDocument_iface;
57     IPersistStreamInit IPersistStreamInit_iface;
58     LONG ref;
59     HRESULT error;
60
61     /* IXMLDocument */
62     xmlDocPtr xmldoc;
63
64     /* IPersistStream */
65     IStream *stream;
66 } xmldoc;
67
68 static inline xmldoc *impl_from_IXMLDocument(IXMLDocument *iface)
69 {
70     return CONTAINING_RECORD(iface, xmldoc, IXMLDocument_iface);
71 }
72
73 static inline xmldoc *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
74 {
75     return CONTAINING_RECORD(iface, xmldoc, IPersistStreamInit_iface);
76 }
77
78 static HRESULT WINAPI xmldoc_QueryInterface(IXMLDocument *iface, REFIID riid, void** ppvObject)
79 {
80     xmldoc *This = impl_from_IXMLDocument(iface);
81
82     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
83
84     if (IsEqualGUID(riid, &IID_IUnknown) ||
85         IsEqualGUID(riid, &IID_IXMLDocument) ||
86         IsEqualGUID(riid, &IID_IXMLDOMDocument))
87     {
88         *ppvObject = iface;
89     }
90     else if (IsEqualGUID(&IID_IPersistStreamInit, riid) ||
91              IsEqualGUID(&IID_IPersistStream, riid))
92     {
93         *ppvObject = &This->IPersistStreamInit_iface;
94     }
95     else
96     {
97         FIXME("interface %s not implemented\n", debugstr_guid(riid));
98         *ppvObject = NULL;
99         return E_NOINTERFACE;
100     }
101
102     IXMLDocument_AddRef(iface);
103
104     return S_OK;
105 }
106
107 static ULONG WINAPI xmldoc_AddRef(IXMLDocument *iface)
108 {
109     xmldoc *This = impl_from_IXMLDocument(iface);
110     TRACE("%p\n", This);
111     return InterlockedIncrement(&This->ref);
112 }
113
114 static ULONG WINAPI xmldoc_Release(IXMLDocument *iface)
115 {
116     xmldoc *This = impl_from_IXMLDocument(iface);
117     LONG ref;
118
119     TRACE("%p\n", This);
120
121     ref = InterlockedDecrement(&This->ref);
122     if (ref == 0)
123     {
124         xmlFreeDoc(This->xmldoc);
125         if (This->stream) IStream_Release(This->stream);
126         heap_free(This);
127     }
128
129     return ref;
130 }
131
132 static HRESULT WINAPI xmldoc_GetTypeInfoCount(IXMLDocument *iface, UINT* pctinfo)
133 {
134     xmldoc *This = impl_from_IXMLDocument(iface);
135
136     TRACE("(%p)->(%p)\n", This, pctinfo);
137
138     *pctinfo = 1;
139
140     return S_OK;
141 }
142
143 static HRESULT WINAPI xmldoc_GetTypeInfo(IXMLDocument *iface, UINT iTInfo,
144                                          LCID lcid, ITypeInfo** ppTInfo)
145 {
146     xmldoc *This = impl_from_IXMLDocument(iface);
147     HRESULT hr;
148
149     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
150
151     hr = get_typeinfo(IXMLDocument_tid, ppTInfo);
152
153     return hr;
154 }
155
156 static HRESULT WINAPI xmldoc_GetIDsOfNames(IXMLDocument *iface, REFIID riid,
157                                            LPOLESTR* rgszNames, UINT cNames,
158                                            LCID lcid, DISPID* rgDispId)
159 {
160     xmldoc *This = impl_from_IXMLDocument(iface);
161     ITypeInfo *typeinfo;
162     HRESULT hr;
163
164     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
165           lcid, rgDispId);
166
167     if(!rgszNames || cNames == 0 || !rgDispId)
168         return E_INVALIDARG;
169
170     hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
171     if(SUCCEEDED(hr))
172     {
173         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
174         ITypeInfo_Release(typeinfo);
175     }
176
177     return hr;
178 }
179
180 static HRESULT WINAPI xmldoc_Invoke(IXMLDocument *iface, DISPID dispIdMember,
181                                     REFIID riid, LCID lcid, WORD wFlags,
182                                     DISPPARAMS* pDispParams, VARIANT* pVarResult,
183                                     EXCEPINFO* pExcepInfo, UINT* puArgErr)
184 {
185     xmldoc *This = impl_from_IXMLDocument(iface);
186     ITypeInfo *typeinfo;
187     HRESULT hr;
188
189     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
190           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
191
192     hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
193     if(SUCCEEDED(hr))
194     {
195         hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDocument_iface, dispIdMember, wFlags,
196                 pDispParams, pVarResult, pExcepInfo, puArgErr);
197         ITypeInfo_Release(typeinfo);
198     }
199
200     return hr;
201 }
202
203 static HRESULT WINAPI xmldoc_get_root(IXMLDocument *iface, IXMLElement **p)
204 {
205     xmldoc *This = impl_from_IXMLDocument(iface);
206     xmlNodePtr root;
207
208     TRACE("(%p, %p)\n", iface, p);
209
210     if (!p)
211         return E_INVALIDARG;
212
213     *p = NULL;
214
215     if (!(root = xmlDocGetRootElement(This->xmldoc)))
216         return E_FAIL;
217
218     return XMLElement_create((IUnknown *)This, root, (LPVOID *)p, FALSE);
219 }
220
221 static HRESULT WINAPI xmldoc_get_fileSize(IXMLDocument *iface, BSTR *p)
222 {
223     FIXME("(%p, %p): stub\n", iface, p);
224     return E_NOTIMPL;
225 }
226
227 static HRESULT WINAPI xmldoc_put_fileModifiedDate(IXMLDocument *iface, BSTR *p)
228 {
229     FIXME("(%p, %p): stub\n", iface, p);
230     return E_NOTIMPL;
231 }
232
233 static HRESULT WINAPI xmldoc_get_fileUpdatedDate(IXMLDocument *iface, BSTR *p)
234 {
235     FIXME("(%p, %p): stub\n", iface, p);
236     return E_NOTIMPL;
237 }
238
239 static HRESULT WINAPI xmldoc_get_URL(IXMLDocument *iface, BSTR *p)
240 {
241     FIXME("(%p, %p): stub\n", iface, p);
242     return E_NOTIMPL;
243 }
244
245 typedef struct {
246     IBindStatusCallback IBindStatusCallback_iface;
247 } bsc;
248
249 static HRESULT WINAPI bsc_QueryInterface(
250     IBindStatusCallback *iface,
251     REFIID riid,
252     LPVOID *ppobj )
253 {
254     if (IsEqualGUID(riid, &IID_IUnknown) ||
255         IsEqualGUID(riid, &IID_IBindStatusCallback))
256     {
257         IBindStatusCallback_AddRef( iface );
258         *ppobj = iface;
259         return S_OK;
260     }
261
262     TRACE("interface %s not implemented\n", debugstr_guid(riid));
263     return E_NOINTERFACE;
264 }
265
266 static ULONG WINAPI bsc_AddRef(
267     IBindStatusCallback *iface )
268 {
269     return 2;
270 }
271
272 static ULONG WINAPI bsc_Release(
273     IBindStatusCallback *iface )
274 {
275     return 1;
276 }
277
278 static HRESULT WINAPI bsc_OnStartBinding(
279         IBindStatusCallback* iface,
280         DWORD dwReserved,
281         IBinding* pib)
282 {
283     return S_OK;
284 }
285
286 static HRESULT WINAPI bsc_GetPriority(
287         IBindStatusCallback* iface,
288         LONG* pnPriority)
289 {
290     return S_OK;
291 }
292
293 static HRESULT WINAPI bsc_OnLowResource(
294         IBindStatusCallback* iface,
295         DWORD reserved)
296 {
297     return S_OK;
298 }
299
300 static HRESULT WINAPI bsc_OnProgress(
301         IBindStatusCallback* iface,
302         ULONG ulProgress,
303         ULONG ulProgressMax,
304         ULONG ulStatusCode,
305         LPCWSTR szStatusText)
306 {
307     return S_OK;
308 }
309
310 static HRESULT WINAPI bsc_OnStopBinding(
311         IBindStatusCallback* iface,
312         HRESULT hresult,
313         LPCWSTR szError)
314 {
315     return S_OK;
316 }
317
318 static HRESULT WINAPI bsc_GetBindInfo(
319         IBindStatusCallback* iface,
320         DWORD* grfBINDF,
321         BINDINFO* pbindinfo)
322 {
323     *grfBINDF = BINDF_RESYNCHRONIZE;
324
325     return S_OK;
326 }
327
328 static HRESULT WINAPI bsc_OnDataAvailable(
329         IBindStatusCallback* iface,
330         DWORD grfBSCF,
331         DWORD dwSize,
332         FORMATETC* pformatetc,
333         STGMEDIUM* pstgmed)
334 {
335     return S_OK;
336 }
337
338 static HRESULT WINAPI bsc_OnObjectAvailable(
339         IBindStatusCallback* iface,
340         REFIID riid,
341         IUnknown* punk)
342 {
343     return S_OK;
344 }
345
346 static const struct IBindStatusCallbackVtbl bsc_vtbl =
347 {
348     bsc_QueryInterface,
349     bsc_AddRef,
350     bsc_Release,
351     bsc_OnStartBinding,
352     bsc_GetPriority,
353     bsc_OnLowResource,
354     bsc_OnProgress,
355     bsc_OnStopBinding,
356     bsc_GetBindInfo,
357     bsc_OnDataAvailable,
358     bsc_OnObjectAvailable
359 };
360
361 static bsc xmldoc_bsc = { { &bsc_vtbl } };
362
363 static HRESULT WINAPI xmldoc_put_URL(IXMLDocument *iface, BSTR p)
364 {
365     WCHAR url[INTERNET_MAX_URL_LENGTH];
366     IStream *stream;
367     IBindCtx *bctx;
368     IMoniker *moniker;
369     IPersistStreamInit *persist;
370     HRESULT hr;
371
372     TRACE("(%p, %s)\n", iface, debugstr_w(p));
373
374     if (!p)
375         return E_INVALIDARG;
376
377     if (!PathIsURLW(p))
378     {
379         WCHAR fullpath[MAX_PATH];
380         DWORD needed = sizeof(url) / sizeof(WCHAR);
381
382         if (!PathSearchAndQualifyW(p, fullpath, sizeof(fullpath) / sizeof(WCHAR)))
383         {
384             ERR("can't find path\n");
385             return E_FAIL;
386         }
387
388         if (FAILED(UrlCreateFromPathW(fullpath, url, &needed, 0)))
389         {
390             ERR("can't create url from path\n");
391             return E_FAIL;
392         }
393
394         p = url;
395     }
396
397     hr = CreateURLMoniker(NULL, p, &moniker);
398     if (FAILED(hr))
399         return hr;
400
401     CreateAsyncBindCtx(0, &xmldoc_bsc.IBindStatusCallback_iface, 0, &bctx);
402
403     hr = IMoniker_BindToStorage(moniker, bctx, NULL, &IID_IStream, (LPVOID *)&stream);
404     IBindCtx_Release(bctx);
405     IMoniker_Release(moniker);
406     if (FAILED(hr))
407         return hr;
408
409     hr = IXMLDocument_QueryInterface(iface, &IID_IPersistStreamInit, (LPVOID *)&persist);
410     if (FAILED(hr))
411     {
412         IStream_Release(stream);
413         return hr;
414     }
415
416     hr = IPersistStreamInit_Load(persist, stream);
417     IPersistStreamInit_Release(persist);
418     IStream_Release(stream);
419
420     return hr;
421 }
422
423 static HRESULT WINAPI xmldoc_get_mimeType(IXMLDocument *iface, BSTR *p)
424 {
425     FIXME("(%p, %p): stub\n", iface, p);
426     return E_NOTIMPL;
427 }
428
429 static HRESULT WINAPI xmldoc_get_readyState(IXMLDocument *iface, LONG *p)
430 {
431     FIXME("(%p, %p): stub\n", iface, p);
432     return E_NOTIMPL;
433 }
434
435 static HRESULT WINAPI xmldoc_get_charset(IXMLDocument *iface, BSTR *p)
436 {
437     FIXME("(%p, %p): stub\n", iface, p);
438     return E_NOTIMPL;
439 }
440
441 static HRESULT WINAPI xmldoc_put_charset(IXMLDocument *iface, BSTR p)
442 {
443     FIXME("(%p, %p): stub\n", iface, p);
444     return E_NOTIMPL;
445 }
446
447 static HRESULT WINAPI xmldoc_get_version(IXMLDocument *iface, BSTR *p)
448 {
449     xmldoc *This = impl_from_IXMLDocument(iface);
450
451     TRACE("(%p, %p)\n", This, p);
452
453     if (!p) return E_INVALIDARG;
454     *p = bstr_from_xmlChar(This->xmldoc->version);
455
456     return S_OK;
457 }
458
459 static HRESULT WINAPI xmldoc_get_doctype(IXMLDocument *iface, BSTR *p)
460 {
461     xmldoc *This = impl_from_IXMLDocument(iface);
462     xmlDtd *dtd;
463
464     TRACE("(%p, %p)\n", This, p);
465
466     if (!p) return E_INVALIDARG;
467
468     dtd = xmlGetIntSubset(This->xmldoc);
469     if (!dtd) return S_FALSE;
470
471     *p = bstr_from_xmlChar(dtd->name);
472     CharUpperBuffW(*p, SysStringLen(*p));
473
474     return S_OK;
475 }
476
477 static HRESULT WINAPI xmldoc_get_dtdURl(IXMLDocument *iface, BSTR *p)
478 {
479     FIXME("(%p, %p): stub\n", iface, p);
480     return E_NOTIMPL;
481 }
482
483 static xmlElementType type_msxml_to_libxml(LONG type)
484 {
485     switch (type)
486     {
487         case XMLELEMTYPE_ELEMENT:
488             return XML_ELEMENT_NODE;
489         case XMLELEMTYPE_TEXT:
490             return XML_TEXT_NODE;
491         case XMLELEMTYPE_COMMENT:
492             return XML_COMMENT_NODE;
493         case XMLELEMTYPE_DOCUMENT:
494             return XML_DOCUMENT_NODE;
495         case XMLELEMTYPE_DTD:
496             return XML_DTD_NODE;
497         case XMLELEMTYPE_PI:
498             return XML_PI_NODE;
499         default:
500             break;
501     }
502
503     return -1; /* FIXME: what is OTHER in msxml? */
504 }
505
506 static HRESULT WINAPI xmldoc_createElement(IXMLDocument *iface, VARIANT vType,
507                                            VARIANT var1, IXMLElement **ppElem)
508 {
509     xmlNodePtr node;
510     static const xmlChar empty[] = "\0";
511
512     TRACE("(%p)->(%s %s %p)\n", iface, debugstr_variant(&vType),
513         debugstr_variant(&var1), ppElem);
514
515     if (!ppElem)
516         return E_INVALIDARG;
517
518     *ppElem = NULL;
519
520     if (V_VT(&vType) != VT_I4)
521         return E_INVALIDARG;
522
523     if(type_msxml_to_libxml(V_I4(&vType)) == -1)
524         return E_NOTIMPL;
525
526     node = xmlNewNode(NULL, empty);
527     node->type = type_msxml_to_libxml(V_I4(&vType));
528
529     /* FIXME: create xmlNodePtr based on vType and var1 */
530     return XMLElement_create((IUnknown *)iface, node, (LPVOID *)ppElem, TRUE);
531 }
532
533 static const struct IXMLDocumentVtbl xmldoc_vtbl =
534 {
535     xmldoc_QueryInterface,
536     xmldoc_AddRef,
537     xmldoc_Release,
538     xmldoc_GetTypeInfoCount,
539     xmldoc_GetTypeInfo,
540     xmldoc_GetIDsOfNames,
541     xmldoc_Invoke,
542     xmldoc_get_root,
543     xmldoc_get_fileSize,
544     xmldoc_put_fileModifiedDate,
545     xmldoc_get_fileUpdatedDate,
546     xmldoc_get_URL,
547     xmldoc_put_URL,
548     xmldoc_get_mimeType,
549     xmldoc_get_readyState,
550     xmldoc_get_charset,
551     xmldoc_put_charset,
552     xmldoc_get_version,
553     xmldoc_get_doctype,
554     xmldoc_get_dtdURl,
555     xmldoc_createElement
556 };
557
558 /************************************************************************
559  * xmldoc implementation of IPersistStreamInit.
560  */
561 static HRESULT WINAPI xmldoc_IPersistStreamInit_QueryInterface(
562     IPersistStreamInit *iface, REFIID riid, LPVOID *ppvObj)
563 {
564     xmldoc *this = impl_from_IPersistStreamInit(iface);
565     return IXMLDocument_QueryInterface(&this->IXMLDocument_iface, riid, ppvObj);
566 }
567
568 static ULONG WINAPI xmldoc_IPersistStreamInit_AddRef(
569     IPersistStreamInit *iface)
570 {
571     xmldoc *this = impl_from_IPersistStreamInit(iface);
572     return IXMLDocument_AddRef(&this->IXMLDocument_iface);
573 }
574
575 static ULONG WINAPI xmldoc_IPersistStreamInit_Release(
576     IPersistStreamInit *iface)
577 {
578     xmldoc *this = impl_from_IPersistStreamInit(iface);
579     return IXMLDocument_Release(&this->IXMLDocument_iface);
580 }
581
582 static HRESULT WINAPI xmldoc_IPersistStreamInit_GetClassID(
583     IPersistStreamInit *iface, CLSID *classid)
584 {
585     xmldoc *this = impl_from_IPersistStreamInit(iface);
586     TRACE("(%p,%p)\n", this, classid);
587
588     if (!classid) return E_POINTER;
589
590     *classid = CLSID_XMLDocument;
591     return S_OK;
592 }
593
594 static HRESULT WINAPI xmldoc_IPersistStreamInit_IsDirty(
595     IPersistStreamInit *iface)
596 {
597     FIXME("(%p): stub!\n", iface);
598     return E_NOTIMPL;
599 }
600
601 static xmlDocPtr parse_xml(char *ptr, int len)
602 {
603 #ifdef HAVE_XMLREADMEMORY
604     return xmlReadMemory(ptr, len, NULL, NULL,
605                          XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOBLANKS);
606 #else
607     return xmlParseMemory(ptr, len);
608 #endif
609 }
610
611 static HRESULT WINAPI xmldoc_IPersistStreamInit_Load(
612     IPersistStreamInit *iface, LPSTREAM pStm)
613 {
614     xmldoc *This = impl_from_IPersistStreamInit(iface);
615     HRESULT hr;
616     HGLOBAL hglobal;
617     DWORD read, written, len;
618     BYTE buf[4096];
619     char *ptr;
620
621     TRACE("(%p, %p)\n", iface, pStm);
622
623     if (!pStm)
624         return E_INVALIDARG;
625
626     /* release previously allocated stream */
627     if (This->stream) IStream_Release(This->stream);
628     hr = CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
629     if (FAILED(hr))
630         return hr;
631
632     do
633     {
634         IStream_Read(pStm, buf, sizeof(buf), &read);
635         hr = IStream_Write(This->stream, buf, read, &written);
636     } while(SUCCEEDED(hr) && written != 0 && read != 0);
637
638     if (FAILED(hr))
639     {
640         ERR("Failed to copy stream\n");
641         return hr;
642     }
643
644     hr = GetHGlobalFromStream(This->stream, &hglobal);
645     if (FAILED(hr))
646         return hr;
647
648     len = GlobalSize(hglobal);
649     ptr = GlobalLock(hglobal);
650     if (len != 0)
651     {
652         xmlFreeDoc(This->xmldoc);
653         This->xmldoc = parse_xml(ptr, len);
654     }
655     GlobalUnlock(hglobal);
656
657     if (!This->xmldoc)
658     {
659         ERR("Failed to parse xml\n");
660         return E_FAIL;
661     }
662
663     return S_OK;
664 }
665
666 static HRESULT WINAPI xmldoc_IPersistStreamInit_Save(
667     IPersistStreamInit *iface, LPSTREAM pStm, BOOL fClearDirty)
668 {
669     FIXME("(%p, %p, %d): stub!\n", iface, pStm, fClearDirty);
670     return E_NOTIMPL;
671 }
672
673 static HRESULT WINAPI xmldoc_IPersistStreamInit_GetSizeMax(
674     IPersistStreamInit *iface, ULARGE_INTEGER *pcbSize)
675 {
676     xmldoc *This = impl_from_IPersistStreamInit(iface);
677     TRACE("(%p, %p)\n", This, pcbSize);
678     return E_NOTIMPL;
679 }
680
681 static HRESULT WINAPI xmldoc_IPersistStreamInit_InitNew(
682     IPersistStreamInit *iface)
683 {
684     xmldoc *This = impl_from_IPersistStreamInit(iface);
685     TRACE("(%p)\n", This);
686     return S_OK;
687 }
688
689 static const IPersistStreamInitVtbl xmldoc_IPersistStreamInit_VTable =
690 {
691   xmldoc_IPersistStreamInit_QueryInterface,
692   xmldoc_IPersistStreamInit_AddRef,
693   xmldoc_IPersistStreamInit_Release,
694   xmldoc_IPersistStreamInit_GetClassID,
695   xmldoc_IPersistStreamInit_IsDirty,
696   xmldoc_IPersistStreamInit_Load,
697   xmldoc_IPersistStreamInit_Save,
698   xmldoc_IPersistStreamInit_GetSizeMax,
699   xmldoc_IPersistStreamInit_InitNew
700 };
701
702 HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
703 {
704     xmldoc *doc;
705
706     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
707
708     doc = heap_alloc(sizeof (*doc));
709     if(!doc)
710         return E_OUTOFMEMORY;
711
712     doc->IXMLDocument_iface.lpVtbl = &xmldoc_vtbl;
713     doc->IPersistStreamInit_iface.lpVtbl = &xmldoc_IPersistStreamInit_VTable;
714     doc->ref = 1;
715     doc->error = S_OK;
716     doc->xmldoc = NULL;
717     doc->stream = NULL;
718
719     *ppObj = &doc->IXMLDocument_iface;
720
721     TRACE("returning iface %p\n", *ppObj);
722     return S_OK;
723 }
724
725 #else
726
727 HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
728 {
729     MESSAGE("This program tried to use an XMLDocument object, but\n"
730             "libxml2 support was not present at compile time.\n");
731     return E_NOTIMPL;
732 }
733
734 #endif