msxml3: Implement IXMLElement::removeChild() with some tests.
[wine.git] / dlls / msxml3 / xmlelem.c
blob8fe24ebfb180e77941fce89cc721de0fdde16408
1 /*
2 * XML Element implementation
4 * Copyright 2007 James Hawkins
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.
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.
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
21 #define COBJMACROS
23 #include "config.h"
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 "ocidl.h"
33 #include "wine/debug.h"
35 #include "msxml_private.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
39 #ifdef HAVE_LIBXML2
41 static HRESULT XMLElementCollection_create( IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj );
43 /**********************************************************************
44 * IXMLElement
46 typedef struct _xmlelem
48 const IXMLElementVtbl *lpVtbl;
49 LONG ref;
50 xmlNodePtr node;
51 } xmlelem;
53 static inline xmlelem *impl_from_IXMLElement(IXMLElement *iface)
55 return (xmlelem *)((char*)iface - FIELD_OFFSET(xmlelem, lpVtbl));
58 static HRESULT WINAPI xmlelem_QueryInterface(IXMLElement *iface, REFIID riid, void** ppvObject)
60 xmlelem *This = impl_from_IXMLElement(iface);
62 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
64 if (IsEqualGUID(riid, &IID_IUnknown) ||
65 IsEqualGUID(riid, &IID_IXMLElement))
67 *ppvObject = iface;
69 else
71 FIXME("interface %s not implemented\n", debugstr_guid(riid));
72 return E_NOINTERFACE;
75 IXMLElement_AddRef(iface);
77 return S_OK;
80 static ULONG WINAPI xmlelem_AddRef(IXMLElement *iface)
82 xmlelem *This = impl_from_IXMLElement(iface);
83 TRACE("%p\n", This);
84 return InterlockedIncrement(&This->ref);
87 static ULONG WINAPI xmlelem_Release(IXMLElement *iface)
89 xmlelem *This = impl_from_IXMLElement(iface);
90 LONG ref;
92 TRACE("%p\n", This);
94 ref = InterlockedDecrement(&This->ref);
95 if (ref == 0)
97 HeapFree(GetProcessHeap(), 0, This);
100 return ref;
103 static HRESULT WINAPI xmlelem_GetTypeInfoCount(IXMLElement *iface, UINT* pctinfo)
105 xmlelem *This = impl_from_IXMLElement(iface);
107 TRACE("(%p)->(%p)\n", This, pctinfo);
109 *pctinfo = 1;
111 return S_OK;
114 static HRESULT WINAPI xmlelem_GetTypeInfo(IXMLElement *iface, UINT iTInfo,
115 LCID lcid, ITypeInfo** ppTInfo)
117 xmlelem *This = impl_from_IXMLElement(iface);
118 HRESULT hr;
120 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
122 hr = get_typeinfo(IXMLElement_tid, ppTInfo);
124 return hr;
127 static HRESULT WINAPI xmlelem_GetIDsOfNames(IXMLElement *iface, REFIID riid,
128 LPOLESTR* rgszNames, UINT cNames,
129 LCID lcid, DISPID* rgDispId)
131 xmlelem *This = impl_from_IXMLElement(iface);
132 ITypeInfo *typeinfo;
133 HRESULT hr;
135 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
136 lcid, rgDispId);
138 if(!rgszNames || cNames == 0 || !rgDispId)
139 return E_INVALIDARG;
141 hr = get_typeinfo(IXMLElement_tid, &typeinfo);
142 if(SUCCEEDED(hr))
144 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
145 ITypeInfo_Release(typeinfo);
148 return hr;
151 static HRESULT WINAPI xmlelem_Invoke(IXMLElement *iface, DISPID dispIdMember,
152 REFIID riid, LCID lcid, WORD wFlags,
153 DISPPARAMS* pDispParams, VARIANT* pVarResult,
154 EXCEPINFO* pExcepInfo, UINT* puArgErr)
156 xmlelem *This = impl_from_IXMLElement(iface);
157 ITypeInfo *typeinfo;
158 HRESULT hr;
160 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
161 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
163 hr = get_typeinfo(IXMLElement_tid, &typeinfo);
164 if(SUCCEEDED(hr))
166 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
167 pVarResult, pExcepInfo, puArgErr);
168 ITypeInfo_Release(typeinfo);
171 return hr;
174 static HRESULT WINAPI xmlelem_get_tagName(IXMLElement *iface, BSTR *p)
176 xmlelem *This = impl_from_IXMLElement(iface);
178 TRACE("(%p, %p)\n", iface, p);
180 if (!p)
181 return E_INVALIDARG;
183 *p = bstr_from_xmlChar(This->node->name);
184 CharUpperBuffW(*p, SysStringLen(*p));
186 TRACE("returning %s\n", debugstr_w(*p));
188 return S_OK;
191 static HRESULT WINAPI xmlelem_put_tagName(IXMLElement *iface, BSTR p)
193 FIXME("(%p, %p): stub\n", iface, p);
195 if (!p)
196 return E_INVALIDARG;
198 return E_NOTIMPL;
201 static HRESULT WINAPI xmlelem_get_parent(IXMLElement *iface, IXMLElement **parent)
203 xmlelem *This = impl_from_IXMLElement(iface);
205 TRACE("(%p, %p)\n", iface, parent);
207 if (!parent)
208 return E_INVALIDARG;
210 *parent = NULL;
212 if (!This->node->parent)
213 return S_FALSE;
215 return XMLElement_create((IUnknown *)iface, This->node->parent, (LPVOID *)parent);
218 static HRESULT WINAPI xmlelem_setAttribute(IXMLElement *iface, BSTR strPropertyName,
219 VARIANT PropertyValue)
221 xmlelem *This = impl_from_IXMLElement(iface);
222 xmlChar *name, *value;
223 xmlAttrPtr attr;
225 TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
227 if (!strPropertyName || V_VT(&PropertyValue) != VT_BSTR)
228 return E_INVALIDARG;
230 name = xmlChar_from_wchar(strPropertyName);
231 value = xmlChar_from_wchar(V_BSTR(&PropertyValue));
232 attr = xmlSetProp(This->node, name, value);
234 HeapFree(GetProcessHeap(), 0, name);
235 HeapFree(GetProcessHeap(), 0, value);
236 return (attr) ? S_OK : S_FALSE;
239 static HRESULT WINAPI xmlelem_getAttribute(IXMLElement *iface, BSTR strPropertyName,
240 VARIANT *PropertyValue)
242 xmlelem *This = impl_from_IXMLElement(iface);
243 xmlChar *val = NULL, *name;
244 xmlAttrPtr ptr;
246 TRACE("(%p, %s, %p)\n", iface, debugstr_w(strPropertyName), PropertyValue);
248 if (!PropertyValue)
249 return E_INVALIDARG;
251 VariantInit(PropertyValue);
252 V_BSTR(PropertyValue) = NULL;
254 if (!strPropertyName)
255 return E_INVALIDARG;
257 name = xmlChar_from_wchar(strPropertyName);
258 ptr = This->node->properties;
259 while (ptr)
261 if (!lstrcmpiA((LPSTR)name, (LPCSTR)ptr->name))
263 val = xmlNodeListGetString(ptr->doc, ptr->children, 1);
264 break;
267 ptr = ptr->next;
270 if (val)
272 V_VT(PropertyValue) = VT_BSTR;
273 V_BSTR(PropertyValue) = bstr_from_xmlChar(val);
276 HeapFree(GetProcessHeap(), 0, name);
277 xmlFree(val);
278 TRACE("returning %s\n", debugstr_w(V_BSTR(PropertyValue)));
279 return (val) ? S_OK : S_FALSE;
282 static HRESULT WINAPI xmlelem_removeAttribute(IXMLElement *iface, BSTR strPropertyName)
284 xmlelem *This = impl_from_IXMLElement(iface);
285 xmlChar *name;
286 xmlAttrPtr attr;
287 int res;
288 HRESULT hr = S_FALSE;
290 TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
292 if (!strPropertyName)
293 return E_INVALIDARG;
295 name = xmlChar_from_wchar(strPropertyName);
296 attr = xmlHasProp(This->node, name);
297 if (!attr)
298 goto done;
300 res = xmlRemoveProp(attr);
302 if (res == 0)
303 hr = S_OK;
305 done:
306 HeapFree(GetProcessHeap(), 0, name);
307 return hr;
310 static HRESULT WINAPI xmlelem_get_children(IXMLElement *iface, IXMLElementCollection **p)
312 xmlelem *This = impl_from_IXMLElement(iface);
314 TRACE("(%p, %p)\n", iface, p);
316 if (!p)
317 return E_INVALIDARG;
319 return XMLElementCollection_create((IUnknown *)iface, This->node->children, (LPVOID *)p);
322 static LONG type_libxml_to_msxml(xmlElementType type)
324 switch (type)
326 case XML_ELEMENT_NODE:
327 return XMLELEMTYPE_ELEMENT;
328 case XML_TEXT_NODE:
329 return XMLELEMTYPE_TEXT;
330 case XML_COMMENT_NODE:
331 return XMLELEMTYPE_COMMENT;
332 case XML_DOCUMENT_NODE:
333 return XMLELEMTYPE_DOCUMENT;
334 case XML_DTD_NODE:
335 return XMLELEMTYPE_DTD;
336 case XML_PI_NODE:
337 return XMLELEMTYPE_PI;
338 default:
339 break;
342 return XMLELEMTYPE_OTHER;
345 static HRESULT WINAPI xmlelem_get_type(IXMLElement *iface, LONG *p)
347 xmlelem *This = impl_from_IXMLElement(iface);
349 TRACE("(%p, %p)\n", This, p);
351 if (!p)
352 return E_INVALIDARG;
354 *p = type_libxml_to_msxml(This->node->type);
355 TRACE("returning %d\n", *p);
356 return S_OK;
359 static HRESULT WINAPI xmlelem_get_text(IXMLElement *iface, BSTR *p)
361 xmlelem *This = impl_from_IXMLElement(iface);
362 xmlChar *content;
364 TRACE("(%p, %p)\n", iface, p);
366 if (!p)
367 return E_INVALIDARG;
369 content = xmlNodeGetContent(This->node);
370 *p = bstr_from_xmlChar(content);
371 TRACE("returning %s\n", debugstr_w(*p));
373 xmlFree(content);
374 return S_OK;
377 static HRESULT WINAPI xmlelem_put_text(IXMLElement *iface, BSTR p)
379 xmlelem *This = impl_from_IXMLElement(iface);
380 xmlChar *content;
382 TRACE("(%p, %s)\n", iface, debugstr_w(p));
384 /* FIXME: test which types can be used */
385 if (This->node->type == XML_ELEMENT_NODE)
386 return E_NOTIMPL;
388 content = xmlChar_from_wchar(p);
389 xmlNodeSetContent(This->node, content);
391 HeapFree( GetProcessHeap(), 0, content);
393 return S_OK;
396 static HRESULT WINAPI xmlelem_addChild(IXMLElement *iface, IXMLElement *pChildElem,
397 LONG lIndex, LONG lreserved)
399 xmlelem *This = impl_from_IXMLElement(iface);
400 xmlelem *childElem = impl_from_IXMLElement(pChildElem);
401 xmlNodePtr child;
403 TRACE("(%p, %p, %d, %d)\n", iface, pChildElem, lIndex, lreserved);
405 if (lIndex == 0)
406 child = xmlAddChild(This->node, childElem->node);
407 else
408 child = xmlAddNextSibling(This->node, childElem->node->last);
410 return (child) ? S_OK : S_FALSE;
413 static HRESULT WINAPI xmlelem_removeChild(IXMLElement *iface, IXMLElement *pChildElem)
415 xmlelem *This = impl_from_IXMLElement(iface);
416 xmlelem *childElem = impl_from_IXMLElement(pChildElem);
418 TRACE("(%p, %p)\n", This, childElem);
420 if (!pChildElem)
421 return E_INVALIDARG;
423 /* only supported for This is childElem parent case */
424 if (This->node != childElem->node->parent)
425 return E_INVALIDARG;
427 xmlUnlinkNode(childElem->node);
429 return S_OK;
432 static const struct IXMLElementVtbl xmlelem_vtbl =
434 xmlelem_QueryInterface,
435 xmlelem_AddRef,
436 xmlelem_Release,
437 xmlelem_GetTypeInfoCount,
438 xmlelem_GetTypeInfo,
439 xmlelem_GetIDsOfNames,
440 xmlelem_Invoke,
441 xmlelem_get_tagName,
442 xmlelem_put_tagName,
443 xmlelem_get_parent,
444 xmlelem_setAttribute,
445 xmlelem_getAttribute,
446 xmlelem_removeAttribute,
447 xmlelem_get_children,
448 xmlelem_get_type,
449 xmlelem_get_text,
450 xmlelem_put_text,
451 xmlelem_addChild,
452 xmlelem_removeChild
455 HRESULT XMLElement_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
457 xmlelem *elem;
459 TRACE("(%p,%p)\n", pUnkOuter, ppObj);
461 if (!ppObj)
462 return E_INVALIDARG;
464 *ppObj = NULL;
466 elem = HeapAlloc(GetProcessHeap(), 0, sizeof (*elem));
467 if(!elem)
468 return E_OUTOFMEMORY;
470 elem->lpVtbl = &xmlelem_vtbl;
471 elem->ref = 1;
472 elem->node = node;
474 *ppObj = &elem->lpVtbl;
476 TRACE("returning iface %p\n", *ppObj);
477 return S_OK;
480 /************************************************************************
481 * IXMLElementCollection
483 typedef struct _xmlelem_collection
485 const IXMLElementCollectionVtbl *lpVtbl;
486 const IEnumVARIANTVtbl *lpvtblIEnumVARIANT;
487 LONG ref;
488 LONG length;
489 xmlNodePtr node;
491 /* IEnumVARIANT members */
492 xmlNodePtr current;
493 } xmlelem_collection;
495 static inline xmlelem_collection *impl_from_IXMLElementCollection(IXMLElementCollection *iface)
497 return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpVtbl));
500 static inline xmlelem_collection *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
502 return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpvtblIEnumVARIANT));
505 static HRESULT WINAPI xmlelem_collection_QueryInterface(IXMLElementCollection *iface, REFIID riid, void** ppvObject)
507 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
509 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
511 if (IsEqualGUID(riid, &IID_IUnknown) ||
512 IsEqualGUID(riid, &IID_IXMLElementCollection))
514 *ppvObject = iface;
516 else if (IsEqualGUID(riid, &IID_IEnumVARIANT))
518 *ppvObject = &(This->lpvtblIEnumVARIANT);
520 else
522 FIXME("interface %s not implemented\n", debugstr_guid(riid));
523 return E_NOINTERFACE;
526 IXMLElementCollection_AddRef(iface);
528 return S_OK;
531 static ULONG WINAPI xmlelem_collection_AddRef(IXMLElementCollection *iface)
533 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
534 TRACE("%p\n", This);
535 return InterlockedIncrement(&This->ref);
538 static ULONG WINAPI xmlelem_collection_Release(IXMLElementCollection *iface)
540 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
541 LONG ref;
543 TRACE("%p\n", This);
545 ref = InterlockedDecrement(&This->ref);
546 if (ref == 0)
548 HeapFree(GetProcessHeap(), 0, This);
551 return ref;
554 static HRESULT WINAPI xmlelem_collection_GetTypeInfoCount(IXMLElementCollection *iface, UINT* pctinfo)
556 FIXME("\n");
557 return E_NOTIMPL;
560 static HRESULT WINAPI xmlelem_collection_GetTypeInfo(IXMLElementCollection *iface, UINT iTInfo,
561 LCID lcid, ITypeInfo** ppTInfo)
563 FIXME("\n");
564 return E_NOTIMPL;
567 static HRESULT WINAPI xmlelem_collection_GetIDsOfNames(IXMLElementCollection *iface, REFIID riid,
568 LPOLESTR* rgszNames, UINT cNames,
569 LCID lcid, DISPID* rgDispId)
571 FIXME("\n");
572 return E_NOTIMPL;
575 static HRESULT WINAPI xmlelem_collection_Invoke(IXMLElementCollection *iface, DISPID dispIdMember,
576 REFIID riid, LCID lcid, WORD wFlags,
577 DISPPARAMS* pDispParams, VARIANT* pVarResult,
578 EXCEPINFO* pExcepInfo, UINT* puArgErr)
580 FIXME("\n");
581 return E_NOTIMPL;
584 static HRESULT WINAPI xmlelem_collection_put_length(IXMLElementCollection *iface, LONG v)
586 TRACE("(%p, %d)\n", iface, v);
587 return E_FAIL;
590 static HRESULT WINAPI xmlelem_collection_get_length(IXMLElementCollection *iface, LONG *p)
592 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
594 TRACE("(%p, %p)\n", iface, p);
596 if (!p)
597 return E_INVALIDARG;
599 *p = This->length;
600 return S_OK;
603 static HRESULT WINAPI xmlelem_collection_get__newEnum(IXMLElementCollection *iface, IUnknown **ppUnk)
605 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
607 TRACE("(%p, %p)\n", iface, ppUnk);
609 if (!ppUnk)
610 return E_INVALIDARG;
612 *ppUnk = (IUnknown *)This;
613 IUnknown_AddRef(*ppUnk);
614 return S_OK;
617 static HRESULT WINAPI xmlelem_collection_item(IXMLElementCollection *iface, VARIANT var1,
618 VARIANT var2, IDispatch **ppDisp)
620 xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
621 xmlNodePtr ptr = This->node;
622 int index, i;
624 TRACE("(%p, %p)\n", iface, ppDisp);
626 if (!ppDisp)
627 return E_INVALIDARG;
629 *ppDisp = NULL;
631 index = V_I4(&var1);
632 if (index < 0)
633 return E_INVALIDARG;
634 if (index >= This->length)
635 return E_FAIL;
637 for (i = 0; i < index; i++)
638 ptr = ptr->next;
640 return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)ppDisp);
643 static const struct IXMLElementCollectionVtbl xmlelem_collection_vtbl =
645 xmlelem_collection_QueryInterface,
646 xmlelem_collection_AddRef,
647 xmlelem_collection_Release,
648 xmlelem_collection_GetTypeInfoCount,
649 xmlelem_collection_GetTypeInfo,
650 xmlelem_collection_GetIDsOfNames,
651 xmlelem_collection_Invoke,
652 xmlelem_collection_put_length,
653 xmlelem_collection_get_length,
654 xmlelem_collection_get__newEnum,
655 xmlelem_collection_item
658 /************************************************************************
659 * xmlelem_collection implementation of IEnumVARIANT.
661 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_QueryInterface(
662 IEnumVARIANT *iface, REFIID riid, LPVOID *ppvObj)
664 xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
665 return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj);
668 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_AddRef(
669 IEnumVARIANT *iface)
671 xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
672 return IXMLDocument_AddRef((IXMLDocument *)this);
675 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_Release(
676 IEnumVARIANT *iface)
678 xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
679 return IXMLDocument_Release((IXMLDocument *)this);
682 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Next(
683 IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
685 xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
686 xmlNodePtr ptr = This->current;
688 TRACE("(%p, %d, %p, %p)\n", iface, celt, rgVar, pCeltFetched);
690 if (!rgVar)
691 return E_INVALIDARG;
693 /* FIXME: handle celt */
694 if (pCeltFetched)
695 *pCeltFetched = 1;
697 This->current = This->current->next;
699 V_VT(rgVar) = VT_DISPATCH;
700 return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)&V_DISPATCH(rgVar));
703 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Skip(
704 IEnumVARIANT *iface, ULONG celt)
706 FIXME("(%p, %d): stub\n", iface, celt);
707 return E_NOTIMPL;
710 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Reset(
711 IEnumVARIANT *iface)
713 xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
714 This->current = This->node;
715 return S_OK;
718 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Clone(
719 IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
721 FIXME("(%p, %p): stub\n", iface, ppEnum);
722 return E_NOTIMPL;
725 static const struct IEnumVARIANTVtbl xmlelem_collection_IEnumVARIANTvtbl =
727 xmlelem_collection_IEnumVARIANT_QueryInterface,
728 xmlelem_collection_IEnumVARIANT_AddRef,
729 xmlelem_collection_IEnumVARIANT_Release,
730 xmlelem_collection_IEnumVARIANT_Next,
731 xmlelem_collection_IEnumVARIANT_Skip,
732 xmlelem_collection_IEnumVARIANT_Reset,
733 xmlelem_collection_IEnumVARIANT_Clone
736 static HRESULT XMLElementCollection_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
738 xmlelem_collection *collection;
739 xmlNodePtr ptr;
741 TRACE("(%p,%p)\n", pUnkOuter, ppObj);
743 *ppObj = NULL;
745 if (!node)
746 return S_FALSE;
748 collection = HeapAlloc(GetProcessHeap(), 0, sizeof (*collection));
749 if(!collection)
750 return E_OUTOFMEMORY;
752 collection->lpVtbl = &xmlelem_collection_vtbl;
753 collection->lpvtblIEnumVARIANT = &xmlelem_collection_IEnumVARIANTvtbl;
754 collection->ref = 1;
755 collection->length = 0;
756 collection->node = node;
757 collection->current = node;
759 ptr = node;
760 while (ptr)
762 collection->length++;
763 ptr = ptr->next;
766 *ppObj = &collection->lpVtbl;
768 TRACE("returning iface %p\n", *ppObj);
769 return S_OK;
772 #endif