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/unoconversionutilities.hxx | |
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/unoconversionutilities.hxx')
-rw-r--r-- | extensions/source/ole/unoconversionutilities.hxx | 2363 |
1 files changed, 2363 insertions, 0 deletions
diff --git a/extensions/source/ole/unoconversionutilities.hxx b/extensions/source/ole/unoconversionutilities.hxx new file mode 100644 index 000000000..a73a714ab --- /dev/null +++ b/extensions/source/ole/unoconversionutilities.hxx @@ -0,0 +1,2363 @@ +/* -*- 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 . + */ +#pragma once + +#include <memory> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory2.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/FailReason.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> +#include <com/sun/star/bridge/XBridgeSupplier2.hpp> +#include <com/sun/star/bridge/oleautomation/Date.hpp> +#include <com/sun/star/bridge/oleautomation/Currency.hpp> +#include <com/sun/star/bridge/oleautomation/SCode.hpp> +#include <com/sun/star/bridge/oleautomation/Decimal.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <typelib/typedescription.hxx> +#include <o3tl/any.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include "ole2uno.hxx" +#include <cppuhelper/weakref.hxx> + +#include "unotypewrapper.hxx" +#include <unordered_map> + +// 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<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap; +extern std::unordered_map<sal_uIntPtr, sal_uIntPtr> 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<sal_uIntPtr, WeakReference<XInterface> > 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<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap; + +// 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<sal_Int32>(any); + if( value <= 0x7f && value >= -0x80) + {// -128 bis 127 + sal_Int8 charVal= static_cast<sal_Int8>( value); + any.setValue( &charVal, cppu::UnoType<sal_Int8>::get()); + } + else if( value <= 0x7fff && value >= -0x8000) + {// -32768 bis 32767 + sal_Int16 shortVal= static_cast<sal_Int16>( value); + any.setValue( &shortVal, cppu::UnoType<sal_Int16>::get()); + } +} + +// 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<XMultiServiceFactory> & smgr): + m_nUnoWrapperClass( INTERFACE_OLE_WRAPPER_IMPL), + m_nComWrapperClass( IUNKNOWN_WRAPPER_IMPL), + m_smgr( smgr) + {} + + UnoConversionUtilities( const Reference<XMultiServiceFactory> & 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<Any> createOleArrayWrapperOfDim(SAFEARRAY* pArray, unsigned int dimCount, unsigned int actDim, LONG* index, + VARTYPE type, const Type& unotype); + Sequence<Any> 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<Type> getImplementedInterfaces(IUnknown* pUnk); + +protected: + Reference<XInterface> createAdapter(const Sequence<Type>& types, const Reference<XInterface>& 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<XTypeConverter> 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<XMultiServiceFactory> m_smgr; + // An explicitly supplied service manager when the service + // com.sun.star.bridge.OleBridgeSupplierVar1 is used. That can be a remote + // manager. + Reference<XMultiServiceFactory> m_smgrRemote; + Reference<XSingleServiceFactory> m_xInvocationFactoryLocal; + Reference<XSingleServiceFactory> m_xInvocationFactoryRemote; + +private: + // Holds the type converter which is used for sequence conversion etc. + // Use the getTypeConverter function to obtain the interface. + Reference<XTypeConverter> 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< css::bridge::XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY); + if( xSupplier.is()) + { + sal_Int8 arId[16]; + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(arId)); + Sequence<sal_Int8> seqId( arId, 16); + Any anySource; + anySource <<= xInt; + Any anyDisp = xSupplier->createBridge( + anySource, seqId, css::bridge::ModelDependent::UNO, + css::bridge::ModelDependent::OLE); + + // due to global-process-id check this must be in-process pointer + if (auto v = o3tl::tryAccess<sal_uIntPtr>(anyDisp)) + { + VARIANT* pvariant= reinterpret_cast<VARIANT*>(*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<class T> +Reference< XSingleServiceFactory > UnoConversionUtilities<T>::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<class T> +void UnoConversionUtilities<T>::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<T>::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| <type> + // 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<Any> unoSeq = createOleArrayWrapper( var.parray, oleType, ptype); + Reference<XTypeConverter> conv = getTypeConverter(); + if (conv.is()) + { + try + { + Any anySeq(unoSeq); + Any convAny = conv->convertTo(anySeq, ptype); + rAny = convAny; + } + catch (const IllegalArgumentException& e) + { + throw BridgeRuntimeError( + "[automation bridge]com.sun.star.lang.IllegalArgumentException " + "in UnoConversionUtilities<T>::variantToAny! Message: " + + e.Message); + } + catch (const CannotConvertException& e) + { + throw BridgeRuntimeError( + "[automation bridge]com.sun.star.script.CannotConvertException " + "in UnoConversionUtilities<T>::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<Sequence<Any>>::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<sal_uInt64>::get(), + bReduceValueRange); + } + else + { + //negative value + variantToAny( & var, rAny, cppu::UnoType<sal_Int64>::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<T>::variantToAny \n" + "Cannot convert the value of vartype :\"" + + OUString::number(static_cast<sal_Int32>(var.vt)) + + "\" to the expected UNO type of type class: " + + OUString::number(static_cast<sal_Int32>(ptype.getTypeClass())), + nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); + + if (bFail) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>:variantToAny\n" + "The provided VARIANT of type\" " + OUString::number(static_cast<sal_Int32>(var.vt)) + + "\" is unappropriate for conversion!", Reference<XInterface>(), -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<T>::variantToAny ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::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<class T> +void UnoConversionUtilities<T>::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<T>::anyToVariant \n" + "Cannot convert the value of type :\"" + + rAny.getValueTypeName() + + "\" to the expected Automation type of VARTYPE: " + + OUString::number(static_cast<sal_Int32>(type)), + nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); + + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Conversion of any with " + + rAny.getValueType().getTypeName() + + " to VARIANT with type: " + OUString::number(static_cast<sal_Int32>(type)) + + " failed! Error code: " + OUString::number(hr)); + + } + if(FAILED(hr = VariantCopy(pVariant, &var))) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::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<T>::anyToVariant \n" + "Unexpected exception occurred. Message: " + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred."); + } +} + +template<class T> +void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny) +{ + try + { + bool bIllegal = false; + switch (rAny.getValueTypeClass()) + { + case TypeClass_INTERFACE: + { + Reference<XInterface> xInt; + if (rAny >>= xInt) + { + createUnoObjectWrapper(rAny, pVariant); + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_STRUCT: + { + if (rAny.getValueType() == cppu::UnoType<Date>::get() ) + { + Date d; + if (rAny >>= d) + { + pVariant->vt = VT_DATE; + pVariant->date = d.Value; + } + else + { + bIllegal = true; + } + } + else if(rAny.getValueType() == cppu::UnoType<Decimal>::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<Currency>::get()) + { + Currency c; + if (rAny >>= c) + { + pVariant->vt = VT_CY; + pVariant->cyVal.int64 = c.Value; + } + else + { + bIllegal = true; + } + } + else if(rAny.getValueType() == cppu::UnoType<SCode>::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<T>::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<sal_Unicode>(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<sal_Int32 const *>(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<T>::anyToVariant \n" + "Error during conversion of UNO type to Automation object!"); + + if (FAILED(VariantCopy(pVariant, &var))) + throw BridgeRuntimeError( + "[automation bridge] UnoConversionUtilities<T>::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<T>::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<XInterface>(), rAny.getValueTypeClass(), + FailReason::TYPE_NOT_SUPPORTED, 0); + + break; + } + if (bIllegal) + { + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" + "The provided any of type\" " + rAny.getValueType().getTypeName() + + "\" is unappropriate for conversion!", Reference<XInterface>(), -1); + + } + } + catch (const CannotConvertException &) + { + throw; + } + catch (const IllegalArgumentException &) + { + throw; + } + catch(const BridgeRuntimeError&) + { + throw; + } + catch(const Exception & e) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred. Message: " + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::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<class T> +SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype) +{ + if (rSeq.getValueTypeClass() != TypeClass_SEQUENCE) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n" + "The any does not contain a sequence!", nullptr, 0); + if (elemtype == VT_NULL || elemtype == VT_EMPTY) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::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<sal_Int32> seqElementCounts( dims); + TypeDescription elementTypeDesc; + getElementCountAndTypeOfSequence( rSeq, 1, seqElementCounts, elementTypeDesc ); + + if( elementTypeDesc.is() ) + { + // set up the SAFEARRAY + std::unique_ptr<SAFEARRAYBOUND[]> 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<uno_Sequence* const*>(rSeq.getValue()); + sal_Int32 dimsSeq= dims - 1; + + // arDimSeqIndices contains the current index of a block of data. + // E.g. Sequence<Sequence<sal_Int32>> , the index would refer to Sequence<sal_Int32> + // 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<sal_Int32[]> 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<char*>(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<uno_Sequence**>(&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<char*>(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<VARIANT*>(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<class T> +bool UnoConversionUtilities<T>::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<class T> +size_t UnoConversionUtilities<T>::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 <Sequence <sal_Int32> > > - type is sal_Int32) +template<class T> +void UnoConversionUtilities<T>::getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, + Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc) +{ + sal_Int32 dimCount= (*static_cast<uno_Sequence* const *>(rSeq.getValue()))->nElements; + if( dimCount > seqElementCounts[ dim-1]) + seqElementCounts.getArray()[ 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<typelib_IndirectTypeDescription*>(pSeqDesc)->pType; + + // if the elements are Sequences then do recursion + if( dim < seqElementCounts.getLength() ) + { + uno_Sequence* pSeq = *static_cast<uno_Sequence* const*>(rSeq.getValue()); + uno_Sequence** arSequences= reinterpret_cast<uno_Sequence**>(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 <Sequence <sal_Int32> > > - type is sal_Int32) + typeDesc= pElementDescRef; + } + typelib_typedescription_release( pSeqDesc); +} + + +template<class T> +SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq) +{ + SAFEARRAY* pArray = nullptr; + sal_uInt32 n = 0; + + if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE ) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper\n" + "The UNO argument is not a sequence", nullptr, -1); + + uno_Sequence * punoSeq= *static_cast<uno_Sequence* const *>(rSeq.getValue()); + + typelib_TypeDescriptionReference* pSeqTypeRef= rSeq.getValueTypeRef(); + typelib_TypeDescription* pSeqType= nullptr; + TYPELIB_DANGER_GET( &pSeqType, pSeqTypeRef); + typelib_IndirectTypeDescription * pSeqIndDec= reinterpret_cast<typelib_IndirectTypeDescription*>(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<class T> +void UnoConversionUtilities<T>::createUnoObjectWrapper(const Any & rObj, VARIANT * pVar) +{ + MutexGuard guard(getBridgeMutex()); + + Reference<XInterface> xInt; + + TypeClass tc = rObj.getValueTypeClass(); + if (tc != TypeClass_INTERFACE && tc != TypeClass_STRUCT) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::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<T>::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<XInterface> xIntWrapper; + // Does a UNO wrapper exist already ? + auto it_uno = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(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<XInterface> xIntComWrapper = xInt; + + // Adapter? then get the COM wrapper to which the adapter delegates its calls + auto it = AdapterToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get())); + if( it != AdapterToWrapperMap.end() ) + xIntComWrapper= reinterpret_cast<XInterface*>(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<XInvocation> xInv; + Reference<XSingleServiceFactory> xInvFactory= getInvocationFactory(rObj); + if (xInvFactory.is()) + { + Sequence<Any> params(2); + params.getArray()[0] = rObj; + params.getArray()[1] <<= OUString("FromOLE"); + Reference<XInterface> xInt2 = xInvFactory->createInstanceWithArguments(params); + xInv.set(xInt2, UNO_QUERY); + } + + if (xInv.is()) + { + Reference<XInterface> xNewWrapper = createUnoWrapperInstance(); + Reference<css::lang::XInitialization> 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<Any>(params, 3)); + } + else + { + Any params[2]; + params[0] <<= xInv; + params[1] <<= vartype; + xInitWrapper->initialize( Sequence<Any>(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<sal_uIntPtr>(xInt.get())]= xNewWrapper; + convertSelfToCom(xNewWrapper, pVar); + return; + } + } +} + +template<class T> +void UnoConversionUtilities<T>::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<T>::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<Any> unoSeq = createOleArrayWrapper(var.parray, oleTypeFlags); + rAny.setValue( &unoSeq, cppu::UnoType<decltype(unoSeq)>::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<sal_Int16>::get()); + break; + case VT_I4: + rAny.setValue( & var.lVal, cppu::UnoType<sal_Int32>::get()); + // necessary for use in JavaScript ( see "reduceRange") + if( bReduceValueRange) + reduceRange(rAny); + break; + case VT_R4: + rAny.setValue( & var.fltVal, cppu::UnoType<float>::get()); + break; + case VT_R8: + rAny.setValue(& var.dblVal, cppu::UnoType<double>::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<decltype(b)>::get()); + break; + } + case VT_UNKNOWN: + case VT_DISPATCH: + { + //check if it is a UNO type + CComQIPtr<IUnoTypeWrapper> spType(static_cast<IUnknown*>(var.byref)); + if (spType) + { + CComBSTR sName; + if (FAILED(spType->get_Name(&sName))) + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::variantToAny \n" + "Failed to get the type name from a UnoTypeWrapper!"); + Type type; + if (!getType(sName, type)) + { + throw CannotConvertException( + OUString::Concat("[automation bridge]UnoConversionUtilities<T>::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<sal_Int8>::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<cppu::UnoUnsignedShortType>::get() ); + break; + case VT_UI4: + rAny.setValue( & var.ulVal, cppu::UnoType<sal_uInt32>::get()); + break; + case VT_INT: + rAny.setValue( & var.intVal, cppu::UnoType<sal_Int32>::get()); + break; + case VT_UINT: + rAny.setValue( & var.uintVal, cppu::UnoType<sal_uInt32>::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<T>::variantToAny ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::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<class T> +Any UnoConversionUtilities<T>::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<T>::createOleObjectWrapper \n" + "The VARIANT does not contain an object type! ", nullptr, -1); + + MutexGuard guard( getBridgeMutex()); + + CComPtr<IUnknown> spUnknown; + CComPtr<IDispatch> 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<IDispatch> 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<XInterface>::get(); + break; + case VT_DISPATCH: + desiredType = cppu::UnoType<XInvocation>::get(); + break; + default: + desiredType = aType; + } + } + + // COM pointer are NULL, no wrapper required + if (spUnknown == nullptr) + { + Reference<XInterface> 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<IUnoObjectWrapper> spUno( spUnknown); + if( spUno) + { // it is a wrapper + Reference<XInterface> 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<XInterface> xIntWrapper; + auto cit_currWrapper= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(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<sal_uIntPtr>(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<Type> seqTypes= getImplementedInterfaces(spUnknown); + Reference<XInterface> 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<T>::createOleObjectWrapper \n" + "The COM object is not suitable for the UNO type: " + + desiredType.getTypeName(), nullptr, -1); + } + else + { + //There is an adapter available + Reference<XInterface> xIntAdapter(reinterpret_cast<XInterface*>(it->second)); + ret = xIntAdapter->queryInterface( desiredType); + if ( ! ret.hasValue()) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::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<Type> seqTypes = getImplementedInterfaces(spUnknown); + if (seqTypes.getLength() == 0 && + aType != VOID_TYPE && aType != cppu::UnoType<XInvocation>::get()) + { + seqTypes = Sequence<Type>( & aType, 1); + } + + //There is no existing wrapper, therefore we create one for the real COM object + Reference<XInterface> xIntNewProxy= createComWrapperInstance(); + if ( ! xIntNewProxy.is()) + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "Could not create proxy object for COM object!"); + + // initialize the COM wrapper + Reference<XInitialization> xInit( xIntNewProxy, UNO_QUERY); + OSL_ASSERT( xInit.is()); + + Any params[3]; + params[0] <<= reinterpret_cast<sal_uIntPtr>(spUnknown.p); + params[1] <<= (pVar->vt == VT_DISPATCH); + params[2] <<= seqTypes; + + xInit->initialize( Sequence<Any>( params, 3)); + ComPtrToWrapperMap[reinterpret_cast<sal_uInt64>(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<XInvocation>::get()|| + (aType == VOID_TYPE && seqTypes.getLength() == 0 )) + { + ret = xIntNewProxy->queryInterface(desiredType); + } + else + { + Reference<XInterface> xIntAdapter = + createAdapter(seqTypes, xIntNewProxy); + ret = xIntAdapter->queryInterface(desiredType); + } + return ret; +} +template<class T> +Reference<XInterface> UnoConversionUtilities<T>::createAdapter(const Sequence<Type>& seqTypes, + const Reference<XInterface>& 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<XInterface> xIntAdapted; + Reference<XInvocation> xInv(receiver, UNO_QUERY); + Reference<XInvocationAdapterFactory2> xAdapterFac( xIntAdapterFac, UNO_QUERY); + if( xAdapterFac.is()) + xIntAdapted= xAdapterFac->createAdapter( xInv, seqTypes); + + if( !xIntAdapted.is()) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::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<sal_uInt64,sal_uInt64>::value_type VALUE; + AdapterToWrapperMap.insert( VALUE( reinterpret_cast<sal_uInt64>(xIntAdapted.get()), reinterpret_cast<sal_uInt64>(receiver.get()))); + WrapperToAdapterMap.insert( VALUE( reinterpret_cast<sal_uInt64>(receiver.get()), reinterpret_cast<sal_uInt64>(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<class T> +bool UnoConversionUtilities<T>::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 <IJScriptValueObject> spValue; + VARIANT_BOOL varBool; + CComBSTR bstrType; + CComVariant varValue; + CComPtr<IDispatch> spDisp( varDisp.pdispVal); + if(spDisp) + { + if(SUCCEEDED( spDisp->QueryInterface( __uuidof( IJScriptValueObject), + reinterpret_cast<void**> (&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<T>::convertValueObject ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::convertValueObject !"); + } + return ret; +} + +template<class T> +void UnoConversionUtilities<T>::dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type) +{ + try + { + if( pvar->vt != VT_DISPATCH) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + IDispatchEx* pdispEx; + HRESULT hr; + if( FAILED( hr= pvar->pdispVal->QueryInterface( IID_IDispatchEx, + reinterpret_cast<void**>( &pdispEx)))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::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<OLECHAR **>(&sLength), 1, LOCALE_USER_DEFAULT, &dispid))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::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<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + if( FAILED( VariantChangeType( &result, &result, 0, VT_I4))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::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<typelib_IndirectTypeDescription*>(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<OLECHAR *>(o3tl::toW(ousIndex.getStr())); + + if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sindex , 1, LOCALE_USER_DEFAULT, &dispid))) + { + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::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<T>::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<uno_Sequence* const *>(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<void*>( 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<T>::convertValueObject ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::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<class T> +Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapperOfDim(SAFEARRAY* pArray, + unsigned int dimCount, unsigned int actDim, LONG* index, VARTYPE type, const Type& unotype) +{ + LONG lBound; + LONG uBound; + LONG nCountElements; + + SafeArrayGetLBound(pArray, actDim, &lBound); + SafeArrayGetUBound(pArray, actDim, &uBound); + nCountElements= uBound - lBound +1; + + Sequence<Any> anySeq(nCountElements); + Any* pUnoArray = anySeq.getArray(); + + for (index[actDim - 1] = lBound; index[actDim - 1] <= uBound; index[actDim - 1]++) + { + if (actDim > 1 ) + { + Sequence<Any> element = createOleArrayWrapperOfDim(pArray, dimCount, + actDim - 1, index, type, getElementTypeOfSequence(unotype)); + + pUnoArray[index[actDim - 1] - lBound].setValue(&element, cppu::UnoType<decltype(element)>::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: + 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<class T> +Type UnoConversionUtilities<T>::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<typelib_IndirectTypeDescription *>(pDescSeq)->pType); + typelib_typedescription_release(pDescSeq); + } + return retValue; +} +template<class T> +Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unoType) +{ + sal_uInt32 dim = SafeArrayGetDim(pArray); + + Sequence<Any> ret; + + if (dim > 0) + { + std::unique_ptr<LONG[]> 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<class T> +bool UnoConversionUtilities<T>::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<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, + &id); + + if( SUCCEEDED ( hr) ) + return true; + } + + return false; +} + +template<class T> +VARTYPE UnoConversionUtilities<T>::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<class T> +Sequence<Type> UnoConversionUtilities<T>::getImplementedInterfaces(IUnknown* pUnk) +{ + Sequence<Type> 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<Sequence<Any>>::get()); + Sequence<Any> seqAny; + if( anyNames >>= seqAny) + { + seqTypes.realloc( seqAny.getLength()); + auto pseqTypes = seqTypes.getArray(); + for( sal_Int32 i=0; i < seqAny.getLength(); i++) + { + OUString typeName; + seqAny[i] >>= typeName; + pseqTypes[i]= Type( TypeClass_INTERFACE, typeName); + } + } + } + } + return seqTypes; +} +template<class T> +Reference<XTypeConverter> UnoConversionUtilities<T>::getTypeConverter() +{ + if ( ! m_typeConverter.is()) + { + MutexGuard guard(getBridgeMutex()); + if ( ! m_typeConverter.is()) + { + Reference<XInterface> xIntConverter = + m_smgr->createInstance("com.sun.star.script.Converter"); + if (xIntConverter.is()) + m_typeConverter.set(xIntConverter, UNO_QUERY); + } + } + return m_typeConverter; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |