gphoto2.ds: Set supported groups.
[wine.git] / dlls / mshtml / dispex.c
blobb04fb81023288b85e1c2ada98d1127675c3c9b15
1 /*
2 * Copyright 2008-2009 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
20 #include <assert.h>
22 #define COBJMACROS
23 #define NONAMELESSUNION
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29 #include "mscoree.h"
31 #include "wine/debug.h"
33 #include "mshtml_private.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
37 #define MAX_ARGS 16
39 static CRITICAL_SECTION cs_dispex_static_data;
40 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
42 0, 0, &cs_dispex_static_data,
43 { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
44 0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
46 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
49 static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0};
51 typedef struct {
52 DISPID id;
53 BSTR name;
54 tid_t tid;
55 SHORT call_vtbl_off;
56 SHORT put_vtbl_off;
57 SHORT get_vtbl_off;
58 SHORT func_disp_idx;
59 USHORT argc;
60 VARTYPE prop_vt;
61 VARTYPE *arg_types;
62 } func_info_t;
64 struct dispex_data_t {
65 dispex_static_data_t *desc;
67 DWORD func_cnt;
68 DWORD func_size;
69 func_info_t *funcs;
70 func_info_t **name_table;
71 DWORD func_disp_cnt;
73 struct list entry;
76 typedef struct {
77 VARIANT var;
78 LPWSTR name;
79 DWORD flags;
80 } dynamic_prop_t;
82 #define DYNPROP_DELETED 0x01
84 typedef struct {
85 DispatchEx dispex;
86 IUnknown IUnknown_iface;
87 LONG ref;
88 DispatchEx *obj;
89 func_info_t *info;
90 } func_disp_t;
92 typedef struct {
93 func_disp_t *func_obj;
94 VARIANT val;
95 } func_obj_entry_t;
97 struct dispex_dynamic_data_t {
98 DWORD buf_size;
99 DWORD prop_cnt;
100 dynamic_prop_t *props;
101 func_obj_entry_t *func_disps;
104 #define DISPID_DYNPROP_0 0x50000000
105 #define DISPID_DYNPROP_MAX 0x5fffffff
107 #define FDEX_VERSION_MASK 0xf0000000
109 static ITypeLib *typelib;
110 static ITypeInfo *typeinfos[LAST_tid];
111 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
113 static REFIID tid_ids[] = {
114 #define XIID(iface) &IID_ ## iface,
115 #define XDIID(iface) &DIID_ ## iface,
116 TID_LIST
117 #undef XIID
118 #undef XDIID
121 static HRESULT load_typelib(void)
123 HRESULT hres;
124 ITypeLib *tl;
126 hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
127 if(FAILED(hres)) {
128 ERR("LoadRegTypeLib failed: %08x\n", hres);
129 return hres;
132 if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
133 ITypeLib_Release(tl);
134 return hres;
137 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
139 HRESULT hres;
141 if (!typelib)
142 hres = load_typelib();
143 if (!typelib)
144 return hres;
146 if(!typeinfos[tid]) {
147 ITypeInfo *ti;
149 hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti);
150 if(FAILED(hres)) {
151 ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_mshtml_guid(tid_ids[tid]), hres);
152 return hres;
155 if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
156 ITypeInfo_Release(ti);
159 *typeinfo = typeinfos[tid];
160 return S_OK;
163 void release_typelib(void)
165 dispex_data_t *iter;
166 unsigned i;
168 while(!list_empty(&dispex_data_list)) {
169 iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
170 list_remove(&iter->entry);
172 for(i=0; i < iter->func_cnt; i++)
173 SysFreeString(iter->funcs[i].name);
175 heap_free(iter->funcs);
176 heap_free(iter->name_table);
177 heap_free(iter);
180 if(!typelib)
181 return;
183 for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
184 if(typeinfos[i])
185 ITypeInfo_Release(typeinfos[i]);
187 ITypeLib_Release(typelib);
188 DeleteCriticalSection(&cs_dispex_static_data);
191 HRESULT get_class_typeinfo(const CLSID *clsid, ITypeInfo **typeinfo)
193 HRESULT hres;
195 if (!typelib)
196 hres = load_typelib();
197 if (!typelib)
198 return hres;
200 hres = ITypeLib_GetTypeInfoOfGuid(typelib, clsid, typeinfo);
201 if(FAILED(hres))
202 ERR("GetTypeInfoOfGuid failed: %08x\n", hres);
203 return hres;
206 /* Not all argument types are supported yet */
207 #define BUILTIN_ARG_TYPES_SWITCH \
208 CASE_VT(VT_I2, INT16, V_I2); \
209 CASE_VT(VT_UI2, UINT16, V_UI2); \
210 CASE_VT(VT_I4, INT32, V_I4); \
211 CASE_VT(VT_R4, float, V_R4); \
212 CASE_VT(VT_BSTR, BSTR, V_BSTR); \
213 CASE_VT(VT_BOOL, VARIANT_BOOL, V_BOOL)
215 /* List all types used by IDispatchEx-based properties */
216 #define BUILTIN_TYPES_SWITCH \
217 BUILTIN_ARG_TYPES_SWITCH; \
218 CASE_VT(VT_VARIANT, VARIANT, *); \
219 CASE_VT(VT_PTR, void*, V_BYREF); \
220 CASE_VT(VT_UNKNOWN, IUnknown*, V_UNKNOWN); \
221 CASE_VT(VT_DISPATCH, IDispatch*, V_DISPATCH)
223 static BOOL is_arg_type_supported(VARTYPE vt)
225 switch(vt) {
226 #define CASE_VT(x,a,b) case x: return TRUE
227 BUILTIN_ARG_TYPES_SWITCH;
228 #undef CASE_VT
230 return FALSE;
233 static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti)
235 func_info_t *info;
236 BSTR name;
237 HRESULT hres;
239 hres = ITypeInfo_GetDocumentation(dti, desc->memid, &name, NULL, NULL, NULL);
240 if(FAILED(hres))
241 return;
243 for(info = data->funcs; info < data->funcs+data->func_cnt; info++) {
244 if(info->id == desc->memid || !strcmpW(info->name, name)) {
245 if(info->tid != tid) {
246 SysFreeString(name);
247 return; /* Duplicated in other interface */
249 break;
253 if(info == data->funcs+data->func_cnt) {
254 if(data->func_cnt == data->func_size)
255 data->funcs = heap_realloc_zero(data->funcs, (data->func_size <<= 1)*sizeof(func_info_t));
256 info = data->funcs+data->func_cnt;
258 data->func_cnt++;
260 info->id = desc->memid;
261 info->name = name;
262 info->tid = tid;
263 info->func_disp_idx = -1;
264 info->prop_vt = VT_EMPTY;
265 }else {
266 SysFreeString(name);
269 if(desc->invkind & DISPATCH_METHOD) {
270 unsigned i;
272 info->func_disp_idx = data->func_disp_cnt++;
273 info->argc = desc->cParams;
275 assert(info->argc < MAX_ARGS);
276 assert(desc->funckind == FUNC_DISPATCH);
278 info->arg_types = heap_alloc(sizeof(*info->arg_types) * info->argc);
279 if(!info->arg_types)
280 return; /* FIXME: real error instead of fallback */
282 for(i=0; i < info->argc; i++)
283 info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt;
285 info->prop_vt = desc->elemdescFunc.tdesc.vt;
286 if(info->prop_vt != VT_VOID && !is_arg_type_supported(info->prop_vt)) {
287 TRACE("%s: return type %d\n", debugstr_w(info->name), info->prop_vt);
288 return; /* Fallback to ITypeInfo::Invoke */
291 if(desc->cParamsOpt) {
292 TRACE("%s: optional params\n", debugstr_w(info->name));
293 return; /* Fallback to ITypeInfo::Invoke */
296 for(i=0; i < info->argc; i++) {
297 if(!is_arg_type_supported(info->arg_types[i])) {
298 return; /* Fallback to ITypeInfo for unsupported arg types */
301 if(desc->lprgelemdescParam[i].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
302 TRACE("%s param %d: default value\n", debugstr_w(info->name), i);
303 return; /* Fallback to ITypeInfo::Invoke */
307 assert(info->argc <= MAX_ARGS);
308 assert(desc->callconv == CC_STDCALL);
310 info->call_vtbl_off = desc->oVft/sizeof(void*);
311 }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) {
312 VARTYPE vt = VT_EMPTY;
314 if(desc->invkind & DISPATCH_PROPERTYGET) {
315 vt = desc->elemdescFunc.tdesc.vt;
316 info->get_vtbl_off = desc->oVft/sizeof(void*);
318 if(desc->invkind & DISPATCH_PROPERTYPUT) {
319 assert(desc->cParams == 1);
320 vt = desc->lprgelemdescParam->tdesc.vt;
321 info->put_vtbl_off = desc->oVft/sizeof(void*);
324 assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt);
325 info->prop_vt = vt;
329 static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp_typeinfo, const DISPID *blacklist_dispids)
331 unsigned i = 7; /* skip IDispatch functions */
332 const DISPID *blacklist_iter;
333 ITypeInfo *typeinfo;
334 FUNCDESC *funcdesc;
335 HRESULT hres;
337 hres = get_typeinfo(tid, &typeinfo);
338 if(FAILED(hres))
339 return hres;
341 while(1) {
342 hres = ITypeInfo_GetFuncDesc(typeinfo, i++, &funcdesc);
343 if(FAILED(hres))
344 break;
346 if(blacklist_dispids) {
347 for(blacklist_iter = blacklist_dispids; *blacklist_iter != DISPID_UNKNOWN; blacklist_iter++) {
348 if(*blacklist_iter == funcdesc->memid)
349 break;
353 if(!blacklist_dispids || *blacklist_iter == DISPID_UNKNOWN) {
354 TRACE("adding...\n");
355 add_func_info(data, tid, funcdesc, disp_typeinfo ? disp_typeinfo : typeinfo);
358 ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc);
361 return S_OK;
364 void dispex_info_add_interface(dispex_data_t *info, tid_t tid, const DISPID *blacklist_dispids)
366 HRESULT hres;
368 hres = process_interface(info, tid, NULL, blacklist_dispids);
369 if(FAILED(hres))
370 ERR("process_interface failed: %08x\n", hres);
373 static int dispid_cmp(const void *p1, const void *p2)
375 return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
378 static int func_name_cmp(const void *p1, const void *p2)
380 return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
383 static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_mode_t compat_mode)
385 const tid_t *tid;
386 dispex_data_t *data;
387 DWORD i;
388 ITypeInfo *dti;
389 HRESULT hres;
391 if(desc->disp_tid) {
392 hres = get_typeinfo(desc->disp_tid, &dti);
393 if(FAILED(hres)) {
394 ERR("Could not get disp type info: %08x\n", hres);
395 return NULL;
399 data = heap_alloc(sizeof(dispex_data_t));
400 if (!data) {
401 ERR("Out of memory\n");
402 return NULL;
404 data->desc = desc;
405 data->func_cnt = 0;
406 data->func_disp_cnt = 0;
407 data->func_size = 16;
408 data->funcs = heap_alloc_zero(data->func_size*sizeof(func_info_t));
409 if (!data->funcs) {
410 heap_free (data);
411 ERR("Out of memory\n");
412 return NULL;
414 list_add_tail(&dispex_data_list, &data->entry);
416 if(desc->init_info)
417 desc->init_info(data, compat_mode);
419 for(tid = desc->iface_tids; *tid; tid++) {
420 hres = process_interface(data, *tid, dti, NULL);
421 if(FAILED(hres))
422 break;
425 if(!data->func_cnt) {
426 heap_free(data->funcs);
427 data->name_table = NULL;
428 data->funcs = NULL;
429 data->func_size = 0;
430 return data;
434 data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
435 qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
437 data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
438 for(i=0; i < data->func_cnt; i++)
439 data->name_table[i] = data->funcs+i;
440 qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
441 return data;
444 static int id_cmp(const void *p1, const void *p2)
446 return *(const DISPID*)p1 - *(const DISPID*)p2;
449 HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
451 unsigned i, func_cnt;
452 FUNCDESC *funcdesc;
453 ITypeInfo *ti;
454 TYPEATTR *attr;
455 DISPID *ids;
456 HRESULT hres;
458 hres = get_typeinfo(tid, &ti);
459 if(FAILED(hres))
460 return hres;
462 hres = ITypeInfo_GetTypeAttr(ti, &attr);
463 if(FAILED(hres)) {
464 ITypeInfo_Release(ti);
465 return hres;
468 func_cnt = attr->cFuncs;
469 ITypeInfo_ReleaseTypeAttr(ti, attr);
471 ids = heap_alloc(func_cnt*sizeof(DISPID));
472 if(!ids) {
473 ITypeInfo_Release(ti);
474 return E_OUTOFMEMORY;
477 for(i=0; i < func_cnt; i++) {
478 hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
479 if(FAILED(hres))
480 break;
482 ids[i] = funcdesc->memid;
483 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
486 ITypeInfo_Release(ti);
487 if(FAILED(hres)) {
488 heap_free(ids);
489 return hres;
492 qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
494 *ret_size = func_cnt;
495 *ret = ids;
496 return S_OK;
499 static inline BOOL is_custom_dispid(DISPID id)
501 return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
504 static inline BOOL is_dynamic_dispid(DISPID id)
506 return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
509 dispex_prop_type_t get_dispid_type(DISPID id)
511 if(is_dynamic_dispid(id))
512 return DISPEXPROP_DYNAMIC;
513 if(is_custom_dispid(id))
514 return DISPEXPROP_CUSTOM;
515 return DISPEXPROP_BUILTIN;
518 static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
520 if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
521 V_VT(dest) = VT_BSTR;
522 V_BSTR(dest) = NULL;
523 return S_OK;
526 return VariantCopy(dest, src);
529 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This)
531 if(This->dynamic_data)
532 return This->dynamic_data;
534 This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
535 if(!This->dynamic_data)
536 return NULL;
538 if(This->info->desc->vtbl && This->info->desc->vtbl->populate_props)
539 This->info->desc->vtbl->populate_props(This);
541 return This->dynamic_data;
544 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
546 const BOOL alloc = flags & fdexNameEnsure;
547 dispex_dynamic_data_t *data;
548 dynamic_prop_t *prop;
550 data = get_dynamic_data(This);
551 if(!data)
552 return E_OUTOFMEMORY;
554 for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
555 if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) {
556 if(prop->flags & DYNPROP_DELETED) {
557 if(!alloc)
558 return DISP_E_UNKNOWNNAME;
559 prop->flags &= ~DYNPROP_DELETED;
561 *ret = prop;
562 return S_OK;
566 if(!alloc)
567 return DISP_E_UNKNOWNNAME;
569 TRACE("creating dynamic prop %s\n", debugstr_w(name));
571 if(!data->buf_size) {
572 data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
573 if(!data->props)
574 return E_OUTOFMEMORY;
575 data->buf_size = 4;
576 }else if(data->buf_size == data->prop_cnt) {
577 dynamic_prop_t *new_props;
579 new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
580 if(!new_props)
581 return E_OUTOFMEMORY;
583 data->props = new_props;
584 data->buf_size <<= 1;
587 prop = data->props + data->prop_cnt;
589 prop->name = heap_strdupW(name);
590 if(!prop->name)
591 return E_OUTOFMEMORY;
593 VariantInit(&prop->var);
594 prop->flags = 0;
595 data->prop_cnt++;
596 *ret = prop;
597 return S_OK;
600 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
602 dynamic_prop_t *prop;
603 HRESULT hres;
605 hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
606 if(FAILED(hres))
607 return hres;
609 *ret = &prop->var;
610 return S_OK;
613 HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, DISPID *id)
615 dynamic_prop_t *prop;
616 HRESULT hres;
618 hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop);
619 if(FAILED(hres))
620 return hres;
622 *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props);
623 return S_OK;
626 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
627 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
629 if(This->info->desc->vtbl && This->info->desc->vtbl->value)
630 return This->info->desc->vtbl->value(This, lcid, flags, params, res, ei, caller);
632 switch(flags) {
633 case DISPATCH_PROPERTYGET:
634 V_VT(res) = VT_BSTR;
635 V_BSTR(res) = SysAllocString(objectW);
636 if(!V_BSTR(res))
637 return E_OUTOFMEMORY;
638 break;
639 default:
640 FIXME("Unimplemented flags %x\n", flags);
641 return E_NOTIMPL;
644 return S_OK;
647 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
648 EXCEPINFO *ei)
650 DISPPARAMS params = {dp->rgvarg+dp->cNamedArgs, NULL, dp->cArgs-dp->cNamedArgs, 0};
651 ITypeInfo *ti;
652 IUnknown *unk;
653 UINT argerr=0;
654 HRESULT hres;
656 hres = get_typeinfo(func->tid, &ti);
657 if(FAILED(hres)) {
658 ERR("Could not get type info: %08x\n", hres);
659 return hres;
662 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
663 if(FAILED(hres)) {
664 ERR("Could not get iface %s: %08x\n", debugstr_mshtml_guid(tid_ids[func->tid]), hres);
665 return E_FAIL;
668 hres = ITypeInfo_Invoke(ti, unk, func->id, flags, &params, res, ei, &argerr);
670 IUnknown_Release(unk);
671 return hres;
674 static inline func_disp_t *impl_from_IUnknown(IUnknown *iface)
676 return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface);
679 static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
681 func_disp_t *This = impl_from_IUnknown(iface);
683 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
685 if(IsEqualGUID(&IID_IUnknown, riid)) {
686 *ppv = &This->IUnknown_iface;
687 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
688 return *ppv ? S_OK : E_NOINTERFACE;
689 }else {
690 *ppv = NULL;
691 return E_NOINTERFACE;
694 IUnknown_AddRef((IUnknown*)*ppv);
695 return S_OK;
698 static ULONG WINAPI Function_AddRef(IUnknown *iface)
700 func_disp_t *This = impl_from_IUnknown(iface);
701 LONG ref = InterlockedIncrement(&This->ref);
703 TRACE("(%p) ref=%d\n", This, ref);
705 return ref;
708 static ULONG WINAPI Function_Release(IUnknown *iface)
710 func_disp_t *This = impl_from_IUnknown(iface);
711 LONG ref = InterlockedDecrement(&This->ref);
713 TRACE("(%p) ref=%d\n", This, ref);
715 if(!ref) {
716 assert(!This->obj);
717 release_dispex(&This->dispex);
718 heap_free(This);
721 return ref;
724 static const IUnknownVtbl FunctionUnkVtbl = {
725 Function_QueryInterface,
726 Function_AddRef,
727 Function_Release
730 static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface)
732 return CONTAINING_RECORD(iface, func_disp_t, dispex);
735 static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params,
736 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
738 func_disp_t *This = impl_from_DispatchEx(dispex);
739 HRESULT hres;
741 switch(flags) {
742 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
743 if(!res)
744 return E_INVALIDARG;
745 /* fall through */
746 case DISPATCH_METHOD:
747 if(!This->obj)
748 return E_UNEXPECTED;
749 hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei);
750 break;
751 case DISPATCH_PROPERTYGET: {
752 unsigned name_len;
753 WCHAR *ptr;
754 BSTR str;
756 static const WCHAR func_prefixW[] =
757 {'\n','f','u','n','c','t','i','o','n',' '};
758 static const WCHAR func_suffixW[] =
759 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
761 /* FIXME: This probably should be more generic. Also we should try to get IID_IActiveScriptSite and SID_GetCaller. */
762 if(!caller)
763 return E_ACCESSDENIED;
765 name_len = SysStringLen(This->info->name);
766 ptr = str = SysAllocStringLen(NULL, name_len + (sizeof(func_prefixW)+sizeof(func_suffixW))/sizeof(WCHAR));
767 if(!str)
768 return E_OUTOFMEMORY;
770 memcpy(ptr, func_prefixW, sizeof(func_prefixW));
771 ptr += sizeof(func_prefixW)/sizeof(WCHAR);
773 memcpy(ptr, This->info->name, name_len*sizeof(WCHAR));
774 ptr += name_len;
776 memcpy(ptr, func_suffixW, sizeof(func_suffixW));
778 V_VT(res) = VT_BSTR;
779 V_BSTR(res) = str;
780 return S_OK;
782 default:
783 FIXME("Unimplemented flags %x\n", flags);
784 hres = E_NOTIMPL;
787 return hres;
790 static const dispex_static_data_vtbl_t function_dispex_vtbl = {
791 function_value,
792 NULL,
793 NULL,
794 NULL
797 static const tid_t function_iface_tids[] = {0};
799 static dispex_static_data_t function_dispex = {
800 &function_dispex_vtbl,
801 NULL_tid,
802 function_iface_tids
805 static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
807 func_disp_t *ret;
809 ret = heap_alloc_zero(sizeof(func_disp_t));
810 if(!ret)
811 return NULL;
813 ret->IUnknown_iface.lpVtbl = &FunctionUnkVtbl;
814 init_dispex(&ret->dispex, &ret->IUnknown_iface, &function_dispex);
815 ret->ref = 1;
816 ret->obj = obj;
817 ret->info = info;
819 return ret;
822 static HRESULT invoke_disp_value(DispatchEx *This, IDispatch *func_disp, LCID lcid, WORD flags, DISPPARAMS *dp,
823 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
825 DISPID named_arg = DISPID_THIS;
826 DISPPARAMS new_dp = {NULL, &named_arg, 0, 1};
827 IDispatchEx *dispex;
828 HRESULT hres;
830 if(dp->cNamedArgs) {
831 FIXME("named args not supported\n");
832 return E_NOTIMPL;
835 new_dp.rgvarg = heap_alloc((dp->cArgs+1)*sizeof(VARIANTARG));
836 if(!new_dp.rgvarg)
837 return E_OUTOFMEMORY;
839 new_dp.cArgs = dp->cArgs+1;
840 memcpy(new_dp.rgvarg+1, dp->rgvarg, dp->cArgs*sizeof(VARIANTARG));
842 V_VT(new_dp.rgvarg) = VT_DISPATCH;
843 V_DISPATCH(new_dp.rgvarg) = (IDispatch*)&This->IDispatchEx_iface;
845 hres = IDispatch_QueryInterface(func_disp, &IID_IDispatchEx, (void**)&dispex);
846 TRACE(">>>\n");
847 if(SUCCEEDED(hres)) {
848 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, flags, &new_dp, res, ei, caller);
849 IDispatchEx_Release(dispex);
850 }else {
851 ULONG err = 0;
852 hres = IDispatch_Invoke(func_disp, DISPID_VALUE, &IID_NULL, lcid, flags, &new_dp, res, ei, &err);
854 if(SUCCEEDED(hres))
855 TRACE("<<< %s\n", debugstr_variant(res));
856 else
857 WARN("<<< %08x\n", hres);
859 heap_free(new_dp.rgvarg);
860 return hres;
863 static HRESULT get_func_obj_entry(DispatchEx *This, func_info_t *func, func_obj_entry_t **ret)
865 dispex_dynamic_data_t *dynamic_data;
866 func_obj_entry_t *entry;
868 dynamic_data = get_dynamic_data(This);
869 if(!dynamic_data)
870 return E_OUTOFMEMORY;
872 if(!dynamic_data->func_disps) {
873 dynamic_data->func_disps = heap_alloc_zero(This->info->func_disp_cnt * sizeof(*dynamic_data->func_disps));
874 if(!dynamic_data->func_disps)
875 return E_OUTOFMEMORY;
878 entry = dynamic_data->func_disps + func->func_disp_idx;
879 if(!entry->func_obj) {
880 entry->func_obj = create_func_disp(This, func);
881 if(!entry->func_obj)
882 return E_OUTOFMEMORY;
884 IDispatchEx_AddRef(&entry->func_obj->dispex.IDispatchEx_iface);
885 V_VT(&entry->val) = VT_DISPATCH;
886 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
889 *ret = entry;
890 return S_OK;
893 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
895 int min, max, n;
897 min = 0;
898 max = data->func_cnt-1;
900 while(min <= max) {
901 n = (min+max)/2;
903 if(data->funcs[n].id == id) {
904 *ret = data->funcs+n;
905 return S_OK;
908 if(data->funcs[n].id < id)
909 min = n+1;
910 else
911 max = n-1;
914 WARN("invalid id %x\n", id);
915 return DISP_E_UNKNOWNNAME;
918 static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
920 int min, max, n, c;
922 min = 0;
923 max = This->info->func_cnt-1;
925 while(min <= max) {
926 n = (min+max)/2;
928 c = strcmpiW(This->info->name_table[n]->name, name);
929 if(!c) {
930 if((grfdex & fdexNameCaseSensitive) && strcmpW(This->info->name_table[n]->name, name))
931 break;
933 *ret = This->info->name_table[n]->id;
934 return S_OK;
937 if(c > 0)
938 max = n-1;
939 else
940 min = n+1;
943 if(This->info->desc->vtbl && This->info->desc->vtbl->get_dispid) {
944 HRESULT hres;
946 hres = This->info->desc->vtbl->get_dispid(This, name, grfdex, ret);
947 if(hres != DISP_E_UNKNOWNNAME)
948 return hres;
951 return DISP_E_UNKNOWNNAME;
954 static HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller)
956 V_VT(dst) = VT_EMPTY;
958 if(caller) {
959 IVariantChangeType *change_type = NULL;
960 HRESULT hres;
962 hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type);
963 if(SUCCEEDED(hres)) {
964 hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt);
965 IVariantChangeType_Release(change_type);
966 return hres;
970 switch(vt) {
971 case VT_BOOL:
972 if(V_VT(src) == VT_BSTR) {
973 V_VT(dst) = VT_BOOL;
974 V_BOOL(dst) = variant_bool(V_BSTR(src) && *V_BSTR(src));
975 return S_OK;
977 break;
980 return VariantChangeType(dst, src, 0, vt);
983 static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res)
985 IUnknown *iface;
986 HRESULT hres;
988 if(dp && dp->cArgs) {
989 FIXME("cArgs %d\n", dp->cArgs);
990 return E_NOTIMPL;
993 assert(func->get_vtbl_off);
995 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
996 if(SUCCEEDED(hres)) {
997 switch(func->prop_vt) {
998 #define CASE_VT(vt,type,access) \
999 case vt: { \
1000 type val; \
1001 hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \
1002 if(SUCCEEDED(hres)) \
1003 access(res) = val; \
1005 break
1006 BUILTIN_TYPES_SWITCH;
1007 #undef CASE_VT
1008 default:
1009 FIXME("Unhandled vt %d\n", func->prop_vt);
1010 hres = E_NOTIMPL;
1012 IUnknown_Release(iface);
1015 if(FAILED(hres))
1016 return hres;
1018 if(func->prop_vt != VT_VARIANT)
1019 V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt;
1020 return S_OK;
1023 static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller)
1025 VARIANT *v, tmpv;
1026 IUnknown *iface;
1027 HRESULT hres;
1029 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1030 || dp->cNamedArgs > 1) {
1031 FIXME("invalid args\n");
1032 return E_INVALIDARG;
1035 if(!func->put_vtbl_off) {
1036 FIXME("No setter\n");
1037 return E_FAIL;
1040 v = dp->rgvarg;
1041 if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) {
1042 hres = change_type(&tmpv, v, func->prop_vt, caller);
1043 if(FAILED(hres))
1044 return hres;
1045 v = &tmpv;
1048 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1049 if(SUCCEEDED(hres)) {
1050 switch(func->prop_vt) {
1051 #define CASE_VT(vt,type,access) \
1052 case vt: \
1053 hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \
1054 break
1055 BUILTIN_TYPES_SWITCH;
1056 #undef CASE_VT
1057 default:
1058 FIXME("Unimplemented vt %d\n", func->prop_vt);
1059 hres = E_NOTIMPL;
1062 IUnknown_Release(iface);
1065 if(v == &tmpv)
1066 VariantClear(v);
1067 return hres;
1070 static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res, IServiceProvider *caller)
1072 VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres;
1073 unsigned i, nconv = 0;
1074 IUnknown *iface;
1075 HRESULT hres;
1077 if(dp->cNamedArgs) {
1078 FIXME("Named arguments not supported\n");
1079 return E_NOTIMPL;
1082 if(dp->cArgs != func->argc) {
1083 FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs);
1084 return E_INVALIDARG;
1087 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1088 if(FAILED(hres))
1089 return hres;
1091 for(i=0; i < func->argc; i++) {
1092 arg = dp->rgvarg+dp->cArgs-i-1;
1093 if(func->arg_types[i] == V_VT(arg)) {
1094 arg_ptrs[i] = arg;
1095 }else {
1096 hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller);
1097 if(FAILED(hres))
1098 break;
1099 arg_ptrs[i] = arg_buf + nconv++;
1103 if(SUCCEEDED(hres)) {
1104 if(func->prop_vt == VT_VOID) {
1105 V_VT(&retv) = VT_EMPTY;
1106 }else {
1107 V_VT(&retv) = func->prop_vt;
1108 arg_ptrs[func->argc] = &ret_ref;
1109 V_VT(&ret_ref) = VT_BYREF|func->prop_vt;
1111 switch(func->prop_vt) {
1112 #define CASE_VT(vt,type,access) \
1113 case vt: \
1114 V_BYREF(&ret_ref) = &access(&retv); \
1115 break
1116 BUILTIN_TYPES_SWITCH;
1117 #undef CASE_VT
1118 default:
1119 assert(0);
1123 V_VT(&vhres) = VT_ERROR;
1124 hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR,
1125 func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres);
1128 while(nconv--)
1129 VariantClear(arg_buf+nconv);
1130 IUnknown_Release(iface);
1131 if(FAILED(hres))
1132 return hres;
1133 if(FAILED(V_ERROR(&vhres)))
1134 return V_ERROR(&vhres);
1136 if(res)
1137 *res = retv;
1138 else
1139 VariantClear(&retv);
1140 return V_ERROR(&vhres);
1143 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
1144 EXCEPINFO *ei, IServiceProvider *caller)
1146 HRESULT hres;
1148 switch(flags) {
1149 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1150 if(!res)
1151 return E_INVALIDARG;
1152 /* fall through */
1153 case DISPATCH_METHOD:
1154 if(This->dynamic_data && This->dynamic_data->func_disps
1155 && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1156 func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx;
1158 if(V_VT(&entry->val) != VT_DISPATCH) {
1159 FIXME("calling %s not supported\n", debugstr_variant(&entry->val));
1160 return E_NOTIMPL;
1163 if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != V_DISPATCH(&entry->val)) {
1164 if(!V_DISPATCH(&entry->val)) {
1165 FIXME("Calling null\n");
1166 return E_FAIL;
1169 hres = invoke_disp_value(This, V_DISPATCH(&entry->val), 0, flags, dp, res, ei, NULL);
1170 break;
1174 if(func->call_vtbl_off)
1175 hres = invoke_builtin_function(This, func, dp, res, caller);
1176 else
1177 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1178 break;
1179 case DISPATCH_PROPERTYGET: {
1180 func_obj_entry_t *entry;
1182 if(func->id == DISPID_VALUE) {
1183 BSTR ret;
1185 ret = SysAllocString(objectW);
1186 if(!ret)
1187 return E_OUTOFMEMORY;
1189 V_VT(res) = VT_BSTR;
1190 V_BSTR(res) = ret;
1191 return S_OK;
1194 hres = get_func_obj_entry(This, func, &entry);
1195 if(FAILED(hres))
1196 return hres;
1198 V_VT(res) = VT_EMPTY;
1199 return VariantCopy(res, &entry->val);
1201 case DISPATCH_PROPERTYPUT: {
1202 func_obj_entry_t *entry;
1204 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1205 || dp->cNamedArgs > 1) {
1206 FIXME("invalid args\n");
1207 return E_INVALIDARG;
1211 * NOTE: Although we have IDispatchEx tests showing, that it's not allowed to set
1212 * function property using InvokeEx, it's possible to do that from jscript.
1213 * Native probably uses some undocumented interface in this case, but it should
1214 * be fine for us to allow IDispatchEx handle that.
1216 hres = get_func_obj_entry(This, func, &entry);
1217 if(FAILED(hres))
1218 return hres;
1220 return VariantCopy(&entry->val, dp->rgvarg);
1222 default:
1223 FIXME("Unimplemented flags %x\n", flags);
1224 hres = E_NOTIMPL;
1227 return hres;
1230 static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
1231 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1233 func_info_t *func;
1234 HRESULT hres;
1236 hres = get_builtin_func(This->info, id, &func);
1237 if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
1238 return dispex_value(This, lcid, flags, dp, res, ei, caller);
1239 if(FAILED(hres))
1240 return hres;
1242 if(func->func_disp_idx != -1)
1243 return function_invoke(This, func, flags, dp, res, ei, caller);
1245 switch(flags) {
1246 case DISPATCH_PROPERTYPUT:
1247 if(res)
1248 V_VT(res) = VT_EMPTY;
1249 hres = builtin_propput(This, func, dp, caller);
1250 break;
1251 case DISPATCH_PROPERTYGET:
1252 hres = builtin_propget(This, func, dp, res);
1253 break;
1254 default:
1255 if(!func->get_vtbl_off) {
1256 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1257 }else {
1258 VARIANT v;
1260 hres = builtin_propget(This, func, NULL, &v);
1261 if(FAILED(hres))
1262 return hres;
1264 if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) {
1265 if(V_VT(&v) != VT_DISPATCH) {
1266 FIXME("Not a function %s flags %08x\n", debugstr_variant(&v), flags);
1267 VariantClear(&v);
1268 return E_FAIL;
1271 hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller);
1272 IDispatch_Release(V_DISPATCH(&v));
1273 }else if(res) {
1274 *res = v;
1275 }else {
1276 VariantClear(&v);
1281 return hres;
1284 HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success)
1286 switch(get_dispid_type(id)) {
1287 case DISPEXPROP_CUSTOM:
1288 FIXME("DISPEXPROP_CUSTOM not supported\n");
1289 return E_NOTIMPL;
1291 case DISPEXPROP_DYNAMIC: {
1292 DWORD idx = id - DISPID_DYNPROP_0;
1293 dynamic_prop_t *prop;
1295 prop = This->dynamic_data->props+idx;
1296 VariantClear(&prop->var);
1297 prop->flags |= DYNPROP_DELETED;
1298 *success = VARIANT_TRUE;
1299 return S_OK;
1301 case DISPEXPROP_BUILTIN: {
1302 VARIANT var;
1303 DISPPARAMS dp = {&var,NULL,1,0};
1304 func_info_t *func;
1305 HRESULT hres;
1307 hres = get_builtin_func(This->info, id, &func);
1308 if(FAILED(hres))
1309 return hres;
1311 /* For builtin functions, we set their value to the original function. */
1312 if(func->func_disp_idx != -1) {
1313 func_obj_entry_t *entry;
1315 if(!This->dynamic_data || !This->dynamic_data->func_disps
1316 || !This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1317 *success = VARIANT_FALSE;
1318 return S_OK;
1321 entry = This->dynamic_data->func_disps + func->func_disp_idx;
1322 if(V_VT(&entry->val) == VT_DISPATCH
1323 && V_DISPATCH(&entry->val) == (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface) {
1324 *success = VARIANT_FALSE;
1325 return S_OK;
1328 VariantClear(&entry->val);
1329 V_VT(&entry->val) = VT_DISPATCH;
1330 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
1331 IDispatch_AddRef(V_DISPATCH(&entry->val));
1332 *success = VARIANT_TRUE;
1333 return S_OK;
1336 V_VT(&var) = VT_EMPTY;
1337 hres = builtin_propput(This, func, &dp, NULL);
1338 if(FAILED(hres))
1339 return hres;
1341 *success = VARIANT_TRUE;
1342 return S_OK;
1344 default:
1345 assert(0);
1346 return E_FAIL;
1350 compat_mode_t dispex_compat_mode(DispatchEx *dispex)
1352 return dispex->info->desc->vtbl->get_compat_mode(dispex);
1355 static dispex_data_t *ensure_dispex_info(dispex_static_data_t *desc, compat_mode_t compat_mode)
1357 if(!desc->info_cache[compat_mode]) {
1358 EnterCriticalSection(&cs_dispex_static_data);
1359 if(!desc->info_cache[compat_mode])
1360 desc->info_cache[compat_mode] = preprocess_dispex_data(desc, compat_mode);
1361 LeaveCriticalSection(&cs_dispex_static_data);
1363 return desc->info_cache[compat_mode];
1366 static BOOL ensure_real_info(DispatchEx *dispex)
1368 if(dispex->info != dispex->info->desc->delayed_init_info)
1369 return TRUE;
1371 dispex->info = ensure_dispex_info(dispex->info->desc, dispex_compat_mode(dispex));
1372 return dispex->info != NULL;
1375 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
1377 return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface);
1380 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1382 DispatchEx *This = impl_from_IDispatchEx(iface);
1384 return IUnknown_QueryInterface(This->outer, riid, ppv);
1387 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1389 DispatchEx *This = impl_from_IDispatchEx(iface);
1391 return IUnknown_AddRef(This->outer);
1394 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1396 DispatchEx *This = impl_from_IDispatchEx(iface);
1398 return IUnknown_Release(This->outer);
1401 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1403 DispatchEx *This = impl_from_IDispatchEx(iface);
1405 TRACE("(%p)->(%p)\n", This, pctinfo);
1407 *pctinfo = 1;
1408 return S_OK;
1411 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
1412 LCID lcid, ITypeInfo **ppTInfo)
1414 DispatchEx *This = impl_from_IDispatchEx(iface);
1415 HRESULT hres;
1417 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1419 hres = get_typeinfo(This->info->desc->disp_tid, ppTInfo);
1420 if(FAILED(hres))
1421 return hres;
1423 ITypeInfo_AddRef(*ppTInfo);
1424 return S_OK;
1427 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1428 LPOLESTR *rgszNames, UINT cNames,
1429 LCID lcid, DISPID *rgDispId)
1431 DispatchEx *This = impl_from_IDispatchEx(iface);
1432 UINT i;
1433 HRESULT hres;
1435 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1436 lcid, rgDispId);
1438 for(i=0; i < cNames; i++) {
1439 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
1440 if(FAILED(hres))
1441 return hres;
1444 return S_OK;
1447 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1448 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1449 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1451 DispatchEx *This = impl_from_IDispatchEx(iface);
1453 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1454 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1456 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams,
1457 pVarResult, pExcepInfo, NULL);
1460 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1462 DispatchEx *This = impl_from_IDispatchEx(iface);
1463 dynamic_prop_t *dprop;
1464 HRESULT hres;
1466 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1468 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1469 FIXME("Unsupported grfdex %x\n", grfdex);
1471 if(!ensure_real_info(This))
1472 return E_OUTOFMEMORY;
1474 hres = get_builtin_id(This, bstrName, grfdex, pid);
1475 if(hres != DISP_E_UNKNOWNNAME)
1476 return hres;
1478 hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
1479 if(FAILED(hres))
1480 return hres;
1482 *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
1483 return S_OK;
1486 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1487 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1489 DispatchEx *This = impl_from_IDispatchEx(iface);
1490 HRESULT hres;
1492 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1494 if(!ensure_real_info(This))
1495 return E_OUTOFMEMORY;
1497 if(wFlags == (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF))
1498 wFlags = DISPATCH_PROPERTYPUT;
1500 switch(get_dispid_type(id)) {
1501 case DISPEXPROP_CUSTOM:
1502 if(!This->info->desc->vtbl || !This->info->desc->vtbl->invoke)
1503 return DISP_E_UNKNOWNNAME;
1504 return This->info->desc->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1506 case DISPEXPROP_DYNAMIC: {
1507 DWORD idx = id - DISPID_DYNPROP_0;
1508 dynamic_prop_t *prop;
1510 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1511 return DISP_E_UNKNOWNNAME;
1513 prop = This->dynamic_data->props+idx;
1515 switch(wFlags) {
1516 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1517 if(!pvarRes)
1518 return E_INVALIDARG;
1519 /* fall through */
1520 case DISPATCH_METHOD:
1521 if(V_VT(&prop->var) != VT_DISPATCH) {
1522 FIXME("invoke %s\n", debugstr_variant(&prop->var));
1523 return E_NOTIMPL;
1526 return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1527 case DISPATCH_PROPERTYGET:
1528 if(prop->flags & DYNPROP_DELETED)
1529 return DISP_E_UNKNOWNNAME;
1530 V_VT(pvarRes) = VT_EMPTY;
1531 return variant_copy(pvarRes, &prop->var);
1532 case DISPATCH_PROPERTYPUT:
1533 if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1534 || pdp->cNamedArgs > 1) {
1535 FIXME("invalid args\n");
1536 return E_INVALIDARG;
1539 TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
1540 VariantClear(&prop->var);
1541 hres = variant_copy(&prop->var, pdp->rgvarg);
1542 if(FAILED(hres))
1543 return hres;
1545 prop->flags &= ~DYNPROP_DELETED;
1546 return S_OK;
1547 default:
1548 FIXME("unhandled wFlags %x\n", wFlags);
1549 return E_NOTIMPL;
1552 case DISPEXPROP_BUILTIN:
1553 if(wFlags == DISPATCH_CONSTRUCT) {
1554 if(id == DISPID_VALUE) {
1555 if(This->info->desc->vtbl && This->info->desc->vtbl->value) {
1556 return This->info->desc->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1558 FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
1559 return E_FAIL;
1561 FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
1562 return E_FAIL;
1565 return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1566 default:
1567 assert(0);
1568 return E_FAIL;
1572 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
1574 DispatchEx *This = impl_from_IDispatchEx(iface);
1576 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
1578 /* Not implemented by IE */
1579 return E_NOTIMPL;
1582 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1584 DispatchEx *This = impl_from_IDispatchEx(iface);
1586 TRACE("(%p)->(%x)\n", This, id);
1588 /* Not implemented by IE */
1589 return E_NOTIMPL;
1592 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1594 DispatchEx *This = impl_from_IDispatchEx(iface);
1595 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
1596 return E_NOTIMPL;
1599 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1601 DispatchEx *This = impl_from_IDispatchEx(iface);
1602 func_info_t *func;
1603 HRESULT hres;
1605 TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
1607 if(!ensure_real_info(This))
1608 return E_OUTOFMEMORY;
1610 if(is_dynamic_dispid(id)) {
1611 DWORD idx = id - DISPID_DYNPROP_0;
1613 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1614 return DISP_E_UNKNOWNNAME;
1616 *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
1617 if(!*pbstrName)
1618 return E_OUTOFMEMORY;
1620 return S_OK;
1623 hres = get_builtin_func(This->info, id, &func);
1624 if(FAILED(hres))
1625 return hres;
1627 *pbstrName = SysAllocString(func->name);
1628 if(!*pbstrName)
1629 return E_OUTOFMEMORY;
1630 return S_OK;
1633 static HRESULT next_dynamic_id(DispatchEx *dispex, DWORD idx, DISPID *ret_id)
1635 while(idx < dispex->dynamic_data->prop_cnt && dispex->dynamic_data->props[idx].flags & DYNPROP_DELETED)
1636 idx++;
1638 if(idx == dispex->dynamic_data->prop_cnt) {
1639 *ret_id = DISPID_STARTENUM;
1640 return S_FALSE;
1643 *ret_id = DISPID_DYNPROP_0+idx;
1644 return S_OK;
1647 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1649 DispatchEx *This = impl_from_IDispatchEx(iface);
1650 func_info_t *func;
1651 HRESULT hres;
1653 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
1655 if(!ensure_real_info(This))
1656 return E_OUTOFMEMORY;
1658 if(is_dynamic_dispid(id)) {
1659 DWORD idx = id - DISPID_DYNPROP_0;
1661 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1662 return DISP_E_UNKNOWNNAME;
1664 return next_dynamic_id(This, idx+1, pid);
1667 if(id == DISPID_STARTENUM) {
1668 func = This->info->funcs;
1669 }else {
1670 hres = get_builtin_func(This->info, id, &func);
1671 if(FAILED(hres))
1672 return hres;
1673 func++;
1676 while(func < This->info->funcs + This->info->func_cnt) {
1677 /* FIXME: Skip hidden properties */
1678 if(func->func_disp_idx == -1) {
1679 *pid = func->id;
1680 return S_OK;
1682 func++;
1685 if(get_dynamic_data(This) && This->dynamic_data->prop_cnt)
1686 return next_dynamic_id(This, 0, pid);
1688 *pid = DISPID_STARTENUM;
1689 return S_FALSE;
1692 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1694 DispatchEx *This = impl_from_IDispatchEx(iface);
1695 FIXME("(%p)->(%p)\n", This, ppunk);
1696 return E_NOTIMPL;
1699 static IDispatchExVtbl DispatchExVtbl = {
1700 DispatchEx_QueryInterface,
1701 DispatchEx_AddRef,
1702 DispatchEx_Release,
1703 DispatchEx_GetTypeInfoCount,
1704 DispatchEx_GetTypeInfo,
1705 DispatchEx_GetIDsOfNames,
1706 DispatchEx_Invoke,
1707 DispatchEx_GetDispID,
1708 DispatchEx_InvokeEx,
1709 DispatchEx_DeleteMemberByName,
1710 DispatchEx_DeleteMemberByDispID,
1711 DispatchEx_GetMemberProperties,
1712 DispatchEx_GetMemberName,
1713 DispatchEx_GetNextDispID,
1714 DispatchEx_GetNameSpaceParent
1717 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
1719 if(IsEqualGUID(&IID_IDispatch, riid))
1720 *ppv = &This->IDispatchEx_iface;
1721 else if(IsEqualGUID(&IID_IDispatchEx, riid))
1722 *ppv = &This->IDispatchEx_iface;
1723 else if(IsEqualGUID(&IID_IDispatchJS, riid))
1724 *ppv = NULL;
1725 else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid))
1726 *ppv = NULL;
1727 else if(IsEqualGUID(&IID_IMarshal, riid))
1728 *ppv = NULL;
1729 else if(IsEqualGUID(&IID_IManagedObject, riid))
1730 *ppv = NULL;
1731 else
1732 return FALSE;
1734 if(*ppv)
1735 IUnknown_AddRef((IUnknown*)*ppv);
1736 return TRUE;
1739 void dispex_traverse(DispatchEx *This, nsCycleCollectionTraversalCallback *cb)
1741 dynamic_prop_t *prop;
1743 if(!This->dynamic_data)
1744 return;
1746 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1747 if(V_VT(&prop->var) == VT_DISPATCH)
1748 note_cc_edge((nsISupports*)V_DISPATCH(&prop->var), "dispex_data", cb);
1751 /* FIXME: Traverse func_disps */
1754 void dispex_unlink(DispatchEx *This)
1756 dynamic_prop_t *prop;
1758 if(!This->dynamic_data)
1759 return;
1761 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1762 if(V_VT(&prop->var) == VT_DISPATCH) {
1763 V_VT(&prop->var) = VT_EMPTY;
1764 IDispatch_Release(V_DISPATCH(&prop->var));
1765 }else {
1766 VariantClear(&prop->var);
1771 const void *dispex_get_vtbl(DispatchEx *dispex)
1773 return dispex->info->desc->vtbl;
1776 void release_dispex(DispatchEx *This)
1778 dynamic_prop_t *prop;
1780 if(!This->dynamic_data)
1781 return;
1783 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1784 VariantClear(&prop->var);
1785 heap_free(prop->name);
1788 heap_free(This->dynamic_data->props);
1790 if(This->dynamic_data->func_disps) {
1791 func_obj_entry_t *iter;
1793 for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps + This->info->func_disp_cnt; iter++) {
1794 if(iter->func_obj) {
1795 iter->func_obj->obj = NULL;
1796 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
1798 VariantClear(&iter->val);
1801 heap_free(This->dynamic_data->func_disps);
1804 heap_free(This->dynamic_data);
1807 void init_dispex_with_compat_mode(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data, compat_mode_t compat_mode)
1809 assert(compat_mode < COMPAT_MODE_CNT);
1811 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
1812 dispex->outer = outer;
1813 dispex->dynamic_data = NULL;
1815 if(data->vtbl && data->vtbl->get_compat_mode) {
1816 /* delayed init */
1817 if(!data->delayed_init_info) {
1818 EnterCriticalSection(&cs_dispex_static_data);
1819 if(!data->delayed_init_info) {
1820 dispex_data_t *info = heap_alloc_zero(sizeof(*data->delayed_init_info));
1821 if(info) {
1822 info->desc = data;
1823 data->delayed_init_info = info;
1826 LeaveCriticalSection(&cs_dispex_static_data);
1828 dispex->info = data->delayed_init_info;
1829 }else {
1830 dispex->info = ensure_dispex_info(data, compat_mode);