3f8b13e36536c4541044ce0568bb2ab3ddc01517
[reactos.git] / reactos / dll / win32 / vbscript / vbdisp.c
1 /*
2 * Copyright 2011 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 "vbscript.h"
20
21 #define FDEX_VERSION_MASK 0xf0000000
22
23 static inline BOOL is_func_id(vbdisp_t *This, DISPID id)
24 {
25 return id < This->desc->func_cnt;
26 }
27
28 static BOOL get_func_id(vbdisp_t *This, const WCHAR *name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
29 {
30 unsigned i;
31
32 for(i = invoke_type == VBDISP_ANY ? 0 : 1; i < This->desc->func_cnt; i++) {
33 if(invoke_type == VBDISP_ANY) {
34 if(!search_private && !This->desc->funcs[i].is_public)
35 continue;
36 if(!i && !This->desc->funcs[0].name) /* default value may not exist */
37 continue;
38 }else {
39 if(!This->desc->funcs[i].entries[invoke_type]
40 || (!search_private && !This->desc->funcs[i].entries[invoke_type]->is_public))
41 continue;
42 }
43
44 if(!strcmpiW(This->desc->funcs[i].name, name)) {
45 *id = i;
46 return TRUE;
47 }
48 }
49
50 return FALSE;
51 }
52
53 HRESULT vbdisp_get_id(vbdisp_t *This, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
54 {
55 unsigned i;
56
57 if(get_func_id(This, name, invoke_type, search_private, id))
58 return S_OK;
59
60 for(i=0; i < This->desc->prop_cnt; i++) {
61 if(!search_private && !This->desc->props[i].is_public)
62 continue;
63
64 if(!strcmpiW(This->desc->props[i].name, name)) {
65 *id = i + This->desc->func_cnt;
66 return S_OK;
67 }
68 }
69
70 if(This->desc->typeinfo) {
71 HRESULT hres;
72
73 hres = ITypeInfo_GetIDsOfNames(This->desc->typeinfo, &name, 1, id);
74 if(SUCCEEDED(hres))
75 return S_OK;
76 }
77
78 *id = -1;
79 return DISP_E_UNKNOWNNAME;
80 }
81
82 static VARIANT *get_propput_arg(const DISPPARAMS *dp)
83 {
84 unsigned i;
85
86 for(i=0; i < dp->cNamedArgs; i++) {
87 if(dp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
88 return dp->rgvarg+i;
89 }
90
91 return NULL;
92 }
93
94 static HRESULT invoke_variant_prop(VARIANT *v, WORD flags, DISPPARAMS *dp, VARIANT *res)
95 {
96 HRESULT hres;
97
98 switch(flags) {
99 case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
100 case DISPATCH_PROPERTYGET:
101 if(dp->cArgs) {
102 WARN("called with arguments\n");
103 return DISP_E_MEMBERNOTFOUND; /* That's what tests show */
104 }
105
106 hres = VariantCopyInd(res, v);
107 break;
108
109 case DISPATCH_PROPERTYPUT: {
110 VARIANT *put_val;
111
112 put_val = get_propput_arg(dp);
113 if(!put_val) {
114 WARN("no value to set\n");
115 return DISP_E_PARAMNOTOPTIONAL;
116 }
117
118 if(arg_cnt(dp)) {
119 FIXME("Arguments not supported\n");
120 return E_NOTIMPL;
121 }
122
123 if(res)
124 V_VT(res) = VT_EMPTY;
125
126 hres = VariantCopyInd(v, put_val);
127 break;
128 }
129
130 default:
131 FIXME("unimplemented flags %x\n", flags);
132 return E_NOTIMPL;
133 }
134
135 return hres;
136 }
137
138 static HRESULT invoke_builtin(vbdisp_t *This, const builtin_prop_t *prop, WORD flags, DISPPARAMS *dp, VARIANT *res)
139 {
140 VARIANT args[8];
141 unsigned argn, i;
142
143 switch(flags) {
144 case DISPATCH_PROPERTYGET:
145 if(!(prop->flags & (BP_GET|BP_GETPUT))) {
146 FIXME("property does not support DISPATCH_PROPERTYGET\n");
147 return E_FAIL;
148 }
149 break;
150 case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
151 if(!prop->proc && prop->flags == BP_GET) {
152 const int vt = prop->min_args, val = prop->max_args;
153 switch(vt) {
154 case VT_I2:
155 V_VT(res) = VT_I2;
156 V_I2(res) = val;
157 break;
158 case VT_I4:
159 V_VT(res) = VT_I4;
160 V_I4(res) = val;
161 break;
162 case VT_BSTR: {
163 const string_constant_t *str = (const string_constant_t*)prop->max_args;
164 BSTR ret;
165
166 ret = SysAllocStringLen(str->buf, str->len);
167 if(!ret)
168 return E_OUTOFMEMORY;
169
170 V_VT(res) = VT_BSTR;
171 V_BSTR(res) = ret;
172 break;
173 }
174 DEFAULT_UNREACHABLE;
175 }
176 return S_OK;
177 }
178 break;
179 case DISPATCH_METHOD:
180 if(prop->flags & (BP_GET|BP_GETPUT)) {
181 FIXME("Call on property\n");
182 return E_FAIL;
183 }
184 break;
185 case DISPATCH_PROPERTYPUT:
186 if(!(prop->flags & BP_GETPUT)) {
187 FIXME("property does not support DISPATCH_PROPERTYPUT\n");
188 return E_FAIL;
189 }
190
191 FIXME("call put\n");
192 return E_NOTIMPL;
193 default:
194 FIXME("unsupported flags %x\n", flags);
195 return E_NOTIMPL;
196 }
197
198 argn = arg_cnt(dp);
199
200 if(argn < prop->min_args || argn > (prop->max_args ? prop->max_args : prop->min_args)) {
201 FIXME("invalid number of arguments\n");
202 return E_FAIL;
203 }
204
205 assert(argn < sizeof(args)/sizeof(*args));
206
207 for(i=0; i < argn; i++) {
208 if(V_VT(dp->rgvarg+dp->cArgs-i-1) == (VT_BYREF|VT_VARIANT))
209 args[i] = *V_VARIANTREF(dp->rgvarg+dp->cArgs-i-1);
210 else
211 args[i] = dp->rgvarg[dp->cArgs-i-1];
212 }
213
214 return prop->proc(This, args, dp->cArgs, res);
215 }
216
217 static BOOL run_terminator(vbdisp_t *This)
218 {
219 DISPPARAMS dp = {0};
220
221 if(This->terminator_ran)
222 return TRUE;
223 This->terminator_ran = TRUE;
224
225 if(!This->desc->class_terminate_id)
226 return TRUE;
227
228 This->ref++;
229 exec_script(This->desc->ctx, This->desc->funcs[This->desc->class_terminate_id].entries[VBDISP_CALLGET],
230 This, &dp, NULL);
231 return !--This->ref;
232 }
233
234 static void clean_props(vbdisp_t *This)
235 {
236 unsigned i;
237
238 if(!This->desc)
239 return;
240
241 for(i=0; i < This->desc->array_cnt; i++) {
242 if(This->arrays[i]) {
243 SafeArrayDestroy(This->arrays[i]);
244 This->arrays[i] = NULL;
245 }
246 }
247
248 for(i=0; i < This->desc->prop_cnt; i++)
249 VariantClear(This->props+i);
250 }
251
252 static inline vbdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
253 {
254 return CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface);
255 }
256
257 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
258 {
259 vbdisp_t *This = impl_from_IDispatchEx(iface);
260
261 if(IsEqualGUID(&IID_IUnknown, riid)) {
262 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
263 *ppv = &This->IDispatchEx_iface;
264 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
265 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
266 *ppv = &This->IDispatchEx_iface;
267 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
268 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
269 *ppv = &This->IDispatchEx_iface;
270 }else {
271 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
272 *ppv = NULL;
273 return E_NOINTERFACE;
274 }
275
276 IUnknown_AddRef((IUnknown*)*ppv);
277 return S_OK;
278 }
279
280 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
281 {
282 vbdisp_t *This = impl_from_IDispatchEx(iface);
283 LONG ref = InterlockedIncrement(&This->ref);
284
285 TRACE("(%p) ref=%d\n", This, ref);
286
287 return ref;
288 }
289
290 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
291 {
292 vbdisp_t *This = impl_from_IDispatchEx(iface);
293 LONG ref = InterlockedDecrement(&This->ref);
294
295 TRACE("(%p) ref=%d\n", This, ref);
296
297 if(!ref && run_terminator(This)) {
298 clean_props(This);
299 list_remove(&This->entry);
300 heap_free(This->arrays);
301 heap_free(This);
302 }
303
304 return ref;
305 }
306
307 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
308 {
309 vbdisp_t *This = impl_from_IDispatchEx(iface);
310
311 TRACE("(%p)->(%p)\n", This, pctinfo);
312
313 *pctinfo = 1;
314 return S_OK;
315 }
316
317 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
318 ITypeInfo **ppTInfo)
319 {
320 vbdisp_t *This = impl_from_IDispatchEx(iface);
321 FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
322 return E_NOTIMPL;
323 }
324
325 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
326 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
327 DISPID *rgDispId)
328 {
329 vbdisp_t *This = impl_from_IDispatchEx(iface);
330 FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
331 lcid, rgDispId);
332 return E_NOTIMPL;
333 }
334
335 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
336 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
337 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
338 {
339 vbdisp_t *This = impl_from_IDispatchEx(iface);
340 FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
341 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
342 return E_NOTIMPL;
343 }
344
345 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
346 {
347 vbdisp_t *This = impl_from_IDispatchEx(iface);
348
349 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
350
351 grfdex &= ~FDEX_VERSION_MASK;
352
353 if(!This->desc)
354 return E_UNEXPECTED;
355
356 /* Tests show that fdexNameCaseSensitive is ignored */
357
358 if(grfdex & ~(fdexNameEnsure|fdexNameCaseInsensitive|fdexNameCaseSensitive)) {
359 FIXME("unsupported flags %x\n", grfdex);
360 return E_NOTIMPL;
361 }
362
363 return vbdisp_get_id(This, bstrName, VBDISP_ANY, FALSE, pid);
364 }
365
366 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
367 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
368 {
369 vbdisp_t *This = impl_from_IDispatchEx(iface);
370
371 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
372
373 if(!This->desc)
374 return E_UNEXPECTED;
375
376 if(pvarRes)
377 V_VT(pvarRes) = VT_EMPTY;
378
379 if(id < 0)
380 return DISP_E_MEMBERNOTFOUND;
381
382 if(is_func_id(This, id)) {
383 function_t *func;
384
385 switch(wFlags) {
386 case DISPATCH_METHOD:
387 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
388 func = This->desc->funcs[id].entries[VBDISP_CALLGET];
389 if(!func) {
390 FIXME("no invoke/getter\n");
391 return DISP_E_MEMBERNOTFOUND;
392 }
393
394 return exec_script(This->desc->ctx, func, This, pdp, pvarRes);
395 case DISPATCH_PROPERTYPUT: {
396 VARIANT *put_val;
397 DISPPARAMS dp = {NULL, NULL, 1, 0};
398
399 if(arg_cnt(pdp)) {
400 FIXME("arguments not implemented\n");
401 return E_NOTIMPL;
402 }
403
404 put_val = get_propput_arg(pdp);
405 if(!put_val) {
406 WARN("no value to set\n");
407 return DISP_E_PARAMNOTOPTIONAL;
408 }
409
410 dp.rgvarg = put_val;
411 func = This->desc->funcs[id].entries[V_VT(put_val) == VT_DISPATCH ? VBDISP_SET : VBDISP_LET];
412 if(!func) {
413 FIXME("no letter/setter\n");
414 return DISP_E_MEMBERNOTFOUND;
415 }
416
417 return exec_script(This->desc->ctx, func, This, &dp, NULL);
418 }
419 default:
420 FIXME("flags %x\n", wFlags);
421 return DISP_E_MEMBERNOTFOUND;
422 }
423 }
424
425 if(id < This->desc->prop_cnt + This->desc->func_cnt)
426 return invoke_variant_prop(This->props+(id-This->desc->func_cnt), wFlags, pdp, pvarRes);
427
428 if(This->desc->builtin_prop_cnt) {
429 unsigned min = 0, max = This->desc->builtin_prop_cnt-1, i;
430
431 while(min <= max) {
432 i = (min+max)/2;
433 if(This->desc->builtin_props[i].id == id)
434 return invoke_builtin(This, This->desc->builtin_props+i, wFlags, pdp, pvarRes);
435 if(This->desc->builtin_props[i].id < id)
436 min = i+1;
437 else
438 max = i-1;
439 }
440 }
441
442 return DISP_E_MEMBERNOTFOUND;
443 }
444
445 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
446 {
447 vbdisp_t *This = impl_from_IDispatchEx(iface);
448 FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
449 return E_NOTIMPL;
450 }
451
452 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
453 {
454 vbdisp_t *This = impl_from_IDispatchEx(iface);
455 FIXME("(%p)->(%x)\n", This, id);
456 return E_NOTIMPL;
457 }
458
459 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
460 {
461 vbdisp_t *This = impl_from_IDispatchEx(iface);
462 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
463 return E_NOTIMPL;
464 }
465
466 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
467 {
468 vbdisp_t *This = impl_from_IDispatchEx(iface);
469 FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
470 return E_NOTIMPL;
471 }
472
473 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
474 {
475 vbdisp_t *This = impl_from_IDispatchEx(iface);
476 FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
477 return E_NOTIMPL;
478 }
479
480 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
481 {
482 vbdisp_t *This = impl_from_IDispatchEx(iface);
483 FIXME("(%p)->(%p)\n", This, ppunk);
484 return E_NOTIMPL;
485 }
486
487 static IDispatchExVtbl DispatchExVtbl = {
488 DispatchEx_QueryInterface,
489 DispatchEx_AddRef,
490 DispatchEx_Release,
491 DispatchEx_GetTypeInfoCount,
492 DispatchEx_GetTypeInfo,
493 DispatchEx_GetIDsOfNames,
494 DispatchEx_Invoke,
495 DispatchEx_GetDispID,
496 DispatchEx_InvokeEx,
497 DispatchEx_DeleteMemberByName,
498 DispatchEx_DeleteMemberByDispID,
499 DispatchEx_GetMemberProperties,
500 DispatchEx_GetMemberName,
501 DispatchEx_GetNextDispID,
502 DispatchEx_GetNameSpaceParent
503 };
504
505 static inline vbdisp_t *unsafe_impl_from_IDispatch(IDispatch *iface)
506 {
507 return iface->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl
508 ? CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface)
509 : NULL;
510 }
511
512 HRESULT create_vbdisp(const class_desc_t *desc, vbdisp_t **ret)
513 {
514 vbdisp_t *vbdisp;
515 HRESULT hres = S_OK;
516
517 vbdisp = heap_alloc_zero( FIELD_OFFSET( vbdisp_t, props[desc->prop_cnt] ));
518 if(!vbdisp)
519 return E_OUTOFMEMORY;
520
521 vbdisp->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
522 vbdisp->ref = 1;
523 vbdisp->desc = desc;
524
525 list_add_tail(&desc->ctx->objects, &vbdisp->entry);
526
527 if(desc->array_cnt) {
528 vbdisp->arrays = heap_alloc_zero(desc->array_cnt * sizeof(*vbdisp->arrays));
529 if(vbdisp->arrays) {
530 unsigned i, j;
531
532 for(i=0; i < desc->array_cnt; i++) {
533 if(!desc->array_descs[i].dim_cnt)
534 continue;
535
536 vbdisp->arrays[i] = SafeArrayCreate(VT_VARIANT, desc->array_descs[i].dim_cnt, desc->array_descs[i].bounds);
537 if(!vbdisp->arrays[i]) {
538 hres = E_OUTOFMEMORY;
539 break;
540 }
541 }
542
543 if(SUCCEEDED(hres)) {
544 for(i=0, j=0; i < desc->prop_cnt; i++) {
545 if(desc->props[i].is_array) {
546 V_VT(vbdisp->props+i) = VT_ARRAY|VT_BYREF|VT_VARIANT;
547 V_ARRAYREF(vbdisp->props+i) = vbdisp->arrays + j++;
548 }
549 }
550 }
551 }else {
552 hres = E_OUTOFMEMORY;
553 }
554 }
555
556 if(SUCCEEDED(hres) && desc->class_initialize_id) {
557 DISPPARAMS dp = {0};
558 hres = exec_script(desc->ctx, desc->funcs[desc->class_initialize_id].entries[VBDISP_CALLGET],
559 vbdisp, &dp, NULL);
560 }
561
562 if(FAILED(hres)) {
563 IDispatchEx_Release(&vbdisp->IDispatchEx_iface);
564 return hres;
565 }
566
567 *ret = vbdisp;
568 return S_OK;
569 }
570
571 static HRESULT Procedure_invoke(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
572 {
573 script_ctx_t *ctx = This->desc->ctx;
574 HRESULT hres;
575
576 TRACE("\n");
577
578 IActiveScriptSite_OnEnterScript(ctx->site);
579 hres = exec_script(ctx, This->desc->value_func, NULL, NULL, NULL);
580 IActiveScriptSite_OnLeaveScript(ctx->site);
581
582 return hres;
583 }
584
585 static const builtin_prop_t procedure_props[] = {
586 {DISPID_VALUE, Procedure_invoke, 0}
587 };
588
589 HRESULT create_procedure_disp(script_ctx_t *ctx, vbscode_t *code, IDispatch **ret)
590 {
591 class_desc_t *desc;
592 vbdisp_t *vbdisp;
593 HRESULT hres;
594
595 desc = heap_alloc_zero(sizeof(*desc));
596 if(!desc)
597 return E_OUTOFMEMORY;
598
599 desc->ctx = ctx;
600 desc->builtin_prop_cnt = sizeof(procedure_props)/sizeof(*procedure_props);
601 desc->builtin_props = procedure_props;
602 desc->value_func = &code->main_code;
603
604 hres = create_vbdisp(desc, &vbdisp);
605 if(FAILED(hres)) {
606 heap_free(desc);
607 return hres;
608 }
609
610 desc->next = ctx->procs;
611 ctx->procs = desc;
612
613 *ret = (IDispatch*)&vbdisp->IDispatchEx_iface;
614 return S_OK;
615 }
616
617 struct _ident_map_t {
618 const WCHAR *name;
619 BOOL is_var;
620 union {
621 dynamic_var_t *var;
622 function_t *func;
623 } u;
624 };
625
626 static inline DISPID ident_to_id(ScriptDisp *This, ident_map_t *ident)
627 {
628 return (ident-This->ident_map)+1;
629 }
630
631 static inline ident_map_t *id_to_ident(ScriptDisp *This, DISPID id)
632 {
633 return 0 < id && id <= This->ident_map_cnt ? This->ident_map+id-1 : NULL;
634 }
635
636 static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name)
637 {
638 ident_map_t *ret;
639
640 if(!This->ident_map_size) {
641 This->ident_map = heap_alloc(4 * sizeof(*This->ident_map));
642 if(!This->ident_map)
643 return NULL;
644 This->ident_map_size = 4;
645 }else if(This->ident_map_cnt == This->ident_map_size) {
646 ident_map_t *new_map;
647
648 new_map = heap_realloc(This->ident_map, 2*This->ident_map_size*sizeof(*new_map));
649 if(!new_map)
650 return NULL;
651 This->ident_map = new_map;
652 This->ident_map_size *= 2;
653 }
654
655 ret = This->ident_map + This->ident_map_cnt++;
656 ret->name = name;
657 return ret;
658 }
659
660 static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface)
661 {
662 return CONTAINING_RECORD(iface, ScriptDisp, IDispatchEx_iface);
663 }
664
665 static HRESULT WINAPI ScriptDisp_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
666 {
667 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
668
669 if(IsEqualGUID(&IID_IUnknown, riid)) {
670 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
671 *ppv = &This->IDispatchEx_iface;
672 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
673 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
674 *ppv = &This->IDispatchEx_iface;
675 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
676 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
677 *ppv = &This->IDispatchEx_iface;
678 }else {
679 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
680 *ppv = NULL;
681 return E_NOINTERFACE;
682 }
683
684 IUnknown_AddRef((IUnknown*)*ppv);
685 return S_OK;
686 }
687
688 static ULONG WINAPI ScriptDisp_AddRef(IDispatchEx *iface)
689 {
690 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
691 LONG ref = InterlockedIncrement(&This->ref);
692
693 TRACE("(%p) ref=%d\n", This, ref);
694
695 return ref;
696 }
697
698 static ULONG WINAPI ScriptDisp_Release(IDispatchEx *iface)
699 {
700 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
701 LONG ref = InterlockedDecrement(&This->ref);
702
703 TRACE("(%p) ref=%d\n", This, ref);
704
705 if(!ref) {
706 assert(!This->ctx);
707 heap_free(This->ident_map);
708 heap_free(This);
709 }
710
711 return ref;
712 }
713
714 static HRESULT WINAPI ScriptDisp_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
715 {
716 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
717
718 TRACE("(%p)->(%p)\n", This, pctinfo);
719
720 *pctinfo = 1;
721 return S_OK;
722 }
723
724 static HRESULT WINAPI ScriptDisp_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
725 ITypeInfo **ppTInfo)
726 {
727 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
728 FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
729 return E_NOTIMPL;
730 }
731
732 static HRESULT WINAPI ScriptDisp_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
733 LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
734 {
735 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
736 UINT i;
737 HRESULT hres;
738
739 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
740 lcid, rgDispId);
741
742 for(i=0; i < cNames; i++) {
743 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
744 if(FAILED(hres))
745 return hres;
746 }
747
748 return S_OK;
749 }
750
751 static HRESULT WINAPI ScriptDisp_Invoke(IDispatchEx *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
752 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
753 {
754 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
755
756 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
757 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
758
759 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
760 pDispParams, pVarResult, pExcepInfo, NULL);
761 }
762
763 static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
764 {
765 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
766 dynamic_var_t *var;
767 ident_map_t *ident;
768 function_t *func;
769
770 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
771
772 if(!This->ctx)
773 return E_UNEXPECTED;
774
775 for(ident = This->ident_map; ident < This->ident_map+This->ident_map_cnt; ident++) {
776 if(!strcmpiW(ident->name, bstrName)) {
777 *pid = ident_to_id(This, ident);
778 return S_OK;
779 }
780 }
781
782 for(var = This->ctx->global_vars; var; var = var->next) {
783 if(!strcmpiW(var->name, bstrName)) {
784 ident = add_ident(This, var->name);
785 if(!ident)
786 return E_OUTOFMEMORY;
787
788 ident->is_var = TRUE;
789 ident->u.var = var;
790 *pid = ident_to_id(This, ident);
791 return S_OK;
792 }
793 }
794
795 for(func = This->ctx->global_funcs; func; func = func->next) {
796 if(!strcmpiW(func->name, bstrName)) {
797 ident = add_ident(This, func->name);
798 if(!ident)
799 return E_OUTOFMEMORY;
800
801 ident->is_var = FALSE;
802 ident->u.func = func;
803 *pid = ident_to_id(This, ident);
804 return S_OK;
805 }
806 }
807
808 *pid = -1;
809 return DISP_E_UNKNOWNNAME;
810 }
811
812 static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
813 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
814 {
815 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
816 ident_map_t *ident;
817 HRESULT hres;
818
819 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
820
821 ident = id_to_ident(This, id);
822 if(!ident)
823 return DISP_E_MEMBERNOTFOUND;
824
825 if(ident->is_var) {
826 if(ident->u.var->is_const) {
827 FIXME("const not supported\n");
828 return E_NOTIMPL;
829 }
830
831 return invoke_variant_prop(&ident->u.var->v, wFlags, pdp, pvarRes);
832 }
833
834 switch(wFlags) {
835 case DISPATCH_METHOD:
836 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
837 IActiveScriptSite_OnEnterScript(This->ctx->site);
838 hres = exec_script(This->ctx, ident->u.func, NULL, pdp, pvarRes);
839 IActiveScriptSite_OnLeaveScript(This->ctx->site);
840 break;
841 default:
842 FIXME("Unsupported flags %x\n", wFlags);
843 hres = E_NOTIMPL;
844 }
845
846 return hres;
847 }
848
849 static HRESULT WINAPI ScriptDisp_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
850 {
851 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
852 FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
853 return E_NOTIMPL;
854 }
855
856 static HRESULT WINAPI ScriptDisp_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
857 {
858 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
859 FIXME("(%p)->(%x)\n", This, id);
860 return E_NOTIMPL;
861 }
862
863 static HRESULT WINAPI ScriptDisp_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
864 {
865 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
866 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
867 return E_NOTIMPL;
868 }
869
870 static HRESULT WINAPI ScriptDisp_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
871 {
872 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
873 FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
874 return E_NOTIMPL;
875 }
876
877 static HRESULT WINAPI ScriptDisp_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
878 {
879 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
880 FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
881 return E_NOTIMPL;
882 }
883
884 static HRESULT WINAPI ScriptDisp_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
885 {
886 ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
887 FIXME("(%p)->(%p)\n", This, ppunk);
888 return E_NOTIMPL;
889 }
890
891 static IDispatchExVtbl ScriptDispVtbl = {
892 ScriptDisp_QueryInterface,
893 ScriptDisp_AddRef,
894 ScriptDisp_Release,
895 ScriptDisp_GetTypeInfoCount,
896 ScriptDisp_GetTypeInfo,
897 ScriptDisp_GetIDsOfNames,
898 ScriptDisp_Invoke,
899 ScriptDisp_GetDispID,
900 ScriptDisp_InvokeEx,
901 ScriptDisp_DeleteMemberByName,
902 ScriptDisp_DeleteMemberByDispID,
903 ScriptDisp_GetMemberProperties,
904 ScriptDisp_GetMemberName,
905 ScriptDisp_GetNextDispID,
906 ScriptDisp_GetNameSpaceParent
907 };
908
909 HRESULT create_script_disp(script_ctx_t *ctx, ScriptDisp **ret)
910 {
911 ScriptDisp *script_disp;
912
913 script_disp = heap_alloc_zero(sizeof(*script_disp));
914 if(!script_disp)
915 return E_OUTOFMEMORY;
916
917 script_disp->IDispatchEx_iface.lpVtbl = &ScriptDispVtbl;
918 script_disp->ref = 1;
919 script_disp->ctx = ctx;
920
921 *ret = script_disp;
922 return S_OK;
923 }
924
925 void collect_objects(script_ctx_t *ctx)
926 {
927 vbdisp_t *iter, *iter2;
928
929 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &ctx->objects, vbdisp_t, entry)
930 run_terminator(iter);
931
932 while(!list_empty(&ctx->objects)) {
933 iter = LIST_ENTRY(list_head(&ctx->objects), vbdisp_t, entry);
934
935 IDispatchEx_AddRef(&iter->IDispatchEx_iface);
936 clean_props(iter);
937 iter->desc = NULL;
938 list_remove(&iter->entry);
939 list_init(&iter->entry);
940 IDispatchEx_Release(&iter->IDispatchEx_iface);
941 }
942 }
943
944 HRESULT disp_get_id(IDispatch *disp, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
945 {
946 IDispatchEx *dispex;
947 vbdisp_t *vbdisp;
948 HRESULT hres;
949
950 vbdisp = unsafe_impl_from_IDispatch(disp);
951 if(vbdisp)
952 return vbdisp_get_id(vbdisp, name, invoke_type, search_private, id);
953
954 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
955 if(FAILED(hres)) {
956 TRACE("using IDispatch\n");
957 return IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, id);
958 }
959
960 hres = IDispatchEx_GetDispID(dispex, name, fdexNameCaseInsensitive, id);
961 IDispatchEx_Release(dispex);
962 return hres;
963 }
964
965 #define RPC_E_SERVER_UNAVAILABLE 0x800706ba
966
967 HRESULT map_hres(HRESULT hres)
968 {
969 if(SUCCEEDED(hres) || HRESULT_FACILITY(hres) == FACILITY_VBS)
970 return hres;
971
972 switch(hres) {
973 case E_NOTIMPL: return MAKE_VBSERROR(VBSE_ACTION_NOT_SUPPORTED);
974 case E_NOINTERFACE: return MAKE_VBSERROR(VBSE_OLE_NOT_SUPPORTED);
975 case DISP_E_UNKNOWNINTERFACE: return MAKE_VBSERROR(VBSE_OLE_NO_PROP_OR_METHOD);
976 case DISP_E_MEMBERNOTFOUND: return MAKE_VBSERROR(VBSE_OLE_NO_PROP_OR_METHOD);
977 case DISP_E_PARAMNOTFOUND: return MAKE_VBSERROR(VBSE_NAMED_PARAM_NOT_FOUND);
978 case DISP_E_TYPEMISMATCH: return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
979 case DISP_E_UNKNOWNNAME: return MAKE_VBSERROR(VBSE_OLE_NO_PROP_OR_METHOD);
980 case DISP_E_NONAMEDARGS: return MAKE_VBSERROR(VBSE_NAMED_ARGS_NOT_SUPPORTED);
981 case DISP_E_BADVARTYPE: return MAKE_VBSERROR(VBSE_INVALID_TYPELIB_VARIABLE);
982 case DISP_E_OVERFLOW: return MAKE_VBSERROR(VBSE_OVERFLOW);
983 case DISP_E_BADINDEX: return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
984 case DISP_E_UNKNOWNLCID: return MAKE_VBSERROR(VBSE_LOCALE_SETTING_NOT_SUPPORTED);
985 case DISP_E_ARRAYISLOCKED: return MAKE_VBSERROR(VBSE_ARRAY_LOCKED);
986 case DISP_E_BADPARAMCOUNT: return MAKE_VBSERROR(VBSE_FUNC_ARITY_MISMATCH);
987 case DISP_E_PARAMNOTOPTIONAL: return MAKE_VBSERROR(VBSE_PARAMETER_NOT_OPTIONAL);
988 case DISP_E_NOTACOLLECTION: return MAKE_VBSERROR(VBSE_NOT_ENUM);
989 case TYPE_E_DLLFUNCTIONNOTFOUND: return MAKE_VBSERROR(VBSE_INVALID_DLL_FUNCTION_NAME);
990 case TYPE_E_TYPEMISMATCH: return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
991 case TYPE_E_OUTOFBOUNDS: return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
992 case TYPE_E_IOERROR: return MAKE_VBSERROR(VBSE_IO_ERROR);
993 case TYPE_E_CANTCREATETMPFILE: return MAKE_VBSERROR(VBSE_CANT_CREATE_TMP_FILE);
994 case STG_E_FILENOTFOUND: return MAKE_VBSERROR(VBSE_OLE_FILE_NOT_FOUND);
995 case STG_E_PATHNOTFOUND: return MAKE_VBSERROR(VBSE_PATH_NOT_FOUND);
996 case STG_E_TOOMANYOPENFILES: return MAKE_VBSERROR(VBSE_TOO_MANY_FILES);
997 case STG_E_ACCESSDENIED: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
998 case STG_E_INSUFFICIENTMEMORY: return MAKE_VBSERROR(VBSE_OUT_OF_MEMORY);
999 case STG_E_NOMOREFILES: return MAKE_VBSERROR(VBSE_TOO_MANY_FILES);
1000 case STG_E_DISKISWRITEPROTECTED: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
1001 case STG_E_WRITEFAULT: return MAKE_VBSERROR(VBSE_IO_ERROR);
1002 case STG_E_READFAULT: return MAKE_VBSERROR(VBSE_IO_ERROR);
1003 case STG_E_SHAREVIOLATION: return MAKE_VBSERROR(VBSE_PATH_FILE_ACCESS);
1004 case STG_E_LOCKVIOLATION: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
1005 case STG_E_FILEALREADYEXISTS: return MAKE_VBSERROR(VBSE_FILE_ALREADY_EXISTS);
1006 case STG_E_MEDIUMFULL: return MAKE_VBSERROR(VBSE_DISK_FULL);
1007 case STG_E_INVALIDNAME: return MAKE_VBSERROR(VBSE_FILE_NOT_FOUND);
1008 case STG_E_INUSE: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
1009 case STG_E_NOTCURRENT: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
1010 case STG_E_CANTSAVE: return MAKE_VBSERROR(VBSE_IO_ERROR);
1011 case REGDB_E_CLASSNOTREG: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1012 case MK_E_UNAVAILABLE: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1013 case MK_E_INVALIDEXTENSION: return MAKE_VBSERROR(VBSE_OLE_FILE_NOT_FOUND);
1014 case MK_E_CANTOPENFILE: return MAKE_VBSERROR(VBSE_OLE_FILE_NOT_FOUND);
1015 case CO_E_CLASSSTRING: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1016 case CO_E_APPNOTFOUND: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1017 case CO_E_APPDIDNTREG: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1018 case E_ACCESSDENIED: return MAKE_VBSERROR(VBSE_PERMISSION_DENIED);
1019 case E_OUTOFMEMORY: return MAKE_VBSERROR(VBSE_OUT_OF_MEMORY);
1020 case E_INVALIDARG: return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL);
1021 case RPC_E_SERVER_UNAVAILABLE: return MAKE_VBSERROR(VBSE_SERVER_NOT_FOUND);
1022 case CO_E_SERVER_EXEC_FAILURE: return MAKE_VBSERROR(VBSE_CANT_CREATE_OBJECT);
1023 }
1024
1025 return hres;
1026 }
1027
1028 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp, VARIANT *retv)
1029 {
1030 const WORD flags = DISPATCH_METHOD|(retv ? DISPATCH_PROPERTYGET : 0);
1031 IDispatchEx *dispex;
1032 EXCEPINFO ei;
1033 HRESULT hres;
1034
1035 memset(&ei, 0, sizeof(ei));
1036 if(retv)
1037 V_VT(retv) = VT_EMPTY;
1038
1039 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1040 if(FAILED(hres)) {
1041 UINT err = 0;
1042
1043 TRACE("using IDispatch\n");
1044 return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, dp, retv, &ei, &err);
1045 }
1046
1047 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei, NULL /* CALLER_FIXME */);
1048 IDispatchEx_Release(dispex);
1049 return hres;
1050 }
1051
1052 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp)
1053 {
1054 IDispatchEx *dispex;
1055 EXCEPINFO ei = {0};
1056 HRESULT hres;
1057
1058 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1059 if(SUCCEEDED(hres)) {
1060 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, NULL /* FIXME! */);
1061 IDispatchEx_Release(dispex);
1062 }else {
1063 ULONG err = 0;
1064
1065 TRACE("using IDispatch\n");
1066 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, &err);
1067 }
1068
1069 return hres;
1070 }