|  | /* | 
|  | * XML Element implementation | 
|  | * | 
|  | * Copyright 2007 James Hawkins | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #define COBJMACROS | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "ole2.h" | 
|  | #include "msxml2.h" | 
|  | #include "ocidl.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | #include "msxml_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(msxml); | 
|  |  | 
|  | #ifdef HAVE_LIBXML2 | 
|  |  | 
|  | static HRESULT XMLElementCollection_create( IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj ); | 
|  |  | 
|  | /********************************************************************** | 
|  | * IXMLElement | 
|  | */ | 
|  | typedef struct _xmlelem | 
|  | { | 
|  | const IXMLElementVtbl *lpVtbl; | 
|  | LONG ref; | 
|  | xmlNodePtr node; | 
|  | } xmlelem; | 
|  |  | 
|  | static inline xmlelem *impl_from_IXMLElement(IXMLElement *iface) | 
|  | { | 
|  | return (xmlelem *)((char*)iface - FIELD_OFFSET(xmlelem, lpVtbl)); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_QueryInterface(IXMLElement *iface, REFIID riid, void** ppvObject) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  |  | 
|  | TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); | 
|  |  | 
|  | if (IsEqualGUID(riid, &IID_IUnknown) || | 
|  | IsEqualGUID(riid, &IID_IXMLElement)) | 
|  | { | 
|  | *ppvObject = iface; | 
|  | } | 
|  | else | 
|  | { | 
|  | FIXME("interface %s not implemented\n", debugstr_guid(riid)); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | IXMLElement_AddRef(iface); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_AddRef(IXMLElement *iface) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | TRACE("%p\n", This); | 
|  | return InterlockedIncrement(&This->ref); | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_Release(IXMLElement *iface) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | LONG ref; | 
|  |  | 
|  | TRACE("%p\n", This); | 
|  |  | 
|  | ref = InterlockedDecrement(&This->ref); | 
|  | if (ref == 0) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  | } | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_GetTypeInfoCount(IXMLElement *iface, UINT* pctinfo) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", This, pctinfo); | 
|  |  | 
|  | *pctinfo = 1; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_GetTypeInfo(IXMLElement *iface, UINT iTInfo, | 
|  | LCID lcid, ITypeInfo** ppTInfo) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); | 
|  |  | 
|  | hr = get_typeinfo(IXMLElement_tid, ppTInfo); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_GetIDsOfNames(IXMLElement *iface, REFIID riid, | 
|  | LPOLESTR* rgszNames, UINT cNames, | 
|  | LCID lcid, DISPID* rgDispId) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | ITypeInfo *typeinfo; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, | 
|  | lcid, rgDispId); | 
|  |  | 
|  | if(!rgszNames || cNames == 0 || !rgDispId) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | hr = get_typeinfo(IXMLElement_tid, &typeinfo); | 
|  | if(SUCCEEDED(hr)) | 
|  | { | 
|  | hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); | 
|  | ITypeInfo_Release(typeinfo); | 
|  | } | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_Invoke(IXMLElement *iface, DISPID dispIdMember, | 
|  | REFIID riid, LCID lcid, WORD wFlags, | 
|  | DISPPARAMS* pDispParams, VARIANT* pVarResult, | 
|  | EXCEPINFO* pExcepInfo, UINT* puArgErr) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | ITypeInfo *typeinfo; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), | 
|  | lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); | 
|  |  | 
|  | hr = get_typeinfo(IXMLElement_tid, &typeinfo); | 
|  | if(SUCCEEDED(hr)) | 
|  | { | 
|  | hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams, | 
|  | pVarResult, pExcepInfo, puArgErr); | 
|  | ITypeInfo_Release(typeinfo); | 
|  | } | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static inline BSTR str_dup_upper(BSTR str) | 
|  | { | 
|  | INT len = (lstrlenW(str) + 1) * sizeof(WCHAR); | 
|  | BSTR p = SysAllocStringLen(NULL, len); | 
|  | if (p) | 
|  | { | 
|  | memcpy(p, str, len); | 
|  | CharUpperW(p); | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_get_tagName(IXMLElement *iface, BSTR *p) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | BSTR temp; | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | temp = bstr_from_xmlChar(This->node->name); | 
|  | *p = str_dup_upper(temp); | 
|  | SysFreeString(temp); | 
|  |  | 
|  | TRACE("returning %s\n", debugstr_w(*p)); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_put_tagName(IXMLElement *iface, BSTR p) | 
|  | { | 
|  | FIXME("(%p, %p): stub\n", iface, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_get_parent(IXMLElement *iface, IXMLElement **parent) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, parent); | 
|  |  | 
|  | if (!parent) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *parent = NULL; | 
|  |  | 
|  | if (!This->node->parent) | 
|  | return S_FALSE; | 
|  |  | 
|  | return XMLElement_create((IUnknown *)iface, This->node->parent, (LPVOID *)parent); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_setAttribute(IXMLElement *iface, BSTR strPropertyName, | 
|  | VARIANT PropertyValue) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlChar *name, *value; | 
|  | xmlAttrPtr attr; | 
|  |  | 
|  | TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName)); | 
|  |  | 
|  | if (!strPropertyName || V_VT(&PropertyValue) != VT_BSTR) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | name = xmlChar_from_wchar(strPropertyName); | 
|  | value = xmlChar_from_wchar(V_BSTR(&PropertyValue)); | 
|  | attr = xmlSetProp(This->node, name, value); | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, name); | 
|  | HeapFree(GetProcessHeap(), 0, value); | 
|  | return (attr) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_getAttribute(IXMLElement *iface, BSTR strPropertyName, | 
|  | VARIANT *PropertyValue) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlChar *val = NULL, *name; | 
|  | xmlAttrPtr ptr; | 
|  |  | 
|  | TRACE("(%p, %s, %p)\n", iface, debugstr_w(strPropertyName), PropertyValue); | 
|  |  | 
|  | if (!PropertyValue) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | VariantInit(PropertyValue); | 
|  | V_BSTR(PropertyValue) = NULL; | 
|  |  | 
|  | if (!strPropertyName) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | name = xmlChar_from_wchar(strPropertyName); | 
|  | ptr = This->node->properties; | 
|  | while (ptr) | 
|  | { | 
|  | if (!lstrcmpiA((LPSTR)name, (LPCSTR)ptr->name)) | 
|  | { | 
|  | val = xmlNodeListGetString(ptr->doc, ptr->children, 1); | 
|  | break; | 
|  | } | 
|  |  | 
|  | ptr = ptr->next; | 
|  | } | 
|  |  | 
|  | if (val) | 
|  | { | 
|  | V_VT(PropertyValue) = VT_BSTR; | 
|  | V_BSTR(PropertyValue) = bstr_from_xmlChar(val); | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, name); | 
|  | xmlFree(val); | 
|  | TRACE("returning %s\n", debugstr_w(V_BSTR(PropertyValue))); | 
|  | return (val) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_removeAttribute(IXMLElement *iface, BSTR strPropertyName) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlChar *name; | 
|  | xmlAttrPtr attr; | 
|  | int res; | 
|  | HRESULT hr = S_FALSE; | 
|  |  | 
|  | TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName)); | 
|  |  | 
|  | if (!strPropertyName) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | name = xmlChar_from_wchar(strPropertyName); | 
|  | attr = xmlHasProp(This->node, name); | 
|  | if (!attr) | 
|  | goto done; | 
|  |  | 
|  | res = xmlRemoveProp(attr); | 
|  |  | 
|  | if (res == 0) | 
|  | hr = S_OK; | 
|  |  | 
|  | done: | 
|  | HeapFree(GetProcessHeap(), 0, name); | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_get_children(IXMLElement *iface, IXMLElementCollection **p) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | return XMLElementCollection_create((IUnknown *)iface, This->node->children, (LPVOID *)p); | 
|  | } | 
|  |  | 
|  | static LONG type_libxml_to_msxml(xmlElementType type) | 
|  | { | 
|  | switch (type) | 
|  | { | 
|  | case XML_ELEMENT_NODE: | 
|  | return XMLELEMTYPE_ELEMENT; | 
|  | case XML_TEXT_NODE: | 
|  | return XMLELEMTYPE_TEXT; | 
|  | case XML_COMMENT_NODE: | 
|  | return XMLELEMTYPE_COMMENT; | 
|  | case XML_DOCUMENT_NODE: | 
|  | return XMLELEMTYPE_DOCUMENT; | 
|  | case XML_DTD_NODE: | 
|  | return XMLELEMTYPE_DTD; | 
|  | case XML_PI_NODE: | 
|  | return XMLELEMTYPE_PI; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return XMLELEMTYPE_OTHER; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_get_type(IXMLElement *iface, LONG *p) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  |  | 
|  | TRACE("(%p, %p)\n", This, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *p = type_libxml_to_msxml(This->node->type); | 
|  | TRACE("returning %d\n", *p); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_get_text(IXMLElement *iface, BSTR *p) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlChar *content; | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | content = xmlNodeGetContent(This->node); | 
|  | *p = bstr_from_xmlChar(content); | 
|  | TRACE("returning %s\n", debugstr_w(*p)); | 
|  |  | 
|  | xmlFree(content); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_put_text(IXMLElement *iface, BSTR p) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlChar *content; | 
|  |  | 
|  | TRACE("(%p, %s)\n", iface, debugstr_w(p)); | 
|  |  | 
|  | /* FIXME: test which types can be used */ | 
|  | if (This->node->type == XML_ELEMENT_NODE) | 
|  | return E_NOTIMPL; | 
|  |  | 
|  | content = xmlChar_from_wchar(p); | 
|  | xmlNodeSetContent(This->node, content); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, content); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_addChild(IXMLElement *iface, IXMLElement *pChildElem, | 
|  | LONG lIndex, LONG lreserved) | 
|  | { | 
|  | xmlelem *This = impl_from_IXMLElement(iface); | 
|  | xmlelem *childElem = impl_from_IXMLElement(pChildElem); | 
|  | xmlNodePtr child; | 
|  |  | 
|  | TRACE("(%p, %p, %d, %d)\n", iface, pChildElem, lIndex, lreserved); | 
|  |  | 
|  | if (lIndex == 0) | 
|  | child = xmlAddChild(This->node, childElem->node); | 
|  | else | 
|  | child = xmlAddNextSibling(This->node, childElem->node->last); | 
|  |  | 
|  | return (child) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_removeChild(IXMLElement *iface, IXMLElement *pChildElem) | 
|  | { | 
|  | FIXME("(%p, %p): stub\n", iface, pChildElem); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static const struct IXMLElementVtbl xmlelem_vtbl = | 
|  | { | 
|  | xmlelem_QueryInterface, | 
|  | xmlelem_AddRef, | 
|  | xmlelem_Release, | 
|  | xmlelem_GetTypeInfoCount, | 
|  | xmlelem_GetTypeInfo, | 
|  | xmlelem_GetIDsOfNames, | 
|  | xmlelem_Invoke, | 
|  | xmlelem_get_tagName, | 
|  | xmlelem_put_tagName, | 
|  | xmlelem_get_parent, | 
|  | xmlelem_setAttribute, | 
|  | xmlelem_getAttribute, | 
|  | xmlelem_removeAttribute, | 
|  | xmlelem_get_children, | 
|  | xmlelem_get_type, | 
|  | xmlelem_get_text, | 
|  | xmlelem_put_text, | 
|  | xmlelem_addChild, | 
|  | xmlelem_removeChild | 
|  | }; | 
|  |  | 
|  | HRESULT XMLElement_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj) | 
|  | { | 
|  | xmlelem *elem; | 
|  |  | 
|  | TRACE("(%p,%p)\n", pUnkOuter, ppObj); | 
|  |  | 
|  | if (!ppObj) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *ppObj = NULL; | 
|  |  | 
|  | elem = HeapAlloc(GetProcessHeap(), 0, sizeof (*elem)); | 
|  | if(!elem) | 
|  | return E_OUTOFMEMORY; | 
|  |  | 
|  | elem->lpVtbl = &xmlelem_vtbl; | 
|  | elem->ref = 1; | 
|  | elem->node = node; | 
|  |  | 
|  | *ppObj = &elem->lpVtbl; | 
|  |  | 
|  | TRACE("returning iface %p\n", *ppObj); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | * IXMLElementCollection | 
|  | */ | 
|  | typedef struct _xmlelem_collection | 
|  | { | 
|  | const IXMLElementCollectionVtbl *lpVtbl; | 
|  | const IEnumVARIANTVtbl          *lpvtblIEnumVARIANT; | 
|  | LONG ref; | 
|  | LONG length; | 
|  | xmlNodePtr node; | 
|  |  | 
|  | /* IEnumVARIANT members */ | 
|  | xmlNodePtr current; | 
|  | } xmlelem_collection; | 
|  |  | 
|  | static inline xmlelem_collection *impl_from_IXMLElementCollection(IXMLElementCollection *iface) | 
|  | { | 
|  | return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpVtbl)); | 
|  | } | 
|  |  | 
|  | static inline xmlelem_collection *impl_from_IEnumVARIANT(IEnumVARIANT *iface) | 
|  | { | 
|  | return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpvtblIEnumVARIANT)); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_QueryInterface(IXMLElementCollection *iface, REFIID riid, void** ppvObject) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  |  | 
|  | TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); | 
|  |  | 
|  | if (IsEqualGUID(riid, &IID_IUnknown) || | 
|  | IsEqualGUID(riid, &IID_IXMLElementCollection)) | 
|  | { | 
|  | *ppvObject = iface; | 
|  | } | 
|  | else if (IsEqualGUID(riid, &IID_IEnumVARIANT)) | 
|  | { | 
|  | *ppvObject = &(This->lpvtblIEnumVARIANT); | 
|  | } | 
|  | else | 
|  | { | 
|  | FIXME("interface %s not implemented\n", debugstr_guid(riid)); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | IXMLElementCollection_AddRef(iface); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_collection_AddRef(IXMLElementCollection *iface) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  | TRACE("%p\n", This); | 
|  | return InterlockedIncrement(&This->ref); | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_collection_Release(IXMLElementCollection *iface) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  | LONG ref; | 
|  |  | 
|  | TRACE("%p\n", This); | 
|  |  | 
|  | ref = InterlockedDecrement(&This->ref); | 
|  | if (ref == 0) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  | } | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_GetTypeInfoCount(IXMLElementCollection *iface, UINT* pctinfo) | 
|  | { | 
|  | FIXME("\n"); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_GetTypeInfo(IXMLElementCollection *iface, UINT iTInfo, | 
|  | LCID lcid, ITypeInfo** ppTInfo) | 
|  | { | 
|  | FIXME("\n"); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_GetIDsOfNames(IXMLElementCollection *iface, REFIID riid, | 
|  | LPOLESTR* rgszNames, UINT cNames, | 
|  | LCID lcid, DISPID* rgDispId) | 
|  | { | 
|  | FIXME("\n"); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_Invoke(IXMLElementCollection *iface, DISPID dispIdMember, | 
|  | REFIID riid, LCID lcid, WORD wFlags, | 
|  | DISPPARAMS* pDispParams, VARIANT* pVarResult, | 
|  | EXCEPINFO* pExcepInfo, UINT* puArgErr) | 
|  | { | 
|  | FIXME("\n"); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_put_length(IXMLElementCollection *iface, LONG v) | 
|  | { | 
|  | TRACE("(%p, %d)\n", iface, v); | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_get_length(IXMLElementCollection *iface, LONG *p) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, p); | 
|  |  | 
|  | if (!p) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *p = This->length; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_get__newEnum(IXMLElementCollection *iface, IUnknown **ppUnk) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, ppUnk); | 
|  |  | 
|  | if (!ppUnk) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *ppUnk = (IUnknown *)This; | 
|  | IUnknown_AddRef(*ppUnk); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_item(IXMLElementCollection *iface, VARIANT var1, | 
|  | VARIANT var2, IDispatch **ppDisp) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IXMLElementCollection(iface); | 
|  | xmlNodePtr ptr = This->node; | 
|  | int index, i; | 
|  |  | 
|  | TRACE("(%p, %p)\n", iface, ppDisp); | 
|  |  | 
|  | if (!ppDisp) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *ppDisp = NULL; | 
|  |  | 
|  | index = V_I4(&var1); | 
|  | if (index < 0) | 
|  | return E_INVALIDARG; | 
|  | if (index >= This->length) | 
|  | return E_FAIL; | 
|  |  | 
|  | for (i = 0; i < index; i++) | 
|  | ptr = ptr->next; | 
|  |  | 
|  | return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)ppDisp); | 
|  | } | 
|  |  | 
|  | static const struct IXMLElementCollectionVtbl xmlelem_collection_vtbl = | 
|  | { | 
|  | xmlelem_collection_QueryInterface, | 
|  | xmlelem_collection_AddRef, | 
|  | xmlelem_collection_Release, | 
|  | xmlelem_collection_GetTypeInfoCount, | 
|  | xmlelem_collection_GetTypeInfo, | 
|  | xmlelem_collection_GetIDsOfNames, | 
|  | xmlelem_collection_Invoke, | 
|  | xmlelem_collection_put_length, | 
|  | xmlelem_collection_get_length, | 
|  | xmlelem_collection_get__newEnum, | 
|  | xmlelem_collection_item | 
|  | }; | 
|  |  | 
|  | /************************************************************************ | 
|  | * xmlelem_collection implementation of IEnumVARIANT. | 
|  | */ | 
|  | static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_QueryInterface( | 
|  | IEnumVARIANT *iface, REFIID riid, LPVOID *ppvObj) | 
|  | { | 
|  | xmlelem_collection *this = impl_from_IEnumVARIANT(iface); | 
|  | return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj); | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_collection_IEnumVARIANT_AddRef( | 
|  | IEnumVARIANT *iface) | 
|  | { | 
|  | xmlelem_collection *this = impl_from_IEnumVARIANT(iface); | 
|  | return IXMLDocument_AddRef((IXMLDocument *)this); | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI xmlelem_collection_IEnumVARIANT_Release( | 
|  | IEnumVARIANT *iface) | 
|  | { | 
|  | xmlelem_collection *this = impl_from_IEnumVARIANT(iface); | 
|  | return IXMLDocument_Release((IXMLDocument *)this); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Next( | 
|  | IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IEnumVARIANT(iface); | 
|  | xmlNodePtr ptr = This->current; | 
|  |  | 
|  | TRACE("(%p, %d, %p, %p)\n", iface, celt, rgVar, pCeltFetched); | 
|  |  | 
|  | if (!rgVar) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | /* FIXME: handle celt */ | 
|  | if (pCeltFetched) | 
|  | *pCeltFetched = 1; | 
|  |  | 
|  | This->current = This->current->next; | 
|  |  | 
|  | V_VT(rgVar) = VT_DISPATCH; | 
|  | return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)&V_DISPATCH(rgVar)); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Skip( | 
|  | IEnumVARIANT *iface, ULONG celt) | 
|  | { | 
|  | FIXME("(%p, %d): stub\n", iface, celt); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Reset( | 
|  | IEnumVARIANT *iface) | 
|  | { | 
|  | xmlelem_collection *This = impl_from_IEnumVARIANT(iface); | 
|  | This->current = This->node; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Clone( | 
|  | IEnumVARIANT *iface, IEnumVARIANT **ppEnum) | 
|  | { | 
|  | FIXME("(%p, %p): stub\n", iface, ppEnum); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static const struct IEnumVARIANTVtbl xmlelem_collection_IEnumVARIANTvtbl = | 
|  | { | 
|  | xmlelem_collection_IEnumVARIANT_QueryInterface, | 
|  | xmlelem_collection_IEnumVARIANT_AddRef, | 
|  | xmlelem_collection_IEnumVARIANT_Release, | 
|  | xmlelem_collection_IEnumVARIANT_Next, | 
|  | xmlelem_collection_IEnumVARIANT_Skip, | 
|  | xmlelem_collection_IEnumVARIANT_Reset, | 
|  | xmlelem_collection_IEnumVARIANT_Clone | 
|  | }; | 
|  |  | 
|  | static HRESULT XMLElementCollection_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj) | 
|  | { | 
|  | xmlelem_collection *collection; | 
|  | xmlNodePtr ptr; | 
|  |  | 
|  | TRACE("(%p,%p)\n", pUnkOuter, ppObj); | 
|  |  | 
|  | *ppObj = NULL; | 
|  |  | 
|  | if (!node) | 
|  | return S_FALSE; | 
|  |  | 
|  | collection = HeapAlloc(GetProcessHeap(), 0, sizeof (*collection)); | 
|  | if(!collection) | 
|  | return E_OUTOFMEMORY; | 
|  |  | 
|  | collection->lpVtbl = &xmlelem_collection_vtbl; | 
|  | collection->lpvtblIEnumVARIANT = &xmlelem_collection_IEnumVARIANTvtbl; | 
|  | collection->ref = 1; | 
|  | collection->length = 0; | 
|  | collection->node = node; | 
|  | collection->current = node; | 
|  |  | 
|  | ptr = node; | 
|  | while (ptr) | 
|  | { | 
|  | collection->length++; | 
|  | ptr = ptr->next; | 
|  | } | 
|  |  | 
|  | *ppObj = &collection->lpVtbl; | 
|  |  | 
|  | TRACE("returning iface %p\n", *ppObj); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | #endif |