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