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