ntdll: Add some traces to make debugging time zone related problems easier.
[wine] / dlls / msxml3 / xmlelem.c
1 /*
2  * XML Element 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 "ocidl.h"
32
33 #include "wine/debug.h"
34
35 #include "msxml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
38
39 #ifdef HAVE_LIBXML2
40
41 /**********************************************************************
42  * IXMLElement
43  */
44 typedef struct _xmlelem
45 {
46     const IXMLElementVtbl *lpVtbl;
47     LONG ref;
48     xmlNodePtr node;
49 } xmlelem;
50
51 static inline xmlelem *impl_from_IXMLElement(IXMLElement *iface)
52 {
53     return (xmlelem *)((char*)iface - FIELD_OFFSET(xmlelem, lpVtbl));
54 }
55
56 static HRESULT WINAPI xmlelem_QueryInterface(IXMLElement *iface, REFIID riid, void** ppvObject)
57 {
58     xmlelem *This = impl_from_IXMLElement(iface);
59
60     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
61
62     if (IsEqualGUID(riid, &IID_IUnknown) ||
63         IsEqualGUID(riid, &IID_IXMLElement))
64     {
65         *ppvObject = iface;
66     }
67     else
68     {
69         FIXME("interface %s not implemented\n", debugstr_guid(riid));
70         return E_NOINTERFACE;
71     }
72
73     IXMLElement_AddRef(iface);
74
75     return S_OK;
76 }
77
78 static ULONG WINAPI xmlelem_AddRef(IXMLElement *iface)
79 {
80     xmlelem *This = impl_from_IXMLElement(iface);
81     TRACE("%p\n", This);
82     return InterlockedIncrement(&This->ref);
83 }
84
85 static ULONG WINAPI xmlelem_Release(IXMLElement *iface)
86 {
87     xmlelem *This = impl_from_IXMLElement(iface);
88     LONG ref;
89
90     TRACE("%p\n", This);
91
92     ref = InterlockedDecrement(&This->ref);
93     if (ref == 0)
94     {
95         HeapFree(GetProcessHeap(), 0, This);
96     }
97
98     return ref;
99 }
100
101 static HRESULT WINAPI xmlelem_GetTypeInfoCount(IXMLElement *iface, UINT* pctinfo)
102 {
103     FIXME("\n");
104     return E_NOTIMPL;
105 }
106
107 static HRESULT WINAPI xmlelem_GetTypeInfo(IXMLElement *iface, UINT iTInfo,
108                                           LCID lcid, ITypeInfo** ppTInfo)
109 {
110     FIXME("\n");
111     return E_NOTIMPL;
112 }
113
114 static HRESULT WINAPI xmlelem_GetIDsOfNames(IXMLElement *iface, REFIID riid,
115                                             LPOLESTR* rgszNames, UINT cNames,
116                                             LCID lcid, DISPID* rgDispId)
117 {
118     FIXME("\n");
119     return E_NOTIMPL;
120 }
121
122 static HRESULT WINAPI xmlelem_Invoke(IXMLElement *iface, DISPID dispIdMember,
123                                      REFIID riid, LCID lcid, WORD wFlags,
124                                      DISPPARAMS* pDispParams, VARIANT* pVarResult,
125                                      EXCEPINFO* pExcepInfo, UINT* puArgErr)
126 {
127     FIXME("\n");
128     return E_NOTIMPL;
129 }
130
131 static inline BSTR str_dup_upper(BSTR str)
132 {
133     INT len = (lstrlenW(str) + 1) * sizeof(WCHAR);
134     BSTR p = SysAllocStringLen(NULL, len);
135     if (p)
136     {
137         memcpy(p, str, len);
138         CharUpperW(p);
139     }
140     return p;
141 }
142
143 static HRESULT WINAPI xmlelem_get_tagName(IXMLElement *iface, BSTR *p)
144 {
145     xmlelem *This = impl_from_IXMLElement(iface);
146     BSTR temp;
147
148     TRACE("(%p, %p)\n", iface, p);
149
150     if (!p)
151         return E_INVALIDARG;
152
153     temp = bstr_from_xmlChar(This->node->name);
154     *p = str_dup_upper(temp);
155     SysFreeString(temp);
156
157     TRACE("returning %s\n", debugstr_w(*p));
158
159     return S_OK;
160 }
161
162 static HRESULT WINAPI xmlelem_put_tagName(IXMLElement *iface, BSTR p)
163 {
164     FIXME("(%p, %p): stub\n", iface, p);
165
166     if (!p)
167         return E_INVALIDARG;
168
169     return E_NOTIMPL;
170 }
171
172 static HRESULT WINAPI xmlelem_get_parent(IXMLElement *iface, IXMLElement **parent)
173 {
174     xmlelem *This = impl_from_IXMLElement(iface);
175
176     TRACE("(%p, %p)\n", iface, parent);
177
178     if (!parent)
179         return E_INVALIDARG;
180
181     *parent = NULL;
182
183     if (!This->node->parent)
184         return S_FALSE;
185
186     return XMLElement_create((IUnknown *)iface, This->node->parent, (LPVOID *)parent);
187 }
188
189 static HRESULT WINAPI xmlelem_setAttribute(IXMLElement *iface, BSTR strPropertyName,
190                                             VARIANT PropertyValue)
191 {
192     xmlelem *This = impl_from_IXMLElement(iface);
193     xmlChar *name, *value;
194     xmlAttrPtr attr;
195
196     TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
197
198     if (!strPropertyName || V_VT(&PropertyValue) != VT_BSTR)
199         return E_INVALIDARG;
200
201     name = xmlChar_from_wchar(strPropertyName);
202     value = xmlChar_from_wchar(V_BSTR(&PropertyValue));
203     attr = xmlSetProp(This->node, name, value);
204
205     HeapFree(GetProcessHeap(), 0, name);
206     HeapFree(GetProcessHeap(), 0, value);
207     return (attr) ? S_OK : S_FALSE;
208 }
209
210 static HRESULT WINAPI xmlelem_getAttribute(IXMLElement *iface, BSTR strPropertyName,
211                                            VARIANT *PropertyValue)
212 {
213     xmlelem *This = impl_from_IXMLElement(iface);
214     xmlChar *val = NULL, *name;
215     xmlAttrPtr ptr;
216
217     TRACE("(%p, %s, %p)\n", iface, debugstr_w(strPropertyName), PropertyValue);
218
219     if (!PropertyValue)
220         return E_INVALIDARG;
221
222     VariantInit(PropertyValue);
223     V_BSTR(PropertyValue) = NULL;
224
225     if (!strPropertyName)
226         return E_INVALIDARG;
227
228     name = xmlChar_from_wchar(strPropertyName);
229     ptr = This->node->properties;
230     while (ptr)
231     {
232         if (!lstrcmpiA((LPSTR)name, (LPCSTR)ptr->name))
233         {
234             val = xmlNodeListGetString(ptr->doc, ptr->children, 1);
235             break;
236         }
237
238         ptr = ptr->next;
239     }
240
241     if (val)
242     {
243         V_VT(PropertyValue) = VT_BSTR;
244         V_BSTR(PropertyValue) = bstr_from_xmlChar(val);
245     }
246
247     HeapFree(GetProcessHeap(), 0, name);
248     xmlFree(val);
249     TRACE("returning %s\n", debugstr_w(V_BSTR(PropertyValue)));
250     return (val) ? S_OK : S_FALSE;
251 }
252
253 static HRESULT WINAPI xmlelem_removeAttribute(IXMLElement *iface, BSTR strPropertyName)
254 {
255     xmlelem *This = impl_from_IXMLElement(iface);
256     xmlChar *name;
257     xmlAttrPtr attr;
258     int res;
259     HRESULT hr = S_FALSE;
260
261     TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
262
263     if (!strPropertyName)
264         return E_INVALIDARG;
265
266     name = xmlChar_from_wchar(strPropertyName);
267     attr = xmlHasProp(This->node, name);
268     if (!attr)
269         goto done;
270
271     res = xmlRemoveProp(attr);
272
273     if (res == 0)
274         hr = S_OK;
275
276 done:
277     HeapFree(GetProcessHeap(), 0, name);
278     return hr;
279 }
280
281 static HRESULT WINAPI xmlelem_get_children(IXMLElement *iface, IXMLElementCollection **p)
282 {
283     xmlelem *This = impl_from_IXMLElement(iface);
284
285     TRACE("(%p, %p)\n", iface, p);
286
287     if (!p)
288         return E_INVALIDARG;
289
290     return XMLElementCollection_create((IUnknown *)iface, This->node->children, (LPVOID *)p);
291 }
292
293 static long type_libxml_to_msxml(xmlElementType type)
294 {
295     switch (type)
296     {
297         case XML_ELEMENT_NODE:
298             return XMLELEMTYPE_ELEMENT;
299         case XML_TEXT_NODE:
300             return XMLELEMTYPE_TEXT;
301         case XML_COMMENT_NODE:
302             return XMLELEMTYPE_COMMENT;
303         case XML_DOCUMENT_NODE:
304             return XMLELEMTYPE_DOCUMENT;
305         case XML_DTD_NODE:
306             return XMLELEMTYPE_DTD;
307         case XML_PI_NODE:
308             return XMLELEMTYPE_PI;
309         default:
310             break;
311     }
312
313     return XMLELEMTYPE_OTHER;
314 }
315
316 static HRESULT WINAPI xmlelem_get_type(IXMLElement *iface, long *p)
317 {
318     xmlelem *This = impl_from_IXMLElement(iface);
319
320     TRACE("(%p, %p)\n", This, p);
321
322     if (!p)
323         return E_INVALIDARG;
324
325     *p = type_libxml_to_msxml(This->node->type);
326     TRACE("returning %ld\n", *p);
327     return S_OK;
328 }
329
330 static HRESULT WINAPI xmlelem_get_text(IXMLElement *iface, BSTR *p)
331 {
332     xmlelem *This = impl_from_IXMLElement(iface);
333     xmlChar *content;
334
335     TRACE("(%p, %p)\n", iface, p);
336
337     if (!p)
338         return E_INVALIDARG;
339
340     content = xmlNodeGetContent(This->node);
341     *p = bstr_from_xmlChar(content);
342     TRACE("returning %s\n", debugstr_w(*p));
343     return S_OK;
344 }
345
346 static HRESULT WINAPI xmlelem_put_text(IXMLElement *iface, BSTR p)
347 {
348     xmlelem *This = impl_from_IXMLElement(iface);
349     xmlChar *content;
350
351     TRACE("(%p, %s)\n", iface, debugstr_w(p));
352
353     /* FIXME: test which types can be used */
354     if (This->node->type == XML_ELEMENT_NODE)
355         return E_NOTIMPL;
356
357     content = xmlChar_from_wchar(p);
358     xmlNodeSetContent(This->node, content);
359     return S_OK;
360 }
361
362 static HRESULT WINAPI xmlelem_addChild(IXMLElement *iface, IXMLElement *pChildElem,
363                                         long lIndex, long lreserved)
364 {
365     xmlelem *This = impl_from_IXMLElement(iface);
366     xmlelem *childElem = impl_from_IXMLElement(pChildElem);
367     xmlNodePtr child;
368
369     TRACE("(%p, %p, %ld, %ld)\n", iface, pChildElem, lIndex, lreserved);
370
371     if (lIndex == 0)
372         child = xmlAddChild(This->node, childElem->node);
373     else
374         child = xmlAddNextSibling(This->node, childElem->node->last);
375
376     return (child) ? S_OK : S_FALSE;
377 }
378
379 static HRESULT WINAPI xmlelem_removeChild(IXMLElement *iface, IXMLElement *pChildElem)
380 {
381     FIXME("(%p, %p): stub\n", iface, pChildElem);
382     return E_NOTIMPL;
383 }
384
385 static const struct IXMLElementVtbl xmlelem_vtbl =
386 {
387     xmlelem_QueryInterface,
388     xmlelem_AddRef,
389     xmlelem_Release,
390     xmlelem_GetTypeInfoCount,
391     xmlelem_GetTypeInfo,
392     xmlelem_GetIDsOfNames,
393     xmlelem_Invoke,
394     xmlelem_get_tagName,
395     xmlelem_put_tagName,
396     xmlelem_get_parent,
397     xmlelem_setAttribute,
398     xmlelem_getAttribute,
399     xmlelem_removeAttribute,
400     xmlelem_get_children,
401     xmlelem_get_type,
402     xmlelem_get_text,
403     xmlelem_put_text,
404     xmlelem_addChild,
405     xmlelem_removeChild
406 };
407
408 HRESULT XMLElement_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
409 {
410     xmlelem *elem;
411
412     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
413
414     if (!ppObj)
415         return E_INVALIDARG;
416
417     *ppObj = NULL;
418
419     elem = HeapAlloc(GetProcessHeap(), 0, sizeof (*elem));
420     if(!elem)
421         return E_OUTOFMEMORY;
422
423     elem->lpVtbl = &xmlelem_vtbl;
424     elem->ref = 1;
425     elem->node = node;
426
427     *ppObj = &elem->lpVtbl;
428
429     TRACE("returning iface %p\n", *ppObj);
430     return S_OK;
431 }
432
433 /************************************************************************
434  * IXMLElementCollection
435  */
436 typedef struct _xmlelem_collection
437 {
438     const IXMLElementCollectionVtbl *lpVtbl;
439     const IEnumVARIANTVtbl          *lpvtblIEnumVARIANT;
440     LONG ref;
441     LONG length;
442     xmlNodePtr node;
443
444     /* IEnumVARIANT members */
445     xmlNodePtr current;
446 } xmlelem_collection;
447
448 static inline xmlelem_collection *impl_from_IXMLElementCollection(IXMLElementCollection *iface)
449 {
450     return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpVtbl));
451 }
452
453 static inline xmlelem_collection *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
454 {
455     return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpvtblIEnumVARIANT));
456 }
457
458 static HRESULT WINAPI xmlelem_collection_QueryInterface(IXMLElementCollection *iface, REFIID riid, void** ppvObject)
459 {
460     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
461
462     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
463
464     if (IsEqualGUID(riid, &IID_IUnknown) ||
465         IsEqualGUID(riid, &IID_IXMLElementCollection))
466     {
467         *ppvObject = iface;
468     }
469     else if (IsEqualGUID(riid, &IID_IEnumVARIANT))
470     {
471         *ppvObject = (IEnumVARIANT *)&(This->lpvtblIEnumVARIANT);
472     }
473     else
474     {
475         FIXME("interface %s not implemented\n", debugstr_guid(riid));
476         return E_NOINTERFACE;
477     }
478
479     IXMLElementCollection_AddRef(iface);
480
481     return S_OK;
482 }
483
484 static ULONG WINAPI xmlelem_collection_AddRef(IXMLElementCollection *iface)
485 {
486     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
487     TRACE("%p\n", This);
488     return InterlockedIncrement(&This->ref);
489 }
490
491 static ULONG WINAPI xmlelem_collection_Release(IXMLElementCollection *iface)
492 {
493     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
494     LONG ref;
495
496     TRACE("%p\n", This);
497
498     ref = InterlockedDecrement(&This->ref);
499     if (ref == 0)
500     {
501         HeapFree(GetProcessHeap(), 0, This);
502     }
503
504     return ref;
505 }
506
507 static HRESULT WINAPI xmlelem_collection_GetTypeInfoCount(IXMLElementCollection *iface, UINT* pctinfo)
508 {
509     FIXME("\n");
510     return E_NOTIMPL;
511 }
512
513 static HRESULT WINAPI xmlelem_collection_GetTypeInfo(IXMLElementCollection *iface, UINT iTInfo,
514                                                      LCID lcid, ITypeInfo** ppTInfo)
515 {
516     FIXME("\n");
517     return E_NOTIMPL;
518 }
519
520 static HRESULT WINAPI xmlelem_collection_GetIDsOfNames(IXMLElementCollection *iface, REFIID riid,
521                                                        LPOLESTR* rgszNames, UINT cNames,
522                                                        LCID lcid, DISPID* rgDispId)
523 {
524     FIXME("\n");
525     return E_NOTIMPL;
526 }
527
528 static HRESULT WINAPI xmlelem_collection_Invoke(IXMLElementCollection *iface, DISPID dispIdMember,
529                                                 REFIID riid, LCID lcid, WORD wFlags,
530                                                 DISPPARAMS* pDispParams, VARIANT* pVarResult,
531                                                 EXCEPINFO* pExcepInfo, UINT* puArgErr)
532 {
533     FIXME("\n");
534     return E_NOTIMPL;
535 }
536
537 static HRESULT WINAPI xmlelem_collection_put_length(IXMLElementCollection *iface, long v)
538 {
539     TRACE("(%p, %ld)\n", iface, v);
540     return E_FAIL;
541 }
542
543 static HRESULT WINAPI xmlelem_collection_get_length(IXMLElementCollection *iface, long *p)
544 {
545     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
546
547     TRACE("(%p, %p)\n", iface, p);
548
549     if (!p)
550         return E_INVALIDARG;
551
552     *p = This->length;
553     return S_OK;
554 }
555
556 static HRESULT WINAPI xmlelem_collection_get__newEnum(IXMLElementCollection *iface, IUnknown **ppUnk)
557 {
558     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
559
560     TRACE("(%p, %p)\n", iface, ppUnk);
561
562     if (!ppUnk)
563         return E_INVALIDARG;
564
565     *ppUnk = (IUnknown *)This;
566     IUnknown_AddRef(*ppUnk);
567     return S_OK;
568 }
569
570 static HRESULT WINAPI xmlelem_collection_item(IXMLElementCollection *iface, VARIANT var1,
571                                               VARIANT var2, IDispatch **ppDisp)
572 {
573     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
574     xmlNodePtr ptr = This->node;
575     int index, i;
576
577     TRACE("(%p, %p)\n", iface, ppDisp);
578
579     if (!ppDisp)
580         return E_INVALIDARG;
581
582     *ppDisp = NULL;
583
584     index = V_I4(&var1);
585     if (index < 0)
586         return E_INVALIDARG;
587     if (index >= This->length)
588         return E_FAIL;
589
590     for (i = 0; i < index; i++)
591         ptr = ptr->next;
592
593     return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)ppDisp);
594 }
595
596 static const struct IXMLElementCollectionVtbl xmlelem_collection_vtbl =
597 {
598     xmlelem_collection_QueryInterface,
599     xmlelem_collection_AddRef,
600     xmlelem_collection_Release,
601     xmlelem_collection_GetTypeInfoCount,
602     xmlelem_collection_GetTypeInfo,
603     xmlelem_collection_GetIDsOfNames,
604     xmlelem_collection_Invoke,
605     xmlelem_collection_put_length,
606     xmlelem_collection_get_length,
607     xmlelem_collection_get__newEnum,
608     xmlelem_collection_item
609 };
610
611 /************************************************************************
612  * xmlelem_collection implementation of IEnumVARIANT.
613  */
614 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_QueryInterface(
615     IEnumVARIANT *iface, REFIID riid, LPVOID *ppvObj)
616 {
617     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
618     return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj);
619 }
620
621 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_AddRef(
622     IEnumVARIANT *iface)
623 {
624     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
625     return IXMLDocument_AddRef((IXMLDocument *)this);
626 }
627
628 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_Release(
629     IEnumVARIANT *iface)
630 {
631     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
632     return IXMLDocument_Release((IXMLDocument *)this);
633 }
634
635 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Next(
636     IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
637 {
638     xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
639     xmlNodePtr ptr = This->current;
640
641     TRACE("(%p, %d, %p, %p)\n", iface, celt, rgVar, pCeltFetched);
642
643     if (!rgVar)
644         return E_INVALIDARG;
645
646     /* FIXME: handle celt */
647     if (pCeltFetched)
648         *pCeltFetched = 1;
649
650     This->current = This->current->next;
651
652     V_VT(rgVar) = VT_DISPATCH;
653     return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)&V_DISPATCH(rgVar));
654 }
655
656 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Skip(
657     IEnumVARIANT *iface, ULONG celt)
658 {
659     FIXME("(%p, %d): stub\n", iface, celt);
660     return E_NOTIMPL;
661 }
662
663 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Reset(
664     IEnumVARIANT *iface)
665 {
666     xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
667     This->current = This->node;
668     return S_OK;
669 }
670
671 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Clone(
672     IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
673 {
674     FIXME("(%p, %p): stub\n", iface, ppEnum);
675     return E_NOTIMPL;
676 }
677
678 static const struct IEnumVARIANTVtbl xmlelem_collection_IEnumVARIANTvtbl =
679 {
680     xmlelem_collection_IEnumVARIANT_QueryInterface,
681     xmlelem_collection_IEnumVARIANT_AddRef,
682     xmlelem_collection_IEnumVARIANT_Release,
683     xmlelem_collection_IEnumVARIANT_Next,
684     xmlelem_collection_IEnumVARIANT_Skip,
685     xmlelem_collection_IEnumVARIANT_Reset,
686     xmlelem_collection_IEnumVARIANT_Clone
687 };
688
689 HRESULT XMLElementCollection_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
690 {
691     xmlelem_collection *collection;
692     xmlNodePtr ptr;
693
694     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
695
696     *ppObj = NULL;
697
698     if (!node)
699         return S_FALSE;
700
701     collection = HeapAlloc(GetProcessHeap(), 0, sizeof (*collection));
702     if(!collection)
703         return E_OUTOFMEMORY;
704
705     collection->lpVtbl = &xmlelem_collection_vtbl;
706     collection->lpvtblIEnumVARIANT = &xmlelem_collection_IEnumVARIANTvtbl;
707     collection->ref = 1;
708     collection->length = 0;
709     collection->node = node;
710     collection->current = node;
711
712     ptr = node;
713     while (ptr)
714     {
715         collection->length++;
716         ptr = ptr->next;
717     }
718
719     *ppObj = &collection->lpVtbl;
720
721     TRACE("returning iface %p\n", *ppObj);
722     return S_OK;
723 }
724
725 #endif