Release 1.5.29.
[wine] / dlls / mshtml / dispex.c
1 /*
2  * Copyright 2008-2009 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 #include <stdarg.h>
20 #include <assert.h>
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29
30 #include "wine/debug.h"
31
32 #include "mshtml_private.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
35
36 #define MAX_ARGS 16
37
38 static CRITICAL_SECTION cs_dispex_static_data;
39 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
40 {
41     0, 0, &cs_dispex_static_data,
42     { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
43       0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
44 };
45 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
46
47
48 static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0};
49
50 typedef struct {
51     DISPID id;
52     BSTR name;
53     tid_t tid;
54     SHORT call_vtbl_off;
55     SHORT put_vtbl_off;
56     SHORT get_vtbl_off;
57     SHORT func_disp_idx;
58     USHORT argc;
59     VARTYPE prop_vt;
60     VARTYPE *arg_types;
61 } func_info_t;
62
63 struct dispex_data_t {
64     DWORD func_cnt;
65     func_info_t *funcs;
66     func_info_t **name_table;
67     DWORD func_disp_cnt;
68
69     struct list entry;
70 };
71
72 typedef struct {
73     VARIANT var;
74     LPWSTR name;
75     DWORD flags;
76 } dynamic_prop_t;
77
78 #define DYNPROP_DELETED    0x01
79
80 typedef struct {
81     DispatchEx dispex;
82     IUnknown IUnknown_iface;
83     LONG ref;
84     DispatchEx *obj;
85     func_info_t *info;
86 } func_disp_t;
87
88 typedef struct {
89     func_disp_t *func_obj;
90     IDispatch *val;
91 } func_obj_entry_t;
92
93 struct dispex_dynamic_data_t {
94     DWORD buf_size;
95     DWORD prop_cnt;
96     dynamic_prop_t *props;
97     func_obj_entry_t *func_disps;
98 };
99
100 #define DISPID_DYNPROP_0    0x50000000
101 #define DISPID_DYNPROP_MAX  0x5fffffff
102
103 #define FDEX_VERSION_MASK 0xf0000000
104
105 static ITypeLib *typelib;
106 static ITypeInfo *typeinfos[LAST_tid];
107 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
108
109 static REFIID tid_ids[] = {
110 #define XIID(iface) &IID_ ## iface,
111 #define XDIID(iface) &DIID_ ## iface,
112 TID_LIST
113 #undef XIID
114 #undef XDIID
115 };
116
117 static HRESULT load_typelib(void)
118 {
119     HRESULT hres;
120     ITypeLib *tl;
121
122     hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
123     if(FAILED(hres)) {
124         ERR("LoadRegTypeLib failed: %08x\n", hres);
125         return hres;
126     }
127
128     if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
129         ITypeLib_Release(tl);
130     return hres;
131 }
132
133 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
134 {
135     HRESULT hres;
136
137     if (!typelib)
138         hres = load_typelib();
139     if (!typelib)
140         return hres;
141
142     if(!typeinfos[tid]) {
143         ITypeInfo *ti;
144
145         hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti);
146         if(FAILED(hres)) {
147             ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
148             return hres;
149         }
150
151         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
152             ITypeInfo_Release(ti);
153     }
154
155     *typeinfo = typeinfos[tid];
156     return S_OK;
157 }
158
159 void release_typelib(void)
160 {
161     dispex_data_t *iter;
162     unsigned i;
163
164     while(!list_empty(&dispex_data_list)) {
165         iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
166         list_remove(&iter->entry);
167
168         for(i=0; i < iter->func_cnt; i++)
169             SysFreeString(iter->funcs[i].name);
170
171         heap_free(iter->funcs);
172         heap_free(iter->name_table);
173         heap_free(iter);
174     }
175
176     if(!typelib)
177         return;
178
179     for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
180         if(typeinfos[i])
181             ITypeInfo_Release(typeinfos[i]);
182
183     ITypeLib_Release(typelib);
184     DeleteCriticalSection(&cs_dispex_static_data);
185 }
186
187 HRESULT get_htmldoc_classinfo(ITypeInfo **typeinfo)
188 {
189     HRESULT hres;
190
191     if (!typelib)
192         hres = load_typelib();
193     if (!typelib)
194         return hres;
195
196     hres = ITypeLib_GetTypeInfoOfGuid(typelib, &CLSID_HTMLDocument, typeinfo);
197     if(FAILED(hres))
198         ERR("GetTypeInfoOfGuid failed: %08x\n", hres);
199     return hres;
200 }
201
202 /* Not all argument types are supported yet */
203 #define BUILTIN_ARG_TYPES_SWITCH                        \
204     CASE_VT(VT_I2, INT16, V_I2);                        \
205     CASE_VT(VT_I4, INT32, V_I4);                        \
206     CASE_VT(VT_R4, float, V_R4);                        \
207     CASE_VT(VT_BSTR, BSTR, V_BSTR);                     \
208     CASE_VT(VT_BOOL, VARIANT_BOOL, V_BOOL)
209
210 /* List all types used by IDispatchEx-based properties */
211 #define BUILTIN_TYPES_SWITCH                            \
212     BUILTIN_ARG_TYPES_SWITCH;                           \
213     CASE_VT(VT_VARIANT, VARIANT, *);                    \
214     CASE_VT(VT_PTR, void*, V_BYREF);                    \
215     CASE_VT(VT_UNKNOWN, IUnknown*, V_UNKNOWN);          \
216     CASE_VT(VT_DISPATCH, IDispatch*, V_DISPATCH)
217
218 static BOOL is_arg_type_supported(VARTYPE vt)
219 {
220     switch(vt) {
221 #define CASE_VT(x,a,b) case x: return TRUE
222     BUILTIN_ARG_TYPES_SWITCH;
223 #undef CASE_VT
224     }
225     return FALSE;
226 }
227
228 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti)
229 {
230     func_info_t *info;
231     HRESULT hres;
232
233     if(data->func_cnt && data->funcs[data->func_cnt-1].id == desc->memid) {
234         info = data->funcs+data->func_cnt-1;
235     }else {
236         if(data->func_cnt == *size)
237             data->funcs = heap_realloc_zero(data->funcs, (*size <<= 1)*sizeof(func_info_t));
238
239         info = data->funcs+data->func_cnt;
240         hres = ITypeInfo_GetDocumentation(dti, desc->memid, &info->name, NULL, NULL, NULL);
241         if(FAILED(hres))
242             return;
243
244         data->func_cnt++;
245
246         info->id = desc->memid;
247         info->tid = tid;
248         info->func_disp_idx = -1;
249         info->prop_vt = VT_EMPTY;
250     }
251
252     if(desc->invkind & DISPATCH_METHOD) {
253         unsigned i;
254
255         info->func_disp_idx = data->func_disp_cnt++;
256         info->argc = desc->cParams;
257
258         assert(info->argc < MAX_ARGS);
259         assert(desc->funckind == FUNC_DISPATCH);
260
261         info->arg_types = heap_alloc(sizeof(*info->arg_types) * info->argc);
262         if(!info->arg_types)
263             return; /* FIXME: real error instead of fallback */
264
265         for(i=0; i < info->argc; i++)
266             info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt;
267
268         info->prop_vt = desc->elemdescFunc.tdesc.vt;
269         if(info->prop_vt != VT_VOID && !is_arg_type_supported(info->prop_vt)) {
270             TRACE("%s: return type %d\n", debugstr_w(info->name), info->arg_types[i]);
271             return; /* Fallback to ITypeInfo::Invoke */
272         }
273
274         if(desc->cParamsOpt) {
275             TRACE("%s: optional params\n", debugstr_w(info->name));
276             return; /* Fallback to ITypeInfo::Invoke */
277         }
278
279         for(i=0; i < info->argc; i++) {
280             if(!is_arg_type_supported(info->arg_types[i])) {
281                 return; /* Fallback to ITypeInfo for unsupported arg types */
282             }
283
284             if(desc->lprgelemdescParam[i].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
285                 TRACE("%s param %d: default value\n", debugstr_w(info->name), i);
286                 return; /* Fallback to ITypeInfo::Invoke */
287             }
288         }
289
290         assert(info->argc <= MAX_ARGS);
291         assert(desc->callconv == CC_STDCALL);
292
293         info->call_vtbl_off = desc->oVft/sizeof(void*);
294     }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) {
295         VARTYPE vt = VT_EMPTY;
296
297         if(desc->invkind & DISPATCH_PROPERTYGET) {
298             vt = desc->elemdescFunc.tdesc.vt;
299             info->get_vtbl_off = desc->oVft/sizeof(void*);
300         }
301         if(desc->invkind & DISPATCH_PROPERTYPUT) {
302             assert(desc->cParams == 1);
303             vt = desc->lprgelemdescParam->tdesc.vt;
304             info->put_vtbl_off = desc->oVft/sizeof(void*);
305         }
306
307         assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt);
308         info->prop_vt = vt;
309     }
310 }
311
312 static int dispid_cmp(const void *p1, const void *p2)
313 {
314     return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
315 }
316
317 static int func_name_cmp(const void *p1, const void *p2)
318 {
319     return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
320 }
321
322 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
323 {
324     const tid_t *tid = This->data->iface_tids;
325     FUNCDESC *funcdesc;
326     dispex_data_t *data;
327     DWORD size = 16, i;
328     ITypeInfo *ti, *dti;
329     HRESULT hres;
330
331     TRACE("(%p)\n", This);
332
333     if(This->data->disp_tid) {
334         hres = get_typeinfo(This->data->disp_tid, &dti);
335         if(FAILED(hres)) {
336             ERR("Could not get disp type info: %08x\n", hres);
337             return NULL;
338         }
339     }
340
341     data = heap_alloc(sizeof(dispex_data_t));
342     data->func_cnt = 0;
343     data->func_disp_cnt = 0;
344     data->funcs = heap_alloc_zero(size*sizeof(func_info_t));
345     list_add_tail(&dispex_data_list, &data->entry);
346
347     while(*tid) {
348         hres = get_typeinfo(*tid, &ti);
349         if(FAILED(hres))
350             break;
351
352         i=7;
353         while(1) {
354             hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
355             if(FAILED(hres))
356                 break;
357
358             add_func_info(data, &size, *tid, funcdesc, dti);
359             ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
360         }
361
362         tid++;
363     }
364
365     if(!data->func_cnt) {
366         heap_free(data->funcs);
367         data->name_table = NULL;
368         data->funcs = NULL;
369         return data;
370     }
371
372
373     data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
374     qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
375
376     for(i = 1; i < data->func_cnt && data->funcs[i-1].id != data->funcs[i].id; i++);
377     if(i < data->func_cnt) {
378         unsigned j = i--;
379
380         /* We have at least one duplicated property. This may happen if more than one
381          * interface implements the same property. We have to remove these duplicated
382          * entries. */
383
384         while(j < data->func_cnt) {
385             while(j+1 < data->func_cnt && data->funcs[j+1].id == data->funcs[j].id)
386                 j++;
387             data->funcs[i++] = data->funcs[j++];
388         }
389         data->func_cnt = i;
390     }
391
392     data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
393     for(i=0; i < data->func_cnt; i++)
394         data->name_table[i] = data->funcs+i;
395     qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
396
397     return data;
398 }
399
400 static int id_cmp(const void *p1, const void *p2)
401 {
402     return *(const DISPID*)p1 - *(const DISPID*)p2;
403 }
404
405 HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
406 {
407     unsigned i, func_cnt;
408     FUNCDESC *funcdesc;
409     ITypeInfo *ti;
410     TYPEATTR *attr;
411     DISPID *ids;
412     HRESULT hres;
413
414     hres = get_typeinfo(tid, &ti);
415     if(FAILED(hres))
416         return hres;
417
418     hres = ITypeInfo_GetTypeAttr(ti, &attr);
419     if(FAILED(hres)) {
420         ITypeInfo_Release(ti);
421         return hres;
422     }
423
424     func_cnt = attr->cFuncs;
425     ITypeInfo_ReleaseTypeAttr(ti, attr);
426
427     ids = heap_alloc(func_cnt*sizeof(DISPID));
428     if(!ids) {
429         ITypeInfo_Release(ti);
430         return E_OUTOFMEMORY;
431     }
432
433     for(i=0; i < func_cnt; i++) {
434         hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
435         if(FAILED(hres))
436             break;
437
438         ids[i] = funcdesc->memid;
439         ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
440     }
441
442     ITypeInfo_Release(ti);
443     if(FAILED(hres)) {
444         heap_free(ids);
445         return hres;
446     }
447
448     qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
449
450     *ret_size = func_cnt;
451     *ret = ids;
452     return S_OK;
453 }
454
455 static dispex_data_t *get_dispex_data(DispatchEx *This)
456 {
457     if(This->data->data)
458         return This->data->data;
459
460     EnterCriticalSection(&cs_dispex_static_data);
461
462     if(!This->data->data)
463         This->data->data = preprocess_dispex_data(This);
464
465     LeaveCriticalSection(&cs_dispex_static_data);
466
467     return This->data->data;
468 }
469
470 static inline BOOL is_custom_dispid(DISPID id)
471 {
472     return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
473 }
474
475 static inline BOOL is_dynamic_dispid(DISPID id)
476 {
477     return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
478 }
479
480 dispex_prop_type_t get_dispid_type(DISPID id)
481 {
482     if(is_dynamic_dispid(id))
483         return DISPEXPROP_DYNAMIC;
484     if(is_custom_dispid(id))
485         return DISPEXPROP_CUSTOM;
486     return DISPEXPROP_BUILTIN;
487 }
488
489 static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
490 {
491     if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
492         V_VT(dest) = VT_BSTR;
493         V_BSTR(dest) = NULL;
494         return S_OK;
495     }
496
497     return VariantCopy(dest, src);
498 }
499
500 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This)
501 {
502     if(This->dynamic_data)
503         return This->dynamic_data;
504
505     This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
506     if(!This->dynamic_data)
507         return NULL;
508
509     if(This->data->vtbl && This->data->vtbl->populate_props)
510         This->data->vtbl->populate_props(This);
511
512     return This->dynamic_data;
513 }
514
515 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
516 {
517     const BOOL alloc = flags & fdexNameEnsure;
518     dispex_dynamic_data_t *data;
519     dynamic_prop_t *prop;
520
521     data = get_dynamic_data(This);
522     if(!data)
523         return E_OUTOFMEMORY;
524
525     for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
526         if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) {
527             if(prop->flags & DYNPROP_DELETED) {
528                 if(!alloc)
529                     return DISP_E_UNKNOWNNAME;
530                 prop->flags &= ~DYNPROP_DELETED;
531             }
532             *ret = prop;
533             return S_OK;
534         }
535     }
536
537     if(!alloc)
538         return DISP_E_UNKNOWNNAME;
539
540     TRACE("creating dynamic prop %s\n", debugstr_w(name));
541
542     if(!data->buf_size) {
543         data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
544         if(!data->props)
545             return E_OUTOFMEMORY;
546         data->buf_size = 4;
547     }else if(data->buf_size == data->prop_cnt) {
548         dynamic_prop_t *new_props;
549
550         new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
551         if(!new_props)
552             return E_OUTOFMEMORY;
553
554         data->props = new_props;
555         data->buf_size <<= 1;
556     }
557
558     prop = data->props + data->prop_cnt;
559
560     prop->name = heap_strdupW(name);
561     if(!prop->name)
562         return E_OUTOFMEMORY;
563
564     VariantInit(&prop->var);
565     prop->flags = 0;
566     data->prop_cnt++;
567     *ret = prop;
568     return S_OK;
569 }
570
571 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
572 {
573     dynamic_prop_t *prop;
574     HRESULT hres;
575
576     hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
577     if(FAILED(hres))
578         return hres;
579
580     *ret = &prop->var;
581     return S_OK;
582 }
583
584 HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, DISPID *id)
585 {
586     dynamic_prop_t *prop;
587     HRESULT hres;
588
589     hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop);
590     if(FAILED(hres))
591         return hres;
592
593     *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props);
594     return S_OK;
595 }
596
597 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
598         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
599 {
600     if(This->data->vtbl && This->data->vtbl->value)
601         return This->data->vtbl->value(This, lcid, flags, params, res, ei, caller);
602
603     switch(flags) {
604     case DISPATCH_PROPERTYGET:
605         V_VT(res) = VT_BSTR;
606         V_BSTR(res) = SysAllocString(objectW);
607         if(!V_BSTR(res))
608             return E_OUTOFMEMORY;
609         break;
610     default:
611         FIXME("Unimplemented flags %x\n", flags);
612         return E_NOTIMPL;
613     }
614
615     return S_OK;
616 }
617
618 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
619         EXCEPINFO *ei)
620 {
621     ITypeInfo *ti;
622     IUnknown *unk;
623     UINT argerr=0;
624     HRESULT hres;
625
626     hres = get_typeinfo(func->tid, &ti);
627     if(FAILED(hres)) {
628         ERR("Could not get type info: %08x\n", hres);
629         return hres;
630     }
631
632     hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
633     if(FAILED(hres)) {
634         ERR("Could not get iface %s: %08x\n", debugstr_guid(tid_ids[func->tid]), hres);
635         return E_FAIL;
636     }
637
638     hres = ITypeInfo_Invoke(ti, unk, func->id, flags, dp, res, ei, &argerr);
639
640     IUnknown_Release(unk);
641     return hres;
642 }
643
644 static inline func_disp_t *impl_from_IUnknown(IUnknown *iface)
645 {
646     return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface);
647 }
648
649 static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
650 {
651     func_disp_t *This = impl_from_IUnknown(iface);
652
653     if(IsEqualGUID(&IID_IUnknown, riid)) {
654         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
655         *ppv = &This->IUnknown_iface;
656     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
657         return *ppv ? S_OK : E_NOINTERFACE;
658     }else {
659         *ppv = NULL;
660         return E_NOINTERFACE;
661     }
662
663     IUnknown_AddRef((IUnknown*)*ppv);
664     return S_OK;
665 }
666
667 static ULONG WINAPI Function_AddRef(IUnknown *iface)
668 {
669     func_disp_t *This = impl_from_IUnknown(iface);
670     LONG ref = InterlockedIncrement(&This->ref);
671
672     TRACE("(%p) ref=%d\n", This, ref);
673
674     return ref;
675 }
676
677 static ULONG WINAPI Function_Release(IUnknown *iface)
678 {
679     func_disp_t *This = impl_from_IUnknown(iface);
680     LONG ref = InterlockedDecrement(&This->ref);
681
682     TRACE("(%p) ref=%d\n", This, ref);
683
684     if(!ref) {
685         assert(!This->obj);
686         release_dispex(&This->dispex);
687         heap_free(This);
688     }
689
690     return ref;
691 }
692
693 static const IUnknownVtbl FunctionUnkVtbl = {
694     Function_QueryInterface,
695     Function_AddRef,
696     Function_Release
697 };
698
699 static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface)
700 {
701     return CONTAINING_RECORD(iface, func_disp_t, dispex);
702 }
703
704 static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params,
705         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
706 {
707     func_disp_t *This = impl_from_DispatchEx(dispex);
708     HRESULT hres;
709
710     switch(flags) {
711     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
712         if(!res)
713             return E_INVALIDARG;
714         /* fall through */
715     case DISPATCH_METHOD:
716         if(!This->obj)
717             return E_UNEXPECTED;
718         hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei);
719         break;
720     default:
721         FIXME("Unimplemented flags %x\n", flags);
722         hres = E_NOTIMPL;
723     }
724
725     return hres;
726 }
727
728 static const dispex_static_data_vtbl_t function_dispex_vtbl = {
729     function_value,
730     NULL,
731     NULL,
732     NULL
733 };
734
735 static const tid_t function_iface_tids[] = {0};
736
737 static dispex_static_data_t function_dispex = {
738     &function_dispex_vtbl,
739     NULL_tid,
740     NULL,
741     function_iface_tids
742 };
743
744 static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
745 {
746     func_disp_t *ret;
747
748     ret = heap_alloc_zero(sizeof(func_disp_t));
749     if(!ret)
750         return NULL;
751
752     ret->IUnknown_iface.lpVtbl = &FunctionUnkVtbl;
753     init_dispex(&ret->dispex, &ret->IUnknown_iface,  &function_dispex);
754     ret->ref = 1;
755     ret->obj = obj;
756     ret->info = info;
757
758     return ret;
759 }
760
761 static HRESULT invoke_disp_value(DispatchEx *This, IDispatch *func_disp, LCID lcid, WORD flags, DISPPARAMS *dp,
762         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
763 {
764     DISPID named_arg = DISPID_THIS;
765     DISPPARAMS new_dp = {NULL, &named_arg, 0, 1};
766     IDispatchEx *dispex;
767     HRESULT hres;
768
769     if(dp->cNamedArgs) {
770         FIXME("named args not supported\n");
771         return E_NOTIMPL;
772     }
773
774     new_dp.rgvarg = heap_alloc((dp->cArgs+1)*sizeof(VARIANTARG));
775     if(!new_dp.rgvarg)
776         return E_OUTOFMEMORY;
777
778     new_dp.cArgs = dp->cArgs+1;
779     memcpy(new_dp.rgvarg+1, dp->rgvarg, dp->cArgs*sizeof(VARIANTARG));
780
781     V_VT(new_dp.rgvarg) = VT_DISPATCH;
782     V_DISPATCH(new_dp.rgvarg) = (IDispatch*)&This->IDispatchEx_iface;
783
784     hres = IDispatch_QueryInterface(func_disp, &IID_IDispatchEx, (void**)&dispex);
785     TRACE(">>>\n");
786     if(SUCCEEDED(hres)) {
787         hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, flags, &new_dp, res, ei, caller);
788         IDispatchEx_Release(dispex);
789     }else {
790         ULONG err = 0;
791         hres = IDispatch_Invoke(func_disp, DISPID_VALUE, &IID_NULL, lcid, flags, &new_dp, res, ei, &err);
792     }
793     if(SUCCEEDED(hres))
794         TRACE("<<< %s\n", debugstr_variant(res));
795     else
796         WARN("<<< %08x\n", hres);
797
798     heap_free(new_dp.rgvarg);
799     return hres;
800 }
801
802 static HRESULT get_func_obj_entry(DispatchEx *This, func_info_t *func, func_obj_entry_t **ret)
803 {
804     dispex_dynamic_data_t *dynamic_data;
805
806     dynamic_data = get_dynamic_data(This);
807     if(!dynamic_data)
808         return E_OUTOFMEMORY;
809
810     if(!dynamic_data->func_disps) {
811         dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(*dynamic_data->func_disps));
812         if(!dynamic_data->func_disps)
813             return E_OUTOFMEMORY;
814     }
815
816     if(!dynamic_data->func_disps[func->func_disp_idx].func_obj) {
817         func_disp_t *func_obj;
818
819         func_obj = create_func_disp(This, func);
820         if(!func_obj)
821             return E_OUTOFMEMORY;
822
823         dynamic_data->func_disps[func->func_disp_idx].func_obj = func_obj;
824
825         IDispatchEx_AddRef(&func_obj->dispex.IDispatchEx_iface);
826         dynamic_data->func_disps[func->func_disp_idx].val = (IDispatch*)&func_obj->dispex.IDispatchEx_iface;
827     }
828
829     *ret = dynamic_data->func_disps+func->func_disp_idx;
830     return S_OK;
831 }
832
833 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
834 {
835     int min, max, n;
836
837     min = 0;
838     max = data->func_cnt-1;
839
840     while(min <= max) {
841         n = (min+max)/2;
842
843         if(data->funcs[n].id == id) {
844             *ret = data->funcs+n;
845             return S_OK;
846         }
847
848         if(data->funcs[n].id < id)
849             min = n+1;
850         else
851             max = n-1;
852     }
853
854     WARN("invalid id %x\n", id);
855     return DISP_E_UNKNOWNNAME;
856 }
857
858 static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
859 {
860     dispex_data_t *data;
861     int min, max, n, c;
862
863     data = get_dispex_data(This);
864     if(!data)
865         return E_FAIL;
866
867     min = 0;
868     max = data->func_cnt-1;
869
870     while(min <= max) {
871         n = (min+max)/2;
872
873         c = strcmpiW(data->name_table[n]->name, name);
874         if(!c) {
875             if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, name))
876                 break;
877
878             *ret = data->name_table[n]->id;
879             return S_OK;
880         }
881
882         if(c > 0)
883             max = n-1;
884         else
885             min = n+1;
886     }
887
888     if(This->data->vtbl && This->data->vtbl->get_dispid) {
889         HRESULT hres;
890
891         hres = This->data->vtbl->get_dispid(This, name, grfdex, ret);
892         if(hres != DISP_E_UNKNOWNNAME)
893             return hres;
894     }
895
896     return DISP_E_UNKNOWNNAME;
897 }
898
899 static HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller)
900 {
901     V_VT(dst) = VT_EMPTY;
902
903     if(caller) {
904         IVariantChangeType *change_type = NULL;
905         HRESULT hres;
906
907         hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type);
908         if(SUCCEEDED(hres)) {
909             hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt);
910             IVariantChangeType_Release(change_type);
911             return hres;
912         }
913     }
914
915     switch(vt) {
916     case VT_BOOL:
917         if(V_VT(src) == VT_BSTR) {
918             V_VT(dst) = VT_BOOL;
919             V_BOOL(dst) = V_BSTR(src) && *V_BSTR(src) ? VARIANT_TRUE : VARIANT_FALSE;
920             return S_OK;
921         }
922         break;
923     }
924
925     return VariantChangeType(dst, src, 0, vt);
926 }
927
928 static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res)
929 {
930     IUnknown *iface;
931     HRESULT hres;
932
933     if(dp && dp->cArgs) {
934         FIXME("cArgs %d\n", dp->cArgs);
935         return E_NOTIMPL;
936     }
937
938     assert(func->get_vtbl_off);
939
940     hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
941     if(SUCCEEDED(hres)) {
942         switch(func->prop_vt) {
943 #define CASE_VT(vt,type,access) \
944         case vt: { \
945             type val; \
946             hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \
947             if(SUCCEEDED(hres)) \
948                 access(res) = val; \
949             } \
950             break
951         BUILTIN_TYPES_SWITCH;
952 #undef CASE_VT
953         default:
954             FIXME("Unhandled vt %d\n", func->prop_vt);
955             hres = E_NOTIMPL;
956         }
957         IUnknown_Release(iface);
958     }
959
960     if(FAILED(hres))
961         return hres;
962
963     if(func->prop_vt != VT_VARIANT)
964         V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt;
965     return S_OK;
966 }
967
968 static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller)
969 {
970     VARIANT *v, tmpv;
971     IUnknown *iface;
972     HRESULT hres;
973
974     if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
975             || dp->cNamedArgs > 1) {
976         FIXME("invalid args\n");
977         return E_INVALIDARG;
978     }
979
980     if(!func->put_vtbl_off) {
981         FIXME("No setter\n");
982         return E_FAIL;
983     }
984
985     v = dp->rgvarg;
986     if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) {
987         hres = change_type(&tmpv, v, func->prop_vt, caller);
988         if(FAILED(hres))
989             return hres;
990         v = &tmpv;
991     }
992
993     hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
994     if(SUCCEEDED(hres)) {
995         switch(func->prop_vt) {
996 #define CASE_VT(vt,type,access) \
997         case vt: \
998             hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \
999             break
1000         BUILTIN_TYPES_SWITCH;
1001 #undef CASE_VT
1002         default:
1003             FIXME("Unimplemented vt %d\n", func->prop_vt);
1004             hres = E_NOTIMPL;
1005         }
1006
1007         IUnknown_Release(iface);
1008     }
1009
1010     if(v == &tmpv)
1011         VariantClear(v);
1012     return hres;
1013 }
1014
1015 static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res, IServiceProvider *caller)
1016 {
1017     VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres;
1018     unsigned i, nconv = 0;
1019     IUnknown *iface;
1020     HRESULT hres;
1021
1022     if(dp->cNamedArgs) {
1023         FIXME("Named arguments not supported\n");
1024         return E_NOTIMPL;
1025     }
1026
1027     if(dp->cArgs != func->argc) {
1028         FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs);
1029         return E_INVALIDARG;
1030     }
1031
1032     hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1033     if(FAILED(hres))
1034         return hres;
1035
1036     for(i=0; i < func->argc; i++) {
1037         arg = dp->rgvarg+dp->cArgs-i-1;
1038         if(func->arg_types[i] == V_VT(arg)) {
1039             arg_ptrs[i] = arg;
1040         }else {
1041             hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller);
1042             if(FAILED(hres))
1043                 break;
1044             arg_ptrs[i] = arg_buf + nconv++;
1045         }
1046     }
1047
1048     if(SUCCEEDED(hres)) {
1049         if(func->prop_vt == VT_VOID) {
1050             V_VT(&retv) = VT_EMPTY;
1051         }else {
1052             V_VT(&retv) = func->prop_vt;
1053             arg_ptrs[func->argc] = &ret_ref;
1054             V_VT(&ret_ref) = VT_BYREF|func->prop_vt;
1055
1056             switch(func->prop_vt) {
1057 #define CASE_VT(vt,type,access)                         \
1058             case vt:                                    \
1059                 V_BYREF(&ret_ref) = &access(&retv);     \
1060                 break
1061             BUILTIN_TYPES_SWITCH;
1062 #undef CASE_VT
1063             default:
1064                 assert(0);
1065             }
1066         }
1067
1068         V_VT(&vhres) = VT_ERROR;
1069         hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR,
1070                     func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres);
1071     }
1072
1073     while(nconv--)
1074         VariantClear(arg_buf+nconv);
1075     IUnknown_Release(iface);
1076     if(FAILED(hres))
1077         return hres;
1078     if(FAILED(V_ERROR(&vhres)))
1079         return V_ERROR(&vhres);
1080
1081     if(res)
1082         *res = retv;
1083     else
1084         VariantClear(&retv);
1085     return V_ERROR(&vhres);
1086 }
1087
1088 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
1089         EXCEPINFO *ei, IServiceProvider *caller)
1090 {
1091     HRESULT hres;
1092
1093     switch(flags) {
1094     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1095         if(!res)
1096             return E_INVALIDARG;
1097         /* fall through */
1098     case DISPATCH_METHOD:
1099         if(This->dynamic_data && This->dynamic_data->func_disps
1100            && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1101             func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx;
1102
1103             if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != entry->val) {
1104                 if(!entry->val) {
1105                     FIXME("Calling null\n");
1106                     return E_FAIL;
1107                 }
1108
1109                 hres = invoke_disp_value(This, entry->val, 0, flags, dp, res, ei, NULL);
1110                 break;
1111             }
1112         }
1113
1114         if(func->call_vtbl_off)
1115             hres = invoke_builtin_function(This, func, dp, res, caller);
1116         else
1117             hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1118         break;
1119     case DISPATCH_PROPERTYGET: {
1120         func_obj_entry_t *entry;
1121
1122         if(func->id == DISPID_VALUE) {
1123             BSTR ret;
1124
1125             ret = SysAllocString(objectW);
1126             if(!ret)
1127                 return E_OUTOFMEMORY;
1128
1129             V_VT(res) = VT_BSTR;
1130             V_BSTR(res) = ret;
1131             return S_OK;
1132         }
1133
1134         hres = get_func_obj_entry(This, func, &entry);
1135         if(FAILED(hres))
1136             return hres;
1137
1138         V_VT(res) = VT_DISPATCH;
1139         V_DISPATCH(res) = entry->val;
1140         if(V_DISPATCH(res))
1141             IDispatch_AddRef(V_DISPATCH(res));
1142         hres = S_OK;
1143         break;
1144     }
1145     case DISPATCH_PROPERTYPUT: {
1146         func_obj_entry_t *entry;
1147         VARIANT *v;
1148
1149         if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1150            || dp->cNamedArgs > 1) {
1151             FIXME("invalid args\n");
1152             return E_INVALIDARG;
1153         }
1154
1155         v = dp->rgvarg;
1156         /* FIXME: not exactly right */
1157         if(V_VT(v) != VT_DISPATCH)
1158             return E_NOTIMPL;
1159
1160         hres = get_func_obj_entry(This, func, &entry);
1161         if(FAILED(hres))
1162             return hres;
1163
1164         if(entry->val)
1165             IDispatch_Release(entry->val);
1166         entry->val = V_DISPATCH(v);
1167         if(entry->val)
1168             IDispatch_AddRef(entry->val);
1169         hres = S_OK;
1170         break;
1171     }
1172     default:
1173         FIXME("Unimplemented flags %x\n", flags);
1174         hres = E_NOTIMPL;
1175     }
1176
1177     return hres;
1178 }
1179
1180 static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
1181         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1182 {
1183     dispex_data_t *data;
1184     func_info_t *func;
1185     HRESULT hres;
1186
1187     data = get_dispex_data(This);
1188     if(!data)
1189         return E_FAIL;
1190
1191     hres = get_builtin_func(data, id, &func);
1192     if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
1193         return dispex_value(This, lcid, flags, dp, res, ei, caller);
1194     if(FAILED(hres))
1195         return hres;
1196
1197     if(func->func_disp_idx != -1)
1198         return function_invoke(This, func, flags, dp, res, ei, caller);
1199
1200     switch(flags) {
1201     case DISPATCH_PROPERTYPUT:
1202         if(res)
1203             V_VT(res) = VT_EMPTY;
1204         hres = builtin_propput(This, func, dp, caller);
1205         break;
1206     case DISPATCH_PROPERTYGET:
1207         hres = builtin_propget(This, func, dp, res);
1208         break;
1209     default:
1210         if(!func->get_vtbl_off) {
1211             hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1212         }else {
1213             VARIANT v;
1214
1215             hres = builtin_propget(This, func, NULL, &v);
1216             if(FAILED(hres))
1217                 return hres;
1218
1219             if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) {
1220                 if(V_VT(&v) != VT_DISPATCH) {
1221                     FIXME("Not a function %s\n", debugstr_variant(&v));
1222                     VariantClear(&v);
1223                     return E_FAIL;
1224                 }
1225
1226                 hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller);
1227                 IDispatch_Release(V_DISPATCH(&v));
1228             }else if(res) {
1229                 *res = v;
1230             }else {
1231                 VariantClear(&v);
1232             }
1233         }
1234     }
1235
1236     return hres;
1237 }
1238
1239 HRESULT remove_prop(DispatchEx *This, BSTR name, VARIANT_BOOL *success)
1240 {
1241     dynamic_prop_t *prop;
1242     DISPID id;
1243     HRESULT hres;
1244
1245     hres = get_builtin_id(This, name, 0, &id);
1246     if(hres == S_OK) {
1247         DISPID named_id = DISPID_PROPERTYPUT;
1248         VARIANT var;
1249         DISPPARAMS dp = {&var,&named_id,1,1};
1250         EXCEPINFO ei;
1251
1252         V_VT(&var) = VT_EMPTY;
1253         memset(&ei, 0, sizeof(ei));
1254         hres = invoke_builtin_prop(This, id, 0, DISPATCH_PROPERTYPUT, &dp, NULL, &ei, NULL);
1255         if(FAILED(hres))
1256             return hres;
1257
1258         *success = VARIANT_TRUE;
1259         return S_OK;
1260     }
1261
1262     hres = get_dynamic_prop(This, name, 0, &prop);
1263     if(FAILED(hres)) {
1264         if(hres != DISP_E_UNKNOWNNAME)
1265             return hres;
1266         *success = VARIANT_FALSE;
1267         return S_OK;
1268     }
1269
1270     VariantClear(&prop->var);
1271     prop->flags |= DYNPROP_DELETED;
1272     *success = VARIANT_TRUE;
1273     return S_OK;
1274 }
1275
1276 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
1277 {
1278     return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface);
1279 }
1280
1281 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1282 {
1283     DispatchEx *This = impl_from_IDispatchEx(iface);
1284
1285     return IUnknown_QueryInterface(This->outer, riid, ppv);
1286 }
1287
1288 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1289 {
1290     DispatchEx *This = impl_from_IDispatchEx(iface);
1291
1292     return IUnknown_AddRef(This->outer);
1293 }
1294
1295 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1296 {
1297     DispatchEx *This = impl_from_IDispatchEx(iface);
1298
1299     return IUnknown_Release(This->outer);
1300 }
1301
1302 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1303 {
1304     DispatchEx *This = impl_from_IDispatchEx(iface);
1305
1306     TRACE("(%p)->(%p)\n", This, pctinfo);
1307
1308     *pctinfo = 1;
1309     return S_OK;
1310 }
1311
1312 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
1313                                               LCID lcid, ITypeInfo **ppTInfo)
1314 {
1315     DispatchEx *This = impl_from_IDispatchEx(iface);
1316     HRESULT hres;
1317
1318     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1319
1320     hres = get_typeinfo(This->data->disp_tid, ppTInfo);
1321     if(FAILED(hres))
1322         return hres;
1323
1324     ITypeInfo_AddRef(*ppTInfo);
1325     return S_OK;
1326 }
1327
1328 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1329                                                 LPOLESTR *rgszNames, UINT cNames,
1330                                                 LCID lcid, DISPID *rgDispId)
1331 {
1332     DispatchEx *This = impl_from_IDispatchEx(iface);
1333     UINT i;
1334     HRESULT hres;
1335
1336     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1337           lcid, rgDispId);
1338
1339     for(i=0; i < cNames; i++) {
1340         hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
1341         if(FAILED(hres))
1342             return hres;
1343     }
1344
1345     return S_OK;
1346 }
1347
1348 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1349                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1350                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1351 {
1352     DispatchEx *This = impl_from_IDispatchEx(iface);
1353
1354     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1355           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1356
1357     return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams,
1358             pVarResult, pExcepInfo, NULL);
1359 }
1360
1361 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1362 {
1363     DispatchEx *This = impl_from_IDispatchEx(iface);
1364     dynamic_prop_t *dprop;
1365     HRESULT hres;
1366
1367     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1368
1369     if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1370         FIXME("Unsupported grfdex %x\n", grfdex);
1371
1372     hres = get_builtin_id(This, bstrName, grfdex, pid);
1373     if(hres != DISP_E_UNKNOWNNAME)
1374         return hres;
1375
1376     hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
1377     if(FAILED(hres))
1378         return hres;
1379
1380     *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
1381     return S_OK;
1382 }
1383
1384 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1385         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1386 {
1387     DispatchEx *This = impl_from_IDispatchEx(iface);
1388     HRESULT hres;
1389
1390     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1391
1392     switch(get_dispid_type(id)) {
1393     case DISPEXPROP_CUSTOM:
1394         if(!This->data->vtbl || !This->data->vtbl->invoke)
1395             return DISP_E_UNKNOWNNAME;
1396         return This->data->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1397
1398     case DISPEXPROP_DYNAMIC: {
1399         DWORD idx = id - DISPID_DYNPROP_0;
1400         dynamic_prop_t *prop;
1401
1402         if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1403             return DISP_E_UNKNOWNNAME;
1404
1405         prop = This->dynamic_data->props+idx;
1406
1407         switch(wFlags) {
1408         case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1409             if(!pvarRes)
1410                 return E_INVALIDARG;
1411             /* fall through */
1412         case DISPATCH_METHOD:
1413             if(V_VT(&prop->var) != VT_DISPATCH) {
1414                 FIXME("invoke %s\n", debugstr_variant(&prop->var));
1415                 return E_NOTIMPL;
1416             }
1417
1418             return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1419         case DISPATCH_PROPERTYGET:
1420             if(prop->flags & DYNPROP_DELETED)
1421                 return DISP_E_UNKNOWNNAME;
1422             V_VT(pvarRes) = VT_EMPTY;
1423             return variant_copy(pvarRes, &prop->var);
1424         case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF:
1425         case DISPATCH_PROPERTYPUT:
1426             if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1427                || pdp->cNamedArgs > 1) {
1428                 FIXME("invalid args\n");
1429                 return E_INVALIDARG;
1430             }
1431
1432             TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
1433             VariantClear(&prop->var);
1434             hres = variant_copy(&prop->var, pdp->rgvarg);
1435             if(FAILED(hres))
1436                 return hres;
1437
1438             prop->flags &= ~DYNPROP_DELETED;
1439             return S_OK;
1440         default:
1441             FIXME("unhandled wFlags %x\n", wFlags);
1442             return E_NOTIMPL;
1443         }
1444     }
1445     case DISPEXPROP_BUILTIN:
1446         if(wFlags == DISPATCH_CONSTRUCT) {
1447             if(id == DISPID_VALUE) {
1448                 if(This->data->vtbl && This->data->vtbl->value) {
1449                     return This->data->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1450                 }
1451                 FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
1452                 return E_FAIL;
1453             }
1454             FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
1455             return E_FAIL;
1456         }
1457
1458         return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1459     default:
1460         assert(0);
1461         return E_FAIL;
1462     }
1463 }
1464
1465 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
1466 {
1467     DispatchEx *This = impl_from_IDispatchEx(iface);
1468
1469     TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
1470
1471     /* Not implemented by IE */
1472     return E_NOTIMPL;
1473 }
1474
1475 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1476 {
1477     DispatchEx *This = impl_from_IDispatchEx(iface);
1478
1479     TRACE("(%p)->(%x)\n", This, id);
1480
1481     /* Not implemented by IE */
1482     return E_NOTIMPL;
1483 }
1484
1485 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1486 {
1487     DispatchEx *This = impl_from_IDispatchEx(iface);
1488     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
1489     return E_NOTIMPL;
1490 }
1491
1492 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1493 {
1494     DispatchEx *This = impl_from_IDispatchEx(iface);
1495     dispex_data_t *data;
1496     func_info_t *func;
1497     HRESULT hres;
1498
1499     TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
1500
1501     if(is_dynamic_dispid(id)) {
1502         DWORD idx = id - DISPID_DYNPROP_0;
1503
1504         if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1505             return DISP_E_UNKNOWNNAME;
1506
1507         *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
1508         if(!*pbstrName)
1509             return E_OUTOFMEMORY;
1510
1511         return S_OK;
1512     }
1513
1514     data = get_dispex_data(This);
1515     if(!data)
1516         return E_FAIL;
1517
1518     hres = get_builtin_func(data, id, &func);
1519     if(FAILED(hres))
1520         return hres;
1521
1522     *pbstrName = SysAllocString(func->name);
1523     if(!*pbstrName)
1524         return E_OUTOFMEMORY;
1525     return S_OK;
1526 }
1527
1528 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1529 {
1530     DispatchEx *This = impl_from_IDispatchEx(iface);
1531     dispex_data_t *data;
1532     func_info_t *func;
1533     HRESULT hres;
1534
1535     TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
1536
1537     if(is_dynamic_dispid(id)) {
1538         DWORD idx = id - DISPID_DYNPROP_0;
1539
1540         if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1541             return DISP_E_UNKNOWNNAME;
1542
1543         while(++idx < This->dynamic_data->prop_cnt && This->dynamic_data->props[idx].flags & DYNPROP_DELETED);
1544
1545         if(idx == This->dynamic_data->prop_cnt) {
1546             *pid = DISPID_STARTENUM;
1547             return S_FALSE;
1548         }
1549
1550         *pid = DISPID_DYNPROP_0+idx;
1551         return S_OK;
1552     }
1553
1554     data = get_dispex_data(This);
1555     if(!data)
1556         return E_FAIL;
1557
1558     if(id == DISPID_STARTENUM) {
1559         func = data->funcs;
1560     }else {
1561         hres = get_builtin_func(data, id, &func);
1562         if(FAILED(hres))
1563             return hres;
1564         func++;
1565     }
1566
1567     while(func < data->funcs+data->func_cnt) {
1568         /* FIXME: Skip hidden properties */
1569         if(func->func_disp_idx == -1) {
1570             *pid = func->id;
1571             return S_OK;
1572         }
1573         func++;
1574     }
1575
1576     if(get_dynamic_data(This) && This->dynamic_data->prop_cnt) {
1577         *pid = DISPID_DYNPROP_0;
1578         return S_OK;
1579     }
1580
1581     *pid = DISPID_STARTENUM;
1582     return S_FALSE;
1583 }
1584
1585 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1586 {
1587     DispatchEx *This = impl_from_IDispatchEx(iface);
1588     FIXME("(%p)->(%p)\n", This, ppunk);
1589     return E_NOTIMPL;
1590 }
1591
1592 static IDispatchExVtbl DispatchExVtbl = {
1593     DispatchEx_QueryInterface,
1594     DispatchEx_AddRef,
1595     DispatchEx_Release,
1596     DispatchEx_GetTypeInfoCount,
1597     DispatchEx_GetTypeInfo,
1598     DispatchEx_GetIDsOfNames,
1599     DispatchEx_Invoke,
1600     DispatchEx_GetDispID,
1601     DispatchEx_InvokeEx,
1602     DispatchEx_DeleteMemberByName,
1603     DispatchEx_DeleteMemberByDispID,
1604     DispatchEx_GetMemberProperties,
1605     DispatchEx_GetMemberName,
1606     DispatchEx_GetNextDispID,
1607     DispatchEx_GetNameSpaceParent
1608 };
1609
1610 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
1611 {
1612     static const IID IID_UndocumentedScriptIface =
1613         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
1614     static const IID IID_IDispatchJS =
1615         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
1616
1617     if(IsEqualGUID(&IID_IDispatch, riid)) {
1618         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1619         *ppv = &This->IDispatchEx_iface;
1620     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1621         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1622         *ppv = &This->IDispatchEx_iface;
1623     }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
1624         TRACE("(%p)->(IID_IDispatchJS %p) returning NULL\n", This, ppv);
1625         *ppv = NULL;
1626     }else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
1627         TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
1628         *ppv = NULL;
1629     }else {
1630         return FALSE;
1631     }
1632
1633     if(*ppv)
1634         IUnknown_AddRef((IUnknown*)*ppv);
1635     return TRUE;
1636 }
1637
1638 void dispex_traverse(DispatchEx *This, nsCycleCollectionTraversalCallback *cb)
1639 {
1640     dynamic_prop_t *prop;
1641
1642     if(!This->dynamic_data)
1643         return;
1644
1645     for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1646         if(V_VT(&prop->var) == VT_DISPATCH)
1647             note_cc_edge((nsISupports*)V_DISPATCH(&prop->var), "dispex_data", cb);
1648     }
1649
1650     /* FIXME: Traverse func_disps */
1651 }
1652
1653 void dispex_unlink(DispatchEx *This)
1654 {
1655     dynamic_prop_t *prop;
1656
1657     if(!This->dynamic_data)
1658         return;
1659
1660     for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1661         if(V_VT(&prop->var) == VT_DISPATCH) {
1662             V_VT(&prop->var) = VT_EMPTY;
1663             IDispatch_Release(V_DISPATCH(&prop->var));
1664         }else {
1665             VariantClear(&prop->var);
1666         }
1667     }
1668 }
1669
1670 void release_dispex(DispatchEx *This)
1671 {
1672     dynamic_prop_t *prop;
1673
1674     if(!This->dynamic_data)
1675         return;
1676
1677     for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1678         VariantClear(&prop->var);
1679         heap_free(prop->name);
1680     }
1681
1682     heap_free(This->dynamic_data->props);
1683
1684     if(This->dynamic_data->func_disps) {
1685         func_obj_entry_t *iter;
1686
1687         for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps+This->data->data->func_disp_cnt; iter++) {
1688             if(iter->func_obj) {
1689                 iter->func_obj->obj = NULL;
1690                 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
1691             }
1692             if(iter->val)
1693                 IDispatch_Release(iter->val);
1694         }
1695
1696         heap_free(This->dynamic_data->func_disps);
1697     }
1698
1699     heap_free(This->dynamic_data);
1700 }
1701
1702 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
1703 {
1704     dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
1705     dispex->outer = outer;
1706     dispex->data = data;
1707     dispex->dynamic_data = NULL;
1708 }