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