diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /extensions/source/ole/oleobjw.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extensions/source/ole/oleobjw.cxx')
-rw-r--r-- | extensions/source/ole/oleobjw.cxx | 2513 |
1 files changed, 2513 insertions, 0 deletions
diff --git a/extensions/source/ole/oleobjw.cxx b/extensions/source/ole/oleobjw.cxx new file mode 100644 index 000000000..85f410c54 --- /dev/null +++ b/extensions/source/ole/oleobjw.cxx @@ -0,0 +1,2513 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 . + */ + +#include "ole2uno.hxx" +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <osl/diagnose.h> +#include <osl/doublecheckedlocking.h> +#include <osl/thread.h> + +#include <memory> +#include <string_view> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/FailReason.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> + +#include <com/sun/star/bridge/oleautomation/NamedArgument.hpp> +#include <com/sun/star/bridge/oleautomation/PropertyPutArgument.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include <typelib/typedescription.hxx> +#include <rtl/uuid.h> +#include <rtl/ustring.hxx> + +#include "jscriptclasses.hxx" + +#include "oleobjw.hxx" +#include "unoobjw.hxx" +#include <stdio.h> +using namespace osl; +using namespace cppu; +using namespace com::sun::star::script; +using namespace com::sun::star::lang; +using namespace com::sun::star::bridge; +using namespace com::sun::star::bridge::oleautomation; +using namespace com::sun::star::bridge::ModelDependent; +using namespace ::com::sun::star; + + +#define JSCRIPT_ID_PROPERTY L"_environment" +#define JSCRIPT_ID L"jscript" + +// key: XInterface pointer created by Invocation Adapter Factory +// value: XInterface pointer to the wrapper class. +// Entries to the map are made within +// Any createOleObjectWrapper(IUnknown* pUnknown, const Type& aType); +// Entries are being deleted if the wrapper class's destructor has been +// called. +// Before UNO object is wrapped to COM object this map is checked +// to see if the UNO object is already a wrapper. +std::unordered_map<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap; +// key: XInterface of the wrapper object. +// value: XInterface of the Interface created by the Invocation Adapter Factory. +// A COM wrapper is responsible for removing the corresponding entry +// in AdapterToWrapperMap if it is being destroyed. Because the wrapper does not +// know about its adapted interface it uses WrapperToAdapterMap to get the +// adapted interface which is then used to locate the entry in AdapterToWrapperMap. +std::unordered_map<sal_uIntPtr,sal_uIntPtr> WrapperToAdapterMap; + +std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap; + +IUnknownWrapper::IUnknownWrapper( Reference<XMultiServiceFactory> const & xFactory, + sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass): + UnoConversionUtilities<IUnknownWrapper>( xFactory, unoWrapperClass, comWrapperClass), + m_pxIdlClass( nullptr), m_eJScript( JScriptUndefined), + m_bComTlbIndexInit(false), m_bHasDfltMethod(false), m_bHasDfltProperty(false) +{ +} + + +IUnknownWrapper::~IUnknownWrapper() +{ + o2u_attachCurrentThread(); + MutexGuard guard(getBridgeMutex()); + XInterface * xIntRoot = static_cast<OWeakObject *>(this); +#if OSL_DEBUG_LEVEL > 0 + acquire(); // make sure we don't delete us twice because of Reference + OSL_ASSERT( Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY).get() == xIntRoot ); +#endif + + // remove entries in global maps + auto it= WrapperToAdapterMap.find( reinterpret_cast<sal_uIntPtr>(xIntRoot)); + if( it != WrapperToAdapterMap.end()) + { + sal_uIntPtr adapter= it->second; + + AdapterToWrapperMap.erase( adapter); + WrapperToAdapterMap.erase( it); + } + + auto it_c= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_spUnknown.p)); + if(it_c != ComPtrToWrapperMap.end()) + ComPtrToWrapperMap.erase(it_c); +} + +Any IUnknownWrapper::queryInterface(const Type& t) +{ + if (t == cppu::UnoType<XDefaultMethod>::get() && !m_bHasDfltMethod ) + return Any(); + if (t == cppu::UnoType<XDefaultProperty>::get() && !m_bHasDfltProperty ) + return Any(); + if ( ( t == cppu::UnoType<XInvocation>::get() || t == cppu::UnoType<XAutomationInvocation>::get() ) && !m_spDispatch) + return Any(); + // XDirectInvocation seems to be an oracle replacement for XAutomationInvocation, however it is flawed especially wrt. assumptions about whether to invoke a + // Put or Get property, the implementation code has no business guessing that, it's up to the caller to decide that. Worse XDirectInvocation duplicates lots of code. + // XAutomationInvocation provides separate calls for put& get + // properties. Note: Currently the basic runtime doesn't call put properties directly, it should... after all the basic runtime should know whether it is calling a put or get property. + // For the moment for ease of merging we will let the XDirectInvoke and XAuthomationInvocation interfaces stay side by side (and for the moment at least I would prefer the basic + // runtime to call XAutomationInvocation instead of XDirectInvoke + return WeakImplHelper<XBridgeSupplier2, + XInitialization, XAutomationObject, XDefaultProperty, XDefaultMethod, XDirectInvocation, XAutomationInvocation >::queryInterface(t); +} + +Reference<XIntrospectionAccess> SAL_CALL IUnknownWrapper::getIntrospection() +{ + Reference<XIntrospectionAccess> ret; + + return ret; +} + +Any SAL_CALL IUnknownWrapper::invokeGetProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) +{ + Any aResult; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( !aDescGet ) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + aResult = invokeWithDispIdComTlb( aDescGet, aPropertyName, aParams, aOutParamIndex, aOutParam ); + } + catch ( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invokeGetProperty ! Message : \n " + + e.Message, + nullptr, anyEx ); + } + return aResult; +} + +Any SAL_CALL IUnknownWrapper::invokePutProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) +{ + Any aResult; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( !aDescPut ) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + aResult = invokeWithDispIdComTlb( aDescPut, aPropertyName, aParams, aOutParamIndex, aOutParam ); + } + catch ( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invokePutProperty ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + return aResult; +} + + +Any SAL_CALL IUnknownWrapper::invoke( const OUString& aFunctionName, + const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, + Sequence< Any >& aOutParam ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + Any ret; + + try + { + o2u_attachCurrentThread(); + + TypeDescription methodDesc; + getMethodInfo(aFunctionName, methodDesc); + if( methodDesc.is()) + { + ret = invokeWithDispIdUnoTlb(aFunctionName, + aParams, + aOutParamIndex, + aOutParam); + } + else + { + ret= invokeWithDispIdComTlb( aFunctionName, + aParams, + aOutParamIndex, + aOutParam); + } + } + catch (const IllegalArgumentException &) + { + throw; + } + catch (const CannotConvertException &) + { + throw; + } + catch (const BridgeRuntimeError & e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invoke ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch(...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::Invoke !"); + } + return ret; +} + +void SAL_CALL IUnknownWrapper::setValue( const OUString& aPropertyName, + const Any& aValue ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + try + { + o2u_attachCurrentThread(); + + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + //check if there is such a property at all or if it is read only + if ( ! aDescPut && ! aDescGet && ! aVarDesc) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + + if ( (! aDescPut && aDescGet) + || (aVarDesc && aVarDesc->wVarFlags == VARFLAG_FREADONLY) ) + { + //read-only + SAL_WARN( "extensions.olebridge", "[automation bridge] Property " << aPropertyName << " is read-only"); + // ignore silently + return; + } + + HRESULT hr= S_OK; + DISPPARAMS dispparams; + CComVariant varArg; + CComVariant varRefArg; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + + // converting UNO value to OLE variant + DISPID dispidPut= DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = &dispidPut; + dispparams.cArgs = 1; + dispparams.cNamedArgs = 1; + dispparams.rgvarg = & varArg; + + OSL_ASSERT(aDescPut || aVarDesc); + + VARTYPE vt = 0; + DISPID dispid = 0; + INVOKEKIND invkind = INVOKE_PROPERTYPUT; + //determine the expected type, dispid, invoke kind (DISPATCH_PROPERTYPUT, + //DISPATCH_PROPERTYPUTREF) + if (aDescPut) + { + vt = getElementTypeDesc(& aDescPut->lprgelemdescParam[0].tdesc); + dispid = aDescPut->memid; + invkind = aDescPut->invkind; + } + else + { + vt = getElementTypeDesc( & aVarDesc->elemdescVar.tdesc); + dispid = aVarDesc->memid; + if (vt == VT_UNKNOWN || vt == VT_DISPATCH || + (vt & VT_ARRAY) || (vt & VT_BYREF)) + { + invkind = INVOKE_PROPERTYPUTREF; + } + } + + // convert the uno argument + if (vt & VT_BYREF) + { + anyToVariant( & varRefArg, aValue, ::sal::static_int_cast< VARTYPE, int >( vt ^ VT_BYREF ) ); + varArg.vt = vt; + if( (vt & VT_TYPEMASK) == VT_VARIANT) + varArg.byref = & varRefArg; + else if ((vt & VT_TYPEMASK) == VT_DECIMAL) + varArg.byref = & varRefArg.decVal; + else + varArg.byref = & varRefArg.byref; + } + else + { + anyToVariant(& varArg, aValue, vt); + } + // call to IDispatch + hr = m_spDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, ::sal::static_int_cast< WORD, INVOKEKIND >( invkind ), + &dispparams, & varResult, & excepinfo, &uArgErr); + + // lookup error code + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw RuntimeException(); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException(); + break; + case DISP_E_EXCEPTION: + throw InvocationTargetException(); + break; + case DISP_E_MEMBERNOTFOUND: + throw UnknownPropertyException(); + break; + case DISP_E_NONAMEDARGS: + throw RuntimeException(); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )) ; + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::UNKNOWN, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException(); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException(); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + } + catch (const CannotConvertException &) + { + throw; + } + catch (const UnknownPropertyException &) + { + throw; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::setValue ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch (...) + { + throw RuntimeException( + "[automation bridge] unexpected exception in " + "IUnknownWrapper::setValue !"); + } +} + +Any SAL_CALL IUnknownWrapper::getValue( const OUString& aPropertyName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + Any ret; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + // I was going to implement an XServiceInfo interface to allow the type + // of the automation object to be exposed... but it seems + // from looking at comments in the code that it is possible for + // this object to actually wrap a UNO object ( I guess if automation is + // used from MSO to create Openoffice objects ) Therefore, those objects + // will more than likely already have their own XServiceInfo interface. + // Instead here I chose a name that should be illegal both in COM and + // UNO ( from an IDL point of view ) therefore I think this is a safe + // hack + if ( aPropertyName == "$GetTypeName" ) + { + if ( pInfo && m_sTypeName.getLength() == 0 ) + { + m_sTypeName = "IDispatch"; + CComBSTR sName; + + if ( SUCCEEDED( pInfo->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) ) + { + OUString sTmp( o3tl::toU(LPCOLESTR(sName))); + if ( sTmp.startsWith("_") ) + sTmp = sTmp.copy(1); + // do we own the memory for pTypeLib, msdn doc is vague + // I'll assume we do + CComPtr< ITypeLib > pTypeLib; + unsigned int index; + if ( SUCCEEDED( pInfo->GetContainingTypeLib( &pTypeLib.p, &index )) ) + { + if ( SUCCEEDED( pTypeLib->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) ) + { + OUString sLibName( o3tl::toU(LPCOLESTR(sName))); + m_sTypeName = sLibName + "." + sTmp; + + } + } + } + + } + ret <<= m_sTypeName; + return ret; + } + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( ! aDescGet && ! aDescPut && ! aVarDesc) + { + //property not found + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + // write-only should not be possible + OSL_ASSERT( aDescGet || ! aDescPut); + + HRESULT hr; + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + DISPID dispid; + if (aDescGet) + dispid = aDescGet->memid; + else if (aVarDesc) + dispid = aVarDesc->memid; + else + dispid = aDescPut->memid; + + hr = m_spDispatch->Invoke(dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_PROPERTYGET, + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (hr == S_OK) + { + // If the com object implements uno interfaces then we have + // to convert the attribute into the expected type. + TypeDescription attrInfo; + getAttributeInfo(aPropertyName, attrInfo); + if( attrInfo.is() ) + variantToAny( &varResult, ret, Type( attrInfo.get()->pWeakRef)); + else + variantToAny(&varResult, ret); + } + + // lookup error code + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + case DISP_E_BADVARTYPE: + case DISP_E_EXCEPTION: + throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + case DISP_E_MEMBERNOTFOUND: + throw UnknownPropertyException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + default: + throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + } + } + catch ( const UnknownPropertyException& ) + { + throw; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::getValue ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + catch (...) + { + throw RuntimeException( + "[automation bridge] unexpected exception in " + "IUnknownWrapper::getValue !"); + } + return ret; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasMethod( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + bool ret = false; + + try + { + o2u_attachCurrentThread(); + ITypeInfo* pInfo = getTypeInfo(); + FuncDesc aDesc(pInfo); + getFuncDesc(aName, & aDesc); + // Automation properties can have arguments. Those are treated as methods and + //are called through XInvocation::invoke. + if ( ! aDesc) + { + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc( aName, & aDescGet, & aDescPut, & aVarDesc); + if ((aDescGet && aDescGet->cParams > 0) + || (aDescPut && aDescPut->cParams > 0)) + ret = true; + } + else + ret = true; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasMethod ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + catch (...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasMethod !"); + } + return ret; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasProperty( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException("[automation bridge] The object does not have an " + "IDispatch interface"); + } + bool ret = false; + try + { + o2u_attachCurrentThread(); + + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aName, & aDescGet, & aDescPut, & aVarDesc); + + // we should probably just check the func kind + // basic has been modified to handle properties ( 'get' ) props at + // least with parameters + // additionally you can call invoke(Get|Set)Property on the bridge + // you can determine if a property has parameter is hasMethod + // returns true for the name + if (aVarDesc + || aDescPut + || aDescGet ) + { + ret = true; + } + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasProperty ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch (...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasProperty !"); + } + return ret; +} + +Any SAL_CALL IUnknownWrapper::createBridge( const Any& modelDepObject, + const Sequence< sal_Int8 >& /*aProcessId*/, sal_Int16 sourceModelType, + sal_Int16 destModelType ) +{ + Any ret; + o2u_attachCurrentThread(); + + if ( + (sourceModelType == UNO) && + (destModelType == OLE) && + (modelDepObject.getValueTypeClass() == TypeClass_INTERFACE) + ) + { + Reference<XInterface> xInt( *static_cast<XInterface* const *>(modelDepObject.getValue())); + Reference<XInterface> xSelf( static_cast<OWeakObject*>(this)); + + if (xInt == xSelf) + { + VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT))); + + VariantInit(pVariant); + if (m_bOriginalDispatch) + { + pVariant->vt = VT_DISPATCH; + pVariant->pdispVal = m_spDispatch; + pVariant->pdispVal->AddRef(); + } + else + { + pVariant->vt = VT_UNKNOWN; + pVariant->punkVal = m_spUnknown; + pVariant->punkVal->AddRef(); + } + + ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get()); + } + } + + return ret; +} +/** @internal + @exception IllegalArgumentException + @exception CannotConvertException + @exception InvocationTargetException + @RuntimeException +*/ +Any IUnknownWrapper::invokeWithDispIdUnoTlb(const OUString& sFunctionName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + Any ret; + HRESULT hr= S_OK; + + sal_Int32 parameterCount= Params.getLength(); + sal_Int32 outParameterCount= 0; + typelib_InterfaceMethodTypeDescription* pMethod= nullptr; + TypeDescription methodDesc; + getMethodInfo(sFunctionName, methodDesc); + + // We need to know whether the IDispatch is from a JScript object. + // Then out and in/out parameters have to be treated differently than + // with common COM objects. + bool bJScriptObject= isJScriptObject(); + std::unique_ptr<CComVariant[]> sarParams; + std::unique_ptr<CComVariant[]> sarParamsRef; + CComVariant *pVarParams= nullptr; + CComVariant *pVarParamsRef= nullptr; + bool bConvRet= true; + + if( methodDesc.is()) + { + pMethod = reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(methodDesc.get()); + parameterCount = pMethod->nParams; + // Create the Array for the array being passed in DISPPARAMS + // the array also contains the outparameter (but not the values) + if( pMethod->nParams > 0) + { + sarParams.reset(new CComVariant[ parameterCount]); + pVarParams = sarParams.get(); + } + + // Create the Array for the out an in/out parameter. These values + // are referenced by the VT_BYREF VARIANTs in DISPPARAMS. + // We need to find out the number of out and in/out parameter. + for( sal_Int32 i=0; i < parameterCount; i++) + { + if( pMethod->pParams[i].bOut) + outParameterCount++; + } + + if( !bJScriptObject) + { + sarParamsRef.reset(new CComVariant[outParameterCount]); + pVarParamsRef = sarParamsRef.get(); + // build up the parameters for IDispatch::Invoke + sal_Int32 outParamIndex=0; + int i = 0; + try + { + for( i= 0; i < parameterCount; i++) + { + // In parameter + if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut) + { + anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]); + } + // Out parameter + in/out parameter + else if( pMethod->pParams[i].bOut ) + { + CComVariant var; + if(pMethod->pParams[i].bIn) + { + anyToVariant( & var,Params[i]); + pVarParamsRef[outParamIndex] = var; + } + + switch( pMethod->pParams[i].pTypeRef->eTypeClass) + { + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_STRUCT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt= VT_DISPATCH; + pVarParamsRef[ outParamIndex].pdispVal= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_DISPATCH | VT_BYREF; + pVarParams[parameterCount - i -1].ppdispVal= &pVarParamsRef[outParamIndex].pdispVal; + break; + case typelib_TypeClass_ENUM: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_I4; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_I4 | VT_BYREF; + pVarParams[parameterCount - i -1].plVal= &pVarParamsRef[outParamIndex].lVal; + break; + case typelib_TypeClass_SEQUENCE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_ARRAY| VT_VARIANT; + pVarParamsRef[ outParamIndex].parray= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_ARRAY| VT_BYREF | VT_VARIANT; + pVarParams[parameterCount - i -1].pparray= &pVarParamsRef[outParamIndex].parray; + break; + case typelib_TypeClass_ANY: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_EMPTY; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF; + pVarParams[parameterCount - i -1].pvarVal = &pVarParamsRef[outParamIndex]; + break; + case typelib_TypeClass_BOOLEAN: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_BOOL; + pVarParamsRef[ outParamIndex].boolVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_BOOL| VT_BYREF; + pVarParams[parameterCount - i -1].pboolVal = + & pVarParamsRef[outParamIndex].boolVal; + break; + + case typelib_TypeClass_STRING: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_BSTR; + pVarParamsRef[ outParamIndex].bstrVal= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_BSTR| VT_BYREF; + pVarParams[parameterCount - i -1].pbstrVal= + & pVarParamsRef[outParamIndex].bstrVal; + break; + + case typelib_TypeClass_FLOAT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_R4; + pVarParamsRef[ outParamIndex].fltVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_R4| VT_BYREF; + pVarParams[parameterCount - i -1].pfltVal = + & pVarParamsRef[outParamIndex].fltVal; + break; + case typelib_TypeClass_DOUBLE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_R8; + pVarParamsRef[ outParamIndex].dblVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_R8| VT_BYREF; + pVarParams[parameterCount - i -1].pdblVal= + & pVarParamsRef[outParamIndex].dblVal; + break; + case typelib_TypeClass_BYTE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_UI1; + pVarParamsRef[ outParamIndex].bVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_UI1| VT_BYREF; + pVarParams[parameterCount - i -1].pbVal= + & pVarParamsRef[outParamIndex].bVal; + break; + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_I2; + pVarParamsRef[ outParamIndex].iVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_I2| VT_BYREF; + pVarParams[parameterCount - i -1].piVal= + & pVarParamsRef[outParamIndex].iVal; + break; + + default: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_EMPTY; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF; + pVarParams[parameterCount - i -1].pvarVal = + & pVarParamsRef[outParamIndex]; + } + outParamIndex++; + } // end else if + } // end for + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + else // it is a JScriptObject + { + int i = 0; + try + { + for( ; i< parameterCount; i++) + { + // In parameter + if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut) + { + anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]); + } + // Out parameter + in/out parameter + else if( pMethod->pParams[i].bOut ) + { + CComObject<JScriptOutParam>* pParamObject; + if( !SUCCEEDED( CComObject<JScriptOutParam>::CreateInstance( &pParamObject))) + { + throw BridgeRuntimeError( + "[automation bridge]IUnknownWrapper::" + "invokeWithDispIdUnoTlb\n" + "Could not create out parameter at index: " + + OUString::number(static_cast<sal_Int32>(i))); + } + + CComPtr<IUnknown> pUnk(pParamObject->GetUnknown()); + CComQIPtr<IDispatch> pDisp( pUnk); + + pVarParams[ parameterCount - i -1].vt= VT_DISPATCH; + pVarParams[ parameterCount - i -1].pdispVal= pDisp; + pVarParams[ parameterCount - i -1].pdispVal->AddRef(); + // if the param is in/out then put the parameter on index 0 + if( pMethod->pParams[i].bIn ) // in / out + { + CComVariant varParam; + anyToVariant( &varParam, Params.getConstArray()[i]); + CComDispatchDriver dispDriver( pDisp); + if(FAILED( dispDriver.PutPropertyByName( L"0", &varParam))) + throw BridgeRuntimeError( + "[automation bridge]IUnknownWrapper::" + "invokeWithDispIdUnoTlb\n" + "Could not set property \"0\" for the in/out " + "param!"); + + } + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + } + // No type description Available, that is we have to deal with a COM component, + // that does not implements UNO interfaces ( IDispatch based) + else + { + //We should not run into this block, because invokeWithDispIdComTlb should + //have been called instead. + OSL_ASSERT(false); + } + + + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + DISPPARAMS dispparams= { pVarParams, nullptr, static_cast<UINT>(parameterCount), 0}; + + // Get the DISPID + FuncDesc aDesc(getTypeInfo()); + getFuncDesc(sFunctionName, & aDesc); + // invoking OLE method + hr = m_spDispatch->Invoke(aDesc->memid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_METHOD, + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (hr == S_OK) + { + if( outParameterCount && pMethod) + { + OutParamIndex.realloc( outParameterCount); + auto pOutParamIndex = OutParamIndex.getArray(); + OutParam.realloc( outParameterCount); + auto pOutParam = OutParam.getArray(); + sal_Int32 outIndex=0; + int i = 0; + try + { + for( ; i < parameterCount; i++) + { + if( pMethod->pParams[i].bOut ) + { + pOutParamIndex[outIndex]= static_cast<sal_Int16>(i); + Any outAny; + if( !bJScriptObject) + { + variantToAny( &pVarParamsRef[outIndex], outAny, + Type(pMethod->pParams[i].pTypeRef), false); + pOutParam[outIndex++]= outAny; + } + else //JScriptObject + { + if( pVarParams[i].vt == VT_DISPATCH) + { + CComDispatchDriver pDisp( pVarParams[i].pdispVal); + if( pDisp) + { + CComVariant varOut; + if( SUCCEEDED( pDisp.GetPropertyByName( L"0", &varOut))) + { + variantToAny( &varOut, outAny, + Type(pMethod->pParams[parameterCount - 1 - i].pTypeRef), false); + pOutParam[outParameterCount - 1 - outIndex++]= outAny; + } + else + bConvRet= false; + } + else + bConvRet= false; + } + else + bConvRet= false; + } + } + if( !bConvRet) break; + } + } + catch(IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch(CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + // return value, no type information available + if ( bConvRet) + { + try + { + if( pMethod ) + variantToAny(&varResult, ret, Type( pMethod->pReturnTypeRef), false); + else + variantToAny(&varResult, ret, false); + } + catch (IllegalArgumentException & e) + { + e.Message = + "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n" + "Could not convert return value! \n Message: \n" + e.Message; + throw; + } + catch (CannotConvertException & e) + { + e.Message = + "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n" + "Could not convert return value! \n Message: \n" + e.Message; + throw; + } + } + } + + if( !bConvRet) // conversion of return or out parameter failed + throw CannotConvertException("Call to COM object failed. Conversion of return or out value failed", + Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY ), TypeClass_UNKNOWN, + FailReason::UNKNOWN, 0);// lookup error code + // conversion of return or out parameter failed + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException(); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException(); + break; + case DISP_E_EXCEPTION: + throw InvocationTargetException(); + break; + case DISP_E_MEMBERNOTFOUND: + throw IllegalArgumentException(); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException(); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException() ; + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException() ; + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + + return ret; +} + + +// XInitialization +void SAL_CALL IUnknownWrapper::initialize( const Sequence< Any >& aArguments ) +{ + // 1.parameter is IUnknown + // 2.parameter is a boolean which indicates if the COM pointer was an IUnknown or IDispatch + // 3.parameter is a Sequence<Type> + o2u_attachCurrentThread(); + OSL_ASSERT(aArguments.getLength() == 3); + + m_spUnknown= *static_cast<IUnknown* const *>(aArguments[0].getValue()); + m_spUnknown.QueryInterface( & m_spDispatch.p); + + aArguments[1] >>= m_bOriginalDispatch; + aArguments[2] >>= m_seqTypes; + + ITypeInfo* pType = nullptr; + try + { + // a COM object implementation that has no TypeInfo is still a legal COM object; + // such objects can at least be transported through UNO using the bridge + // so we should allow to create wrappers for them as well + pType = getTypeInfo(); + } + catch( const BridgeRuntimeError& ) + {} + catch( const Exception& ) + {} + + if ( pType ) + { + try + { + // Get Default member + CComBSTR defaultMemberName; + if ( SUCCEEDED( pType->GetDocumentation(0, &defaultMemberName, nullptr, nullptr, nullptr ) ) ) + { + OUString usName(o3tl::toU(LPCOLESTR(defaultMemberName))); + FuncDesc aDescGet(pType); + FuncDesc aDescPut(pType); + VarDesc aVarDesc(pType); + // see if this is a property first ( more likely to be a property then a method ) + getPropDesc( usName, & aDescGet, & aDescPut, & aVarDesc); + + if ( !aDescGet && !aDescPut ) + { + getFuncDesc( usName, &aDescGet ); + if ( !aDescGet ) + throw BridgeRuntimeError( "[automation bridge]IUnknownWrapper::initialize() Failed to get Function or Property desc. for " + usName ); + } + // now for some funny heuristics to make basic understand what to do + // a single aDescGet ( that doesn't take any params ) would be + // a read only ( defaultmember ) property e.g. this object + // should implement XDefaultProperty + // a single aDescGet ( that *does* ) take params is basically a + // default method e.g. implement XDefaultMethod + + // a DescPut ( I guess we only really support a default param with '1' param ) as a setValue ( but I guess we can leave it through, the object will fail if we don't get it right anyway ) + if ( aDescPut || ( aDescGet && aDescGet->cParams == 0 ) ) + m_bHasDfltProperty = true; + if ( aDescGet->cParams > 0 ) + m_bHasDfltMethod = true; + if ( m_bHasDfltProperty || m_bHasDfltMethod ) + m_sDefaultMember = usName; + } + } + catch ( const BridgeRuntimeError & e ) + { + throw RuntimeException( e.message ); + } + catch( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "[automation bridge] unexpected exception in IUnknownWrapper::initialize() error message: \n" + e.Message, + nullptr, anyEx ); + } + } +} + + +// XDirectInvocation +uno::Any SAL_CALL IUnknownWrapper::directInvoke( const OUString& aName, const uno::Sequence< uno::Any >& aParams ) +{ + Any aResult; + + if ( !m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + o2u_attachCurrentThread(); + DISPID dispid; + if ( !getDispid( aName, &dispid ) ) + throw IllegalArgumentException( + "[automation bridge] The object does not have a function or property " + + aName, Reference<XInterface>(), 0); + + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr = 0; + INVOKEKIND pInvkinds[2]; + pInvkinds[0] = INVOKE_FUNC; + pInvkinds[1] = aParams.getLength() ? INVOKE_PROPERTYPUT : INVOKE_PROPERTYGET; + HRESULT hInvRes = E_FAIL; + + // try Invoke first, if it does not work, try put/get property + for ( sal_Int32 nStep = 0; FAILED( hInvRes ) && nStep < 2; nStep++ ) + { + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + + std::unique_ptr<DISPID[]> arDispidNamedArgs; + std::unique_ptr<CComVariant[]> ptrArgs; + std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments + CComVariant * arArgs = nullptr; + CComVariant * arRefArgs = nullptr; + + dispparams.cArgs = aParams.getLength(); + + // Determine the number of named arguments + for ( uno::Any const & any : aParams ) + if ( any.getValueType() == cppu::UnoType<NamedArgument>::get() ) + dispparams.cNamedArgs ++; + + // fill the named arguments + if ( dispparams.cNamedArgs > 0 + && ( dispparams.cNamedArgs != 1 || pInvkinds[nStep] != INVOKE_PROPERTYPUT ) ) + { + int nSizeAr = dispparams.cNamedArgs + 1; + if ( pInvkinds[nStep] == INVOKE_PROPERTYPUT ) + nSizeAr = dispparams.cNamedArgs; + + std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]); + OLECHAR ** pNames = saNames.get(); + pNames[0] = const_cast<OLECHAR*>(o3tl::toW(aName.getStr())); + + int cNamedArg = 0; + for ( size_t nInd = 0; nInd < dispparams.cArgs; nInd++ ) + { + if (auto v = o3tl::tryAccess<NamedArgument>(aParams[nInd])) + { + const NamedArgument& arg = *v; + + //We put the parameter names in reverse order into the array, + //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs + //The first name in the array is the method name + pNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr())); + } + } + + arDispidNamedArgs.reset( new DISPID[nSizeAr] ); + HRESULT hr = getTypeInfo()->GetIDsOfNames( pNames, nSizeAr, arDispidNamedArgs.get() ); + if ( hr == E_NOTIMPL ) + hr = m_spDispatch->GetIDsOfNames(IID_NULL, pNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() ); + + if ( SUCCEEDED( hr ) ) + { + if ( pInvkinds[nStep] == DISPATCH_PROPERTYPUT ) + { + DISPID* arIDs = arDispidNamedArgs.get(); + arIDs[0] = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = arIDs; + } + else + { + DISPID* arIDs = arDispidNamedArgs.get(); + dispparams.rgdispidNamedArgs = & arIDs[1]; + } + } + else if (hr == DISP_E_UNKNOWNNAME) + { + throw IllegalArgumentException( + "[automation bridge]One of the named arguments is wrong!", + Reference<XInterface>(), 0); + } + else + { + throw InvocationTargetException( + "[automation bridge] ITypeInfo::GetIDsOfNames returned error " + + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any()); + } + } + + //Convert arguments + ptrArgs.reset(new CComVariant[dispparams.cArgs]); + ptrRefArgs.reset(new CComVariant[dispparams.cArgs]); + arArgs = ptrArgs.get(); + arRefArgs = ptrRefArgs.get(); + + sal_Int32 nInd = 0; + try + { + sal_Int32 revIndex = 0; + for ( nInd = 0; nInd < sal_Int32(dispparams.cArgs); nInd++) + { + revIndex = dispparams.cArgs - nInd - 1; + arRefArgs[revIndex].byref = nullptr; + Any anyArg; + if ( nInd < aParams.getLength() ) + anyArg = aParams.getConstArray()[nInd]; + + // Property Put arguments + if ( anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get() ) + { + PropertyPutArgument arg; + anyArg >>= arg; + anyArg = arg.Value; + } + // named argument + if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + { + NamedArgument aNamedArgument; + anyArg >>= aNamedArgument; + anyArg = aNamedArgument.Value; + } + + if ( nInd < aParams.getLength() && anyArg.getValueTypeClass() != TypeClass_VOID ) + { + anyToVariant( &arArgs[revIndex], anyArg, VT_VARIANT ); + } + else + { + arArgs[revIndex].vt = VT_ERROR; + arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( nInd ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = nInd; + throw; + } + + dispparams.rgvarg = arArgs; + // invoking OLE method + hInvRes = m_spDispatch->Invoke( dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + ::sal::static_int_cast< WORD, INVOKEKIND >( pInvkinds[nStep] ), + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + } + + // converting return value and out parameter back to UNO + if ( SUCCEEDED( hInvRes ) ) + variantToAny( &varResult, aResult, false ); + else + { + // map error codes to exceptions + OUString message; + switch ( hInvRes ) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException("[automation bridge] Wrong " + "number of arguments. Object returned DISP_E_BADPARAMCOUNT.", + nullptr, 0); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException("[automation bridge] One or more " + "arguments have the wrong type. Object returned " + "DISP_E_BADVARTYPE.", nullptr); + break; + case DISP_E_EXCEPTION: + message = OUString::Concat("[automation bridge]: ") + + std::u16string_view(o3tl::toU(excepinfo.bstrDescription), + ::SysStringLen(excepinfo.bstrDescription)); + throw InvocationTargetException(message, Reference<XInterface>(), Any()); + break; + case DISP_E_MEMBERNOTFOUND: + message = "[automation bridge]: A function with the name \"" + + aName + "\" is not supported. Object returned " + "DISP_E_MEMBERNOTFOUND."; + throw IllegalArgumentException(message, nullptr, 0); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException("[automation bridge] Object " + "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("[automation bridge] Call failed.", + static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("[automation bridge]Call failed." + "Object returned DISP_E_PARAMNOTFOUND.", + nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("[automation bridge] Call failed. " + "Object returned DISP_E_TYPEMISMATCH", + static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNLCID.",nullptr); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("[automation bridge] Call failed." + "Object returned DISP_E_PARAMNOTOPTIONAL", + static_cast<XInterface*>(static_cast<XWeak*>(this)), + TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + } + + return aResult; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasMember( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + o2u_attachCurrentThread(); + DISPID dispid; + return getDispid( aName, &dispid ); +} + + +// UnoConversionUtilities -------------------------------------------------------------------------------- +Reference< XInterface > IUnknownWrapper::createUnoWrapperInstance() +{ + if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else + return Reference<XInterface>(); +} +Reference<XInterface> IUnknownWrapper::createComWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + + +void IUnknownWrapper::getMethodInfo(std::u16string_view sName, TypeDescription& methodInfo) +{ + TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName); + if( desc.is()) + { + typelib_TypeDescription* pMember= desc.get(); + if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_METHOD ) + methodInfo= pMember; + } +} + +void IUnknownWrapper::getAttributeInfo(std::u16string_view sName, TypeDescription& attributeInfo) +{ + TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName); + if( desc.is()) + { + typelib_TypeDescription* pMember= desc.get(); + if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE ) + { + attributeInfo= reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(pMember)->pAttributeTypeRef; + } + } +} +TypeDescription IUnknownWrapper::getInterfaceMemberDescOfCurrentCall(std::u16string_view sName) +{ + TypeDescription ret; + + for( auto const & rType : std::as_const(m_seqTypes) ) + { + TypeDescription _curDesc( rType ); + _curDesc.makeComplete(); + typelib_InterfaceTypeDescription * pInterface= reinterpret_cast<typelib_InterfaceTypeDescription*>(_curDesc.get()); + if( pInterface) + { + typelib_InterfaceMemberTypeDescription* pMember= nullptr; + //find the member description of the current call + for( int j=0; j < pInterface->nAllMembers; j++) + { + typelib_TypeDescriptionReference* pTypeRefMember = pInterface->ppAllMembers[j]; + typelib_TypeDescription* pDescMember= nullptr; + TYPELIB_DANGER_GET( &pDescMember, pTypeRefMember); + + typelib_InterfaceMemberTypeDescription* pInterfaceMember= + reinterpret_cast<typelib_InterfaceMemberTypeDescription*>(pDescMember); + if( OUString( pInterfaceMember->pMemberName) == sName) + { + pMember= pInterfaceMember; + break; + } + TYPELIB_DANGER_RELEASE( pDescMember); + } + + if( pMember) + { + ret= &pMember->aBase; + TYPELIB_DANGER_RELEASE( &pMember->aBase); + } + } + if( ret.is()) + break; + } + return ret; +} + +bool IUnknownWrapper::isJScriptObject() +{ + if( m_eJScript == JScriptUndefined) + { + CComDispatchDriver disp( m_spDispatch); + if( disp) + { + CComVariant result; + if( SUCCEEDED( disp.GetPropertyByName( JSCRIPT_ID_PROPERTY, &result))) + { + if(result.vt == VT_BSTR) + { + CComBSTR name( result.bstrVal); + name.ToLower(); + if( name == CComBSTR(JSCRIPT_ID)) + m_eJScript= IsJScript; + } + } + } + if( m_eJScript == JScriptUndefined) + m_eJScript= NoJScript; + } + + return m_eJScript != NoJScript; +} + + +/** @internal + The function ultimately calls IDispatch::Invoke on the wrapped COM object. + The COM object does not implement UNO Interfaces ( via IDispatch). This + is the case when the OleObjectFactory service has been used to create a + component. + @exception IllegalArgumentException + @exception CannotConvertException + @InvocationTargetException + @RuntimeException + @BridgeRuntimeError +*/ +Any IUnknownWrapper::invokeWithDispIdComTlb(const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + // Get type info for the call. It can be a method call or property put or + // property get operation. + FuncDesc aFuncDesc(getTypeInfo()); + getFuncDescForInvoke(sFuncName, Params, & aFuncDesc); + return invokeWithDispIdComTlb( aFuncDesc, sFuncName, Params, OutParamIndex, OutParam ); +} + +Any IUnknownWrapper::invokeWithDispIdComTlb(FuncDesc& aFuncDesc, + const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + Any ret; + HRESULT result; + + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + sal_Int32 i = 0; + sal_Int32 nUnoArgs = Params.getLength(); + DISPID idPropertyPut = DISPID_PROPERTYPUT; + std::unique_ptr<DISPID[]> arDispidNamedArgs; + std::unique_ptr<CComVariant[]> ptrArgs; + std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments + CComVariant * arArgs = nullptr; + CComVariant * arRefArgs = nullptr; + sal_Int32 revIndex = 0; + + //Set the array of DISPIDs for named args if it is a property put operation. + //If there are other named arguments another array is set later on. + if (aFuncDesc->invkind == INVOKE_PROPERTYPUT + || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + dispparams.rgdispidNamedArgs = & idPropertyPut; + + //Determine the number of named arguments + for (int iParam = 0; iParam < nUnoArgs; iParam ++) + { + const Any & curArg = Params[iParam]; + if (curArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + dispparams.cNamedArgs ++; + } + //In a property put operation a property value is a named argument (DISPID_PROPERTYPUT). + //Therefore the number of named arguments is increased by one. + //Although named, the argument is not named in an actual language, such as Basic, + //therefore it is never a com.sun.star.bridge.oleautomation.NamedArgument + if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT + || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF) + dispparams.cNamedArgs ++; + + //Determine the number of all arguments and named arguments + if (aFuncDesc->cParamsOpt == -1) + { + //Attribute vararg is set on this method. "Unlimited" number of args + //supported. There can be no optional or defaultvalue on any of the arguments. + dispparams.cArgs = nUnoArgs; + } + else + { + //If there are named arguments, then the dispparams.cArgs + //is the number of supplied args, otherwise it is the expected number. + if (dispparams.cNamedArgs) + dispparams.cArgs = nUnoArgs; + else + dispparams.cArgs = aFuncDesc->cParams; + } + + //check if there are not too many arguments supplied + if (::sal::static_int_cast< sal_uInt32, int >( nUnoArgs ) > dispparams.cArgs) + { + throw IllegalArgumentException( + "[automation bridge] There are too many arguments for this method", + Reference<XInterface>(), static_cast<sal_Int16>(dispparams.cArgs)); + } + + //Set up the array of DISPIDs (DISPPARAMS::rgdispidNamedArgs) + //for the named arguments. + //If there is only one named arg and if it is because of a property put + //operation, then we need not set up the DISPID array. + if (dispparams.cNamedArgs > 0 && + ! (dispparams.cNamedArgs == 1 && + (aFuncDesc->invkind == INVOKE_PROPERTYPUT || + aFuncDesc->invkind == INVOKE_PROPERTYPUTREF))) + { + //set up an array containing the member and parameter names + //which is then used in ITypeInfo::GetIDsOfNames + //First determine the size of the array of names which is passed to + //ITypeInfo::GetIDsOfNames. It must hold the method names + the named + //args. + int nSizeAr = dispparams.cNamedArgs + 1; + if (aFuncDesc->invkind == INVOKE_PROPERTYPUT + || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + { + nSizeAr = dispparams.cNamedArgs; //counts the DISID_PROPERTYPUT + } + + std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]); + OLECHAR ** arNames = saNames.get(); + arNames[0] = const_cast<OLECHAR*>(o3tl::toW(sFuncName.getStr())); + + int cNamedArg = 0; + for (size_t iParams = 0; iParams < dispparams.cArgs; iParams ++) + { + const Any & curArg = Params[iParams]; + if (auto v = o3tl::tryAccess<NamedArgument>(curArg)) + { + const NamedArgument& arg = *v; + //We put the parameter names in reverse order into the array, + //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs + //The first name in the array is the method name + arNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr())); + } + } + + //Prepare the array of DISPIDs for ITypeInfo::GetIDsOfNames + //it must be big enough to contain the DISPIDs of the member + parameters + arDispidNamedArgs.reset(new DISPID[nSizeAr]); + HRESULT hr = getTypeInfo()->GetIDsOfNames(arNames, nSizeAr, + arDispidNamedArgs.get()); + if ( hr == E_NOTIMPL ) + hr = m_spDispatch->GetIDsOfNames(IID_NULL, arNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() ); + + if (hr == S_OK) + { + // In a "property put" operation, the property value is a named param with the + //special DISPID DISPID_PROPERTYPUT + if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT + || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF) + { + //Element at index 0 in the DISPID array must be DISPID_PROPERTYPUT + //The first item in the array arDispidNamedArgs is the DISPID for + //the method. We replace it with DISPID_PROPERTYPUT. + DISPID* arIDs = arDispidNamedArgs.get(); + arIDs[0] = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = arIDs; + } + else + { + //The first item in the array arDispidNamedArgs is the DISPID for + //the method. It must be removed + DISPID* arIDs = arDispidNamedArgs.get(); + dispparams.rgdispidNamedArgs = & arIDs[1]; + } + } + else if (hr == DISP_E_UNKNOWNNAME) + { + throw IllegalArgumentException( + "[automation bridge]One of the named arguments is wrong!", + Reference<XInterface>(), 0); + } + else + { + throw InvocationTargetException( + "[automation bridge] ITypeInfo::GetIDsOfNames returned error " + + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any()); + } + } + + //Convert arguments + ptrArgs.reset(new CComVariant[dispparams.cArgs]); + ptrRefArgs.reset(new CComVariant[dispparams.cArgs]); + arArgs = ptrArgs.get(); + arRefArgs = ptrRefArgs.get(); + try + { + for (i = 0; i < static_cast<sal_Int32>(dispparams.cArgs); i++) + { + revIndex= dispparams.cArgs - i -1; + arRefArgs[revIndex].byref=nullptr; + Any anyArg; + if ( i < nUnoArgs) + anyArg= Params.getConstArray()[i]; + + unsigned short paramFlags = PARAMFLAG_FOPT | PARAMFLAG_FIN; + VARTYPE varType = VT_VARIANT; + if (aFuncDesc->cParamsOpt != -1 || aFuncDesc->cParams != (i + 1)) + { + paramFlags = aFuncDesc->lprgelemdescParam[i].paramdesc.wParamFlags; + varType = getElementTypeDesc(&aFuncDesc->lprgelemdescParam[i].tdesc); + } + + // Make sure that there is a UNO parameter for every + // expected parameter. If there is no UNO parameter where the + // called function expects one, then it must be optional. Otherwise + // it's a UNO programming error. + if (i >= nUnoArgs && !(paramFlags & PARAMFLAG_FOPT)) + { + throw IllegalArgumentException( + ("ole automation bridge: The called function expects an argument at position: " + + OUString::number(i) + " (index starting at 0)."), + Reference<XInterface>(), static_cast<sal_Int16>(i)); + } + + // Property Put arguments + if (anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get()) + { + PropertyPutArgument arg; + anyArg >>= arg; + anyArg = arg.Value; + } + // named argument + if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + { + NamedArgument aNamedArgument; + anyArg >>= aNamedArgument; + anyArg = aNamedArgument.Value; + } + // out param + if (paramFlags & PARAMFLAG_FOUT && + ! (paramFlags & PARAMFLAG_FIN) ) + { + VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF ); + if (i < nUnoArgs) + { + arRefArgs[revIndex].vt= type; + } + else + { + //optional arg + arRefArgs[revIndex].vt = VT_ERROR; + arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + if( type == VT_VARIANT ) + { + arArgs[revIndex].vt= VT_VARIANT | VT_BYREF; + arArgs[revIndex].byref= &arRefArgs[revIndex]; + } + else + { + arArgs[revIndex].vt= varType; + if (type == VT_DECIMAL) + arArgs[revIndex].byref= & arRefArgs[revIndex].decVal; + else + arArgs[revIndex].byref= & arRefArgs[revIndex].byref; + } + } + // in/out + in byref params + else if (varType & VT_BYREF) + { + VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF ); + CComVariant var; + + if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID) + { + anyToVariant( & arRefArgs[revIndex], anyArg, type); + } + else if (paramFlags & PARAMFLAG_FHASDEFAULT) + { + //optional arg with default + VariantCopy( & arRefArgs[revIndex], + & aFuncDesc->lprgelemdescParam[i].paramdesc. + pparamdescex->varDefaultValue); + } + else + { + //optional arg + //e.g: call func(x) in basic : func() ' no arg supplied + OSL_ASSERT(paramFlags & PARAMFLAG_FOPT); + arRefArgs[revIndex].vt = VT_ERROR; + arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + + // Set the converted arguments in the array which will be + // DISPPARAMS::rgvarg + // byref arg VT_XXX |VT_BYREF + arArgs[revIndex].vt = varType; + if (revIndex == 0 && aFuncDesc->invkind == INVOKE_PROPERTYPUT) + { + arArgs[revIndex] = arRefArgs[revIndex]; + } + else if (type == VT_DECIMAL) + { + arArgs[revIndex].byref= & arRefArgs[revIndex].decVal; + } + else if (type == VT_VARIANT) + { + if ( ! (paramFlags & PARAMFLAG_FOUT)) + arArgs[revIndex] = arRefArgs[revIndex]; + else + arArgs[revIndex].byref = & arRefArgs[revIndex]; + } + else + { + arArgs[revIndex].byref = & arRefArgs[revIndex].byref; + arArgs[revIndex].vt = ::sal::static_int_cast< VARTYPE, int >( arRefArgs[revIndex].vt | VT_BYREF ); + } + + } + // in parameter no VT_BYREF except for array, interfaces + else + { // void any stands for optional param + if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID) + { + anyToVariant( & arArgs[revIndex], anyArg, varType); + } + //optional arg but no void any supplied + //Basic: obj.func() ' first parameter left out because it is optional + else if (paramFlags & PARAMFLAG_FHASDEFAULT) + { + //optional arg with default either as direct arg : VT_XXX or + VariantCopy( & arArgs[revIndex], + & aFuncDesc->lprgelemdescParam[i].paramdesc. + pparamdescex->varDefaultValue); + } + else if (paramFlags & PARAMFLAG_FOPT) + { + arArgs[revIndex].vt = VT_ERROR; + arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + else + { + arArgs[revIndex].vt = VT_EMPTY; + arArgs[revIndex].lVal = 0; + } + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + dispparams.rgvarg= arArgs; + // invoking OLE method + result = m_spDispatch->Invoke(aFuncDesc->memid, + IID_NULL, + LOCALE_USER_DEFAULT, + ::sal::static_int_cast< WORD, INVOKEKIND >( aFuncDesc->invkind ), + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (result == S_OK) + { + + // allocate space for the out param Sequence and indices Sequence + int outParamsCount= 0; // includes in/out parameter + for (int j = 0; j < aFuncDesc->cParams; j++) + { + if (aFuncDesc->lprgelemdescParam[j].paramdesc.wParamFlags & + PARAMFLAG_FOUT) + outParamsCount++; + } + + OutParamIndex.realloc(outParamsCount); + OutParam.realloc(outParamsCount); + // Convert out params + if (outParamsCount) + { + auto pOutParamIndex = OutParamIndex.getArray(); + auto pOutParam = OutParam.getArray(); + int outParamIndex=0; + for (int paramIndex = 0; paramIndex < nUnoArgs; paramIndex ++) + { + //Determine the index within the method signature + int realParamIndex = paramIndex; + int revParamIndex = dispparams.cArgs - paramIndex - 1; + if (Params[paramIndex].getValueType() + == cppu::UnoType<NamedArgument>::get()) + { + //dispparams.rgdispidNamedArgs contains the mapping from index + //of named args list to index of parameter list + realParamIndex = dispparams.rgdispidNamedArgs[revParamIndex]; + } + + // no named arg, always come before named args + if (! (aFuncDesc->lprgelemdescParam[realParamIndex].paramdesc.wParamFlags + & PARAMFLAG_FOUT)) + continue; + Any outAny; + // variantToAny is called with the "reduce range" parameter set to sal_False. + // That causes VT_I4 values not to be converted down to a "lower" type. That + // feature exist for JScript only because it only uses VT_I4 for integer types. + try + { + variantToAny( & arRefArgs[revParamIndex], outAny, false ); + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = static_cast<sal_Int16>(paramIndex); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = paramIndex; + throw; + } + pOutParam[outParamIndex] = outAny; + pOutParamIndex[outParamIndex] = ::sal::static_int_cast< sal_Int16, int >( paramIndex ); + outParamIndex++; + } + OutParam.realloc(outParamIndex); + OutParamIndex.realloc(outParamIndex); + } + // Return value + variantToAny(&varResult, ret, false); + } + + // map error codes to exceptions + OUString message; + switch (result) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException("[automation bridge] Wrong " + "number of arguments. Object returned DISP_E_BADPARAMCOUNT.", + nullptr, 0); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException("[automation bridge] One or more " + "arguments have the wrong type. Object returned " + "DISP_E_BADVARTYPE.", nullptr); + break; + case DISP_E_EXCEPTION: + message = OUString::Concat("[automation bridge]: ") + + std::u16string_view(o3tl::toU(excepinfo.bstrDescription), + ::SysStringLen(excepinfo.bstrDescription)); + + throw InvocationTargetException(message, Reference<XInterface>(), Any()); + break; + case DISP_E_MEMBERNOTFOUND: + message = "[automation bridge]: A function with the name \"" + + sFuncName + "\" is not supported. Object returned " + "DISP_E_MEMBERNOTFOUND."; + throw IllegalArgumentException(message, nullptr, 0); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException("[automation bridge] Object " + "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("[automation bridge] Call failed.", + static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("[automation bridge]Call failed." + "Object returned DISP_E_PARAMNOTFOUND.", + nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("[automation bridge] Call failed. " + "Object returned DISP_E_TYPEMISMATCH", + static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNLCID.",nullptr); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("[automation bridge] Call failed." + "Object returned DISP_E_PARAMNOTOPTIONAL", + static_cast<XInterface*>(static_cast<XWeak*>(this)), + TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + + return ret; +} + +void IUnknownWrapper::getFuncDescForInvoke(const OUString & sFuncName, + const Sequence<Any> & seqArgs, + FUNCDESC** pFuncDesc) +{ + int nUnoArgs = seqArgs.getLength(); + const Any * arArgs = seqArgs.getConstArray(); + ITypeInfo* pInfo = getTypeInfo(); + + //If the last of the positional arguments is a PropertyPutArgument + //then obtain the type info for the property put operation. + + //The property value is always the last argument, in a positional argument list + //or in a list of named arguments. A PropertyPutArgument is actually a named argument + //hence it must not be put in an extra NamedArgument structure + if (nUnoArgs > 0 && + arArgs[nUnoArgs - 1].getValueType() == cppu::UnoType<PropertyPutArgument>::get()) + { + // DISPATCH_PROPERTYPUT + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(sFuncName, & aDescGet, & aDescPut, & aVarDesc); + if ( ! aDescPut) + { + throw IllegalArgumentException( + "[automation bridge] The object does not have a writeable property: " + + sFuncName, Reference<XInterface>(), 0); + } + *pFuncDesc = aDescPut.Detach(); + } + else + { // DISPATCH_METHOD + FuncDesc aFuncDesc(pInfo); + getFuncDesc(sFuncName, & aFuncDesc); + if ( ! aFuncDesc) + { + // Fallback: DISPATCH_PROPERTYGET can mostly be called as + // DISPATCH_METHOD + ITypeInfo * pTypeInfo = getTypeInfo(); + FuncDesc aDescPut(pTypeInfo); + VarDesc aVarDesc(pTypeInfo); + getPropDesc(sFuncName, & aFuncDesc, & aDescPut, & aVarDesc); + if ( ! aFuncDesc ) + { + throw IllegalArgumentException( + "[automation bridge] The object does not have a function" + " or readable property \"" + + sFuncName + "\"", Reference<XInterface>(), 0); + } + } + *pFuncDesc = aFuncDesc.Detach(); + } +} +bool IUnknownWrapper::getDispid(const OUString& sFuncName, DISPID * id) +{ + OSL_ASSERT(m_spDispatch); + LPOLESTR lpsz = const_cast<LPOLESTR> (o3tl::toW(sFuncName.getStr())); + HRESULT hr = m_spDispatch->GetIDsOfNames(IID_NULL, &lpsz, 1, LOCALE_USER_DEFAULT, id); + return hr == S_OK; +} +void IUnknownWrapper::getFuncDesc(const OUString & sFuncName, FUNCDESC ** pFuncDesc) + +{ + OSL_ASSERT( * pFuncDesc == nullptr); + buildComTlbIndex(); + typedef TLBFuncIndexMap::const_iterator cit; + //We assume there is only one entry with the function name. A property + //would have two entries. + cit itIndex= m_mapComFunc.find(sFuncName); + if (itIndex == m_mapComFunc.end()) + { + //try case insensitive with IDispatch::GetIDsOfNames + DISPID id; + if (getDispid(sFuncName, &id)) + { + CComBSTR memberName; + unsigned int pcNames=0; + // get the case sensitive name + if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames))) + { + //get the associated index and add an entry to the map + //with the name sFuncName which differs in the casing of the letters to + //the actual name as obtained from ITypeInfo + OUString sRealName(o3tl::toU(LPCOLESTR(memberName))); + cit itOrg = m_mapComFunc.find(sRealName); + OSL_ASSERT(itOrg != m_mapComFunc.end()); + // maybe this is a property, if so we need + // to store either both id's ( put/get ) or + // just the get. Storing both is more consistent + std::pair<cit, cit> pItems = m_mapComFunc.equal_range( sRealName ); + for ( ;pItems.first != pItems.second; ++pItems.first ) + m_mapComFunc.insert( TLBFuncIndexMap::value_type ( std::make_pair(sFuncName, pItems.first->second ) )); + itIndex = + m_mapComFunc.find( sFuncName ); + } + } + } + +#if OSL_DEBUG_LEVEL >= 1 + // There must only be one entry if sFuncName represents a function or two + // if it is a property + std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName.toAsciiLowerCase()); + int numEntries = 0; + for ( ;p.first != p.second; p.first ++, numEntries ++); + OSL_ASSERT( ! (numEntries > 3) ); +#endif + if( itIndex != m_mapComFunc.end()) + { + ITypeInfo* pType= getTypeInfo(); + FUNCDESC * pDesc = nullptr; + if (!SUCCEEDED(pType->GetFuncDesc(itIndex->second, & pDesc))) + { + throw BridgeRuntimeError("[automation bridge] Could not get " + "FUNCDESC for " + sFuncName); + } + if (pDesc->invkind == INVOKE_FUNC) + { + (*pFuncDesc) = pDesc; + } + else + { + pType->ReleaseFuncDesc(pDesc); + } + } + //else no entry found for sFuncName, pFuncDesc will not be filled in +} + +void IUnknownWrapper::getPropDesc(const OUString & sFuncName, FUNCDESC ** pFuncDescGet, + FUNCDESC** pFuncDescPut, VARDESC** pVarDesc) +{ + OSL_ASSERT( * pFuncDescGet == nullptr && * pFuncDescPut == nullptr); + buildComTlbIndex(); + typedef TLBFuncIndexMap::const_iterator cit; + std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName); + if (p.first == m_mapComFunc.end()) + { + //try case insensitive with IDispatch::GetIDsOfNames + DISPID id; + if (getDispid(sFuncName, &id)) + { + CComBSTR memberName; + unsigned int pcNames=0; + // get the case sensitive name + if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames))) + { + //As opposed to getFuncDesc, we do not add the value because we would + // need to find the get and set description for the property. This would + //mean to iterate over all FUNCDESCs again. + p = m_mapComFunc.equal_range(OUString(o3tl::toU(LPCOLESTR(memberName)))); + } + } + } + + for ( int i = 0 ;p.first != p.second; p.first ++, i ++) + { + // There are a maximum of two entries, property put and property get + OSL_ASSERT( ! (i > 2) ); + ITypeInfo* pType= getTypeInfo(); + FUNCDESC * pFuncDesc = nullptr; + if (SUCCEEDED( pType->GetFuncDesc(p.first->second, & pFuncDesc))) + { + if (pFuncDesc->invkind == INVOKE_PROPERTYGET) + { + (*pFuncDescGet) = pFuncDesc; + } + else if (pFuncDesc->invkind == INVOKE_PROPERTYPUT || + pFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + { + //a property can have 3 entries, put, put ref, get + // If INVOKE_PROPERTYPUTREF or INVOKE_PROPERTYPUT is used + //depends on what is found first. + if ( * pFuncDescPut) + { + //we already have found one + pType->ReleaseFuncDesc(pFuncDesc); + } + else + { + (*pFuncDescPut) = pFuncDesc; + } + } + else + { + pType->ReleaseFuncDesc(pFuncDesc); + } + } + //ITypeInfo::GetFuncDesc may even provide a funcdesc for a VARDESC + // with invkind = INVOKE_FUNC. Since this function should only return + //a value for a real property (XInvokation::hasMethod, ..::hasProperty + //we need to make sure that sFuncName represents a real property. + VARDESC * pVD = nullptr; + if (SUCCEEDED(pType->GetVarDesc(p.first->second, & pVD))) + (*pVarDesc) = pVD; + } + //else no entry for sFuncName, pFuncDesc will not be filled in +} + +VARTYPE IUnknownWrapper::getUserDefinedElementType( ITypeInfo* pTypeInfo, const DWORD nHrefType ) +{ + VARTYPE _type( VT_NULL ); + if ( pTypeInfo ) + { + CComPtr<ITypeInfo> spRefInfo; + pTypeInfo->GetRefTypeInfo( nHrefType, &spRefInfo.p ); + if ( spRefInfo ) + { + TypeAttr attr( spRefInfo ); + spRefInfo->GetTypeAttr( &attr ); + if ( attr->typekind == TKIND_ENUM ) + { + // We use the type of the first enum value. + if ( attr->cVars == 0 ) + { + throw BridgeRuntimeError("[automation bridge] Could not obtain type description"); + } + VarDesc var( spRefInfo ); + spRefInfo->GetVarDesc( 0, &var ); + _type = var->lpvarValue->vt; + } + else if ( attr->typekind == TKIND_INTERFACE ) + { + _type = VT_UNKNOWN; + } + else if ( attr->typekind == TKIND_DISPATCH ) + { + _type = VT_DISPATCH; + } + else if ( attr->typekind == TKIND_ALIAS ) + { + // TKIND_ALIAS is a type that is an alias for another type. So get that alias type. + _type = getUserDefinedElementType( pTypeInfo, attr->tdescAlias.hreftype ); + } + else + { + throw BridgeRuntimeError( "[automation bridge] Unhandled user defined type." ); + } + } + } + return _type; +} + +VARTYPE IUnknownWrapper::getElementTypeDesc(const TYPEDESC *desc) +{ + VARTYPE _type( VT_NULL ); + + if (desc->vt == VT_PTR) + { + _type = getElementTypeDesc(desc->lptdesc); + _type |= VT_BYREF; + } + else if (desc->vt == VT_SAFEARRAY) + { + _type = getElementTypeDesc(desc->lptdesc); + _type |= VT_ARRAY; + } + else if (desc->vt == VT_USERDEFINED) + { + ITypeInfo* thisInfo = getTypeInfo(); //kept by this instance + _type = getUserDefinedElementType( thisInfo, desc->hreftype ); + } + else + { + _type = desc->vt; + } + return _type; +} + +void IUnknownWrapper::buildComTlbIndex() +{ + if ( ! m_bComTlbIndexInit) + { + MutexGuard guard(getBridgeMutex()); + { + if ( ! m_bComTlbIndexInit) + { + OUString sError; + ITypeInfo* pType= getTypeInfo(); + TypeAttr typeAttr(pType); + if( SUCCEEDED( pType->GetTypeAttr( &typeAttr))) + { + for( WORD i= 0; i < typeAttr->cFuncs; i++) + { + FuncDesc funcDesc(pType); + if( SUCCEEDED( pType->GetFuncDesc( i, &funcDesc))) + { + CComBSTR memberName; + unsigned int pcNames=0; + if( SUCCEEDED(pType->GetNames( funcDesc->memid, & memberName, 1, &pcNames))) + { + OUString usName(o3tl::toU(LPCOLESTR(memberName))); + m_mapComFunc.emplace(usName, i); + } + else + { + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetNames failed."; + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetFuncDesc failed."; + } + + //If we create an Object in JScript and a property then it + //has VARDESC instead of FUNCDESC + for (WORD i = 0; i < typeAttr->cVars; i++) + { + VarDesc varDesc(pType); + if (SUCCEEDED(pType->GetVarDesc(i, & varDesc))) + { + CComBSTR memberName; + unsigned int pcNames = 0; + if (SUCCEEDED(pType->GetNames(varDesc->memid, & memberName, 1, &pcNames))) + { + if (varDesc->varkind == VAR_DISPATCH) + { + OUString usName(o3tl::toU(LPCOLESTR(memberName))); + m_mapComFunc.emplace(usName, i); + } + } + else + { + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetNames failed."; + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetVarDesc failed."; + + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetTypeAttr failed."; + + if (sError.getLength()) + { + throw BridgeRuntimeError(sError); + } + + m_bComTlbIndexInit = true; + } + } + } +} + +ITypeInfo* IUnknownWrapper::getTypeInfo() +{ + if( !m_spDispatch) + { + throw BridgeRuntimeError("The object has no IDispatch interface!"); + } + + if( !m_spTypeInfo ) + { + MutexGuard guard(getBridgeMutex()); + if( ! m_spTypeInfo) + { + CComPtr< ITypeInfo > spType; + if( !SUCCEEDED( m_spDispatch->GetTypeInfo( 0, LOCALE_USER_DEFAULT, &spType.p))) + { + throw BridgeRuntimeError("[automation bridge]The dispatch object does not " + "support ITypeInfo!"); + } + + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + + //If this is a dual interface then TYPEATTR::typekind is usually TKIND_INTERFACE + //We need to get the type description for TKIND_DISPATCH + TypeAttr typeAttr(spType.p); + if( SUCCEEDED(spType->GetTypeAttr( &typeAttr))) + { + if (typeAttr->typekind == TKIND_INTERFACE && + typeAttr->wTypeFlags & TYPEFLAG_FDUAL) + { + HREFTYPE refDispatch; + if (!SUCCEEDED(spType->GetRefTypeOfImplType(::sal::static_int_cast< UINT, int >( -1 ), &refDispatch))) + { + throw BridgeRuntimeError( + "[automation bridge] Could not obtain type information " + "for dispatch interface." ); + } + CComPtr<ITypeInfo> spTypeDisp; + if (SUCCEEDED(spType->GetRefTypeInfo(refDispatch, & spTypeDisp))) + m_spTypeInfo= spTypeDisp; + } + else if (typeAttr->typekind == TKIND_DISPATCH) + { + m_spTypeInfo= spType; + } + else + { + throw BridgeRuntimeError( + "[automation bridge] Automation object does not " + "provide type information."); + } + } + } + } + return m_spTypeInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |