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