HRESULT ComObject::invoke (const Method &method, bool isProperty, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pReturnValue, EXCEPINFO *pExcepInfo, UINT *pArgErr) { HRESULT hresult; try { // Construct Tcl script to invoke operation on the servant. TclObject script(m_servant); // Get the method or property to invoke on the servant. std::string operation; if ((wFlags & DISPATCH_PROPERTYGET) != 0 && isProperty) { operation = getPrefix + method.name(); } else if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { operation = setPrefix + method.name(); } else if (wFlags & DISPATCH_METHOD) { operation = method.name(); } else { return DISP_E_MEMBERNOTFOUND; } script.lappend( Tcl_NewStringObj(const_cast<char *>(operation.c_str()), -1)); // Set the argument error pointer in case we need to use it. UINT argErr; if (pArgErr == 0) { pArgErr = &argErr; } // Convert arguments to Tcl values. // TODO: Should handle named arguments differently than positional // arguments. const Method::Parameters ¶meters = method.parameters(); int argIndex = pDispParams->cArgs - 1; Method::Parameters::const_iterator pParam; for (pParam = parameters.begin(); pParam != parameters.end(); ++pParam, --argIndex) { // Append argument value. VARIANT *pArg = &(pDispParams->rgvarg[argIndex]); try { script.lappend(getArgument(pArg, *pParam)); } catch (_com_error &) { *pArgErr = argIndex; throw; } } if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { VARIANT *pArg = &(pDispParams->rgvarg[argIndex]); try { TclObject value(pArg, method.type(), m_interp); script.lappend(value); } catch (_com_error &) { *pArgErr = argIndex; throw; } } // Execute the Tcl script. TclObject result; int completionCode = eval(script, &result); if (completionCode == TCL_OK) { hresult = S_OK; } else { if (m_isSink) { Tcl_BackgroundError(m_interp); } hresult = hresultFromErrorCode(); if (FAILED(hresult)) { fillExcepInfo( pExcepInfo, hresult, m_servant.c_str(), result.c_str()); hresult = DISP_E_EXCEPTION; } } // Copy values to out arguments. argIndex = pDispParams->cArgs - 1; for (pParam = parameters.begin(); pParam != parameters.end(); ++pParam, --argIndex) { VARIANT *pArg = &(pDispParams->rgvarg[argIndex]); if ((pParam->flags() & PARAMFLAG_FOUT) && (V_VT(pArg) & VT_BYREF)) { // Get name of Tcl variable that holds out value. TclObject varName = getOutVariableName(*pParam); // Copy variable value to out argument. TclObject value; if (getVariable(varName, value) == TCL_OK) { putOutVariant(m_interp, pArg, value, pParam->type()); } } } // Convert return value. if (pReturnValue != 0 && method.type().vartype() != VT_VOID) { // Must increment reference count of interface pointers returned // from methods. result.toVariant(pReturnValue, method.type(), m_interp, true); } } catch (_com_error &e) { fillExcepInfo(pExcepInfo, e.Error(), m_servant.c_str(), 0); hresult = DISP_E_EXCEPTION; } return hresult; }
static void putOutVariant (Tcl_Interp *interp, VARIANT *pDest, TclObject &tclObject, const Type &type) { switch (type.vartype()) { case VT_BOOL: *V_BOOLREF(pDest) = tclObject.getBool() ? VARIANT_TRUE : VARIANT_FALSE; break; case VT_R4: *V_R4REF(pDest) = static_cast<float>(tclObject.getDouble()); break; case VT_R8: *V_R8REF(pDest) = tclObject.getDouble(); break; case VT_DISPATCH: case VT_UNKNOWN: case VT_USERDEFINED: { IUnknown *pUnknown; Tcl_Obj *pObj = tclObject; if (pObj->typePtr == &Extension::unknownPointerType) { pUnknown = static_cast<IUnknown *>(pObj->internalRep.otherValuePtr); } else { Reference *pRef = Extension::referenceHandles.find( interp, tclObject); pUnknown = (pRef == 0) ? 0 : pRef->unknown(); } *V_UNKNOWNREF(pDest) = pUnknown; // The COM rules say we must increment the reference count of // interface pointers returned from methods. if (pUnknown != 0) { pUnknown->AddRef(); } } break; case VT_BSTR: *V_BSTRREF(pDest) = tclObject.getBSTR(); break; case VT_VARIANT: { // Must increment reference count of interface pointers returned // from methods. tclObject.toVariant( V_VARIANTREF(pDest), Type::variant(), interp, true); } break; case VT_SAFEARRAY: if (*V_ARRAYREF(pDest) != 0) { SafeArrayDestroy(*V_ARRAYREF(pDest)); } *V_ARRAYREF(pDest) = tclObject.getSafeArray(type.elementType(), interp); break; default: *V_I4REF(pDest) = tclObject.getLong(); } }