| /* |
| * Copyright 2008-2009 Jacek Caban for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| #include <assert.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "ole2.h" |
| #include "mscoree.h" |
| |
| #include "wine/debug.h" |
| |
| #include "mshtml_private.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(mshtml); |
| |
| #define MAX_ARGS 16 |
| |
| static CRITICAL_SECTION cs_dispex_static_data; |
| static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg = |
| { |
| 0, 0, &cs_dispex_static_data, |
| { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") } |
| }; |
| static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 }; |
| |
| |
| static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0}; |
| |
| typedef struct { |
| DISPID id; |
| BSTR name; |
| tid_t tid; |
| SHORT call_vtbl_off; |
| SHORT put_vtbl_off; |
| SHORT get_vtbl_off; |
| SHORT func_disp_idx; |
| USHORT argc; |
| VARTYPE prop_vt; |
| VARTYPE *arg_types; |
| } func_info_t; |
| |
| struct dispex_data_t { |
| dispex_static_data_t *desc; |
| |
| DWORD func_cnt; |
| DWORD func_size; |
| func_info_t *funcs; |
| func_info_t **name_table; |
| DWORD func_disp_cnt; |
| |
| struct list entry; |
| }; |
| |
| typedef struct { |
| VARIANT var; |
| LPWSTR name; |
| DWORD flags; |
| } dynamic_prop_t; |
| |
| #define DYNPROP_DELETED 0x01 |
| |
| typedef struct { |
| DispatchEx dispex; |
| IUnknown IUnknown_iface; |
| LONG ref; |
| DispatchEx *obj; |
| func_info_t *info; |
| } func_disp_t; |
| |
| typedef struct { |
| func_disp_t *func_obj; |
| VARIANT val; |
| } func_obj_entry_t; |
| |
| struct dispex_dynamic_data_t { |
| DWORD buf_size; |
| DWORD prop_cnt; |
| dynamic_prop_t *props; |
| func_obj_entry_t *func_disps; |
| }; |
| |
| #define DISPID_DYNPROP_0 0x50000000 |
| #define DISPID_DYNPROP_MAX 0x5fffffff |
| |
| #define FDEX_VERSION_MASK 0xf0000000 |
| |
| static ITypeLib *typelib; |
| static ITypeInfo *typeinfos[LAST_tid]; |
| static struct list dispex_data_list = LIST_INIT(dispex_data_list); |
| |
| static REFIID tid_ids[] = { |
| #define XIID(iface) &IID_ ## iface, |
| #define XDIID(iface) &DIID_ ## iface, |
| TID_LIST |
| #undef XIID |
| #undef XDIID |
| }; |
| |
| static HRESULT load_typelib(void) |
| { |
| HRESULT hres; |
| ITypeLib *tl; |
| |
| hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl); |
| if(FAILED(hres)) { |
| ERR("LoadRegTypeLib failed: %08x\n", hres); |
| return hres; |
| } |
| |
| if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL)) |
| ITypeLib_Release(tl); |
| return hres; |
| } |
| |
| static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo) |
| { |
| HRESULT hres; |
| |
| if (!typelib) |
| hres = load_typelib(); |
| if (!typelib) |
| return hres; |
| |
| if(!typeinfos[tid]) { |
| ITypeInfo *ti; |
| |
| hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti); |
| if(FAILED(hres)) { |
| ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_mshtml_guid(tid_ids[tid]), hres); |
| return hres; |
| } |
| |
| if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL)) |
| ITypeInfo_Release(ti); |
| } |
| |
| *typeinfo = typeinfos[tid]; |
| return S_OK; |
| } |
| |
| void release_typelib(void) |
| { |
| dispex_data_t *iter; |
| unsigned i; |
| |
| while(!list_empty(&dispex_data_list)) { |
| iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry); |
| list_remove(&iter->entry); |
| |
| for(i=0; i < iter->func_cnt; i++) |
| SysFreeString(iter->funcs[i].name); |
| |
| heap_free(iter->funcs); |
| heap_free(iter->name_table); |
| heap_free(iter); |
| } |
| |
| if(!typelib) |
| return; |
| |
| for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++) |
| if(typeinfos[i]) |
| ITypeInfo_Release(typeinfos[i]); |
| |
| ITypeLib_Release(typelib); |
| DeleteCriticalSection(&cs_dispex_static_data); |
| } |
| |
| HRESULT get_class_typeinfo(const CLSID *clsid, ITypeInfo **typeinfo) |
| { |
| HRESULT hres; |
| |
| if (!typelib) |
| hres = load_typelib(); |
| if (!typelib) |
| return hres; |
| |
| hres = ITypeLib_GetTypeInfoOfGuid(typelib, clsid, typeinfo); |
| if(FAILED(hres)) |
| ERR("GetTypeInfoOfGuid failed: %08x\n", hres); |
| return hres; |
| } |
| |
| /* Not all argument types are supported yet */ |
| #define BUILTIN_ARG_TYPES_SWITCH \ |
| CASE_VT(VT_I2, INT16, V_I2); \ |
| CASE_VT(VT_I4, INT32, V_I4); \ |
| CASE_VT(VT_R4, float, V_R4); \ |
| CASE_VT(VT_BSTR, BSTR, V_BSTR); \ |
| CASE_VT(VT_BOOL, VARIANT_BOOL, V_BOOL) |
| |
| /* List all types used by IDispatchEx-based properties */ |
| #define BUILTIN_TYPES_SWITCH \ |
| BUILTIN_ARG_TYPES_SWITCH; \ |
| CASE_VT(VT_VARIANT, VARIANT, *); \ |
| CASE_VT(VT_PTR, void*, V_BYREF); \ |
| CASE_VT(VT_UNKNOWN, IUnknown*, V_UNKNOWN); \ |
| CASE_VT(VT_DISPATCH, IDispatch*, V_DISPATCH) |
| |
| static BOOL is_arg_type_supported(VARTYPE vt) |
| { |
| switch(vt) { |
| #define CASE_VT(x,a,b) case x: return TRUE |
| BUILTIN_ARG_TYPES_SWITCH; |
| #undef CASE_VT |
| } |
| return FALSE; |
| } |
| |
| static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti) |
| { |
| func_info_t *info; |
| BSTR name; |
| HRESULT hres; |
| |
| hres = ITypeInfo_GetDocumentation(dti, desc->memid, &name, NULL, NULL, NULL); |
| if(FAILED(hres)) |
| return; |
| |
| for(info = data->funcs; info < data->funcs+data->func_cnt; info++) { |
| if(info->id == desc->memid || !strcmpW(info->name, name)) { |
| if(info->tid != tid) { |
| SysFreeString(name); |
| return; /* Duplicated in other interface */ |
| } |
| break; |
| } |
| } |
| |
| if(info == data->funcs+data->func_cnt) { |
| if(data->func_cnt == data->func_size) |
| data->funcs = heap_realloc_zero(data->funcs, (data->func_size <<= 1)*sizeof(func_info_t)); |
| info = data->funcs+data->func_cnt; |
| |
| data->func_cnt++; |
| |
| info->id = desc->memid; |
| info->name = name; |
| info->tid = tid; |
| info->func_disp_idx = -1; |
| info->prop_vt = VT_EMPTY; |
| }else { |
| SysFreeString(name); |
| } |
| |
| if(desc->invkind & DISPATCH_METHOD) { |
| unsigned i; |
| |
| info->func_disp_idx = data->func_disp_cnt++; |
| info->argc = desc->cParams; |
| |
| assert(info->argc < MAX_ARGS); |
| assert(desc->funckind == FUNC_DISPATCH); |
| |
| info->arg_types = heap_alloc(sizeof(*info->arg_types) * info->argc); |
| if(!info->arg_types) |
| return; /* FIXME: real error instead of fallback */ |
| |
| for(i=0; i < info->argc; i++) |
| info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt; |
| |
| info->prop_vt = desc->elemdescFunc.tdesc.vt; |
| if(info->prop_vt != VT_VOID && !is_arg_type_supported(info->prop_vt)) { |
| TRACE("%s: return type %d\n", debugstr_w(info->name), info->prop_vt); |
| return; /* Fallback to ITypeInfo::Invoke */ |
| } |
| |
| if(desc->cParamsOpt) { |
| TRACE("%s: optional params\n", debugstr_w(info->name)); |
| return; /* Fallback to ITypeInfo::Invoke */ |
| } |
| |
| for(i=0; i < info->argc; i++) { |
| if(!is_arg_type_supported(info->arg_types[i])) { |
| return; /* Fallback to ITypeInfo for unsupported arg types */ |
| } |
| |
| if(desc->lprgelemdescParam[i].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) { |
| TRACE("%s param %d: default value\n", debugstr_w(info->name), i); |
| return; /* Fallback to ITypeInfo::Invoke */ |
| } |
| } |
| |
| assert(info->argc <= MAX_ARGS); |
| assert(desc->callconv == CC_STDCALL); |
| |
| info->call_vtbl_off = desc->oVft/sizeof(void*); |
| }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) { |
| VARTYPE vt = VT_EMPTY; |
| |
| if(desc->invkind & DISPATCH_PROPERTYGET) { |
| vt = desc->elemdescFunc.tdesc.vt; |
| info->get_vtbl_off = desc->oVft/sizeof(void*); |
| } |
| if(desc->invkind & DISPATCH_PROPERTYPUT) { |
| assert(desc->cParams == 1); |
| vt = desc->lprgelemdescParam->tdesc.vt; |
| info->put_vtbl_off = desc->oVft/sizeof(void*); |
| } |
| |
| assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt); |
| info->prop_vt = vt; |
| } |
| } |
| |
| static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp_typeinfo, const DISPID *blacklist_dispids) |
| { |
| unsigned i = 7; /* skip IDispatch functions */ |
| const DISPID *blacklist_iter; |
| ITypeInfo *typeinfo; |
| FUNCDESC *funcdesc; |
| HRESULT hres; |
| |
| hres = get_typeinfo(tid, &typeinfo); |
| if(FAILED(hres)) |
| return hres; |
| |
| while(1) { |
| hres = ITypeInfo_GetFuncDesc(typeinfo, i++, &funcdesc); |
| if(FAILED(hres)) |
| break; |
| |
| if(blacklist_dispids) { |
| for(blacklist_iter = blacklist_dispids; *blacklist_iter != DISPID_UNKNOWN; blacklist_iter++) { |
| if(*blacklist_iter == funcdesc->memid) |
| break; |
| } |
| } |
| |
| if(!blacklist_dispids || *blacklist_iter == DISPID_UNKNOWN) { |
| TRACE("adding...\n"); |
| add_func_info(data, tid, funcdesc, disp_typeinfo ? disp_typeinfo : typeinfo); |
| } |
| |
| ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc); |
| } |
| |
| return S_OK; |
| } |
| |
| void dispex_info_add_interface(dispex_data_t *info, tid_t tid, const DISPID *blacklist_dispids) |
| { |
| HRESULT hres; |
| |
| hres = process_interface(info, tid, NULL, blacklist_dispids); |
| if(FAILED(hres)) |
| ERR("process_interface failed: %08x\n", hres); |
| } |
| |
| static int dispid_cmp(const void *p1, const void *p2) |
| { |
| return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id; |
| } |
| |
| static int func_name_cmp(const void *p1, const void *p2) |
| { |
| return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name); |
| } |
| |
| static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_mode_t compat_mode) |
| { |
| const tid_t *tid; |
| dispex_data_t *data; |
| DWORD i; |
| ITypeInfo *dti; |
| HRESULT hres; |
| |
| if(desc->disp_tid) { |
| hres = get_typeinfo(desc->disp_tid, &dti); |
| if(FAILED(hres)) { |
| ERR("Could not get disp type info: %08x\n", hres); |
| return NULL; |
| } |
| } |
| |
| data = heap_alloc(sizeof(dispex_data_t)); |
| if (!data) { |
| ERR("Out of memory\n"); |
| return NULL; |
| } |
| data->desc = desc; |
| data->func_cnt = 0; |
| data->func_disp_cnt = 0; |
| data->func_size = 16; |
| data->funcs = heap_alloc_zero(data->func_size*sizeof(func_info_t)); |
| if (!data->funcs) { |
| heap_free (data); |
| ERR("Out of memory\n"); |
| return NULL; |
| } |
| list_add_tail(&dispex_data_list, &data->entry); |
| |
| if(desc->init_info) |
| desc->init_info(data, compat_mode); |
| |
| for(tid = desc->iface_tids; *tid; tid++) { |
| hres = process_interface(data, *tid, dti, NULL); |
| if(FAILED(hres)) |
| break; |
| } |
| |
| if(!data->func_cnt) { |
| heap_free(data->funcs); |
| data->name_table = NULL; |
| data->funcs = NULL; |
| data->func_size = 0; |
| return data; |
| } |
| |
| |
| data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t)); |
| qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp); |
| |
| data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*)); |
| for(i=0; i < data->func_cnt; i++) |
| data->name_table[i] = data->funcs+i; |
| qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp); |
| return data; |
| } |
| |
| static int id_cmp(const void *p1, const void *p2) |
| { |
| return *(const DISPID*)p1 - *(const DISPID*)p2; |
| } |
| |
| HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret) |
| { |
| unsigned i, func_cnt; |
| FUNCDESC *funcdesc; |
| ITypeInfo *ti; |
| TYPEATTR *attr; |
| DISPID *ids; |
| HRESULT hres; |
| |
| hres = get_typeinfo(tid, &ti); |
| if(FAILED(hres)) |
| return hres; |
| |
| hres = ITypeInfo_GetTypeAttr(ti, &attr); |
| if(FAILED(hres)) { |
| ITypeInfo_Release(ti); |
| return hres; |
| } |
| |
| func_cnt = attr->cFuncs; |
| ITypeInfo_ReleaseTypeAttr(ti, attr); |
| |
| ids = heap_alloc(func_cnt*sizeof(DISPID)); |
| if(!ids) { |
| ITypeInfo_Release(ti); |
| return E_OUTOFMEMORY; |
| } |
| |
| for(i=0; i < func_cnt; i++) { |
| hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc); |
| if(FAILED(hres)) |
| break; |
| |
| ids[i] = funcdesc->memid; |
| ITypeInfo_ReleaseFuncDesc(ti, funcdesc); |
| } |
| |
| ITypeInfo_Release(ti); |
| if(FAILED(hres)) { |
| heap_free(ids); |
| return hres; |
| } |
| |
| qsort(ids, func_cnt, sizeof(DISPID), id_cmp); |
| |
| *ret_size = func_cnt; |
| *ret = ids; |
| return S_OK; |
| } |
| |
| static inline BOOL is_custom_dispid(DISPID id) |
| { |
| return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX; |
| } |
| |
| static inline BOOL is_dynamic_dispid(DISPID id) |
| { |
| return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX; |
| } |
| |
| dispex_prop_type_t get_dispid_type(DISPID id) |
| { |
| if(is_dynamic_dispid(id)) |
| return DISPEXPROP_DYNAMIC; |
| if(is_custom_dispid(id)) |
| return DISPEXPROP_CUSTOM; |
| return DISPEXPROP_BUILTIN; |
| } |
| |
| static HRESULT variant_copy(VARIANT *dest, VARIANT *src) |
| { |
| if(V_VT(src) == VT_BSTR && !V_BSTR(src)) { |
| V_VT(dest) = VT_BSTR; |
| V_BSTR(dest) = NULL; |
| return S_OK; |
| } |
| |
| return VariantCopy(dest, src); |
| } |
| |
| static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This) |
| { |
| if(This->dynamic_data) |
| return This->dynamic_data; |
| |
| This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t)); |
| if(!This->dynamic_data) |
| return NULL; |
| |
| if(This->info->desc->vtbl && This->info->desc->vtbl->populate_props) |
| This->info->desc->vtbl->populate_props(This); |
| |
| return This->dynamic_data; |
| } |
| |
| static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret) |
| { |
| const BOOL alloc = flags & fdexNameEnsure; |
| dispex_dynamic_data_t *data; |
| dynamic_prop_t *prop; |
| |
| data = get_dynamic_data(This); |
| if(!data) |
| return E_OUTOFMEMORY; |
| |
| for(prop = data->props; prop < data->props+data->prop_cnt; prop++) { |
| if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) { |
| if(prop->flags & DYNPROP_DELETED) { |
| if(!alloc) |
| return DISP_E_UNKNOWNNAME; |
| prop->flags &= ~DYNPROP_DELETED; |
| } |
| *ret = prop; |
| return S_OK; |
| } |
| } |
| |
| if(!alloc) |
| return DISP_E_UNKNOWNNAME; |
| |
| TRACE("creating dynamic prop %s\n", debugstr_w(name)); |
| |
| if(!data->buf_size) { |
| data->props = heap_alloc(sizeof(dynamic_prop_t)*4); |
| if(!data->props) |
| return E_OUTOFMEMORY; |
| data->buf_size = 4; |
| }else if(data->buf_size == data->prop_cnt) { |
| dynamic_prop_t *new_props; |
| |
| new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1)); |
| if(!new_props) |
| return E_OUTOFMEMORY; |
| |
| data->props = new_props; |
| data->buf_size <<= 1; |
| } |
| |
| prop = data->props + data->prop_cnt; |
| |
| prop->name = heap_strdupW(name); |
| if(!prop->name) |
| return E_OUTOFMEMORY; |
| |
| VariantInit(&prop->var); |
| prop->flags = 0; |
| data->prop_cnt++; |
| *ret = prop; |
| return S_OK; |
| } |
| |
| HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret) |
| { |
| dynamic_prop_t *prop; |
| HRESULT hres; |
| |
| hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop); |
| if(FAILED(hres)) |
| return hres; |
| |
| *ret = &prop->var; |
| return S_OK; |
| } |
| |
| HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, DISPID *id) |
| { |
| dynamic_prop_t *prop; |
| HRESULT hres; |
| |
| hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop); |
| if(FAILED(hres)) |
| return hres; |
| |
| *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props); |
| return S_OK; |
| } |
| |
| static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params, |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) |
| { |
| if(This->info->desc->vtbl && This->info->desc->vtbl->value) |
| return This->info->desc->vtbl->value(This, lcid, flags, params, res, ei, caller); |
| |
| switch(flags) { |
| case DISPATCH_PROPERTYGET: |
| V_VT(res) = VT_BSTR; |
| V_BSTR(res) = SysAllocString(objectW); |
| if(!V_BSTR(res)) |
| return E_OUTOFMEMORY; |
| break; |
| default: |
| FIXME("Unimplemented flags %x\n", flags); |
| return E_NOTIMPL; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res, |
| EXCEPINFO *ei) |
| { |
| DISPPARAMS params = {dp->rgvarg+dp->cNamedArgs, NULL, dp->cArgs-dp->cNamedArgs, 0}; |
| ITypeInfo *ti; |
| IUnknown *unk; |
| UINT argerr=0; |
| HRESULT hres; |
| |
| hres = get_typeinfo(func->tid, &ti); |
| if(FAILED(hres)) { |
| ERR("Could not get type info: %08x\n", hres); |
| return hres; |
| } |
| |
| hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk); |
| if(FAILED(hres)) { |
| ERR("Could not get iface %s: %08x\n", debugstr_mshtml_guid(tid_ids[func->tid]), hres); |
| return E_FAIL; |
| } |
| |
| hres = ITypeInfo_Invoke(ti, unk, func->id, flags, ¶ms, res, ei, &argerr); |
| |
| IUnknown_Release(unk); |
| return hres; |
| } |
| |
| static inline func_disp_t *impl_from_IUnknown(IUnknown *iface) |
| { |
| return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface); |
| } |
| |
| static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) |
| { |
| func_disp_t *This = impl_from_IUnknown(iface); |
| |
| TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); |
| |
| if(IsEqualGUID(&IID_IUnknown, riid)) { |
| *ppv = &This->IUnknown_iface; |
| }else if(dispex_query_interface(&This->dispex, riid, ppv)) { |
| return *ppv ? S_OK : E_NOINTERFACE; |
| }else { |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI Function_AddRef(IUnknown *iface) |
| { |
| func_disp_t *This = impl_from_IUnknown(iface); |
| LONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) ref=%d\n", This, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI Function_Release(IUnknown *iface) |
| { |
| func_disp_t *This = impl_from_IUnknown(iface); |
| LONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) ref=%d\n", This, ref); |
| |
| if(!ref) { |
| assert(!This->obj); |
| release_dispex(&This->dispex); |
| heap_free(This); |
| } |
| |
| return ref; |
| } |
| |
| static const IUnknownVtbl FunctionUnkVtbl = { |
| Function_QueryInterface, |
| Function_AddRef, |
| Function_Release |
| }; |
| |
| static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface) |
| { |
| return CONTAINING_RECORD(iface, func_disp_t, dispex); |
| } |
| |
| static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params, |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) |
| { |
| func_disp_t *This = impl_from_DispatchEx(dispex); |
| HRESULT hres; |
| |
| switch(flags) { |
| case DISPATCH_METHOD|DISPATCH_PROPERTYGET: |
| if(!res) |
| return E_INVALIDARG; |
| /* fall through */ |
| case DISPATCH_METHOD: |
| if(!This->obj) |
| return E_UNEXPECTED; |
| hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei); |
| break; |
| case DISPATCH_PROPERTYGET: { |
| unsigned name_len; |
| WCHAR *ptr; |
| BSTR str; |
| |
| static const WCHAR func_prefixW[] = |
| {'\n','f','u','n','c','t','i','o','n',' '}; |
| static const WCHAR func_suffixW[] = |
| {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'}; |
| |
| /* FIXME: This probably should be more generic. Also we should try to get IID_IActiveScriptSite and SID_GetCaller. */ |
| if(!caller) |
| return E_ACCESSDENIED; |
| |
| name_len = SysStringLen(This->info->name); |
| ptr = str = SysAllocStringLen(NULL, name_len + (sizeof(func_prefixW)+sizeof(func_suffixW))/sizeof(WCHAR)); |
| if(!str) |
| return E_OUTOFMEMORY; |
| |
| memcpy(ptr, func_prefixW, sizeof(func_prefixW)); |
| ptr += sizeof(func_prefixW)/sizeof(WCHAR); |
| |
| memcpy(ptr, This->info->name, name_len*sizeof(WCHAR)); |
| ptr += name_len; |
| |
| memcpy(ptr, func_suffixW, sizeof(func_suffixW)); |
| |
| V_VT(res) = VT_BSTR; |
| V_BSTR(res) = str; |
| return S_OK; |
| } |
| default: |
| FIXME("Unimplemented flags %x\n", flags); |
| hres = E_NOTIMPL; |
| } |
| |
| return hres; |
| } |
| |
| static const dispex_static_data_vtbl_t function_dispex_vtbl = { |
| function_value, |
| NULL, |
| NULL, |
| NULL |
| }; |
| |
| static const tid_t function_iface_tids[] = {0}; |
| |
| static dispex_static_data_t function_dispex = { |
| &function_dispex_vtbl, |
| NULL_tid, |
| function_iface_tids |
| }; |
| |
| static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info) |
| { |
| func_disp_t *ret; |
| |
| ret = heap_alloc_zero(sizeof(func_disp_t)); |
| if(!ret) |
| return NULL; |
| |
| ret->IUnknown_iface.lpVtbl = &FunctionUnkVtbl; |
| init_dispex(&ret->dispex, &ret->IUnknown_iface, &function_dispex); |
| ret->ref = 1; |
| ret->obj = obj; |
| ret->info = info; |
| |
| return ret; |
| } |
| |
| static HRESULT invoke_disp_value(DispatchEx *This, IDispatch *func_disp, LCID lcid, WORD flags, DISPPARAMS *dp, |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) |
| { |
| DISPID named_arg = DISPID_THIS; |
| DISPPARAMS new_dp = {NULL, &named_arg, 0, 1}; |
| IDispatchEx *dispex; |
| HRESULT hres; |
| |
| if(dp->cNamedArgs) { |
| FIXME("named args not supported\n"); |
| return E_NOTIMPL; |
| } |
| |
| new_dp.rgvarg = heap_alloc((dp->cArgs+1)*sizeof(VARIANTARG)); |
| if(!new_dp.rgvarg) |
| return E_OUTOFMEMORY; |
| |
| new_dp.cArgs = dp->cArgs+1; |
| memcpy(new_dp.rgvarg+1, dp->rgvarg, dp->cArgs*sizeof(VARIANTARG)); |
| |
| V_VT(new_dp.rgvarg) = VT_DISPATCH; |
| V_DISPATCH(new_dp.rgvarg) = (IDispatch*)&This->IDispatchEx_iface; |
| |
| hres = IDispatch_QueryInterface(func_disp, &IID_IDispatchEx, (void**)&dispex); |
| TRACE(">>>\n"); |
| if(SUCCEEDED(hres)) { |
| hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, flags, &new_dp, res, ei, caller); |
| IDispatchEx_Release(dispex); |
| }else { |
| ULONG err = 0; |
| hres = IDispatch_Invoke(func_disp, DISPID_VALUE, &IID_NULL, lcid, flags, &new_dp, res, ei, &err); |
| } |
| if(SUCCEEDED(hres)) |
| TRACE("<<< %s\n", debugstr_variant(res)); |
| else |
| WARN("<<< %08x\n", hres); |
| |
| heap_free(new_dp.rgvarg); |
| return hres; |
| } |
| |
| static HRESULT get_func_obj_entry(DispatchEx *This, func_info_t *func, func_obj_entry_t **ret) |
| { |
| dispex_dynamic_data_t *dynamic_data; |
| func_obj_entry_t *entry; |
| |
| dynamic_data = get_dynamic_data(This); |
| if(!dynamic_data) |
| return E_OUTOFMEMORY; |
| |
| if(!dynamic_data->func_disps) { |
| dynamic_data->func_disps = heap_alloc_zero(This->info->func_disp_cnt * sizeof(*dynamic_data->func_disps)); |
| if(!dynamic_data->func_disps) |
| return E_OUTOFMEMORY; |
| } |
| |
| entry = dynamic_data->func_disps + func->func_disp_idx; |
| if(!entry->func_obj) { |
| entry->func_obj = create_func_disp(This, func); |
| if(!entry->func_obj) |
| return E_OUTOFMEMORY; |
| |
| IDispatchEx_AddRef(&entry->func_obj->dispex.IDispatchEx_iface); |
| V_VT(&entry->val) = VT_DISPATCH; |
| V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface; |
| } |
| |
| *ret = entry; |
| return S_OK; |
| } |
| |
| static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret) |
| { |
| int min, max, n; |
| |
| min = 0; |
| max = data->func_cnt-1; |
| |
| while(min <= max) { |
| n = (min+max)/2; |
| |
| if(data->funcs[n].id == id) { |
| *ret = data->funcs+n; |
| return S_OK; |
| } |
| |
| if(data->funcs[n].id < id) |
| min = n+1; |
| else |
| max = n-1; |
| } |
| |
| WARN("invalid id %x\n", id); |
| return DISP_E_UNKNOWNNAME; |
| } |
| |
| static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret) |
| { |
| int min, max, n, c; |
| |
| min = 0; |
| max = This->info->func_cnt-1; |
| |
| while(min <= max) { |
| n = (min+max)/2; |
| |
| c = strcmpiW(This->info->name_table[n]->name, name); |
| if(!c) { |
| if((grfdex & fdexNameCaseSensitive) && strcmpW(This->info->name_table[n]->name, name)) |
| break; |
| |
| *ret = This->info->name_table[n]->id; |
| return S_OK; |
| } |
| |
| if(c > 0) |
| max = n-1; |
| else |
| min = n+1; |
| } |
| |
| if(This->info->desc->vtbl && This->info->desc->vtbl->get_dispid) { |
| HRESULT hres; |
| |
| hres = This->info->desc->vtbl->get_dispid(This, name, grfdex, ret); |
| if(hres != DISP_E_UNKNOWNNAME) |
| return hres; |
| } |
| |
| return DISP_E_UNKNOWNNAME; |
| } |
| |
| static HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller) |
| { |
| V_VT(dst) = VT_EMPTY; |
| |
| if(caller) { |
| IVariantChangeType *change_type = NULL; |
| HRESULT hres; |
| |
| hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type); |
| if(SUCCEEDED(hres)) { |
| hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt); |
| IVariantChangeType_Release(change_type); |
| return hres; |
| } |
| } |
| |
| switch(vt) { |
| case VT_BOOL: |
| if(V_VT(src) == VT_BSTR) { |
| V_VT(dst) = VT_BOOL; |
| V_BOOL(dst) = V_BSTR(src) && *V_BSTR(src) ? VARIANT_TRUE : VARIANT_FALSE; |
| return S_OK; |
| } |
| break; |
| } |
| |
| return VariantChangeType(dst, src, 0, vt); |
| } |
| |
| static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res) |
| { |
| IUnknown *iface; |
| HRESULT hres; |
| |
| if(dp && dp->cArgs) { |
| FIXME("cArgs %d\n", dp->cArgs); |
| return E_NOTIMPL; |
| } |
| |
| assert(func->get_vtbl_off); |
| |
| hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface); |
| if(SUCCEEDED(hres)) { |
| switch(func->prop_vt) { |
| #define CASE_VT(vt,type,access) \ |
| case vt: { \ |
| type val; \ |
| hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \ |
| if(SUCCEEDED(hres)) \ |
| access(res) = val; \ |
| } \ |
| break |
| BUILTIN_TYPES_SWITCH; |
| #undef CASE_VT |
| default: |
| FIXME("Unhandled vt %d\n", func->prop_vt); |
| hres = E_NOTIMPL; |
| } |
| IUnknown_Release(iface); |
| } |
| |
| if(FAILED(hres)) |
| return hres; |
| |
| if(func->prop_vt != VT_VARIANT) |
| V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt; |
| return S_OK; |
| } |
| |
| static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller) |
| { |
| VARIANT *v, tmpv; |
| IUnknown *iface; |
| HRESULT hres; |
| |
| if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT) |
| || dp->cNamedArgs > 1) { |
| FIXME("invalid args\n"); |
| return E_INVALIDARG; |
| } |
| |
| if(!func->put_vtbl_off) { |
| FIXME("No setter\n"); |
| return E_FAIL; |
| } |
| |
| v = dp->rgvarg; |
| if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) { |
| hres = change_type(&tmpv, v, func->prop_vt, caller); |
| if(FAILED(hres)) |
| return hres; |
| v = &tmpv; |
| } |
| |
| hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface); |
| if(SUCCEEDED(hres)) { |
| switch(func->prop_vt) { |
| #define CASE_VT(vt,type,access) \ |
| case vt: \ |
| hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \ |
| break |
| BUILTIN_TYPES_SWITCH; |
| #undef CASE_VT |
| default: |
| FIXME("Unimplemented vt %d\n", func->prop_vt); |
| hres = E_NOTIMPL; |
| } |
| |
| IUnknown_Release(iface); |
| } |
| |
| if(v == &tmpv) |
| VariantClear(v); |
| return hres; |
| } |
| |
| static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res, IServiceProvider *caller) |
| { |
| VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres; |
| unsigned i, nconv = 0; |
| IUnknown *iface; |
| HRESULT hres; |
| |
| if(dp->cNamedArgs) { |
| FIXME("Named arguments not supported\n"); |
| return E_NOTIMPL; |
| } |
| |
| if(dp->cArgs != func->argc) { |
| FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs); |
| return E_INVALIDARG; |
| } |
| |
| hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface); |
| if(FAILED(hres)) |
| return hres; |
| |
| for(i=0; i < func->argc; i++) { |
| arg = dp->rgvarg+dp->cArgs-i-1; |
| if(func->arg_types[i] == V_VT(arg)) { |
| arg_ptrs[i] = arg; |
| }else { |
| hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller); |
| if(FAILED(hres)) |
| break; |
| arg_ptrs[i] = arg_buf + nconv++; |
| } |
| } |
| |
| if(SUCCEEDED(hres)) { |
| if(func->prop_vt == VT_VOID) { |
| V_VT(&retv) = VT_EMPTY; |
| }else { |
| V_VT(&retv) = func->prop_vt; |
| arg_ptrs[func->argc] = &ret_ref; |
| V_VT(&ret_ref) = VT_BYREF|func->prop_vt; |
| |
| switch(func->prop_vt) { |
| #define CASE_VT(vt,type,access) \ |
| case vt: \ |
| V_BYREF(&ret_ref) = &access(&retv); \ |
| break |
| BUILTIN_TYPES_SWITCH; |
| #undef CASE_VT |
| default: |
| assert(0); |
| } |
| } |
| |
| V_VT(&vhres) = VT_ERROR; |
| hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR, |
| func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres); |
| } |
| |
| while(nconv--) |
| VariantClear(arg_buf+nconv); |
| IUnknown_Release(iface); |
| if(FAILED(hres)) |
| return hres; |
| if(FAILED(V_ERROR(&vhres))) |
| return V_ERROR(&vhres); |
| |
| if(res) |
| *res = retv; |
| else |
| VariantClear(&retv); |
| return V_ERROR(&vhres); |
| } |
| |
| static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res, |
| EXCEPINFO *ei, IServiceProvider *caller) |
| { |
| HRESULT hres; |
| |
| switch(flags) { |
| case DISPATCH_METHOD|DISPATCH_PROPERTYGET: |
| if(!res) |
| return E_INVALIDARG; |
| /* fall through */ |
| case DISPATCH_METHOD: |
| if(This->dynamic_data && This->dynamic_data->func_disps |
| && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) { |
| func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx; |
| |
| if(V_VT(&entry->val) != VT_DISPATCH) { |
| FIXME("calling %s not supported\n", debugstr_variant(&entry->val)); |
| return E_NOTIMPL; |
| } |
| |
| if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != V_DISPATCH(&entry->val)) { |
| if(!V_DISPATCH(&entry->val)) { |
| FIXME("Calling null\n"); |
| return E_FAIL; |
| } |
| |
| hres = invoke_disp_value(This, V_DISPATCH(&entry->val), 0, flags, dp, res, ei, NULL); |
| break; |
| } |
| } |
| |
| if(func->call_vtbl_off) |
| hres = invoke_builtin_function(This, func, dp, res, caller); |
| else |
| hres = typeinfo_invoke(This, func, flags, dp, res, ei); |
| break; |
| case DISPATCH_PROPERTYGET: { |
| func_obj_entry_t *entry; |
| |
| if(func->id == DISPID_VALUE) { |
| BSTR ret; |
| |
| ret = SysAllocString(objectW); |
| if(!ret) |
| return E_OUTOFMEMORY; |
| |
| V_VT(res) = VT_BSTR; |
| V_BSTR(res) = ret; |
| return S_OK; |
| } |
| |
| hres = get_func_obj_entry(This, func, &entry); |
| if(FAILED(hres)) |
| return hres; |
| |
| V_VT(res) = VT_EMPTY; |
| return VariantCopy(res, &entry->val); |
| } |
| case DISPATCH_PROPERTYPUT: { |
| func_obj_entry_t *entry; |
| |
| if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT) |
| || dp->cNamedArgs > 1) { |
| FIXME("invalid args\n"); |
| return E_INVALIDARG; |
| } |
| |
| /* |
| * NOTE: Although we have IDispatchEx tests showing, that it's not allowed to set |
| * function property using InvokeEx, it's possible to do that from jscript. |
| * Native probably uses some undocumented interface in this case, but it should |
| * be fine for us to allow IDispatchEx handle that. |
| */ |
| hres = get_func_obj_entry(This, func, &entry); |
| if(FAILED(hres)) |
| return hres; |
| |
| return VariantCopy(&entry->val, dp->rgvarg); |
| } |
| default: |
| FIXME("Unimplemented flags %x\n", flags); |
| hres = E_NOTIMPL; |
| } |
| |
| return hres; |
| } |
| |
| static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp, |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) |
| { |
| func_info_t *func; |
| HRESULT hres; |
| |
| hres = get_builtin_func(This->info, id, &func); |
| if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME) |
| return dispex_value(This, lcid, flags, dp, res, ei, caller); |
| if(FAILED(hres)) |
| return hres; |
| |
| if(func->func_disp_idx != -1) |
| return function_invoke(This, func, flags, dp, res, ei, caller); |
| |
| switch(flags) { |
| case DISPATCH_PROPERTYPUT: |
| if(res) |
| V_VT(res) = VT_EMPTY; |
| hres = builtin_propput(This, func, dp, caller); |
| break; |
| case DISPATCH_PROPERTYGET: |
| hres = builtin_propget(This, func, dp, res); |
| break; |
| default: |
| if(!func->get_vtbl_off) { |
| hres = typeinfo_invoke(This, func, flags, dp, res, ei); |
| }else { |
| VARIANT v; |
| |
| hres = builtin_propget(This, func, NULL, &v); |
| if(FAILED(hres)) |
| return hres; |
| |
| if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) { |
| if(V_VT(&v) != VT_DISPATCH) { |
| FIXME("Not a function %s flags %08x\n", debugstr_variant(&v), flags); |
| VariantClear(&v); |
| return E_FAIL; |
| } |
| |
| hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller); |
| IDispatch_Release(V_DISPATCH(&v)); |
| }else if(res) { |
| *res = v; |
| }else { |
| VariantClear(&v); |
| } |
| } |
| } |
| |
| return hres; |
| } |
| |
| HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success) |
| { |
| switch(get_dispid_type(id)) { |
| case DISPEXPROP_CUSTOM: |
| FIXME("DISPEXPROP_CUSTOM not supported\n"); |
| return E_NOTIMPL; |
| |
| case DISPEXPROP_DYNAMIC: { |
| DWORD idx = id - DISPID_DYNPROP_0; |
| dynamic_prop_t *prop; |
| |
| prop = This->dynamic_data->props+idx; |
| VariantClear(&prop->var); |
| prop->flags |= DYNPROP_DELETED; |
| *success = VARIANT_TRUE; |
| return S_OK; |
| } |
| case DISPEXPROP_BUILTIN: { |
| VARIANT var; |
| DISPPARAMS dp = {&var,NULL,1,0}; |
| func_info_t *func; |
| HRESULT hres; |
| |
| hres = get_builtin_func(This->info, id, &func); |
| if(FAILED(hres)) |
| return hres; |
| |
| /* For builtin functions, we set their value to the original function. */ |
| if(func->func_disp_idx != -1) { |
| func_obj_entry_t *entry; |
| |
| if(!This->dynamic_data || !This->dynamic_data->func_disps |
| || !This->dynamic_data->func_disps[func->func_disp_idx].func_obj) { |
| *success = VARIANT_FALSE; |
| return S_OK; |
| } |
| |
| entry = This->dynamic_data->func_disps + func->func_disp_idx; |
| if(V_VT(&entry->val) == VT_DISPATCH |
| && V_DISPATCH(&entry->val) == (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface) { |
| *success = VARIANT_FALSE; |
| return S_OK; |
| } |
| |
| VariantClear(&entry->val); |
| V_VT(&entry->val) = VT_DISPATCH; |
| V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface; |
| IDispatch_AddRef(V_DISPATCH(&entry->val)); |
| *success = VARIANT_TRUE; |
| return S_OK; |
| } |
| |
| V_VT(&var) = VT_EMPTY; |
| hres = builtin_propput(This, func, &dp, NULL); |
| if(FAILED(hres)) |
| return hres; |
| |
| *success = VARIANT_TRUE; |
| return S_OK; |
| } |
| default: |
| assert(0); |
| return E_FAIL; |
| } |
| } |
| |
| compat_mode_t dispex_compat_mode(DispatchEx *dispex) |
| { |
| return dispex->info->desc->vtbl->get_compat_mode(dispex); |
| } |
| |
| static dispex_data_t *ensure_dispex_info(dispex_static_data_t *desc, compat_mode_t compat_mode) |
| { |
| if(!desc->info_cache[compat_mode]) { |
| EnterCriticalSection(&cs_dispex_static_data); |
| if(!desc->info_cache[compat_mode]) |
| desc->info_cache[compat_mode] = preprocess_dispex_data(desc, compat_mode); |
| LeaveCriticalSection(&cs_dispex_static_data); |
| } |
| return desc->info_cache[compat_mode]; |
| } |
| |
| static BOOL ensure_real_info(DispatchEx *dispex) |
| { |
| if(dispex->info != dispex->info->desc->delayed_init_info) |
| return TRUE; |
| |
| dispex->info = ensure_dispex_info(dispex->info->desc, dispex_compat_mode(dispex)); |
| return dispex->info != NULL; |
| } |
| |
| static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface) |
| { |
| return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface); |
| } |
| |
| static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| return IUnknown_QueryInterface(This->outer, riid, ppv); |
| } |
| |
| static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| return IUnknown_AddRef(This->outer); |
| } |
| |
| static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| return IUnknown_Release(This->outer); |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| TRACE("(%p)->(%p)\n", This, pctinfo); |
| |
| *pctinfo = 1; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, |
| LCID lcid, ITypeInfo **ppTInfo) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| HRESULT hres; |
| |
| TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); |
| |
| hres = get_typeinfo(This->info->desc->disp_tid, ppTInfo); |
| if(FAILED(hres)) |
| return hres; |
| |
| ITypeInfo_AddRef(*ppTInfo); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, |
| LPOLESTR *rgszNames, UINT cNames, |
| LCID lcid, DISPID *rgDispId) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| UINT i; |
| HRESULT hres; |
| |
| TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, |
| lcid, rgDispId); |
| |
| for(i=0; i < cNames; i++) { |
| hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i); |
| if(FAILED(hres)) |
| return hres; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, |
| REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, |
| VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), |
| lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); |
| |
| return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams, |
| pVarResult, pExcepInfo, NULL); |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| dynamic_prop_t *dprop; |
| HRESULT hres; |
| |
| TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); |
| |
| if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) |
| FIXME("Unsupported grfdex %x\n", grfdex); |
| |
| if(!ensure_real_info(This)) |
| return E_OUTOFMEMORY; |
| |
| hres = get_builtin_id(This, bstrName, grfdex, pid); |
| if(hres != DISP_E_UNKNOWNNAME) |
| return hres; |
| |
| hres = get_dynamic_prop(This, bstrName, grfdex, &dprop); |
| if(FAILED(hres)) |
| return hres; |
| |
| *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, |
| VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| HRESULT hres; |
| |
| TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); |
| |
| if(!ensure_real_info(This)) |
| return E_OUTOFMEMORY; |
| |
| if(wFlags == (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF)) |
| wFlags = DISPATCH_PROPERTYPUT; |
| |
| switch(get_dispid_type(id)) { |
| case DISPEXPROP_CUSTOM: |
| if(!This->info->desc->vtbl || !This->info->desc->vtbl->invoke) |
| return DISP_E_UNKNOWNNAME; |
| return This->info->desc->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); |
| |
| case DISPEXPROP_DYNAMIC: { |
| DWORD idx = id - DISPID_DYNPROP_0; |
| dynamic_prop_t *prop; |
| |
| if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx) |
| return DISP_E_UNKNOWNNAME; |
| |
| prop = This->dynamic_data->props+idx; |
| |
| switch(wFlags) { |
| case DISPATCH_METHOD|DISPATCH_PROPERTYGET: |
| if(!pvarRes) |
| return E_INVALIDARG; |
| /* fall through */ |
| case DISPATCH_METHOD: |
| if(V_VT(&prop->var) != VT_DISPATCH) { |
| FIXME("invoke %s\n", debugstr_variant(&prop->var)); |
| return E_NOTIMPL; |
| } |
| |
| return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller); |
| case DISPATCH_PROPERTYGET: |
| if(prop->flags & DYNPROP_DELETED) |
| return DISP_E_UNKNOWNNAME; |
| V_VT(pvarRes) = VT_EMPTY; |
| return variant_copy(pvarRes, &prop->var); |
| case DISPATCH_PROPERTYPUT: |
| if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT) |
| || pdp->cNamedArgs > 1) { |
| FIXME("invalid args\n"); |
| return E_INVALIDARG; |
| } |
| |
| TRACE("put %s\n", debugstr_variant(pdp->rgvarg)); |
| VariantClear(&prop->var); |
| hres = variant_copy(&prop->var, pdp->rgvarg); |
| if(FAILED(hres)) |
| return hres; |
| |
| prop->flags &= ~DYNPROP_DELETED; |
| return S_OK; |
| default: |
| FIXME("unhandled wFlags %x\n", wFlags); |
| return E_NOTIMPL; |
| } |
| } |
| case DISPEXPROP_BUILTIN: |
| if(wFlags == DISPATCH_CONSTRUCT) { |
| if(id == DISPID_VALUE) { |
| if(This->info->desc->vtbl && This->info->desc->vtbl->value) { |
| return This->info->desc->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller); |
| } |
| FIXME("DISPATCH_CONSTRUCT flag but missing value function\n"); |
| return E_FAIL; |
| } |
| FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n"); |
| return E_FAIL; |
| } |
| |
| return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); |
| default: |
| assert(0); |
| return E_FAIL; |
| } |
| } |
| |
| static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); |
| |
| /* Not implemented by IE */ |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| |
| TRACE("(%p)->(%x)\n", This, id); |
| |
| /* Not implemented by IE */ |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| func_info_t *func; |
| HRESULT hres; |
| |
| TRACE("(%p)->(%x %p)\n", This, id, pbstrName); |
| |
| if(!ensure_real_info(This)) |
| return E_OUTOFMEMORY; |
| |
| if(is_dynamic_dispid(id)) { |
| DWORD idx = id - DISPID_DYNPROP_0; |
| |
| if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx) |
| return DISP_E_UNKNOWNNAME; |
| |
| *pbstrName = SysAllocString(This->dynamic_data->props[idx].name); |
| if(!*pbstrName) |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| hres = get_builtin_func(This->info, id, &func); |
| if(FAILED(hres)) |
| return hres; |
| |
| *pbstrName = SysAllocString(func->name); |
| if(!*pbstrName) |
| return E_OUTOFMEMORY; |
| return S_OK; |
| } |
| |
| static HRESULT next_dynamic_id(DispatchEx *dispex, DWORD idx, DISPID *ret_id) |
| { |
| while(idx < dispex->dynamic_data->prop_cnt && dispex->dynamic_data->props[idx].flags & DYNPROP_DELETED) |
| idx++; |
| |
| if(idx == dispex->dynamic_data->prop_cnt) { |
| *ret_id = DISPID_STARTENUM; |
| return S_FALSE; |
| } |
| |
| *ret_id = DISPID_DYNPROP_0+idx; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| func_info_t *func; |
| HRESULT hres; |
| |
| TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid); |
| |
| if(!ensure_real_info(This)) |
| return E_OUTOFMEMORY; |
| |
| if(is_dynamic_dispid(id)) { |
| DWORD idx = id - DISPID_DYNPROP_0; |
| |
| if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx) |
| return DISP_E_UNKNOWNNAME; |
| |
| return next_dynamic_id(This, idx+1, pid); |
| } |
| |
| if(id == DISPID_STARTENUM) { |
| func = This->info->funcs; |
| }else { |
| hres = get_builtin_func(This->info, id, &func); |
| if(FAILED(hres)) |
| return hres; |
| func++; |
| } |
| |
| while(func < This->info->funcs + This->info->func_cnt) { |
| /* FIXME: Skip hidden properties */ |
| if(func->func_disp_idx == -1) { |
| *pid = func->id; |
| return S_OK; |
| } |
| func++; |
| } |
| |
| if(get_dynamic_data(This) && This->dynamic_data->prop_cnt) |
| return next_dynamic_id(This, 0, pid); |
| |
| *pid = DISPID_STARTENUM; |
| return S_FALSE; |
| } |
| |
| static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) |
| { |
| DispatchEx *This = impl_from_IDispatchEx(iface); |
| FIXME("(%p)->(%p)\n", This, ppunk); |
| return E_NOTIMPL; |
| } |
| |
| static IDispatchExVtbl DispatchExVtbl = { |
| DispatchEx_QueryInterface, |
| DispatchEx_AddRef, |
| DispatchEx_Release, |
| DispatchEx_GetTypeInfoCount, |
| DispatchEx_GetTypeInfo, |
| DispatchEx_GetIDsOfNames, |
| DispatchEx_Invoke, |
| DispatchEx_GetDispID, |
| DispatchEx_InvokeEx, |
| DispatchEx_DeleteMemberByName, |
| DispatchEx_DeleteMemberByDispID, |
| DispatchEx_GetMemberProperties, |
| DispatchEx_GetMemberName, |
| DispatchEx_GetNextDispID, |
| DispatchEx_GetNameSpaceParent |
| }; |
| |
| BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv) |
| { |
| if(IsEqualGUID(&IID_IDispatch, riid)) |
| *ppv = &This->IDispatchEx_iface; |
| else if(IsEqualGUID(&IID_IDispatchEx, riid)) |
| *ppv = &This->IDispatchEx_iface; |
| else if(IsEqualGUID(&IID_IDispatchJS, riid)) |
| *ppv = NULL; |
| else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) |
| *ppv = NULL; |
| else if(IsEqualGUID(&IID_IMarshal, riid)) |
| *ppv = NULL; |
| else if(IsEqualGUID(&IID_IManagedObject, riid)) |
| *ppv = NULL; |
| else |
| return FALSE; |
| |
| if(*ppv) |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return TRUE; |
| } |
| |
| void dispex_traverse(DispatchEx *This, nsCycleCollectionTraversalCallback *cb) |
| { |
| dynamic_prop_t *prop; |
| |
| if(!This->dynamic_data) |
| return; |
| |
| for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) { |
| if(V_VT(&prop->var) == VT_DISPATCH) |
| note_cc_edge((nsISupports*)V_DISPATCH(&prop->var), "dispex_data", cb); |
| } |
| |
| /* FIXME: Traverse func_disps */ |
| } |
| |
| void dispex_unlink(DispatchEx *This) |
| { |
| dynamic_prop_t *prop; |
| |
| if(!This->dynamic_data) |
| return; |
| |
| for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) { |
| if(V_VT(&prop->var) == VT_DISPATCH) { |
| V_VT(&prop->var) = VT_EMPTY; |
| IDispatch_Release(V_DISPATCH(&prop->var)); |
| }else { |
| VariantClear(&prop->var); |
| } |
| } |
| } |
| |
| const dispex_static_data_vtbl_t *dispex_get_vtbl(DispatchEx *dispex) |
| { |
| return dispex->info->desc->vtbl; |
| } |
| |
| void release_dispex(DispatchEx *This) |
| { |
| dynamic_prop_t *prop; |
| |
| if(!This->dynamic_data) |
| return; |
| |
| for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) { |
| VariantClear(&prop->var); |
| heap_free(prop->name); |
| } |
| |
| heap_free(This->dynamic_data->props); |
| |
| if(This->dynamic_data->func_disps) { |
| func_obj_entry_t *iter; |
| |
| for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps + This->info->func_disp_cnt; iter++) { |
| if(iter->func_obj) { |
| iter->func_obj->obj = NULL; |
| IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface); |
| } |
| VariantClear(&iter->val); |
| } |
| |
| heap_free(This->dynamic_data->func_disps); |
| } |
| |
| heap_free(This->dynamic_data); |
| } |
| |
| void init_dispex_with_compat_mode(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data, compat_mode_t compat_mode) |
| { |
| assert(compat_mode < COMPAT_MODE_CNT); |
| |
| dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl; |
| dispex->outer = outer; |
| dispex->dynamic_data = NULL; |
| |
| if(data->vtbl && data->vtbl->get_compat_mode) { |
| /* delayed init */ |
| if(!data->delayed_init_info) { |
| EnterCriticalSection(&cs_dispex_static_data); |
| if(!data->delayed_init_info) { |
| dispex_data_t *info = heap_alloc_zero(sizeof(*data->delayed_init_info)); |
| if(info) { |
| info->desc = data; |
| data->delayed_init_info = info; |
| } |
| } |
| LeaveCriticalSection(&cs_dispex_static_data); |
| } |
| dispex->info = data->delayed_init_info; |
| }else { |
| dispex->info = ensure_dispex_info(data, compat_mode); |
| } |
| } |