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