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