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