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