int Script::Execute() { LOG(TRACE) << "Entering Script::Execute"; CComVariant result = L""; CComBSTR error_description = L""; if (this->script_engine_host_ == NULL) { LOG(WARN) << "Script engine host is NULL"; return ENOSUCHDOCUMENT; } CComVariant temp_function; if (!this->CreateAnonymousFunction(&temp_function)) { LOG(WARN) << "Cannot create anonymous function"; return EUNEXPECTEDJSERROR; } if (temp_function.vt != VT_DISPATCH) { LOG(DEBUG) << "No return value that we care about"; return WD_SUCCESS; } // Grab the "call" method out of the returned function DISPID call_member_id; OLECHAR FAR* call_member_name = L"call"; HRESULT hr = temp_function.pdispVal->GetIDsOfNames(IID_NULL, &call_member_name, 1, LOCALE_USER_DEFAULT, &call_member_id); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot locate call method on anonymous function"; return EUNEXPECTEDJSERROR; } CComPtr<IHTMLWindow2> win; hr = this->script_engine_host_->get_parentWindow(&win); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot get parent window, IHTMLDocument2::get_parentWindow failed"; return EUNEXPECTEDJSERROR; } // IDispatch::Invoke() expects the arguments to be passed into it // in reverse order. To accomplish this, we create a new variant // array of size n + 1 where n is the number of arguments we have. // we copy each element of arguments_array_ into the new array in // reverse order, and add an extra argument, the window object, // to the end of the array to use as the "this" parameter for the // function invocation. size_t arg_count = this->argument_array_.size(); std::vector<CComVariant> argument_array_copy(arg_count + 1); CComVariant window_variant(win); argument_array_copy[arg_count].Copy(&window_variant); for (size_t index = 0; index < arg_count; ++index) { argument_array_copy[arg_count - 1 - index].Copy(&this->argument_array_[index]); } DISPPARAMS call_parameters = { 0 }; memset(&call_parameters, 0, sizeof call_parameters); call_parameters.cArgs = static_cast<unsigned int>(argument_array_copy.size()); call_parameters.rgvarg = &argument_array_copy[0]; int return_code = WD_SUCCESS; EXCEPINFO exception; memset(&exception, 0, sizeof exception); hr = temp_function.pdispVal->Invoke(call_member_id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &call_parameters, &result, &exception, 0); if (FAILED(hr)) { if (DISP_E_EXCEPTION == hr) { error_description = exception.bstrDescription ? exception.bstrDescription : L"EUNEXPECTEDJSERROR"; CComBSTR error_source(exception.bstrSource ? exception.bstrSource : L"EUNEXPECTEDJSERROR"); LOG(INFO) << "Exception message was: '" << error_description << "'"; LOG(INFO) << "Exception source was: '" << error_source << "'"; } else { LOGHR(DEBUG, hr) << "Failed to execute anonymous function, no exception information retrieved"; } result.Clear(); result.vt = VT_BSTR; result.bstrVal = error_description; return_code = EUNEXPECTEDJSERROR; } this->result_.Copy(&result); return return_code; }
int Script::Execute() { LOG(TRACE) << "Entering Script::Execute"; VARIANT result; if (this->script_engine_host_ == NULL) { LOG(WARN) << "Script engine host is NULL"; return ENOSUCHDOCUMENT; } CComVariant temp_function; if (!this->CreateAnonymousFunction(&temp_function)) { LOG(WARN) << "Cannot create anonymous function"; return EUNEXPECTEDJSERROR; } if (temp_function.vt != VT_DISPATCH) { LOG(DEBUG) << "No return value that we care about"; return SUCCESS; } // Grab the "call" method out of the returned function DISPID call_member_id; OLECHAR FAR* call_member_name = L"call"; HRESULT hr = temp_function.pdispVal->GetIDsOfNames(IID_NULL, &call_member_name, 1, LOCALE_USER_DEFAULT, &call_member_id); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot locate call method on anonymous function"; return EUNEXPECTEDJSERROR; } DISPPARAMS call_parameters = { 0 }; memset(&call_parameters, 0, sizeof call_parameters); long lower = 0; ::SafeArrayGetLBound(this->argument_array_, 1, &lower); long upper = 0; ::SafeArrayGetUBound(this->argument_array_, 1, &upper); long nargs = 1 + upper - lower; call_parameters.cArgs = nargs + 1; CComPtr<IHTMLWindow2> win; hr = this->script_engine_host_->get_parentWindow(&win); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot get parent window, IHTMLDocument2::get_parentWindow failed"; return EUNEXPECTEDJSERROR; } _variant_t* vargs = new _variant_t[nargs + 1]; hr = ::VariantCopy(&(vargs[nargs]), &CComVariant(win)); long index; for (int i = 0; i < nargs; i++) { index = i; CComVariant v; ::SafeArrayGetElement(this->argument_array_, &index, reinterpret_cast<void*>(&v)); hr = ::VariantCopy(&(vargs[nargs - 1 - i]), &v); } call_parameters.rgvarg = vargs; int return_code = SUCCESS; EXCEPINFO exception; memset(&exception, 0, sizeof exception); hr = temp_function.pdispVal->Invoke(call_member_id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &call_parameters, &result, &exception, 0); if (FAILED(hr)) { if (DISP_E_EXCEPTION == hr) { CComBSTR error_description(exception.bstrDescription ? exception.bstrDescription : L"EUNEXPECTEDJSERROR"); CComBSTR error_source(exception.bstrSource ? exception.bstrSource : L"EUNEXPECTEDJSERROR"); LOG(INFO) << "Exception message was: '" << error_description << "'"; LOG(INFO) << "Exception source was: '" << error_source << "'"; } else { LOGHR(DEBUG, hr) << "Failed to execute anonymous function, no exception information retrieved"; } ::VariantClear(&result); result.vt = VT_USERDEFINED; if (exception.bstrDescription != NULL) { result.bstrVal = ::SysAllocStringByteLen(reinterpret_cast<char*>(exception.bstrDescription), ::SysStringByteLen(exception.bstrDescription)); } else { result.bstrVal = ::SysAllocStringByteLen(NULL, 0); } return_code = EUNEXPECTEDJSERROR; } // If the script returned an IHTMLElement, we need to copy it to make it valid. if(VT_DISPATCH == result.vt) { CComQIPtr<IHTMLElement> element(result.pdispVal); if(element) { IHTMLElement* &dom_element = *(reinterpret_cast<IHTMLElement**>(&result.pdispVal)); hr = element.CopyTo(&dom_element); } } this->result_ = result; delete[] vargs; return return_code; }