msxml3: Remove hasChildNodes() forward.
[wine] / dlls / msxml3 / dispex.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #define COBJMACROS
20
21 #include "config.h"
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "ole2.h"
29 #include "msxml6.h"
30 #include "wininet.h"
31 #include "urlmon.h"
32 #include "winreg.h"
33 #include "shlwapi.h"
34
35 #include "wine/debug.h"
36 #include "wine/list.h"
37 #include "wine/unicode.h"
38
39 #include "msxml_private.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
42
43 #define DISPATCHEX(x)  ((IDispatchEx*)  &(x)->lpIDispatchExVtbl)
44
45 typedef struct {
46     DISPID id;
47     BSTR name;
48     enum tid_t tid;
49 } func_info_t;
50
51 struct dispex_data_t {
52     DWORD func_cnt;
53     func_info_t *funcs;
54     func_info_t **name_table;
55
56     struct list entry;
57 };
58
59 typedef struct {
60     VARIANT var;
61     LPWSTR name;
62 } dynamic_prop_t;
63
64 struct dispex_dynamic_data_t {
65     DWORD buf_size;
66     DWORD prop_cnt;
67     dynamic_prop_t *props;
68 };
69
70 #define DISPID_DYNPROP_0    0x50000000
71 #define DISPID_DYNPROP_MAX  0x5fffffff
72
73 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
74 static ITypeLib *typelib;
75 static ITypeInfo *typeinfos[LAST_tid];
76
77 static REFIID tid_ids[] = {
78     &IID_IXMLDOMAttribute,
79     &IID_IXMLDOMCDATASection,
80     &IID_IXMLDOMComment,
81     &IID_IXMLDOMDocument,
82     &IID_IXMLDOMDocument2,
83     &IID_IXMLDOMDocumentFragment,
84     &IID_IXMLDOMDocumentType,
85     &IID_IXMLDOMElement,
86     &IID_IXMLDOMEntityReference,
87     &IID_IXMLDOMImplementation,
88     &IID_IXMLDOMNamedNodeMap,
89     &IID_IXMLDOMNode,
90     &IID_IXMLDOMNodeList,
91     &IID_IXMLDOMParseError,
92     &IID_IXMLDOMProcessingInstruction,
93     &IID_IXMLDOMSchemaCollection,
94     &IID_IXMLDOMSelection,
95     &IID_IXMLDOMText,
96     &IID_IXMLElement,
97     &IID_IXMLDOMDocument,
98     &IID_IXMLHTTPRequest,
99     &IID_IXSLProcessor,
100     &IID_IXSLTemplate,
101     &IID_IVBSAXAttributes,
102     &IID_IVBSAXContentHandler,
103     &IID_IVBSAXDeclHandler,
104     &IID_IVBSAXDTDHandler,
105     &IID_IVBSAXEntityResolver,
106     &IID_IVBSAXErrorHandler,
107     &IID_IVBSAXLexicalHandler,
108     &IID_IVBSAXLocator,
109     &IID_IVBSAXXMLFilter,
110     &IID_IVBSAXXMLReader,
111     &IID_IMXAttributes,
112     &IID_IMXReaderControl,
113     &IID_IMXWriter,
114 };
115
116 HRESULT get_typeinfo(enum tid_t tid, ITypeInfo **typeinfo)
117 {
118     HRESULT hres;
119
120     if(!typelib) {
121         ITypeLib *tl;
122
123         hres = LoadRegTypeLib(&LIBID_MSXML2, 3, 0, LOCALE_SYSTEM_DEFAULT, &tl);
124         if(FAILED(hres)) {
125             ERR("LoadRegTypeLib failed: %08x\n", hres);
126             return hres;
127         }
128
129         if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
130             ITypeLib_Release(tl);
131     }
132
133     if(!typeinfos[tid]) {
134         ITypeInfo *typeinfo;
135
136         hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &typeinfo);
137         if(FAILED(hres)) {
138             ERR("GetTypeInfoOfGuid failed: %08x\n", hres);
139             return hres;
140         }
141
142         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), typeinfo, NULL))
143             ITypeInfo_Release(typeinfo);
144     }
145
146     *typeinfo = typeinfos[tid];
147
148     ITypeInfo_AddRef(typeinfos[tid]);
149     return S_OK;
150 }
151
152 void release_typelib(void)
153 {
154     dispex_data_t *iter;
155     unsigned i;
156
157     while(!list_empty(&dispex_data_list)) {
158         iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
159         list_remove(&iter->entry);
160
161         for(i=0; i < iter->func_cnt; i++)
162             SysFreeString(iter->funcs[i].name);
163
164         heap_free(iter->funcs);
165         heap_free(iter->name_table);
166         heap_free(iter);
167     }
168
169     if(!typelib)
170         return;
171
172     for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
173         if(typeinfos[i])
174             ITypeInfo_Release(typeinfos[i]);
175
176     ITypeLib_Release(typelib);
177 }
178
179 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, DISPID id, ITypeInfo *dti)
180 {
181     HRESULT hres;
182
183     if(data->func_cnt && data->funcs[data->func_cnt-1].id == id)
184         return;
185
186     if(data->func_cnt == *size)
187         data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t));
188
189     hres = ITypeInfo_GetDocumentation(dti, id, &data->funcs[data->func_cnt].name, NULL, NULL, NULL);
190     if(FAILED(hres))
191         return;
192
193     data->funcs[data->func_cnt].id = id;
194     data->funcs[data->func_cnt].tid = tid;
195
196     data->func_cnt++;
197 }
198
199 static int dispid_cmp(const void *p1, const void *p2)
200 {
201     return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
202 }
203
204 static int func_name_cmp(const void *p1, const void *p2)
205 {
206     return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
207 }
208
209 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
210 {
211     const tid_t *tid = This->data->iface_tids;
212     FUNCDESC *funcdesc;
213     dispex_data_t *data;
214     DWORD size = 16, i;
215     ITypeInfo *ti, *dti;
216     HRESULT hres;
217
218     TRACE("(%p)\n", This);
219
220     hres = get_typeinfo(This->data->disp_tid, &dti);
221     if(FAILED(hres)) {
222         ERR("Could not get disp type info: %08x\n", hres);
223         return NULL;
224     }
225
226     data = heap_alloc(sizeof(dispex_data_t));
227     data->func_cnt = 0;
228     data->funcs = heap_alloc(size*sizeof(func_info_t));
229     list_add_tail(&dispex_data_list, &data->entry);
230
231     while(*tid) {
232         hres = get_typeinfo(*tid, &ti);
233         if(FAILED(hres))
234             break;
235
236         i=0;
237         while(1) {
238             hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
239             if(FAILED(hres))
240                 break;
241
242             add_func_info(data, &size, *tid, funcdesc->memid, dti);
243             ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
244         }
245
246         ITypeInfo_Release(ti);
247         tid++;
248     }
249
250     if(!data->func_cnt) {
251         heap_free(data->funcs);
252         data->funcs = NULL;
253     }else if(data->func_cnt != size) {
254         data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
255     }
256
257     qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
258
259     if(data->funcs) {
260         data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
261         for(i=0; i < data->func_cnt; i++)
262             data->name_table[i] = data->funcs+i;
263         qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
264     }else {
265         data->name_table = NULL;
266     }
267
268     ITypeInfo_Release(dti);
269     return data;
270 }
271
272 static CRITICAL_SECTION cs_dispex_static_data;
273 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
274 {
275     0, 0, &cs_dispex_static_data,
276     { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
277       0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
278 };
279 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
280
281
282 static dispex_data_t *get_dispex_data(DispatchEx *This)
283 {
284     if(This->data->data)
285         return This->data->data;
286
287     EnterCriticalSection(&cs_dispex_static_data);
288
289     if(!This->data->data)
290         This->data->data = preprocess_dispex_data(This);
291
292     LeaveCriticalSection(&cs_dispex_static_data);
293
294     return This->data->data;
295 }
296
297 static inline BOOL is_custom_dispid(DISPID id)
298 {
299     return MSXML_DISPID_CUSTOM_MIN <= id && id <= MSXML_DISPID_CUSTOM_MAX;
300 }
301
302 static inline BOOL is_dynamic_dispid(DISPID id)
303 {
304     return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
305 }
306
307 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
308 {
309     return (DispatchEx*)((char*)iface - FIELD_OFFSET(DispatchEx, lpIDispatchExVtbl));
310 }
311
312 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
313 {
314     DispatchEx *This = impl_from_IDispatchEx(iface);
315
316     return IUnknown_QueryInterface(This->outer, riid, ppv);
317 }
318
319 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
320 {
321     DispatchEx *This = impl_from_IDispatchEx(iface);
322
323     return IUnknown_AddRef(This->outer);
324 }
325
326 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
327 {
328     DispatchEx *This = impl_from_IDispatchEx(iface);
329
330     return IUnknown_Release(This->outer);
331 }
332
333 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
334 {
335     DispatchEx *This = impl_from_IDispatchEx(iface);
336
337     TRACE("(%p)->(%p)\n", This, pctinfo);
338
339     *pctinfo = 1;
340     return S_OK;
341 }
342
343 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
344                                               LCID lcid, ITypeInfo **ppTInfo)
345 {
346     DispatchEx *This = impl_from_IDispatchEx(iface);
347
348     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
349
350     return get_typeinfo(This->data->disp_tid, ppTInfo);
351 }
352
353 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
354                                                 LPOLESTR *rgszNames, UINT cNames,
355                                                 LCID lcid, DISPID *rgDispId)
356 {
357     DispatchEx *This = impl_from_IDispatchEx(iface);
358     UINT i;
359     HRESULT hres;
360
361     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
362           lcid, rgDispId);
363
364     for(i=0; i < cNames; i++) {
365         hres = IDispatchEx_GetDispID(DISPATCHEX(This), rgszNames[i], 0, rgDispId+i);
366         if(FAILED(hres))
367             return hres;
368     }
369
370     return S_OK;
371 }
372
373 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
374                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
375                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
376 {
377     DispatchEx *This = impl_from_IDispatchEx(iface);
378
379     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
380           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
381
382     return IDispatchEx_InvokeEx(DISPATCHEX(This), dispIdMember, lcid, wFlags,
383                                 pDispParams, pVarResult, pExcepInfo, NULL);
384 }
385
386 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
387 {
388     DispatchEx *This = impl_from_IDispatchEx(iface);
389     dispex_data_t *data;
390     int min, max, n, c;
391
392     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
393
394     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit))
395         FIXME("Unsupported grfdex %x\n", grfdex);
396
397     data = get_dispex_data(This);
398     if(!data)
399         return E_FAIL;
400
401     min = 0;
402     max = data->func_cnt-1;
403
404     while(min <= max) {
405         n = (min+max)/2;
406
407         c = strcmpiW(data->name_table[n]->name, bstrName);
408         if(!c) {
409             if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, bstrName))
410                 break;
411
412             *pid = data->name_table[n]->id;
413             return S_OK;
414         }
415
416         if(c > 0)
417             max = n-1;
418         else
419             min = n+1;
420     }
421
422     if(This->dynamic_data) {
423         unsigned i;
424
425         for(i=0; i < This->dynamic_data->prop_cnt; i++) {
426             if(!strcmpW(This->dynamic_data->props[i].name, bstrName)) {
427                 *pid = DISPID_DYNPROP_0 + i;
428                 return S_OK;
429             }
430         }
431     }
432
433     if(This->data->vtbl && This->data->vtbl->get_dispid) {
434         HRESULT hres;
435
436         hres = This->data->vtbl->get_dispid(This->outer, bstrName, grfdex, pid);
437         if(hres != DISP_E_UNKNOWNNAME)
438             return hres;
439     }
440
441     if(grfdex & fdexNameEnsure) {
442         dispex_dynamic_data_t *dynamic_data;
443
444         TRACE("creating dynamic prop %s\n", debugstr_w(bstrName));
445
446         if(This->dynamic_data) {
447             dynamic_data = This->dynamic_data;
448         }else {
449             dynamic_data = This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
450             if(!dynamic_data)
451                 return E_OUTOFMEMORY;
452         }
453
454         if(!dynamic_data->buf_size) {
455             dynamic_data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
456             if(!dynamic_data->props)
457                 return E_OUTOFMEMORY;
458             dynamic_data->buf_size = 4;
459         }else if(dynamic_data->buf_size == dynamic_data->prop_cnt) {
460             dynamic_prop_t *new_props;
461
462             new_props = heap_realloc(dynamic_data->props, sizeof(dynamic_prop_t)*(dynamic_data->buf_size<<1));
463             if(!new_props)
464                 return E_OUTOFMEMORY;
465
466             dynamic_data->props = new_props;
467             dynamic_data->buf_size <<= 1;
468         }
469
470         dynamic_data->props[dynamic_data->prop_cnt].name = heap_strdupW(bstrName);
471         VariantInit(&dynamic_data->props[dynamic_data->prop_cnt].var);
472         *pid = DISPID_DYNPROP_0 + dynamic_data->prop_cnt++;
473
474         return S_OK;
475     }
476
477     TRACE("not found %s\n", debugstr_w(bstrName));
478     return DISP_E_UNKNOWNNAME;
479 }
480
481 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
482         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
483 {
484     DispatchEx *This = impl_from_IDispatchEx(iface);
485     IUnknown *unk;
486     ITypeInfo *ti;
487     dispex_data_t *data;
488     UINT argerr=0;
489     int min, max, n;
490     HRESULT hres;
491
492     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
493
494     if(is_custom_dispid(id) && This->data->vtbl && This->data->vtbl->invoke)
495         return This->data->vtbl->invoke(This->outer, id, lcid, wFlags, pdp, pvarRes, pei);
496
497     if(wFlags == DISPATCH_CONSTRUCT) {
498         FIXME("DISPATCH_CONSTRUCT not implemented\n");
499         return E_NOTIMPL;
500     }
501
502     if(is_dynamic_dispid(id)) {
503         DWORD idx = id - DISPID_DYNPROP_0;
504         VARIANT *var;
505
506         if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
507             return DISP_E_UNKNOWNNAME;
508
509         var = &This->dynamic_data->props[idx].var;
510
511         switch(wFlags) {
512         case INVOKE_PROPERTYGET:
513             return VariantCopy(pvarRes, var);
514         case INVOKE_PROPERTYPUT:
515             VariantClear(var);
516             return VariantCopy(var, pdp->rgvarg);
517         default:
518             FIXME("unhandled wFlags %x\n", wFlags);
519             return E_NOTIMPL;
520         }
521     }
522
523     data = get_dispex_data(This);
524     if(!data)
525         return E_FAIL;
526
527     min = 0;
528     max = data->func_cnt-1;
529
530     while(min <= max) {
531         n = (min+max)/2;
532
533         if(data->funcs[n].id == id)
534             break;
535
536         if(data->funcs[n].id < id)
537             min = n+1;
538         else
539             max = n-1;
540     }
541
542     if(min > max) {
543         WARN("invalid id %x\n", id);
544         return DISP_E_UNKNOWNNAME;
545     }
546
547     hres = get_typeinfo(data->funcs[n].tid, &ti);
548     if(FAILED(hres)) {
549         ERR("Could not get type info: %08x\n", hres);
550         return hres;
551     }
552
553     hres = IUnknown_QueryInterface(This->outer, tid_ids[data->funcs[n].tid], (void**)&unk);
554     if(FAILED(hres)) {
555         ERR("Could not get iface: %08x\n", hres);
556         return E_FAIL;
557     }
558
559     hres = ITypeInfo_Invoke(ti, unk, id, wFlags, pdp, pvarRes, pei, &argerr);
560
561     ITypeInfo_Release(ti);
562     IUnknown_Release(unk);
563     return hres;
564 }
565
566 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
567 {
568     DispatchEx *This = impl_from_IDispatchEx(iface);
569     TRACE("Not implemented in native msxml3 (%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
570     return E_NOTIMPL;
571 }
572
573 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
574 {
575     DispatchEx *This = impl_from_IDispatchEx(iface);
576     TRACE("Not implemented in native msxml3 (%p)->(%x)\n", This, id);
577     return E_NOTIMPL;
578 }
579
580 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
581 {
582     DispatchEx *This = impl_from_IDispatchEx(iface);
583     TRACE("Not implemented in native msxml3 (%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
584     return E_NOTIMPL;
585 }
586
587 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
588 {
589     DispatchEx *This = impl_from_IDispatchEx(iface);
590     TRACE("Not implemented in native msxml3 (%p)->(%x %p)\n", This, id, pbstrName);
591     return E_NOTIMPL;
592 }
593
594 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
595 {
596     DispatchEx *This = impl_from_IDispatchEx(iface);
597     TRACE(" Not implemented in native msxml3 (%p)->(%x %x %p)\n", This, grfdex, id, pid);
598     return E_NOTIMPL;
599 }
600
601 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
602 {
603     DispatchEx *This = impl_from_IDispatchEx(iface);
604     TRACE("Not implemented in native msxml3 (%p)->(%p)\n", This, ppunk);
605     return E_NOTIMPL;
606 }
607
608 static IDispatchExVtbl DispatchExVtbl = {
609     DispatchEx_QueryInterface,
610     DispatchEx_AddRef,
611     DispatchEx_Release,
612     DispatchEx_GetTypeInfoCount,
613     DispatchEx_GetTypeInfo,
614     DispatchEx_GetIDsOfNames,
615     DispatchEx_Invoke,
616     DispatchEx_GetDispID,
617     DispatchEx_InvokeEx,
618     DispatchEx_DeleteMemberByName,
619     DispatchEx_DeleteMemberByDispID,
620     DispatchEx_GetMemberProperties,
621     DispatchEx_GetMemberName,
622     DispatchEx_GetNextDispID,
623     DispatchEx_GetNameSpaceParent
624 };
625
626 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
627 {
628     static const IID IID_UndocumentedScriptIface =
629         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
630
631     if(IsEqualGUID(&IID_IDispatch, riid)) {
632         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
633         *ppv = DISPATCHEX(This);
634     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
635         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
636         *ppv = DISPATCHEX(This);
637     }else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
638         TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
639         *ppv = NULL;
640     }else if (IsEqualGUID(&IID_IObjectIdentity, riid)) {
641         TRACE("IID_IObjectIdentity not supported returning NULL\n");
642         *ppv = NULL;
643     }else {
644         return FALSE;
645     }
646
647     if(*ppv)
648         IUnknown_AddRef((IUnknown*)*ppv);
649     return TRUE;
650 }
651
652 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
653 {
654     dispex->lpIDispatchExVtbl = &DispatchExVtbl;
655     dispex->outer = outer;
656     dispex->data = data;
657 }