/* -*- 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 . */ #ifndef INCLUDED_EXTENSIONS_SOURCE_OLE_UNOCONVERSIONUTILITIES_HXX #define INCLUDED_EXTENSIONS_SOURCE_OLE_UNOCONVERSIONUTILITIES_HXX #include #include #include #include #include #include #include #include #include #include #include #include #include "ole2uno.hxx" #include #include "unotypewrapper.hxx" #include // for some reason DECIMAL_NEG (wtypes.h) which contains BYTE is not resolved. typedef unsigned char BYTE; // classes for wrapping uno objects #define INTERFACE_OLE_WRAPPER_IMPL 1 #define UNO_OBJECT_WRAPPER_REMOTE_OPT 2 #define INVOCATION_SERVICE "com.sun.star.script.Invocation" // classes for wrapping ole objects #define IUNKNOWN_WRAPPER_IMPL 1 #define INTERFACE_ADAPTER_FACTORY "com.sun.star.script.InvocationAdapterFactory" // COM or JScript objects implementing UNO interfaces have to implement this property #define SUPPORTED_INTERFACES_PROP L"_implementedInterfaces" // Second property without leading underscore for use in VB #define SUPPORTED_INTERFACES_PROP2 L"Bridge_ImplementedInterfaces" using namespace com::sun::star::script; using namespace com::sun::star::beans; using namespace com::sun::star::uno; using namespace com::sun::star::bridge::oleautomation; extern std::unordered_map AdapterToWrapperMap; extern std::unordered_map WrapperToAdapterMap; //Maps IUnknown pointers to a weak reference of the respective wrapper class (e.g. // IUnknownWrapperImpl. It is the responsibility of the wrapper to remove the entry when // it is being destroyed. // Used to ensure that an Automation object is always mapped to the same UNO objects. extern std::unordered_map > ComPtrToWrapperMap; // Maps XInterface pointers to a weak reference of its wrapper class (i.e. // InterfaceOleWrapper). It is the responsibility of the wrapper to remove the entry when // it is being destroyed. It is used to ensure the identity of objects. That is, a UNO interface // is mapped to IDispatch which is kept alive in the COM environment. If the same // UNO interface is mapped again to COM then the IDispach of the first mapped instance // must be returned. extern std::unordered_map > UnoObjToWrapperMap; // createUnoObjectWrapper gets a wrapper instance by calling createUnoWrapperInstance // and initializes it via XInitialization. The wrapper object is required to implement // XBridgeSupplier so that it can convert itself to IDispatch. // class T: Deriving class ( must implement XInterface ) /** All methods are allowed to throw at least a BridgeRuntimeError. */ template< class > class UnoConversionUtilities { public: explicit UnoConversionUtilities( const Reference & smgr): m_nUnoWrapperClass( INTERFACE_OLE_WRAPPER_IMPL), m_nComWrapperClass( IUNKNOWN_WRAPPER_IMPL), m_smgr( smgr) {} UnoConversionUtilities( const Reference & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ) : m_nUnoWrapperClass(unoWrapperClass), m_nComWrapperClass(comWrapperClass), m_smgr(xFactory) {} virtual ~UnoConversionUtilities() {} /** converts only into oleautomation types, that is there is no VT_I1, VT_UI2, VT_UI4 a sal_Unicode character is converted into a BSTR. @exception com.sun.star.lang.IllegalArgumentException If the any was inappropriate for conversion. @exception com.sun.star.script.CannotConvertException The any contains a type class for which no conversion is provided. */ void anyToVariant(VARIANT* pVariant, const Any& rAny); void anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type); /** @exception com.sun.star.lang.IllegalArgumentException If rSeq does not contain a sequence then the exception is thrown. */ SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq); /** @exception com.sun.star.lang.IllegalArgumentException If rSeq does not contain a sequence or elemtype has no proper value then the exception is thrown. */ SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype); /** @exception com.sun.star.lang.IllegalArgumentException If rObj does not contain a struct or interface */ void createUnoObjectWrapper(const Any & rObj, VARIANT * pVar); /** @exception CannotConvertException Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. ArgumentIndex is 0. @IllegalArgumentException Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1, */ void variantToAny(const VARIANT* pVariant, Any& rAny, bool bReduceValueRange = true); /** This method converts variants arguments in calls from COM -> UNO. Only then the expected UNO type is known. @exception CannotConvertException Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. ArgumentIndex is 0. @IllegalArgumentException Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1, */ void variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange = true); /** @exception IllegalArgumentException -if pVar does not contain VT_UNKNOWN or VT_DISPATCH or pVar is used for a particular UNO type which is not supported by pVar */ Any createOleObjectWrapper(VARIANT* pVar, const Type& aType= Type()); /* Return true means var contained a ValueObject, and it was successfully converted. The result is in any. It an error occurred a BridgeRuntimeError will be thrown. */ bool convertValueObject( const VARIANTARG *var, Any& any); void dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type); Sequence createOleArrayWrapperOfDim(SAFEARRAY* pArray, unsigned int dimCount, unsigned int actDim, long* index, VARTYPE type, const Type& unotype); Sequence createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unotype= Type()); VARTYPE mapTypeClassToVartype( TypeClass type); Reference< XSingleServiceFactory > getInvocationFactory(const Any& anyObject); virtual Reference< XInterface > createUnoWrapperInstance()=0; virtual Reference< XInterface > createComWrapperInstance()=0; static bool isJScriptArray(const VARIANT* pvar); Sequence getImplementedInterfaces(IUnknown* pUnk); protected: Reference createAdapter(const Sequence& types, const Reference& receiver); // helper function for Sequence conversion void getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc); // helper function for Sequence conversion static bool incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLength, sal_Int32 * parMultidimensionalIndex); // helper function for Sequence conversion static size_t getOleElementSize( VARTYPE type); static Type getElementTypeOfSequence( const Type& seqType); //Provides a typeconverter Reference getTypeConverter(); // This member determines what class is used to convert a UNO object // or struct to a COM object. It is passed along to the anyToVariant // function in the createBridge function implementation const sal_uInt8 m_nUnoWrapperClass; const sal_uInt8 m_nComWrapperClass; // The servicemanager is either a local smgr or remote when the service // com.sun.star.bridge.OleBridgeSupplierVar1 is used. This service can be // created by createInstanceWithArguments where one can supply a service // manager that is to be used. // Local service manager as supplied by the loader when the creator function // of the service is being called. Reference m_smgr; // An explicitly supplied service manager when the service // com.sun.star.bridge.OleBridgeSupplierVar1 is used. That can be a remote // manager. Reference m_smgrRemote; Reference m_xInvocationFactoryLocal; Reference m_xInvocationFactoryRemote; private: // Holds the type converter which is used for sequence conversion etc. // Use the getTypeConverter function to obtain the interface. Reference m_typeConverter; }; // ask the object for XBridgeSupplier2 and on success bridges // the uno object to IUnknown or IDispatch. // return true the UNO object supports template < class T > bool convertSelfToCom( T& unoInterface, VARIANT * pVar) { bool ret = false; Reference< XInterface > xInt( unoInterface, UNO_QUERY); if( xInt.is()) { Reference< XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY); if( xSupplier.is()) { sal_Int8 arId[16]; rtl_getGlobalProcessId( reinterpret_cast(arId)); Sequence seqId( arId, 16); Any anySource; anySource <<= xInt; Any anyDisp = xSupplier->createBridge(anySource, seqId, UNO, OLE); // due to global-process-id check this must be in-process pointer if (auto v = o3tl::tryAccess(anyDisp)) { VARIANT* pvariant= reinterpret_cast(*v); HRESULT hr; if (FAILED(hr = VariantCopy(pVar, pvariant))) throw BridgeRuntimeError( "[automation bridge] convertSelfToCom\n" "VariantCopy failed! Error: " + OUString::number(hr)); VariantClear( pvariant); CoTaskMemFree( pvariant); ret = true; } } } return ret; } // Gets the invocation factory depending on the Type in the Any. // The factory can be created by a local or remote multi service factory. // In case there is a remote multi service factory available there are // some services or types for which the local factory is used. The exceptions // are: all structs. // Param anyObject - contains the object ( interface, struct) for what we need an invocation object. template Reference< XSingleServiceFactory > UnoConversionUtilities::getInvocationFactory(const Any& anyObject) { Reference< XSingleServiceFactory > retVal; MutexGuard guard( getBridgeMutex()); if( anyObject.getValueTypeClass() != TypeClass_STRUCT && m_smgrRemote.is() ) { if( ! m_xInvocationFactoryRemote.is() ) m_xInvocationFactoryRemote.set(m_smgrRemote->createInstance( INVOCATION_SERVICE), UNO_QUERY); retVal= m_xInvocationFactoryRemote; } else { if( ! m_xInvocationFactoryLocal.is() ) m_xInvocationFactoryLocal.set(m_smgr->createInstance(INVOCATION_SERVICE ), UNO_QUERY); retVal= m_xInvocationFactoryLocal; } return retVal; } template void UnoConversionUtilities::variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange /* = sal_True */) { try { HRESULT hr; bool bFail = false; bool bCannotConvert = false; CComVariant var; // There is no need to support indirect values, since they're not supported by UNO if( FAILED(hr= VariantCopyInd( &var, pArg))) // remove VT_BYREF throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::variantToAny \n" "VariantCopyInd failed for reason : " + OUString::number(hr)); bool bHandled = convertValueObject( & var, rAny); if( bHandled) OSL_ENSURE( rAny.getValueType() == ptype, "type in Value Object must match the type parameter"); if( ! bHandled) { // convert into a variant type that is the equivalent to the type // the sequence expects. Thus variantToAny produces the correct type // E.g. An Array object contains VT_I4 and the sequence expects shorts // than the vartype must be changed. The reason is, you can't specify the // type in JavaScript and the script engine determines the type being used. switch( ptype.getTypeClass()) { case TypeClass_CHAR: // could be: new Array( 12, 'w', "w") if( var.vt == VT_BSTR) { if(SUCCEEDED( hr= VariantChangeType( &var, &var, 0, VT_BSTR))) rAny.setValue( V_BSTR( &var), ptype); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; } else { if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2))) rAny.setValue(& var.iVal, ptype); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; } break; case TypeClass_INTERFACE: // could also be an IUnknown case TypeClass_STRUCT: { rAny = createOleObjectWrapper( & var, ptype); break; } case TypeClass_ENUM: if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I4))) rAny.setValue(& var.lVal, ptype); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_SEQUENCE: // There are different ways of receiving a sequence: // 1: JScript, VARTYPE: VT_DISPATCH // 2. VBScript simple arraysVT_VARIANT|VT_BYREF the referenced VARIANT contains // a VT_ARRAY| // 3. VBScript multi dimensional arrays: VT_ARRAY|VT_BYREF if( pArg->vt == VT_DISPATCH) { dispatchExObject2Sequence( pArg, rAny, ptype); } else { if ((var.vt & VT_ARRAY) != 0) { VARTYPE oleType = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY ); Sequence unoSeq = createOleArrayWrapper( var.parray, oleType, ptype); Reference conv = getTypeConverter(); if (conv.is()) { try { Any anySeq = makeAny(unoSeq); Any convAny = conv->convertTo(anySeq, ptype); rAny = convAny; } catch (const IllegalArgumentException& e) { throw BridgeRuntimeError( "[automation bridge]com.sun.star.lang.IllegalArgumentException " "in UnoConversionUtilities::variantToAny! Message: " + e.Message); } catch (const CannotConvertException& e) { throw BridgeRuntimeError( "[automation bridge]com.sun.star.script.CannotConvertException " "in UnoConversionUtilities::variantToAny! Message: " + e.Message); } } } } break; case TypeClass_VOID: rAny.setValue(nullptr,Type()); break; case TypeClass_ANY: // Any // There could be a JScript Array that needs special handling // If an Any is expected and this Any must contain a Sequence // then we cannot figure out what element type is required. // Therefore we convert to Sequence< Any > if( pArg->vt == VT_DISPATCH && isJScriptArray( pArg)) { dispatchExObject2Sequence( pArg, rAny, cppu::UnoType>::get()); } else if (pArg->vt == VT_DECIMAL) { //Decimal maps to hyper in calls from COM -> UNO // It does not matter if we create a sal_uInt64 or sal_Int64, // because the UNO object is called through invocation which //will do a type conversion if necessary if (var.decVal.sign == 0) { // positive value variantToAny( & var, rAny, cppu::UnoType::get(), bReduceValueRange); } else { //negative value variantToAny( & var, rAny, cppu::UnoType::get(), bReduceValueRange); } } else { variantToAny( & var, rAny); } break; case TypeClass_BOOLEAN: // VARIANT could be VARIANT_BOOL or other if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BOOL))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_STRING: // UString if(var.vt == VT_NULL) var = CComBSTR(""); if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BSTR))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_FLOAT: // float if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R4))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_DOUBLE: // double if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R8))) variantToAny(& var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_BYTE: // BYTE if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I1))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_SHORT: // INT16 if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_LONG: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_I4))) variantToAny( & var, rAny, bReduceValueRange); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_HYPER: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL))) { if (var.decVal.Lo64 > SAL_CONST_UINT64(0x8000000000000000) || var.decVal.Hi32 > 0 || var.decVal.scale > 0) { bFail = true; break; } sal_Int64 value = var.decVal.Lo64; if (var.decVal.sign == DECIMAL_NEG) value |= SAL_CONST_UINT64(0x8000000000000000); rAny <<= value; } else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_UNSIGNED_SHORT: // UINT16 if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI2))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_UNSIGNED_LONG: if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI4))) variantToAny( & var, rAny, bReduceValueRange); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_UNSIGNED_HYPER: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL))) { if (var.decVal.Hi32 > 0 || var.decVal.scale > 0) { bFail = true; break; } rAny <<= var.decVal.Lo64; } else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; case TypeClass_TYPE: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_UNKNOWN))) variantToAny( & var, rAny); else if (hr == DISP_E_TYPEMISMATCH) bCannotConvert = true; else bFail = true; break; default: bCannotConvert = true; break; } } if (bCannotConvert) throw CannotConvertException( "[automation bridge]UnoConversionUtilities::variantToAny \n" "Cannot convert the value of vartype :\"" + OUString::number(static_cast(var.vt)) + "\" to the expected UNO type of type class: " + OUString::number(static_cast(ptype.getTypeClass())), nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); if (bFail) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities:variantToAny\n" "The provided VARIANT of type\" " + OUString::number(static_cast(var.vt)) + "\" is unappropriate for conversion!", Reference(), -1); } catch (const CannotConvertException &) { throw; } catch (const IllegalArgumentException &) { throw; } catch (const BridgeRuntimeError &) { throw; } catch (const Exception & e) { throw BridgeRuntimeError("[automation bridge] unexpected exception in " "UnoConversionUtilities::variantToAny ! Message : \n" + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge] unexpected exception in " "UnoConversionUtilities::variantToAny !"); } } // The function only converts Sequences to SAFEARRAYS with elements of the type // specified by the parameter type. Everything else is forwarded to // anyToVariant(VARIANT* pVariant, const Any& rAny) // Param type must not be VT_BYREF template void UnoConversionUtilities::anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type) { try { HRESULT hr= S_OK; OSL_ASSERT( (type & VT_BYREF) == 0); if (type & VT_ARRAY) { type ^= VT_ARRAY; SAFEARRAY* ar= createUnoSequenceWrapper( rAny, type); if( ar) { VariantClear( pVariant); pVariant->vt= ::sal::static_int_cast< VARTYPE, int >( VT_ARRAY | type ); pVariant->byref= ar; } } else if(type == VT_VARIANT) { anyToVariant(pVariant, rAny); } else { CComVariant var; anyToVariant( &var, rAny); if(FAILED(hr = VariantChangeType(&var, &var, 0, type))) { if (hr == DISP_E_TYPEMISMATCH) throw CannotConvertException( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Cannot convert the value of type :\"" + rAny.getValueTypeName() + "\" to the expected Automation type of VARTYPE: " + OUString::number(static_cast(type)), nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Conversion of any with " + rAny.getValueType().getTypeName() + " to VARIANT with type: " + OUString::number(static_cast(type)) + " failed! Error code: " + OUString::number(hr)); } if(FAILED(hr = VariantCopy(pVariant, &var))) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "VariantCopy failed for reason: " + OUString::number(hr)); } } } catch (const IllegalArgumentException &) { throw; } catch (const CannotConvertException &) { throw; } catch (const BridgeRuntimeError&) { throw; } catch(const Exception & e) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Unexpected exception occurred. Message: " + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Unexpected exception occurred."); } } template void UnoConversionUtilities::anyToVariant(VARIANT* pVariant, const Any& rAny) { try { bool bIllegal = false; switch (rAny.getValueTypeClass()) { case TypeClass_INTERFACE: { Reference xInt; if (rAny >>= xInt) { createUnoObjectWrapper(rAny, pVariant); } else { bIllegal = true; } break; } case TypeClass_STRUCT: { if (rAny.getValueType() == cppu::UnoType::get() ) { Date d; if (rAny >>= d) { pVariant->vt = VT_DATE; pVariant->date = d.Value; } else { bIllegal = true; } } else if(rAny.getValueType() == cppu::UnoType::get()) { Decimal d; if (rAny >>= d) { pVariant->vt = VT_DECIMAL; pVariant->decVal.scale = d.Scale; pVariant->decVal.sign = d.Sign; pVariant->decVal.Lo32 = d.LowValue; pVariant->decVal.Mid32 = d.MiddleValue; pVariant->decVal.Hi32 = d.HighValue; } else { bIllegal = true; } } else if (rAny.getValueType() == cppu::UnoType::get()) { Currency c; if (rAny >>= c) { pVariant->vt = VT_CY; pVariant->cyVal.int64 = c.Value; } else { bIllegal = true; } } else if(rAny.getValueType() == cppu::UnoType::get()) { SCode s; if (rAny >>= s) { pVariant->vt = VT_ERROR; pVariant->scode = s.Value; } else { bIllegal = true; } } else { createUnoObjectWrapper(rAny, pVariant); } break; } case TypeClass_SEQUENCE: // sequence ??? SafeArray descriptor { SAFEARRAY* pArray = createUnoSequenceWrapper(rAny); if (pArray) { V_VT(pVariant) = VT_ARRAY | VT_VARIANT; V_ARRAY(pVariant) = pArray; } else { bIllegal = true; } break; } case TypeClass_VOID: { HRESULT hr = S_OK; if (FAILED(hr = VariantClear(pVariant))) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant\n" "VariantClear failed with error:" + OUString::number(hr)); } break; } case TypeClass_BOOLEAN: { bool value; if (rAny >>= value) { pVariant->vt = VT_BOOL; pVariant->boolVal = value ? VARIANT_TRUE: VARIANT_FALSE; } else { bIllegal = true; } break; } case TypeClass_CHAR: { // Because VT_UI2 does not conform to oleautomation we convert into VT_I2 instead sal_uInt16 value = *o3tl::forceAccess(rAny); pVariant->vt = VT_I2; pVariant->iVal = value; break; } case TypeClass_STRING: { OUString value; if (rAny >>= value) { pVariant->vt = VT_BSTR; pVariant->bstrVal = SysAllocString(o3tl::toW(value.getStr())); } else { bIllegal = true; } break; } case TypeClass_FLOAT: { float value; if (rAny >>= value) { pVariant->vt = VT_R4; pVariant->fltVal = value; } else { bIllegal = true; } break; } case TypeClass_DOUBLE: { double value; if (rAny >>= value) { pVariant->vt = VT_R8; pVariant->dblVal = value; } else { bIllegal = true; } break; } case TypeClass_BYTE: { // ole automation does not know a signed char but only unsigned char sal_Int8 value; if (rAny >>= value) { pVariant->vt = VT_UI1; pVariant->bVal = value; } else { bIllegal = true; } break; } case TypeClass_SHORT: // INT16 case TypeClass_UNSIGNED_SHORT: // UINT16 { sal_Int16 value; if (rAny >>= value) { pVariant->vt = VT_I2; pVariant->iVal = value; } else { bIllegal = true; } break; } case TypeClass_ENUM: { sal_Int32 value = *static_cast(rAny.getValue()); pVariant->vt = VT_I4; pVariant->lVal= value; break; } case TypeClass_LONG: case TypeClass_UNSIGNED_LONG: { sal_Int32 value; if (rAny >>= value) { pVariant->vt = VT_I4; pVariant->lVal= value; } else { bIllegal = true; } break; } case TypeClass_HYPER: { pVariant->vt = VT_DECIMAL; pVariant->decVal.scale = 0; pVariant->decVal.sign = 0; pVariant->decVal.Hi32 = 0; sal_Int64 value; rAny >>= value; if (value & SAL_CONST_UINT64(0x8000000000000000)) pVariant->decVal.sign = DECIMAL_NEG; pVariant->decVal.Lo64 = value; break; } case TypeClass_UNSIGNED_HYPER: { pVariant->vt = VT_DECIMAL; pVariant->decVal.scale = 0; pVariant->decVal.sign = 0; pVariant->decVal.Hi32 = 0; sal_uInt64 value; rAny >>= value; pVariant->decVal.Lo64 = value; break; } case TypeClass_TYPE: { Type type; rAny >>= type; CComVariant var; if (!createUnoTypeWrapper(type.getTypeName(), & var)) throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::anyToVariant \n" "Error during conversion of UNO type to Automation object!"); if (FAILED(VariantCopy(pVariant, &var))) throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::anyToVariant \n" "Unexpected error!"); break; } default: //TypeClass_SERVICE: //TypeClass_EXCEPTION: //When an InvocationTargetException is thrown when calling XInvocation::invoke //on a UNO object, then the target exception is directly used to create a //EXEPINFO structure //TypeClass_TYPEDEF //TypeClass_ANY: //TypeClass_UNKNOWN: //TypeClass_MODULE: throw CannotConvertException( "[automation bridge]UnoConversionUtilities::anyToVariant\n" "There is no conversion for this UNO type to an Automation type." "The destination type class is the type class of the UNO " "argument which was to be converted.", Reference(), rAny.getValueTypeClass(), FailReason::TYPE_NOT_SUPPORTED, 0); break; } if (bIllegal) { throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::anyToVariant\n" "The provided any of type\" " + rAny.getValueType().getTypeName() + "\" is unappropriate for conversion!", Reference(), -1); } } catch (const CannotConvertException &) { throw; } catch (const IllegalArgumentException &) { throw; } catch(const BridgeRuntimeError&) { throw; } catch(const Exception & e) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Unexpected exception occurred. Message: " + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Unexpected exception occurred. " ); } } // Creates an SAFEARRAY of the specified element and if necessary // creates a SAFEARRAY with multiple dimensions. // Used by sal_Bool anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type); template SAFEARRAY* UnoConversionUtilities::createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype) { if (rSeq.getValueTypeClass() != TypeClass_SEQUENCE) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper \n" "The any does not contain a sequence!", nullptr, 0); if (elemtype == VT_NULL || elemtype == VT_EMPTY) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper \n" "No element type supplied!",nullptr, -1); SAFEARRAY* pArray= nullptr; // Get the dimensions. This is done by examining the type name string // The count of brackets determines the dimensions. OUString sTypeName= rSeq.getValueType().getTypeName(); sal_Int32 dims=0; for(sal_Int32 lastIndex=0;(lastIndex= sTypeName.indexOf( L'[', lastIndex)) != -1; lastIndex++,dims++); //get the maximum number of elements per dimensions and the typedescription of the elements Sequence seqElementCounts( dims); TypeDescription elementTypeDesc; getElementCountAndTypeOfSequence( rSeq, 1, seqElementCounts, elementTypeDesc ); if( elementTypeDesc.is() ) { // set up the SAFEARRAY std::unique_ptr sarSafeArrayBound(new SAFEARRAYBOUND[dims]); SAFEARRAYBOUND* prgsabound= sarSafeArrayBound.get(); for( sal_Int32 i=0; i < dims; i++) { //prgsabound[0] is the right most dimension prgsabound[dims - i - 1].lLbound = 0; prgsabound[dims - i - 1].cElements = seqElementCounts[i]; } typelib_TypeDescription* rawTypeDesc= elementTypeDesc.get(); sal_Int32 elementSize= rawTypeDesc->nSize; size_t oleElementSize= getOleElementSize( elemtype); // SafeArrayCreate clears the memory for the data itself. pArray = SafeArrayCreate(elemtype, dims, prgsabound); // convert the Sequence's elements and populate the SAFEARRAY if( pArray) { // Iterate over every Sequence that contains the actual elements void* pSAData; if( SUCCEEDED( SafeArrayAccessData( pArray, &pSAData))) { const sal_Int32* parElementCount= seqElementCounts.getConstArray(); uno_Sequence * pMultiSeq= *static_cast(rSeq.getValue()); sal_Int32 dimsSeq= dims - 1; // arDimSeqIndices contains the current index of a block of data. // E.g. Sequence> , the index would refer to Sequence // In this case arDimSeqIndices would have the size 1. That is the elements are not counted // but the Sequences that contain those elements. // The indices are 0 based std::unique_ptr sarDimsSeqIndices; sal_Int32* arDimsSeqIndices= nullptr; if( dimsSeq > 0) { sarDimsSeqIndices.reset(new sal_Int32[dimsSeq]); arDimsSeqIndices = sarDimsSeqIndices.get(); memset( arDimsSeqIndices, 0, sizeof( sal_Int32 ) * dimsSeq); } char* psaCurrentData= static_cast(pSAData); do { // Get the Sequence at the current index , see arDimsSeqIndices uno_Sequence * pCurrentSeq= pMultiSeq; sal_Int32 curDim=1; // 1 based bool skipSeq= false; while( curDim <= dimsSeq ) { // get the Sequence at the index if valid if( pCurrentSeq->nElements > arDimsSeqIndices[ curDim - 1] ) // don't point to Nirvana { // size of Sequence is 4 sal_Int32 offset= arDimsSeqIndices[ curDim - 1] * 4; pCurrentSeq= *reinterpret_cast(&pCurrentSeq->elements[ offset]); curDim++; } else { // There is no Sequence at this index, so skip this index skipSeq= true; break; } } if( skipSeq) continue; // Calculate the current position within the datablock of the SAFEARRAY // for the next Sequence. sal_Int32 memOffset= 0; sal_Int32 dimWeight= parElementCount[ dims - 1]; // size of the rightmost dimension for(sal_Int32 idims=0; idims < dimsSeq; idims++ ) { memOffset+= arDimsSeqIndices[dimsSeq - 1 - idims] * dimWeight; // now determine the weight of the dimension to the left of the current. if( dims - 2 - idims >=0) dimWeight*= parElementCount[dims - 2 - idims]; } psaCurrentData= static_cast(pSAData) + memOffset * oleElementSize; // convert the Sequence and put the elements into the Safearray for( sal_Int32 i= 0; i < pCurrentSeq->nElements; i++) { Any unoElement( pCurrentSeq->elements + i * elementSize, rawTypeDesc ); // The any is being converted into a VARIANT which value is then copied // to the SAFEARRAY's data block. When copying one has to follow the rules for // copying certain types, as are VT_DISPATCH, VT_UNKNOWN, VT_VARIANT, VT_BSTR. // To increase performance, we just do a memcpy of VARIANT::byref. This is possible // because anyToVariant has already followed the copying rules. To make this // work there must not be a VariantClear. // One Exception is VARIANT because I don't know how VariantCopy works. VARIANT var; VariantInit( &var); anyToVariant( &var, unoElement); if( elemtype == VT_VARIANT ) { VariantCopy( reinterpret_cast(psaCurrentData), &var); VariantClear( &var); } else memcpy( psaCurrentData, &var.byref, oleElementSize); psaCurrentData+= oleElementSize; } } while( incrementMultidimensionalIndex( dimsSeq, parElementCount, arDimsSeqIndices)); SafeArrayUnaccessData( pArray); } } } return pArray; } // Increments a multi dimensional index. // Returns true as long as the index has been successfully incremented, false otherwise. // False is also returned if an overflow of the most significant dimension occurs. E.g. // assume an array with the dimensions (2,2), then the lowest index is (0,0) and the highest // index is (1,1). If the function is being called with the index (1,1) then the overflow would // occur, with the result (0,0) and a sal_False as return value. // Param dimensions - number of dimensions // Param parDimensionsLength - The array contains the size of each dimension, that is the // size of the array equals the parameter dimensions. // The rightmost dimensions is the least significant one // ( parDimensionsLengths[ dimensions -1 ] ). // Param parMultiDimensionalIndex - The array contains the index. Each dimension index is // 0 based. template bool UnoConversionUtilities::incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLengths, sal_Int32 * parMultidimensionalIndex) { if( dimensions < 1) return false; bool ret= true; bool carry= true; // to get into the while loop sal_Int32 currentDimension= dimensions; //most significant is 1 while( carry) { parMultidimensionalIndex[ currentDimension - 1]++; // if carryover, set index to 0 and handle carry on a level above if( parMultidimensionalIndex[ currentDimension - 1] > (parDimensionLengths[ currentDimension - 1] - 1)) parMultidimensionalIndex[ currentDimension - 1]= 0; else carry= false; currentDimension --; // if dimensions drops below 1 and carry is set than then all indices are 0 again // this is signalled by returning sal_False if( currentDimension < 1 && carry) { carry= false; ret= false; } } return ret; } // Determines the size of a certain OLE type. The function takes // only those types into account which are oleautomation types and // can have a value ( unless VT_NULL, VT_EMPTY, VT_ARRAY, VT_BYREF). // Currently used in createUnoSequenceWrapper to calculate addresses // for data within a SAFEARRAY. template size_t UnoConversionUtilities::getOleElementSize( VARTYPE type) { size_t size; switch( type) { case VT_BOOL: size= sizeof( VARIANT_BOOL);break; case VT_UI1: size= sizeof( unsigned char);break; case VT_R8: size= sizeof( double);break; case VT_R4: size= sizeof( float);break; case VT_I2: size= sizeof( short);break; case VT_I4: size= sizeof( long);break; case VT_BSTR: size= sizeof( BSTR); break; case VT_ERROR: size= sizeof( SCODE); break; case VT_DISPATCH: case VT_UNKNOWN: size= sizeof( IUnknown*); break; case VT_VARIANT: size= sizeof( VARIANT);break; default: size= 0; } return size; } //If a Sequence is being converted into a SAFEARRAY then we possibly have // to create a SAFEARRAY with multiple dimensions. This is the case when a // Sequence contains Sequences ( Sequence< Sequence < XXX > > ). The leftmost // Sequence in the declaration is assumed to represent dimension 1. Because // all Sequence elements of a Sequence can have different length, we have to // determine the maximum length which is then the length of the respective // dimension. // getElementCountAndTypeOfSequence determines the length of each dimension and calls itself recursively // in the process. // param rSeq - an Any that has to contain a Sequence // param dim - the dimension for which the number of elements is being determined, // must be one. // param seqElementCounts - contains the maximum number of elements for each // dimension. Index 0 contains the number of dimension one. // After return the Sequence contains the maximum number of // elements for each dimension. // The length of the Sequence must equal the number of dimensions. // param typeClass - TypeClass of the element type that is no Sequence, e.g. // Sequence< Sequence > > - type is sal_Int32) template void UnoConversionUtilities::getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc) { sal_Int32 dimCount= (*static_cast(rSeq.getValue()))->nElements; if( dimCount > seqElementCounts[ dim-1]) seqElementCounts[ dim-1]= dimCount; // we need the element type to construct the any that is // passed into getElementCountAndTypeOfSequence again typelib_TypeDescription* pSeqDesc= nullptr; rSeq.getValueTypeDescription( &pSeqDesc); typelib_TypeDescriptionReference* pElementDescRef= reinterpret_cast(pSeqDesc)->pType; // if the elements are Sequences then do recursion if( dim < seqElementCounts.getLength() ) { uno_Sequence* pSeq = *static_cast(rSeq.getValue()); uno_Sequence** arSequences= reinterpret_cast(pSeq->elements); for( sal_Int32 i=0; i < dimCount; i++) { uno_Sequence* arElement= arSequences[ i]; getElementCountAndTypeOfSequence( Any( &arElement, pElementDescRef), dim + 1 , seqElementCounts, typeDesc); } } else { // determine the element type ( e.g. Sequence< Sequence > > - type is sal_Int32) typeDesc= pElementDescRef; } typelib_typedescription_release( pSeqDesc); } template SAFEARRAY* UnoConversionUtilities::createUnoSequenceWrapper(const Any& rSeq) { SAFEARRAY* pArray = nullptr; sal_uInt32 n = 0; if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE ) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper\n" "The UNO argument is not a sequence", nullptr, -1); uno_Sequence * punoSeq= *static_cast(rSeq.getValue()); typelib_TypeDescriptionReference* pSeqTypeRef= rSeq.getValueTypeRef(); typelib_TypeDescription* pSeqType= nullptr; TYPELIB_DANGER_GET( &pSeqType, pSeqTypeRef); typelib_IndirectTypeDescription * pSeqIndDec= reinterpret_cast(pSeqType); typelib_TypeDescriptionReference * pSeqElementTypeRef= pSeqIndDec->pType; TYPELIB_DANGER_RELEASE( pSeqType); typelib_TypeDescription* pSeqElementDesc= nullptr; TYPELIB_DANGER_GET( &pSeqElementDesc, pSeqElementTypeRef); sal_Int32 nElementSize= pSeqElementDesc->nSize; n= punoSeq->nElements; SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; rgsabound[0].cElements = n; VARIANT oleElement; long safeI[1]; pArray = SafeArrayCreate(VT_VARIANT, 1, rgsabound); Any unoElement; char * pSeqData= punoSeq->elements; for (sal_uInt32 i = 0; i < n; i++) { unoElement.setValue( pSeqData + i * nElementSize, pSeqElementDesc); VariantInit(&oleElement); anyToVariant(&oleElement, unoElement); safeI[0] = i; SafeArrayPutElement(pArray, safeI, &oleElement); VariantClear(&oleElement); } TYPELIB_DANGER_RELEASE( pSeqElementDesc); return pArray; } /* The argument rObj can contain - UNO struct - UNO interface - UNO interface created by this bridge (adapter factory) - UNO interface created by this bridge ( COM Wrapper) pVar must be initialized. */ template void UnoConversionUtilities::createUnoObjectWrapper(const Any & rObj, VARIANT * pVar) { MutexGuard guard(getBridgeMutex()); Reference xInt; TypeClass tc = rObj.getValueTypeClass(); if (tc != TypeClass_INTERFACE && tc != TypeClass_STRUCT) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoObjectWrapper \n" "Cannot create an Automation interface for a UNO type which is not " "a struct or interface!", nullptr, -1); if (rObj.getValueTypeClass() == TypeClass_INTERFACE) { if (! (rObj >>= xInt)) throw IllegalArgumentException( "[automation bridge] UnoConversionUtilities::createUnoObjectWrapper\n " "Could not create wrapper object for UNO object!", nullptr, -1); //If XInterface is NULL, which is a valid value, then simply return NULL. if ( ! xInt.is()) { pVar->vt = VT_UNKNOWN; pVar->punkVal = nullptr; return; } //make sure we have the main XInterface which is used with a map xInt.set(xInt, UNO_QUERY); //If there is already a wrapper for the UNO object then use it Reference xIntWrapper; // Does a UNO wrapper exist already ? auto it_uno = UnoObjToWrapperMap.find( reinterpret_cast(xInt.get())); if(it_uno != UnoObjToWrapperMap.end()) { xIntWrapper = it_uno->second; if (xIntWrapper.is()) { convertSelfToCom(xIntWrapper, pVar); return; } } // Is the object a COM wrapper ( either XInvocation, or Adapter object) // or does it supply an IDispatch by its own ? else { Reference xIntComWrapper = xInt; // Adapter? then get the COM wrapper to which the adapter delegates its calls auto it = AdapterToWrapperMap.find( reinterpret_cast(xInt.get())); if( it != AdapterToWrapperMap.end() ) xIntComWrapper= reinterpret_cast(it->second); if (convertSelfToCom(xIntComWrapper, pVar)) return; } } // If we have no UNO wrapper nor the IDispatch yet then we have to create // a wrapper. For that we need an XInvocation. // create an XInvocation using the invocation service Reference xInv; Reference xInvFactory= getInvocationFactory(rObj); if (xInvFactory.is()) { Sequence params(2); params.getArray()[0] = rObj; params.getArray()[1] <<= OUString("FromOLE"); Reference xInt2 = xInvFactory->createInstanceWithArguments(params); xInv.set(xInt2, UNO_QUERY); } if (xInv.is()) { Reference xNewWrapper = createUnoWrapperInstance(); Reference xInitWrapper(xNewWrapper, UNO_QUERY); if (xInitWrapper.is()) { VARTYPE vartype= getVarType( rObj); if (xInt.is()) { Any params[3]; params[0] <<= xInv; params[1] <<= xInt; params[2] <<= vartype; xInitWrapper->initialize( Sequence(params, 3)); } else { Any params[2]; params[0] <<= xInv; params[1] <<= vartype; xInitWrapper->initialize( Sequence(params, 2)); } // put the newly created object into a map. If the same object will // be mapped again and there is already a wrapper then the old wrapper // will be used. if(xInt.is()) // only interfaces UnoObjToWrapperMap[reinterpret_cast(xInt.get())]= xNewWrapper; convertSelfToCom(xNewWrapper, pVar); return; } } } template void UnoConversionUtilities::variantToAny( const VARIANT* pVariant, Any& rAny, bool bReduceValueRange /* = sal_True */) { HRESULT hr = S_OK; try { CComVariant var; // There is no need to support indirect values, since they're not supported by UNO if( FAILED(hr= VariantCopyInd( &var, pVariant))) // remove VT_BYREF throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::variantToAny \n" "VariantCopyInd failed for reason : " + OUString::number(hr)); if ( ! convertValueObject( & var, rAny)) { if ((var.vt & VT_ARRAY) > 0) { VARTYPE oleTypeFlags = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY ); Sequence unoSeq = createOleArrayWrapper(var.parray, oleTypeFlags); rAny.setValue( &unoSeq, cppu::UnoType::get()); } else { switch (var.vt) { case VT_EMPTY: rAny.setValue(nullptr, Type()); break; case VT_NULL: rAny.setValue(nullptr, Type()); break; case VT_I2: rAny.setValue( & var.iVal, cppu::UnoType::get()); break; case VT_I4: rAny.setValue( & var.lVal, cppu::UnoType::get()); // necessary for use in JavaScript ( see "reduceRange") if( bReduceValueRange) reduceRange(rAny); break; case VT_R4: rAny.setValue( & var.fltVal, cppu::UnoType::get()); break; case VT_R8: rAny.setValue(& var.dblVal, cppu::UnoType::get()); break; case VT_CY: { Currency cy(var.cyVal.int64); rAny <<= cy; break; } case VT_DATE: { Date d(var.date); rAny <<= d; break; } case VT_BSTR: { OUString b(o3tl::toU(var.bstrVal)); rAny.setValue( &b, cppu::UnoType::get()); break; } case VT_UNKNOWN: case VT_DISPATCH: { //check if it is a UNO type CComQIPtr spType(static_cast(var.byref)); if (spType) { CComBSTR sName; if (FAILED(spType->get_Name(&sName))) throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::variantToAny \n" "Failed to get the type name from a UnoTypeWrapper!"); Type type; if (!getType(sName, type)) { throw CannotConvertException( OUStringLiteral("[automation bridge]UnoConversionUtilities::variantToAny \n" "A UNO type with the name: ") + o3tl::toU(LPCOLESTR(sName)) + "does not exist!", nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); } rAny <<= type; } else { rAny = createOleObjectWrapper( & var); } break; } case VT_ERROR: { SCode scode(var.scode); rAny <<= scode; break; } case VT_BOOL: { rAny <<= (var.boolVal == VARIANT_TRUE); break; } case VT_I1: rAny.setValue( & var.cVal, cppu::UnoType::get()); break; case VT_UI1: // there is no unsigned char in UNO rAny <<= sal_Int8(var.bVal); break; case VT_UI2: rAny.setValue( & var.uiVal, cppu::UnoType::get() ); break; case VT_UI4: rAny.setValue( & var.ulVal, cppu::UnoType::get()); break; case VT_INT: rAny.setValue( & var.intVal, cppu::UnoType::get()); break; case VT_UINT: rAny.setValue( & var.uintVal, cppu::UnoType::get()); break; case VT_VOID: rAny.setValue( nullptr, Type()); break; case VT_DECIMAL: { Decimal dec; dec.Scale = var.decVal.scale; dec.Sign = var.decVal.sign; dec.LowValue = var.decVal.Lo32; dec.MiddleValue = var.decVal.Mid32; dec.HighValue = var.decVal.Hi32; rAny <<= dec; break; } default: break; } } } } catch (const IllegalArgumentException &) { throw; } catch (const CannotConvertException &) { throw; } catch (const BridgeRuntimeError &) { throw; } catch (const Exception & e) { throw BridgeRuntimeError("[automation bridge] unexpected exception in " "UnoConversionUtilities::variantToAny ! Message : \n" + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge] unexpected exception in " "UnoConversionUtilities::variantToAny !"); } } // The function converts an IUnknown* into a UNO interface or struct. The // IUnknown pointer can constitute different kind of objects: // 1. a wrapper of a UNO struct (the wrapper was created by this bridge) // 2. a wrapper of a UNO interface (created by this bridge) // 3. a dispatch object that implements UNO interfaces // 4. a dispatch object. // If the parameter "aType" has a value then the COM object ( pUnknown) is supposed to // implement the interface described by "aType". Moreover it ( pUnknown) can implement // several other // UNO interfaces in which case it has to support the SUPPORTED_INTERFACES_PROP (see // #define) property. That property contains all names of interfaces. // "pUnknown" is wrapped by a COM wrapper object that implements XInvocation, e.g. // IUnknownWrapper. Additionally an object of type "aType" is created by help // of the INTERFACE_ADAPTER_FACTORY (see #define) service. The implementation of // "aType" calls on the COM wrapper's XInvocation::invoke. If the COM object supports // more than one UNO interfaces, as can be determined by the property // SUPPORTED_INTERFACES_PROP, then the INTERFACE_ADAPTER_FACTORY creates an object that // implements all these interfaces. // This is only done if "pUnknown" is not already a UNO wrapper, // that is it is actually NOT a UNO object that was converted to a COM object. If it is an // UNO wrapper than the original UNO object is being extracted, queried for "aType" (if // it is no struct) and returned. template Any UnoConversionUtilities::createOleObjectWrapper(VARIANT* pVar, const Type& aType) { //To allow passing "Nothing" in VS 2008 we need to accept VT_EMPTY if (pVar->vt != VT_UNKNOWN && pVar->vt != VT_DISPATCH && pVar->vt != VT_EMPTY) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createOleObjectWrapper \n" "The VARIANT does not contain an object type! ", nullptr, -1); MutexGuard guard( getBridgeMutex()); CComPtr spUnknown; CComPtr spDispatch; if (pVar->vt == VT_UNKNOWN) { spUnknown = pVar->punkVal; if (spUnknown) spUnknown.QueryInterface( & spDispatch.p); } else if (pVar->vt == VT_DISPATCH && pVar->pdispVal != nullptr) { CComPtr spDispatch2(pVar->pdispVal); if (spDispatch2) spDispatch2.QueryInterface( & spUnknown.p); } static Type VOID_TYPE; Any ret; //If no Type is provided and pVar contains IUnknown then we return a XInterface. //If pVar contains an IDispatch then we return a XInvocation. Type desiredType = aType; if (aType == VOID_TYPE) { switch (pVar->vt) { case VT_EMPTY: case VT_UNKNOWN: desiredType = cppu::UnoType::get(); break; case VT_DISPATCH: desiredType = cppu::UnoType::get(); break; default: desiredType = aType; } } // COM pointer are NULL, no wrapper required if (spUnknown == nullptr) { Reference xInt; if( aType.getTypeClass() == TypeClass_INTERFACE) ret.setValue( &xInt, aType); else if( aType.getTypeClass() == TypeClass_STRUCT) ret.setValue( nullptr, aType); else ret <<= xInt; return ret; } // Check if "spUnknown" is a UNO wrapper, that is a UNO object that has been // passed to COM. Then it supports IUnoObjectWrapper // and we extract the original UNO object. CComQIPtr spUno( spUnknown); if( spUno) { // it is a wrapper Reference xInt; if( SUCCEEDED( spUno->getOriginalUnoObject( &xInt))) { ret <<= xInt; } else { Any any; if( SUCCEEDED( spUno->getOriginalUnoStruct(&any))) ret= any; } return ret; } // "spUnknown" is a real COM object. // Before we create a new wrapper object we check if there is an existing wrapper // There can be two kinds of wrappers, those who wrap dispatch - UNO objects, and those who // wrap ordinary dispatch objects. The dispatch-UNO objects usually are adapted to represent // particular UNO interfaces. Reference xIntWrapper; auto cit_currWrapper= ComPtrToWrapperMap.find( reinterpret_cast(spUnknown.p)); if(cit_currWrapper != ComPtrToWrapperMap.end()) xIntWrapper = cit_currWrapper->second; if (xIntWrapper.is()) { //Try to find an adapter for the wrapper //find the proper Adapter. The pointer in the WrapperToAdapterMap are valid as long as //we get a pointer to the wrapper from ComPtrToWrapperMap, because the Adapter hold references //to the wrapper. auto it = WrapperToAdapterMap.find(reinterpret_cast(xIntWrapper.get())); if (it == WrapperToAdapterMap.end()) { // No adapter available. //The COM component could be a UNO object. Then we need to provide // a proxy that implements all interfaces Sequence seqTypes= getImplementedInterfaces(spUnknown); Reference xIntAdapter; if (seqTypes.getLength() > 0) { //It is a COM UNO object xIntAdapter = createAdapter(seqTypes, xIntWrapper); } else { // Some ordinary COM object xIntAdapter = xIntWrapper; } // return the wrapper directly, return XInterface or XInvocation ret = xIntWrapper->queryInterface(desiredType); if ( ! ret.hasValue()) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createOleObjectWrapper \n" "The COM object is not suitable for the UNO type: " + desiredType.getTypeName(), nullptr, -1); } else { //There is an adapter available Reference xIntAdapter(reinterpret_cast(it->second)); ret = xIntAdapter->queryInterface( desiredType); if ( ! ret.hasValue()) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createOleObjectWrapper \n" "The COM object is not suitable for the UNO type: " + desiredType.getTypeName(), nullptr, -1); } return ret; } // No existing wrapper. Therefore create a new proxy. // If the object implements UNO interfaces then get the types. Sequence seqTypes = getImplementedInterfaces(spUnknown); if (seqTypes.getLength() == 0 && aType != VOID_TYPE && aType != cppu::UnoType::get()) { seqTypes = Sequence( & aType, 1); } //There is no existing wrapper, therefore we create one for the real COM object Reference xIntNewProxy= createComWrapperInstance(); if ( ! xIntNewProxy.is()) throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::createOleObjectWrapper \n" "Could not create proxy object for COM object!"); // initialize the COM wrapper Reference xInit( xIntNewProxy, UNO_QUERY); OSL_ASSERT( xInit.is()); Any params[3]; params[0] <<= reinterpret_cast(spUnknown.p); params[1] <<= (pVar->vt == VT_DISPATCH); params[2] <<= seqTypes; xInit->initialize( Sequence( params, 3)); ComPtrToWrapperMap[reinterpret_cast(spUnknown.p)] = xIntNewProxy; // we have a wrapper object //The wrapper implements already XInvocation and XInterface. If //param aType is void then the object is supposed to have XInvocation. if (aType == cppu::UnoType::get()|| (aType == VOID_TYPE && seqTypes.getLength() == 0 )) { ret = xIntNewProxy->queryInterface(desiredType); } else { Reference xIntAdapter = createAdapter(seqTypes, xIntNewProxy); ret = xIntAdapter->queryInterface(desiredType); } return ret; } template Reference UnoConversionUtilities::createAdapter(const Sequence& seqTypes, const Reference& receiver) { Reference< XInterface> xIntAdapterFac; xIntAdapterFac= m_smgr->createInstance(INTERFACE_ADAPTER_FACTORY); // We create an adapter object that does not only implement the required type but also // all types that the COM object pretends to implement. A COM object must therefore // support the property "_implementedInterfaces". Reference xIntAdapted; Reference xInv(receiver, UNO_QUERY); Reference xAdapterFac( xIntAdapterFac, UNO_QUERY); if( xAdapterFac.is()) xIntAdapted= xAdapterFac->createAdapter( xInv, seqTypes); if( !xIntAdapted.is()) { throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::createOleObjectWrapper \n" "Could not create a proxy for COM object! Creation of adapter failed."); } // Put the pointer to the wrapper object and the interface pointer of the adapted interface // in a global map. Thus we can determine in a call to createUnoObjectWrapper whether the UNO // object is a wrapped COM object. In that case we extract the original COM object rather than // creating a wrapper around the UNO object. typedef std::unordered_map::value_type VALUE; AdapterToWrapperMap.insert( VALUE( reinterpret_cast(xIntAdapted.get()), reinterpret_cast(receiver.get()))); WrapperToAdapterMap.insert( VALUE( reinterpret_cast(receiver.get()), reinterpret_cast(xIntAdapted.get()))); return xIntAdapted; } // "convertValueObject" converts a JScriptValue object contained in "var" into // an any. The type contained in the any is stipulated by a "type value" thas // was set within the JScript script on the value object ( see JScriptValue). template bool UnoConversionUtilities::convertValueObject( const VARIANTARG *var, Any& any) { bool ret = false; try { bool bFail = false; HRESULT hr= S_OK; CComVariant varDisp; if(SUCCEEDED(hr = varDisp.ChangeType( VT_DISPATCH, var))) { CComPtr spValue; VARIANT_BOOL varBool; CComBSTR bstrType; CComVariant varValue; CComPtr spDisp( varDisp.pdispVal); if(spDisp) { if(SUCCEEDED( spDisp->QueryInterface( __uuidof( IJScriptValueObject), reinterpret_cast (&spValue)))) { ret = true; // is a ValueObject //If it is an out - param then it does not need to be converted. In/out and // in params does so. if (SUCCEEDED(hr= spValue->IsOutParam( &varBool))) { // if varBool == true then no conversion needed because out param if (varBool == VARIANT_FALSE) { if(SUCCEEDED(hr = spValue->GetValue( & bstrType, & varValue))) { Type type; if (getType(bstrType, type)) variantToAny( & varValue, any, type); else bFail = true; } else bFail = true; } } else bFail = true; } } } else if( hr != DISP_E_TYPEMISMATCH && hr != E_NOINTERFACE) bFail = true; if (bFail) throw BridgeRuntimeError( "[automation bridge] Conversion of ValueObject failed "); } catch (const BridgeRuntimeError &) { throw; } catch (const Exception & e) { throw BridgeRuntimeError("[automation bridge] unexpected exception in " "UnoConversionUtilities::convertValueObject ! Message : \n" + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge] unexpected exception in " "UnoConversionUtilities::convertValueObject !"); } return ret; } template void UnoConversionUtilities::dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type) { try { if( pvar->vt != VT_DISPATCH) throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); IDispatchEx* pdispEx; HRESULT hr; if( FAILED( hr= pvar->pdispVal->QueryInterface( IID_IDispatchEx, reinterpret_cast( &pdispEx)))) throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); DISPID dispid; DISPPARAMS param= {nullptr,nullptr,0,0}; CComVariant result; OLECHAR const * sLength= L"length"; // Get the length of the array. Can also be obtained through GetNextDispID. The // method only returns DISPIDs of the array data. Their names are like "0", "1" etc. if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, const_cast(&sLength), 1, LOCALE_USER_DEFAULT, &dispid))) throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶m, &result, nullptr, nullptr))) throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); if( FAILED( VariantChangeType( &result, &result, 0, VT_I4))) throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); long length= result.lVal; result.Clear(); // get a few basic facts about the sequence, and reallocate: // create the Sequences // get the size of the elements typelib_TypeDescription *pDesc= nullptr; type.getDescription( &pDesc); typelib_IndirectTypeDescription *pSeqDesc= reinterpret_cast(pDesc); typelib_TypeDescriptionReference *pSeqElemDescRef= pSeqDesc->pType; // type of the Sequence' elements Type elemType( pSeqElemDescRef); _typelib_TypeDescription* pSeqElemDesc=nullptr; TYPELIB_DANGER_GET( &pSeqElemDesc, pSeqElemDescRef); sal_uInt32 nelementSize= pSeqElemDesc->nSize; TYPELIB_DANGER_RELEASE( pSeqElemDesc); uno_Sequence *p_uno_Seq; uno_sequence_construct( &p_uno_Seq, pDesc, nullptr, length, cpp_acquire); typelib_TypeClass typeElement= pSeqDesc->pType->eTypeClass; char *pArray= p_uno_Seq->elements; // Get All properties in the object, convert their values to the expected type and // put them into the passed in sequence for( sal_Int32 i= 0; i< length; i++) { OUString ousIndex=OUString::number( i); OLECHAR* sindex = const_cast(o3tl::toW(ousIndex.getStr())); if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sindex , 1, LOCALE_USER_DEFAULT, &dispid))) { throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); } if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶m, &result, nullptr, nullptr))) { throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities::dispatchExObject2Sequence \n" "Conversion of dispatch object to Sequence failed!"); } // If the result is VT_DISPATCH than the Sequence's element type could be Sequence // Look that up in the CoreReflection to make clear. // That requires a recursiv conversion Any any; // Destination address within the out-Sequence "anySeq" where to copy the next converted element void* pDest= pArray + (i * nelementSize); if( result.vt & VT_DISPATCH && typeElement == typelib_TypeClass_SEQUENCE) { variantToAny( &result, any, elemType, false); // copy the converted VARIANT, that is a Sequence to the Sequence uno_Sequence * p_unoSeq= *static_cast(any.getValue()); // just copy the pointer of the uno_Sequence // nelementSize should be 4 !!!! memcpy( pDest, &p_unoSeq, nelementSize); osl_atomic_increment( &p_unoSeq->nRefCount); } else // Element type is no Sequence -> do one conversion { variantToAny( &result, any, elemType, false); if( typeElement == typelib_TypeClass_ANY) { // copy the converted VARIANT to the Sequence uno_type_assignData( pDest, pSeqElemDescRef , &any, pSeqElemDescRef,cpp_queryInterface, cpp_acquire, cpp_release); } else { // type after conversion must be the element type of the sequence OSL_ENSURE(any.getValueTypeClass() == css::uno::TypeClass(typeElement), "wrong conversion"); uno_type_assignData( pDest, pSeqElemDescRef,const_cast( any.getValue()), any.getValueTypeRef(), cpp_queryInterface, cpp_acquire, cpp_release); } } } // else result.Clear(); anySeq.setValue( &p_uno_Seq, pDesc); uno_destructData( &p_uno_Seq, pDesc, cpp_release); typelib_typedescription_release( pDesc); } catch (const BridgeRuntimeError &) { throw; } catch (const Exception & e) { throw BridgeRuntimeError("[automation bridge] unexpected exception in " "UnoConversionUtilities::convertValueObject ! Message : \n" + e.Message); } catch(...) { throw BridgeRuntimeError( "[automation bridge] unexpected exception in " "UnoConversionUtilities::convertValueObject !"); } } /* The argument unotype is the type that is expected by the currently called UNO function. For example: []long, [][]long. If the function calls itself recursively then the element type is passed on. For example a two dimensional SAFEARRAY of type VT_I4 is to be converted. Then unotype has to be either void or [][]long. When the function calls itself recursively then it passes the element type which is []long. */ template Sequence UnoConversionUtilities::createOleArrayWrapperOfDim(SAFEARRAY* pArray, unsigned int dimCount, unsigned int actDim, long* index, VARTYPE type, const Type& unotype) { HRESULT hr= S_OK; long lBound; long uBound; long nCountElements; SafeArrayGetLBound(pArray, actDim, &lBound); SafeArrayGetUBound(pArray, actDim, &uBound); nCountElements= uBound - lBound +1; Sequence anySeq(nCountElements); Any* pUnoArray = anySeq.getArray(); for (index[actDim - 1] = lBound; index[actDim - 1] <= uBound; index[actDim - 1]++) { if (actDim > 1 ) { Sequence element = createOleArrayWrapperOfDim(pArray, dimCount, actDim - 1, index, type, getElementTypeOfSequence(unotype)); pUnoArray[index[actDim - 1] - lBound].setValue(&element, cppu::UnoType::get()); } else { VARIANT variant; VariantInit(&variant); V_VT(&variant) = type; switch (type) { case VT_I2: SafeArrayGetElement(pArray, index, &V_I2(&variant)); break; case VT_I4: SafeArrayGetElement(pArray, index, &V_I4(&variant)); break; case VT_R4: SafeArrayGetElement(pArray, index, &V_R4(&variant)); break; case VT_R8: SafeArrayGetElement(pArray, index, &V_R8(&variant)); break; case VT_CY: SafeArrayGetElement(pArray, index, &V_CY(&variant)); break; case VT_DATE: SafeArrayGetElement(pArray, index, &V_DATE(&variant)); break; case VT_BSTR: hr= SafeArrayGetElement(pArray, index, &V_BSTR(&variant)); break; case VT_DISPATCH: SafeArrayGetElement(pArray, index, &V_DISPATCH(&variant)); break; case VT_ERROR: SafeArrayGetElement(pArray, index, &V_ERROR(&variant)); break; case VT_BOOL: SafeArrayGetElement(pArray, index, &V_BOOL(&variant)); break; case VT_VARIANT: SafeArrayGetElement(pArray, index, &variant); break; case VT_UNKNOWN: SafeArrayGetElement(pArray, index, &V_UNKNOWN(&variant)); break; case VT_I1: SafeArrayGetElement(pArray, index, &V_I1(&variant)); break; case VT_UI1: SafeArrayGetElement(pArray, index, &V_UI1(&variant)); break; case VT_UI2: SafeArrayGetElement(pArray, index, &V_UI2(&variant)); break; case VT_UI4: SafeArrayGetElement(pArray, index, &V_UI4(&variant)); break; default: break; } if( unotype.getTypeClass() == TypeClass_VOID) // the function was called without specifying the destination type variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], false); else variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], getElementTypeOfSequence(unotype), false); VariantClear(&variant); } } return anySeq; } template Type UnoConversionUtilities::getElementTypeOfSequence( const Type& seqType) { Type retValue; if( seqType.getTypeClass() != TypeClass_VOID) { OSL_ASSERT( seqType.getTypeClass() == TypeClass_SEQUENCE); typelib_TypeDescription* pDescSeq= nullptr; seqType.getDescription(& pDescSeq); retValue = Type(reinterpret_cast(pDescSeq)->pType); typelib_typedescription_release(pDescSeq); } return retValue; } template Sequence UnoConversionUtilities::createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unoType) { sal_uInt32 dim = SafeArrayGetDim(pArray); Sequence ret; if (dim > 0) { std::unique_ptr sarIndex(new long[dim]); long * index = sarIndex.get(); for (unsigned int i = 0; i < dim; i++) { index[i] = 0; } ret = createOleArrayWrapperOfDim(pArray, dim, dim, index, type, unoType); } return ret; } // If a VARIANT has the type VT_DISPATCH it can either be a JScript Array // or some other object. This function finds out if it is such an array or // not. Currently there's no way to make sure it's an array // so we assume that when the object has a property "0" then it is an Array. // A JScript has property like "0", "1", "2" etc. which represent the // value at the corresponding index of the array template bool UnoConversionUtilities::isJScriptArray(const VARIANT* rvar) { OSL_ENSURE( rvar->vt == VT_DISPATCH, "param is not a VT_DISPATCH"); HRESULT hr; OLECHAR const * sindex= L"0"; DISPID id; if ( rvar->vt == VT_DISPATCH && rvar->pdispVal ) { hr= rvar->pdispVal->GetIDsOfNames( IID_NULL, const_cast(&sindex), 1, LOCALE_USER_DEFAULT, &id); if( SUCCEEDED ( hr) ) return true; } return false; } template VARTYPE UnoConversionUtilities::mapTypeClassToVartype( TypeClass type) { VARTYPE ret; switch( type) { case TypeClass_INTERFACE: ret= VT_DISPATCH; break; case TypeClass_STRUCT: ret= VT_DISPATCH; break; case TypeClass_ENUM: ret= VT_I4; break; case TypeClass_SEQUENCE: ret= VT_ARRAY; break; case TypeClass_ANY: ret= VT_VARIANT; break; case TypeClass_BOOLEAN: ret= VT_BOOL; break; case TypeClass_CHAR: ret= VT_I2; break; case TypeClass_STRING: ret= VT_BSTR; break; case TypeClass_FLOAT: ret= VT_R4; break; case TypeClass_DOUBLE: ret= VT_R8; break; case TypeClass_BYTE: ret= VT_UI1; break; case TypeClass_SHORT: ret= VT_I2; break; case TypeClass_LONG: ret= VT_I4; break; case TypeClass_UNSIGNED_SHORT: ret= VT_UI2; break; case TypeClass_UNSIGNED_LONG: ret= VT_UI4; break; default: ret= VT_EMPTY; } return ret; } template Sequence UnoConversionUtilities::getImplementedInterfaces(IUnknown* pUnk) { Sequence seqTypes; CComDispatchDriver disp( pUnk); if( disp) { CComVariant var; HRESULT hr= S_OK; // There are two different property names possible. if( FAILED( hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP, &var))) { hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP2, &var); } if (SUCCEEDED( hr)) { // we expect an array( SafeArray or IDispatch) of Strings. Any anyNames; variantToAny( &var, anyNames, cppu::UnoType>::get()); Sequence seqAny; if( anyNames >>= seqAny) { seqTypes.realloc( seqAny.getLength()); for( sal_Int32 i=0; i < seqAny.getLength(); i++) { OUString typeName; seqAny[i] >>= typeName; seqTypes[i]= Type( TypeClass_INTERFACE, typeName); } } } } return seqTypes; } template Reference UnoConversionUtilities::getTypeConverter() { if ( ! m_typeConverter.is()) { MutexGuard guard(getBridgeMutex()); if ( ! m_typeConverter.is()) { Reference xIntConverter = m_smgr->createInstance("com.sun.star.script.Converter"); if (xIntConverter.is()) m_typeConverter.set(xIntConverter, UNO_QUERY); } } return m_typeConverter; } // This function tries to the change the type of a value (contained in the Any) // to the smallest possible that can hold the value. This is actually done only // for types of VT_I4 (see o2u_variantToAny). The reason is the following: // JavaScript passes integer values always as VT_I4. If there is a parameter or // property of type any then the bridge converts the any's content according // to "o2u_variantToAny". Because the VARTYPE is VT_I4 the value would be converted // to TypeClass_LONG. Say the method XPropertySet::setPropertyValue( string name, any value) // would be called on an object and the property actually is of TypeClass_SHORT. // After conversion of the VARIANT parameter the Any would contain type // TypeClass_LONG. Because the corereflection does not cast from long to short // the "setPropertValue" would fail as the value has not the right type. // The corereflection does convert small integer types to bigger types. // Therefore we can reduce the type if possible and avoid the above mentioned // problem. // The function is not used when elements are to be converted for Sequences. inline void reduceRange( Any& any) { OSL_ASSERT( any.getValueTypeClass() == TypeClass_LONG); sal_Int32 value= *o3tl::doAccess(any); if( value <= 0x7f && value >= -0x80) {// -128 bis 127 sal_Int8 charVal= static_cast( value); any.setValue( &charVal, cppu::UnoType::get()); } else if( value <= 0x7fff && value >= -0x8000) {// -32768 bis 32767 sal_Int16 shortVal= static_cast( value); any.setValue( &shortVal, cppu::UnoType::get()); } } #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */