xref: /reactos/dll/win32/vbscript/vbdisp.c (revision d5399189)
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 }
1127