/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
InterfaceOleWrapper::~InterfaceOleWrapper()
{
MutexGuard guard(getBridgeMutex()); // remove entries in global map auto it = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_xOrigin.get())); if(it != UnoObjToWrapperMap.end())
UnoObjToWrapperMap.erase(it);
}
COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::AddRef()
{
acquire(); // does not need to guard because one should not rely on the return value of // AddRef anyway return m_refCount;
}
// Drop the three XInterface methods, add the three corresponding IUnknown ones plus the // four IDispatch ones on top of that.
pTypeAttr->cFuncs = aMethods.getLength() - 3 + 3 + 4;
pTypeAttr->cVars = 0;
pTypeAttr->cImplTypes = 1; // FIXME: I think this, too, is always supposed to be as if just for the seven methods in // IDIspatch?
pTypeAttr->cbSizeVft = 7 * sizeof(void*);
pTypeAttr->cbAlignment = 8;
pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FNONEXTENSIBLE|TYPEFLAG_FDISPATCHABLE;
} else
assert(false);
pTypeAttr->lcid = LOCALE_USER_DEFAULT;
pTypeAttr->memidConstructor = MEMBERID_NIL;
pTypeAttr->memidDestructor = MEMBERID_NIL; // FIXME: Is this correct, just the vtable pointer, right?
pTypeAttr->cbSizeInstance = sizeof(void*);
pTypeAttr->wMajorVerNum = 0;
pTypeAttr->wMinorVerNum = 0;
pTypeAttr->idldescType.wIDLFlags = IDLFLAG_NONE;
SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr: " << pTypeAttr);
SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetDocumentation(" << memid << ")");
if (pBstrName)
{ if (memid == MEMBERID_NIL)
{
*pBstrName = sal::systools::BStr::newBSTR(msImplementationName);
} elseif (memid == DISPID_VALUE)
{ // MEMBERIDs are the same as DISPIDs, apparently?
*pBstrName = SysAllocString(L"Value");
} else
{ // FIXME: Shouldn't we be able to know the names of the members of UNO interfaces?
*pBstrName = sal::systools::BStr::newBSTR(Concat2View("UnknownNameOfMember#" + OUString::number(memid)));
}
} if (pBstrDocString)
*pBstrDocString = SysAllocString(L""); if (pdwHelpContext)
*pdwHelpContext = 0; if (pBstrHelpFile)
*pBstrHelpFile = nullptr;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDllEntry(MEMBERID,
INVOKEKIND,
BSTR *,
BSTR *,
WORD *)
{
SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetDllEntry: E_NOTIMPL"); return E_NOTIMPL;
}
SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeInfo(" << hRefType << ")");
if (!ppTInfo) return E_POINTER;
// FIXME: Is it correct to assume that the only interfaces on which GetRefTypeInfo() would be // called are those that implement ooo::vba::XConnectable?
Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY); if (!xConnectable.is()) return E_NOTIMPL;
// This is not actually called any more by my vbscript test after I added the IProvideClassInfo // thing... so all the CXTypeLib stuff is dead code at the moment.
SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfo(" << iTInfo << ")");
if (!ppTInfo) return E_POINTER;
if (iTInfo != 0) return E_NOTIMPL;
// FIXME: This is surely incorrect. Why is being able to handle GetTypeInfo() here coupled to // being a source for outgoing events, i.e. implementing XConnectable? What would break if we // would use XInterfaceWithIID and its getIID instead?
Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY); if (!xConnectable.is()) return E_NOTIMPL;
SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetIDsOfNames:"); for (unsignedint i = 0; i < cNames; ++i)
{ // Initialise returned rgdispid values.
rgdispid[i] = DISPID_UNKNOWN;
if (m_xInvocation->hasMethod(exactName))
{
d.flags |= DISPATCH_METHOD;
bIsMethod = true;
}
if (d.flags != 0)
{
m_MemberInfos.push_back(d);
iter = m_nameToDispIdMap.emplace(exactName, static_cast<DISPID>(m_MemberInfos.size())).first;
if (exactName != name)
{
iter = m_nameToDispIdMap.emplace(name, static_cast<DISPID>(m_MemberInfos.size())).first;
}
}
}
if (iter == m_nameToDispIdMap.end())
{
ret = DISP_E_UNKNOWNNAME;
SAL_INFO("extensions.olebridge", " " << name << ": UNKNOWN");
} else
{
rgdispid[0] = (*iter).second;
SAL_INFO("extensions.olebridge", " " << name << ": " << rgdispid[0]);
if (bIsMethod && cNames > 1)
{
Reference<XIdlMethod> xIdlMethod;
Reference<XIntrospectionAccess> xIntrospectionAccess = m_xInvocation->getIntrospection(); try
{ if (xIntrospectionAccess.is())
xIdlMethod = xIntrospectionAccess->getMethod(exactName, MethodConcept::ALL);
} catch (const NoSuchMethodException&)
{
} if (xIdlMethod.is())
{ auto aParamInfos = xIdlMethod->getParameterInfos(); for (unsignedint i = 1; i < cNames; ++i)
{ bool bFound = false; for (int j = 0; j < aParamInfos.getLength(); ++j)
{ if (aParamInfos[j].aName.equalsIgnoreAsciiCase(o3tl::toU(rgszNames[i])))
{
rgdispid[i] = j;
bFound = true;
SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": " << rgdispid[i]); break;
}
} if (!bFound)
SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": NOT FOUND");
}
}
}
// Return value should be S_OK only if *all* the names were found. unsignedint i; for (i = 0; i < cNames; ++i) if (rgdispid[i] == DISPID_UNKNOWN) break; if (i == cNames)
ret = S_OK;
}
}
} catch(const BridgeRuntimeError&)
{
OSL_ASSERT(false);
} catch(const Exception&)
{
OSL_ASSERT(false);
} catch(...)
{
OSL_ASSERT(false);
}
return ret;
}
// Note: What the comments here say about JScript possibly holds for Automation clients in general, // like VBScript ones, too. Or not. Hard to say. What is the relevance of JScript nowadays anyway, // and can LO really be used from JScript code on web pages any longer?
// "convertDispparamsArgs" converts VARIANTS to their respecting Any counterparts // The parameters "id", "wFlags" and "pdispparams" equal those as used in // IDispatch::Invoke. The function handles special JavaScript // cases where a VARIANT of type VT_DISPATCH is ambiguous and could represent // an object, array ( JavaScript Array object), out parameter and in/out ( JavaScript Array object) // parameter (JavaScript Array object) // Because all those VT_DISPATCH objects need a different conversion // we have to find out what the object is supposed to be. The function does this // by either using type information or by help of a specialized ValueObject object.
// A. Type Information
// With the help of type information the kind of parameter can be exactly determined // and an appropriate conversion can be chosen. A problem arises if a method expects // an Any. Then the type info does not tell what the type of the value, that is kept // by the any, should be. In this situation the decision whether the param is a // sequence or an object is made upon the fact if the object has a property "0" // ( see function "isJScriptArray"). Since this is unsafe it is recommended to use // the JScript value objects within a JScript script on such an occasion.
// B. JavaScript Value Object ( class JScriptValue )
// A JScriptValue (ValueObject) object is a COM object in that it implements IDispatch and the // IJScriptValue object interface. Such objects are provided by all UNO wrapper // objects used within a JScript script. To obtain an instance one has to call // "_GetValueObject() or Bridge_GetValueObject()" on a UNO wrapper object (class InterfaceOleWrapper). // A value object is appropriately initialized within the script and passed as // parameter to a UNO object method or property. The convertDispparamsArgs function // can easily find out that a param is such an object by querying for the // IJScriptValue interface. By this interface one the type and kind ( out, in/out) // can be determined and the right conversion can be applied. // Using ValueObjects we spare us the effort of acquiring and examining type information // in order to figure out what the an IDispatch parameter is meant for.
// Normal JScript object parameter can be mixed with JScriptValue object. If an // VARIANT contains a VT_DISPATCH that is no JScriptValue than the type information // is used to find out about the required type. void InterfaceOleWrapper::convertDispparamsArgs(DISPID id, unsignedshort/*wFlags*/, DISPPARAMS* pdispparams, Sequence<Any>& rSeq)
{ // Parameters come in reverse order in pdispparams. There might be less parameters than // expected. In that case, assume they are "optional" (but can't be marked as such in UNO IDL), // and fill in the rest with empty Anys. There might also be more than expected. In that case, // assume the oovbaapi UNO IDL hasn't kept up with added optional parameters in MSO, and just // ignore the extra ones, as long as they are empty.
// An example: incoming parameters: <12, 13, "foo/bar.tem"> // // Expected parameters: (string filename, int something, int somethingElse, Any whatever, Any // whateverElse) // // Here the existing incoming parameters are placed in reverse order in the first three outgoing // parameters, and the rest of the outgoing parameters are kept as empty Anys. // // Another example: incoming parameters: <EMPTY, TRUE> // // Expected parameters: (bool flag) // // Here the TRUE is passed as the sole outgoing parameter, and the incoming EMPTY is ignored. // // Still an example: incoming parameters: <"foo.doc", TRUE> // // Expected parameters: (bool flag) // // This throws an error as the incoming string parameter presumably should do something important, // but there is no corresponding outgoing parameter.
//Get type information for the current call
InvocationInfo info; if( ! getInvocationInfoForCall( id, info)) throw BridgeRuntimeError( "[automation bridge]InterfaceOleWrapper::convertDispparamsArgs \n" "Could not obtain type information for current call.");
// Size rSeq according to the number of expected parameters. constint expectedArgs = info.aParamTypes.getLength() + (info.eMemberType == MemberType_PROPERTY ? 1 : 0);
rSeq.realloc( expectedArgs );
Any* pParams = rSeq.getArray();
Any anyParam;
int outgoingArgIndex = 0;
// Go through incoming parameters in reverse order, i.e. in the order as declared in IDL for (int i = std::max(countIncomingArgs, expectedArgs) - 1; i >= 0; i--)
{ // Ignore too many parameters if they are VT_EMPTY anyway if ( outgoingArgIndex >= expectedArgs && pdispparams->rgvarg[i].vt == VT_EMPTY ) continue;
// But otherwise too many parameters is an error if ( outgoingArgIndex >= expectedArgs ) throw BridgeRuntimeError( "[automation bridge] Too many parameters" );
if (i < countIncomingArgs)
{ // A missing (and hopefully optional) arg (in the middle of the argument list) is passed // as an empty Any. if (pdispparams->rgvarg[i].vt == VT_ERROR && pdispparams->rgvarg[i].scode == DISP_E_PARAMNOTFOUND)
{
Any aEmpty;
pParams[ outgoingArgIndex ] = aEmpty;
outgoingArgIndex++; continue;
}
if(convertValueObject( & pdispparams->rgvarg[i], anyParam))
{ //a param is a ValueObject and could be converted
pParams[ outgoingArgIndex ] = anyParam;
outgoingArgIndex++; continue;
}
} else
{ // A missing arg. Let's hope it is de facto optional (there is no way in UNO IDL to mark // a parameter as optional). The corresponding slot in pParams is already a void Any. // Here we don't increase outgoingArgIndex! continue;
}
// If the param is an out, in/out parameter in // JScript (Array object, with value at index 0) then we // extract Array[0] and put the value into varParam. At the end of the loop varParam // is converted if it contains a value otherwise the VARIANT from // DISPPARAMS is converted.
CComVariant varParam;
// Check for JScript out and in/out paramsobjects (VT_DISPATCH). // To find them out we use typeinformation of the function being called.
// No idea how this stuff, originally written for JScript, works for other Automation // clients.
if( pdispparams->rgvarg[i].vt == VT_DISPATCH )
{ if( info.eMemberType == MemberType_METHOD && info.aParamModes[ outgoingArgIndex ] == ParamMode_INOUT)
{ // INOUT-param // Index ( property) "0" contains the actual IN-param. The object is a JScript // Array object. // Get the IN-param at index "0"
IDispatch* pdisp= pdispparams->rgvarg[i].pdispVal;
OLECHAR const * sindex= L"0";
DISPID id2;
DISPPARAMS noParams= {nullptr,nullptr,0,0}; if(SUCCEEDED( hr= pdisp->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, &id2)))
hr= pdisp->Invoke( id2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
& noParams, & varParam, nullptr, nullptr); if( FAILED( hr))
{ throw BridgeRuntimeError( "[automation bridge] Could not determine " "if the object has a member \"0\". Error: " +
OUString::number(hr));
}
}
}
if( varParam.vt == VT_EMPTY) // then it was no in/out parameter
varParam= pdispparams->rgvarg[i];
if( !m_xInvocation.is() )returnfalse;
Reference<XInvocation2> inv2( m_xInvocation, UNO_QUERY); if( inv2.is())
{ // We need the name of the property or method to get its type information. // The name can be identified through the param "id" // that is kept as value in the map m_nameToDispIdMap. // Problem: the Windows JScript engine sometimes changes small letters to capital // letters as happens in xidlclass_obj.createObject( var) // in JScript. // IDispatch::GetIdsOfNames is then called with "CreateObject" !!! // m_nameToDispIdMap can contain several names for one DISPID but only one is // the exact one. If there's no m_xExactName and therefore no exact name then // there's only one entry in the map.
OUString sMemberName;
auto ci1 = std::find_if(m_nameToDispIdMap.cbegin(), m_nameToDispIdMap.cend(),
[&id](const NameToIdMap::value_type& nameToDispId) { return nameToDispId.second == id; }); // item is a pair<OUString, DISPID> if (ci1 != m_nameToDispIdMap.cend())
sMemberName= (*ci1).first; // Get information for the current call ( property or method). // There could be similar names which only differ in the cases // of letters. First we assume that the name which was passed into // GetIDsOfNames is correct. If we won't get information with that // name then we have the invocation service use the XExactName interface. bool validInfo= true;
InvocationInfo invInfo; try{
invInfo= inv2->getInfoForName( sMemberName, false);
} catch(const IllegalArgumentException&)
{
validInfo= false;
}
// XBridgeSupplier2 --------------------------------------------------- // only bridges itself ( this instance of InterfaceOleWrapper)from UNO to IDispatch // If sourceModelType is UNO than any UNO interface implemented by InterfaceOleWrapper // can bridged to IDispatch ( if destModelType == OLE). The IDispatch is // implemented by this class.
Any SAL_CALL InterfaceOleWrapper::createBridge(const Any& modelDepObject, const Sequence<sal_Int8>& /*ProcessId*/,
sal_Int16 sourceModelType,
sal_Int16 destModelType)
{
// "getType" is used in convertValueObject to map the string denoting the type // to an actual Type object. bool getType( const BSTR name, Type & type)
{ bool ret = false;
typelib_TypeDescription * pDesc= nullptr;
OUString str(o3tl::toU(name));
typelib_typedescription_getByName( &pDesc, str.pData ); if( pDesc)
{
type = Type( pDesc->pWeakRef );
typelib_typedescription_release( pDesc);
ret = true;
} return ret;
}
// special Handling for a JScriptValue object
CComQIPtr<IJScriptValueObject> spValueDest(spDispDest); if (spValueDest)
{
VARIANT_BOOL varBool= VARIANT_FALSE; if ((SUCCEEDED(hr = spValueDest->IsOutParam(&varBool))
&& varBool == VARIANT_TRUE)
|| (SUCCEEDED(hr = spValueDest->IsInOutParam(&varBool))
&& varBool == VARIANT_TRUE))
{ if( SUCCEEDED( spValueDest->Set( CComVariant(), *pSource)))
ret= true;
}
} elseif (pDest->vt == VT_DISPATCH)// VT_DISPATCH -> JScript out param
{ // We use IDispatchEx because its GetDispID function causes the creation // of a property if it does not exist already. This is convenient for // out parameters in JScript. Then the user must not specify property "0" // explicitly
CComQIPtr<IDispatchEx> spDispEx( spDispDest); if( spDispEx)
{
CComBSTR nullProp(L"0");
DISPID dwDispID; if( SUCCEEDED( spDispEx->GetDispID( nullProp, fdexNameEnsure, &dwDispID)))
{
DISPPARAMS dispparams = {nullptr, nullptr, 1, 1};
dispparams.rgvarg = pSource;
DISPID dispidPut = DISPID_PROPERTYPUT;
dispparams.rgdispidNamedArgs = &dispidPut;
if (pSource->vt == VT_UNKNOWN || pSource->vt == VT_DISPATCH ||
(pSource->vt & VT_ARRAY) || (pSource->vt & VT_BYREF))
hr = spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
&dispparams, nullptr, nullptr, nullptr); else
hr= spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
&dispparams, nullptr, nullptr, nullptr); if( SUCCEEDED(hr))
ret= true;
}
}
} else
ret= writeBackOutParameter( pDest, pSource);
} else// The param can't be a JScript out-parameter ( an Array object), it could be a VBScript
{ // param. The function checks itself for correct VBScript params
ret= writeBackOutParameter( pDest, pSource);
} return ret;
}
// VisualBasic Script passes arguments as VT_VARIANT | VT_BYREF be it in or out parameter. // Thus we are in charge of freeing an eventual value contained by the inner VARIANT // Please note: VariantCopy doesn't free a VT_BYREF value // The out parameters are expected to have always a valid type staticbool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource)
{
HRESULT hr; bool ret = false; // Out parameter must be VT_BYREF if ((V_VT(pDest) & VT_BYREF) != 0 )
{
VARTYPE oleTypeFlags = V_VT(pSource);
// if caller accept VARIANT as out parameter, any value must be converted if (V_VT(pDest) == (VT_VARIANT | VT_BYREF))
{ // When the user provides a VARIANT rather than a concrete type // we just copy the source to the out, in/out parameter // VT_DISPATCH, VT_UNKNOWN, VT_ARRAY, VT_BSTR in the VARIANT that // is contained in pDest are released by VariantCopy
VariantCopy(V_VARIANTREF(pDest), pSource);
ret = true;
} else
{ // variantarg and variant must have same type if ((V_VT(pDest) & oleTypeFlags) == oleTypeFlags)
{ if ((oleTypeFlags & VT_ARRAY) != 0)
{ // In / Out Param if( *V_ARRAYREF(pDest) != nullptr)
hr= SafeArrayCopyData( V_ARRAY(pSource), *V_ARRAYREF(pDest)); else // Out Param
hr= SafeArrayCopy(V_ARRAY(pSource), V_ARRAYREF(pDest)); if( SUCCEEDED( hr))
ret = true;
} else
{ // copy base type switch (V_VT(pSource))
{ case VT_I2:
{
*V_I2REF(pDest) = V_I2(pSource);
ret = true; break;
} case VT_I4:
*V_I4REF(pDest) = V_I4(pSource);
ret = true; break; case VT_R4:
*V_R4REF(pDest) = V_R4(pSource);
ret = true; break; case VT_R8:
*V_R8REF(pDest) = V_R8(pSource);
ret = true; break; case VT_CY:
*V_CYREF(pDest) = V_CY(pSource);
ret = true; break; case VT_DATE:
*V_DATEREF(pDest) = V_DATE(pSource);
ret = true; break; case VT_BSTR:
SysFreeString( *pDest->pbstrVal);
*V_BSTRREF(pDest) = SysAllocString(V_BSTR(pSource));
ret = true; break; case VT_DISPATCH: if (*V_DISPATCHREF(pDest) != nullptr)
(*V_DISPATCHREF(pDest))->Release();
*V_DISPATCHREF(pDest) = V_DISPATCH(pSource);
if (*V_DISPATCHREF(pDest) != nullptr)
(*V_DISPATCHREF(pDest))->AddRef();
ret = true; break; case VT_ERROR:
*V_ERRORREF(pDest) = V_ERROR(pSource);
ret = true; break; case VT_BOOL:
*V_BOOLREF(pDest) = V_BOOL(pSource);
ret = true; break; case VT_UNKNOWN: if (*V_UNKNOWNREF(pDest) != nullptr)
(*V_UNKNOWNREF(pDest))->Release();
*V_UNKNOWNREF(pDest) = V_UNKNOWN(pSource);
if (*V_UNKNOWNREF(pDest) != nullptr)
(*V_UNKNOWNREF(pDest))->AddRef();
ret = true; break; case VT_I1:
*V_I1REF(pDest) = V_I1(pSource);
ret = true; break; case VT_UI1:
*V_UI1REF(pDest) = V_UI1(pSource);
ret = true; break; case VT_UI2:
*V_UI2REF(pDest) = V_UI2(pSource);
ret = true; break; case VT_UI4:
*V_UI4REF(pDest) = V_UI4(pSource);
ret = true; break; case VT_INT:
*V_INTREF(pDest) = V_INT(pSource);
ret = true; break; case VT_UINT:
*V_UINTREF(pDest) = V_UINT(pSource);
ret = true; break; case VT_DECIMAL:
memcpy(pDest->pdecVal, pSource, sizeof(DECIMAL));
ret = true; break; default: break;
}
}
} else
{ // Handling of special cases // Destination and source types are different if( pDest->vt == (VT_BSTR | VT_BYREF)
&& pSource->vt == VT_I2)
{ // When the user provides a String as out our in/out parameter // and the type is char (TypeClass_CHAR) then we convert to a BSTR // instead of VT_I2 as is done otherwise
OLECHAR buff[]= {0,0};
buff[0]= pSource->iVal;
if (flags != 0)
{ if ((flags & DISPATCH_METHOD) != 0)
{
std::unique_ptr<DISPPARAMS> pNewDispParams;
std::vector<VARIANTARG> vNewArgs;
if (pdispparams->cNamedArgs > 0)
{ // Convert named arguments to positional ones.
// An example: // // Function declaration (in pseudo-code): // int foo(int A, int B, optional int C, optional int D, optional int E, optional int F, optional int G) // // Corresponding parameter numbers (DISPIDs): // 0 1 2 3 4 5 6 // // Actual call: // foo(10, 20, E:=50, D:=40, F:=60) // // That is, A and B are passed positionally, D, E, and F as named arguments, // and the optional C and G parameters are left out. // // Incoming DISPPARAMS: // cArgs=5, cNamedArgs=3 // rgvarg: [60, 40, 50, 20, 10] // rgdispidNamedArgs: [5, 3, 4] // // We calculate nLowestNamedArgDispid = 3 and nHighestNamedArgDispid = 5. // // Result of conversion, no named args: // cArgs=6, cNamedArgs=0 // rgvarg: [60, 50, 40, DISP_E_PARAMNOTFOUND, 20, 10]
// First find the lowest and highest DISPID of the named arguments.
DISPID nLowestNamedArgDispid = 1000000;
DISPID nHighestNamedArgDispid = -1; for (unsignedint i = 0; i < pdispparams->cNamedArgs; ++i)
{ if (pdispparams->rgdispidNamedArgs[i] < nLowestNamedArgDispid)
nLowestNamedArgDispid = pdispparams->rgdispidNamedArgs[i]; if (pdispparams->rgdispidNamedArgs[i] > nHighestNamedArgDispid)
nHighestNamedArgDispid = pdispparams->rgdispidNamedArgs[i];
}
// Make sure named arguments don't overlap with positional ones. The lowest // DISPID of the named arguments should be >= the number of positional // arguments. if (nLowestNamedArgDispid < static_cast<DISPID>(pdispparams->cArgs - pdispparams->cNamedArgs)) return DISP_E_NONAMEDARGS;
// Do the actual conversion.
pNewDispParams.reset(new DISPPARAMS);
vNewArgs.resize(nHighestNamedArgDispid + 1);
pNewDispParams->rgvarg = vNewArgs.data();
pNewDispParams->rgdispidNamedArgs = nullptr;
pNewDispParams->cArgs = nHighestNamedArgDispid + 1;
pNewDispParams->cNamedArgs = 0;
// Initialise all parameter slots as missing for (int i = 0; i < nHighestNamedArgDispid; ++i)
{
pNewDispParams->rgvarg[i].vt = VT_ERROR;
pNewDispParams->rgvarg[i].scode = DISP_E_PARAMNOTFOUND;
}
// Then set the value of those actually present. for (unsignedint i = 0; i < pdispparams->cNamedArgs; ++i)
pNewDispParams->rgvarg[nHighestNamedArgDispid - pdispparams->rgdispidNamedArgs[i]] = pdispparams->rgvarg[i];
for (unsignedint i = pdispparams->cNamedArgs; i < pdispparams->cArgs; ++i)
pNewDispParams->rgvarg[nFirstUnnamedArg + (i-pdispparams->cNamedArgs)] = pdispparams->rgvarg[i];
if (pdispparams->cNamedArgs > 0) return DISP_E_NONAMEDARGS;
// invoke method and take care of exceptions
returnValue = m_xInvocation->invoke(name,
params,
outIndex,
outParams);
// try to write back out parameter if (outIndex.getLength() > 0)
{ const sal_Int16* pOutIndex = outIndex.getConstArray(); const Any* pOutParams = outParams.getConstArray();
for (sal_Int32 i = 0; i < outIndex.getLength(); i++)
{
CComVariant variant; // Currently a Sequence is converted to an SafeArray of VARIANTs.
anyToVariant( &variant, pOutParams[i]);
// out parameter need special handling if they are VT_DISPATCH // and used in JScript int outindex= pOutIndex[i];
writeBackOutParameter2(&(pdispparams->rgvarg[pdispparams->cArgs - 1 - outindex]),
&variant );
}
}
class CXEnumVariant : public IEnumVARIANT, public CComObjectRoot
{ public:
CXEnumVariant()
: mnIndex(1) // ooo::vba::XCollection index starts at one
{
}
int nMemId = 1; auto ArgumentsRange = asNonConstRange(Arguments); // Skip the three XInterface methods for (int i = 3; i < aMethods.getLength(); i++)
{ if (aMethods[i]->getName() == Method)
{ // FIXME: Handle mismatch in type of actual argument and parameter of the method.
// FIXME: Handle mismatch in number of arguments passed and actual number of parameters // of the method.
auto aParamInfos = aMethods[i]->getParameterInfos();
DISPPARAMS aDispParams;
aDispParams.rgdispidNamedArgs = nullptr;
aDispParams.cArgs = Arguments.getLength();
aDispParams.cNamedArgs = 0;
aDispParams.rgvarg = new VARIANT[aDispParams.cArgs]; for (unsigned j = 0; j < aDispParams.cArgs; j++)
{
VariantInit(aDispParams.rgvarg+j); // Note: Reverse order of arguments in Arguments and aDispParams.rgvarg! constunsigned nIncomingArgIndex = aDispParams.cArgs - j - 1;
mpInterfaceOleWrapper->anyToVariant(aDispParams.rgvarg+j, Arguments[nIncomingArgIndex]);
// Handle OUT and INOUT arguments. For instance, the second ('Cancel') parameter to // DocumentBeforeClose() should be a VT_BYREF|VT_BOOL parameter. Need to handle that // here.
if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT ||
aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT)
{ switch (aDispParams.rgvarg[j].vt)
{ case VT_I2:
aDispParams.rgvarg[j].byref = newSHORT(aDispParams.rgvarg[j].iVal);
aDispParams.rgvarg[j].vt |= VT_BYREF; break; case VT_I4:
aDispParams.rgvarg[j].byref = newLONG(aDispParams.rgvarg[j].lVal);
aDispParams.rgvarg[j].vt |= VT_BYREF; break; case VT_BSTR:
aDispParams.rgvarg[j].byref = new BSTR(aDispParams.rgvarg[j].bstrVal);
aDispParams.rgvarg[j].vt |= VT_BYREF; break; case VT_BOOL:
aDispParams.rgvarg[j].byref = new VARIANT_BOOL(aDispParams.rgvarg[j].boolVal);
aDispParams.rgvarg[j].vt |= VT_BYREF; break; default:
assert(false && "Not handled yet"); break;
}
}
}
// In the case of a VBScript client, which uses "late binding", calling Invoke on the // sink it provides will cause a callback to our CXTypeInfo::GetNames for the given // member id, and in that we will tell it the name of the corresponding method, and the // client will know what event handler to invoke based on that name. // // As the outgoing interfaces used (ooo::vba::word::XApplicationOutgoing and others) are // totally not stable and not published in any way, there can be no client that would // have done "compile-time binding" and where the sink would actually be an object with // a vtbl corresponding to the outgoing interface. Late binding clients that work like // VBScript is all we support.
SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Calling Invoke(" << nMemId << ")");
void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
Reference<ooo::vba::XConnectionPoint>& xCP,
Reference<XMultiServiceFactory>& xMSF,
ooo::vba::TypeAndIID aTypeAndIID)
{
SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Init for " << pInterfaceOleWrapper->getImplementationName());
IUnknown *pUnknown; if (SUCCEEDED(QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnknown))))
{ // In case QI for IUnknown returns a different pointer, but nah, it doesn't
SAL_INFO("extensions.olebridge", " (IUnknown@" << pUnknown << ")");
}
HRESULT InterfaceOleWrapper::InvokeGeneral( DISPID dispidMember, unsignedshort wFlags,
DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, unsignedint * /*puArgErr*/, bool& bHandled)
{
HRESULT ret= S_OK; try
{ // DISPID_VALUE | The DEFAULT Value is required in JScript when the situation // is that we put an object into an Array object ( out parameter). We have to return // IDispatch otherwise the object cannot be accessed from the Script. if( dispidMember == DISPID_VALUE && (wFlags & DISPATCH_PROPERTYGET) != 0
&& m_defaultValueType != VT_EMPTY && pvarResult != nullptr)
{ // Special case hack: If it is a ScVbaCheckBox, return the boolean value
Reference<ooo::vba::msforms::XCheckBox> xCheckBox(m_xOrigin, UNO_QUERY); if (xCheckBox.is())
{
bHandled = true;
Any aValue = xCheckBox->getValue();
anyToVariant(pvarResult, aValue); return S_OK;
}
Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(m_smgr)); // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
CComVariant arg; if( pdispparams->cArgs == 1 && SUCCEEDED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])) )
{
Reference<XIdlClass> classStruct= xRefl->forName(OUString(o3tl::toU(arg.bstrVal))); if( classStruct.is())
{
Any anyStruct;
classStruct->createObject( anyStruct);
CComVariant var;
anyToVariant( &var, anyStruct );
if( var.vt == VT_DISPATCH)
{
VariantCopy( pvarResult, & var);
bStruct= true;
}
}
}
ret= bStruct ? S_OK : DISP_E_EXCEPTION;
} elseif (dispidMember == DISPID_CREATE_TYPE_FUNC)
{
bHandled= true; if( !pvarResult) return E_POINTER; // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
CComVariant arg; if( pdispparams->cArgs != 1) return DISP_E_BADPARAMCOUNT; if (FAILED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0]))) return DISP_E_BADVARTYPE;
//check if the provided name represents a valid type
Type type; if (!getType(arg.bstrVal, type))
{
writeExcepinfo(pexcepinfo, OUString::Concat("[automation bridge] A UNO type with the name ") +
o3tl::toU(arg.bstrVal) + " does not exist!"); return DISP_E_EXCEPTION;
}
if (m_xInvocation.is() && (cNames > 0))
{
OUString name(o3tl::toU(rgszNames[0])); // has this name been determined as "bad"
BadNameMap::iterator badIter= m_badNameMap.find( name); if( badIter == m_badNameMap.end() )
{ // name has not been bad before( member exists typedef NameToIdMap::iterator ITnames;
std::pair< ITnames, bool > pair_id= m_nameToDispIdMap.emplace(name, m_currentId++); // new ID inserted ? if( pair_id.second )
{// yes, now create MemberInfo and ad to IdToMemberInfoMap
MemberInfo d(0, name);
m_idToMemberInfoMap.emplace(m_currentId - 1, d);
}
Sequence<Any> params; // holds converted any s if( ! info.flags )
{ // DISPID called for the first time if( wFlags == DISPATCH_METHOD )
{
convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
if( FAILED( ret= doInvoke( pdispparams, pvarResult,
pexcepinfo, puArgErr, info.name, params))
&& ret == DISP_E_MEMBERNOTFOUND)
{ // try to get the exact name
OUString exactName; if (m_xExactName.is())
{
exactName = m_xExactName->getExactName( info.name); // invoke again if( !exactName.isEmpty() )
{ if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
pexcepinfo, puArgErr, exactName, params)))
info.name= exactName;
}
}
} if( SUCCEEDED( ret ) )
info.flags= DISPATCH_METHOD;
} elseif( wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_PROPERTYPUTREF)
{
convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); if( FAILED( ret= doSetProperty( pdispparams, pvarResult,
pexcepinfo, puArgErr, info.name, params))
&& ret == DISP_E_MEMBERNOTFOUND)
{ // try to get the exact name
OUString exactName; if (m_xExactName.is())
{
exactName = m_xExactName->getExactName( info.name); // invoke again if( !exactName.isEmpty() )
{ if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
pexcepinfo, puArgErr, exactName, params)))
info.name= exactName;
}
}
} if( SUCCEEDED( ret ) )
info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET;
} elseif( wFlags == DISPATCH_PROPERTYGET)
{ if( FAILED( ret= doGetProperty( pdispparams, pvarResult,
pexcepinfo, info.name))
&& ret == DISP_E_MEMBERNOTFOUND)
{ // try to get the exact name
OUString exactName; if (m_xExactName.is())
{
exactName = m_xExactName->getExactName( info.name); // invoke again if( !exactName.isEmpty() )
{ if( SUCCEEDED( ret= doGetProperty( pdispparams, pvarResult,
pexcepinfo, exactName)))
info.name= exactName;
}
}
} if( SUCCEEDED( ret ) )
info.flags= DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT;
} elseif( wFlags & DISPATCH_METHOD &&
(wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF))
{
OUString exactName; // convert params for DISPATCH_METHOD or DISPATCH_PROPERTYPUT
convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); // try first as method if( FAILED( ret= doInvoke( pdispparams, pvarResult,
pexcepinfo, puArgErr, info.name, params))
&& ret == DISP_E_MEMBERNOTFOUND)
{ // try to get the exact name if (m_xExactName.is())
{
exactName = m_xExactName->getExactName( info.name); // invoke again if( !exactName.isEmpty() )
{ if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
pexcepinfo, puArgErr, exactName, params)))
info.name= exactName;
}
}
} if( SUCCEEDED( ret ) )
info.flags= DISPATCH_METHOD;
// The returned HRESULT is only appropriate for IDispatch::Invoke static HRESULT mapCannotConvertException(const CannotConvertException &e, unsignedint * puArgErr)
{
HRESULT ret; bool bWriteIndex= true;
switch ( e.Reason)
{ case FailReason::OUT_OF_RANGE:
ret = DISP_E_OVERFLOW; break; case FailReason::IS_NOT_NUMBER:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::IS_NOT_ENUM:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::IS_NOT_BOOL:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::NO_SUCH_INTERFACE:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::SOURCE_IS_NO_DERIVED_TYPE:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::TYPE_NOT_SUPPORTED:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::INVALID:
ret = DISP_E_TYPEMISMATCH; break; case FailReason::NO_DEFAULT_AVAILABLE:
ret = DISP_E_BADPARAMCOUNT; break; case FailReason::UNKNOWN:
ret = E_UNEXPECTED; break; default:
ret = E_UNEXPECTED;
bWriteIndex= false; break;
}
// The function maps the TypeClass of the any to VARTYPE: If // the Any contains STRUCT or INTERFACE then the return value // is VT_DISPATCH. The function is used from o2u_createUnoObjectWrapper // and the result is put into the constructor of the uno - wrapper // object. If a client asks the object for DISPID_VALUE and this // function returned VT_DISPATCH then the IDispatch of the same // object is being returned. // See InterfaceOleWrapper::Invoke, InterfaceOleWrapper::m_defaultValueType
VARTYPE getVarType( const Any& value)
{
VARTYPE ret= VT_EMPTY;
switch ( value.getValueTypeClass())
{ case TypeClass_STRUCT: ret= VT_DISPATCH; break; case TypeClass_INTERFACE: ret= VT_DISPATCH; break; default: break;
} return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.