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