| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| #ifndef _UNO_CONVERSION_UTILITIES |
| #define _UNO_CONVERSION_UTILITIES |
| |
| #include "boost/scoped_array.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/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 "typelib/typedescription.hxx" |
| #include "ole2uno.hxx" |
| |
| #include "unotypewrapper.hxx" |
| #include <hash_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 reinterpret_cast<const sal_Unicode*>(L"com.sun.star.script.Invocation") |
| |
| |
| // classes for wrapping ole objects |
| #define IUNKNOWN_WRAPPER_IMPL 1 |
| |
| #define INTERFACE_ADAPTER_FACTORY reinterpret_cast<const sal_Unicode*>(L"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; |
| #ifdef __MINGW32__ |
| using namespace com::sun::star::bridge; |
| using namespace com::sun::star::bridge::ModelDependent; |
| #endif |
| using namespace com::sun::star::bridge::oleautomation; |
| using namespace boost; |
| namespace ole_adapter |
| { |
| extern hash_map<sal_uInt32, sal_uInt32> AdapterToWrapperMap; |
| extern hash_map<sal_uInt32, sal_uInt32> WrapperToAdapterMap; |
| typedef hash_map<sal_uInt32, sal_uInt32>::iterator IT_Wrap; |
| typedef hash_map<sal_uInt32, sal_uInt32>::iterator CIT_Wrap; |
| //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 hash_map<sal_uInt32, WeakReference<XInterface> > ComPtrToWrapperMap; |
| typedef hash_map<sal_uInt32, WeakReference<XInterface> >::iterator IT_Com; |
| typedef hash_map<sal_uInt32, WeakReference<XInterface> >::const_iterator CIT_Com; |
| |
| // Maps XInterface pointers to a weak reference of its wrapper class (i.e. |
| // InterfaceOleWrapper_Impl). 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, an 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 hash_map<sal_uInt32, WeakReference<XInterface> > UnoObjToWrapperMap; |
| typedef hash_map<sal_uInt32, WeakReference<XInterface> >::iterator IT_Uno; |
| typedef hash_map<sal_uInt32, WeakReference<XInterface> >::const_iterator CIT_Uno; |
| #ifdef __MINGW32__ |
| inline void reduceRange( Any& any); |
| #endif |
| |
| |
| |
| |
| // 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: |
| 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_smgr( xFactory), m_nComWrapperClass( comWrapperClass), m_nUnoWrapperClass( unoWrapperClass) |
| {} |
| |
| 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, sal_Bool bReduceValueRange = sal_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, sal_Bool bReduceValueRange = sal_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 sal_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 |
| sal_Bool incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLength, |
| sal_Int32 * parMultidimensionalIndex); |
| // helper function for Sequence conversion |
| size_t getOleElementSize( VARTYPE type); |
| |
| 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 o2u_anyToVariant |
| // function in the createBridge function implementation |
| sal_uInt8 m_nUnoWrapperClass; |
| 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< XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY); |
| if( xSupplier.is()) |
| { |
| sal_Int8 arId[16]; |
| rtl_getGlobalProcessId( (sal_uInt8*)arId); |
| Sequence<sal_Int8> seqId( arId, 16); |
| Any anySource; |
| anySource <<= xInt; |
| Any anyDisp= xSupplier->createBridge( anySource, seqId, UNO, OLE); |
| if( anyDisp.getValueTypeClass() == TypeClass_UNSIGNED_LONG) |
| { |
| VARIANT* pvariant= *(VARIANT**)anyDisp.getValue(); |
| HRESULT hr; |
| if (FAILED(hr = VariantCopy(pVar, pvariant))) |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] convertSelfToCom\n" |
| "VariantCopy failed! Error: ") + |
| OUString::valueOf(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= Reference<XSingleServiceFactory>( |
| m_smgrRemote->createInstance( INVOCATION_SERVICE), UNO_QUERY); |
| retVal= m_xInvocationFactoryRemote; |
| } |
| else |
| { |
| if( ! m_xInvocationFactoryLocal.is() ) |
| m_xInvocationFactoryLocal= Reference<XSingleServiceFactory>( |
| 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, sal_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, const_cast<VARIANTARG*>(pArg)))) // remove VT_BYREF |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] UnoConversionUtilities<T>::variantToAny \n" |
| "VariantCopyInd failed for reason : ") + OUString::valueOf(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 beeing 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( (void*)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((void*) & 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((void*) & 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. VBSrcript 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 = makeAny(unoSeq); |
| Any convAny = conv->convertTo(anySeq, ptype); |
| rAny = convAny; |
| } |
| catch (IllegalArgumentException& e) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]com.sun.star.lang.IllegalArgumentException " |
| "in UnoConversionUtilities<T>::variantToAny! Message: ") + |
| e.Message); |
| } |
| catch (CannotConvertException& e) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]com.sun.star.script.CannotConvertException " |
| "in UnoConversionUtilities<T>::variantToAny! Message: ") + |
| e.Message); |
| } |
| } |
| } |
| } |
| break; |
| case TypeClass_VOID: |
| rAny.setValue(NULL,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, |
| getCppuType((Sequence<Any>*) 0)); |
| } |
| 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, getCppuType( (sal_uInt64*) 0), |
| bReduceValueRange); |
| } |
| else |
| { |
| //negative value |
| variantToAny( & var, rAny, getCppuType( (sal_Int64*) 0), |
| 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(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: |
| // case TypeClass_SERVICE: break; // meta construct |
| // case TypeClass_TYPEDEF: break; |
| // case TypeClass_UNION: break; |
| // case TypeClass_MODULE: break; // module |
| // case TypeClass_EXCEPTION: break; |
| // case TypeClass_ARRAY: break; // there's no Array at the moment |
| // case TypeClass_UNKNOWN: break; |
| bCannotConvert = true; |
| break; |
| } |
| } |
| if (bCannotConvert) |
| throw CannotConvertException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::variantToAny \n" |
| "Cannot convert the value of vartype :\"") + |
| OUString::valueOf((sal_Int32) var.vt) + |
| OUSTR("\" to the expected UNO type of type class: ") + |
| OUString::valueOf((sal_Int32) ptype.getTypeClass()), |
| 0, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); |
| |
| if (bFail) |
| throw IllegalArgumentException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>:variantToAny\n" |
| "The provided VARIANT of type\" ") + OUString::valueOf((sal_Int32) var.vt) + |
| OUSTR("\" is unappropriate for conversion!"), Reference<XInterface>(), -1); |
| } |
| catch (CannotConvertException &) |
| { |
| throw; |
| } |
| catch (IllegalArgumentException &) |
| { |
| throw; |
| } |
| catch (BridgeRuntimeError &) |
| { |
| throw; |
| } |
| catch (Exception & e) |
| { |
| throw BridgeRuntimeError(OUSTR("[automation bridge] unexpected exception in " |
| "UnoConversionUtilities<T>::variantToAny ! Message : \n") + |
| e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Cannot convert the value of type :\"") + |
| rAny.getValueTypeName() + |
| OUSTR("\" to the expected Automation type of VARTYPE: ") + |
| OUString::valueOf((sal_Int32)type), |
| 0, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); |
| |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Conversion of any with ") + |
| rAny.getValueType().getTypeName() + |
| OUSTR(" to VARIANT with type: ") + OUString::valueOf((sal_Int32) type) + |
| OUSTR(" failed! Error code: ") + OUString::valueOf(hr)); |
| |
| } |
| if(FAILED(hr = VariantCopy(pVariant, &var))) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "VariantCopy failed for reason: ") + OUString::valueOf(hr)); |
| } |
| } |
| } |
| catch (IllegalArgumentException &) |
| { |
| throw; |
| } |
| catch (CannotConvertException & ) |
| { |
| throw; |
| } |
| catch (BridgeRuntimeError&) |
| { |
| throw; |
| } |
| catch(Exception & e) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Unexpected exception occurred. Message: ") + e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Unexpected exception occurred.")); |
| } |
| } |
| |
| template<class T> |
| void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny) |
| { |
| bool bIllegal = false; |
| try |
| { |
| 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() == getCppuType((Date*)0)) |
| { |
| Date d; |
| if (rAny >>= d) |
| { |
| pVariant->vt = VT_DATE; |
| pVariant->date = d.Value; |
| } |
| else |
| { |
| bIllegal = true; |
| } |
| } |
| else if(rAny.getValueType() == getCppuType((Decimal*)0)) |
| { |
| 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() == getCppuType((Currency*)0)) |
| { |
| Currency c; |
| if (rAny >>= c) |
| { |
| pVariant->vt = VT_CY; |
| pVariant->cyVal.int64 = c.Value; |
| } |
| else |
| { |
| bIllegal = true; |
| } |
| } |
| else if(rAny.getValueType() == getCppuType((SCode*)0)) |
| { |
| 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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" |
| "VariantClear failed with error:") + OUString::valueOf(hr)); |
| } |
| break; |
| } |
| case TypeClass_BOOLEAN: |
| { |
| sal_Bool value; |
| if (rAny >>= value) |
| { |
| pVariant->vt = VT_BOOL; |
| pVariant->boolVal = value == sal_True? 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 = *(sal_Unicode*) rAny.getValue(); |
| pVariant->vt = VT_I2; |
| pVariant->iVal = value; |
| break; |
| } |
| case TypeClass_STRING: |
| { |
| OUString value; |
| if (rAny >>= value) |
| { |
| pVariant->vt = VT_BSTR; |
| pVariant->bstrVal = SysAllocString(reinterpret_cast<LPCOLESTR>(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 = *(sal_Int32*) 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) == false) |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] UnoConversionUtilities<T>::anyToVariant \n" |
| "Error during conversion of UNO type to Automation object!")); |
| |
| if (FAILED(VariantCopy(pVariant, &var))) |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] UnoConversionUtilities<T>::anyToVariant \n" |
| "Unexpected error!")); |
| break; |
| } |
| default: |
| //TypeClass_SERVICE: |
| //TypeClass_EXCEPTION: |
| //When a 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_UNSIGNED_OCTET: |
| // TypeClass_UNION: |
| // TypeClass_ARRAY: |
| // TypeClass_UNSIGNED_INT: |
| // TypeClass_UNSIGNED_BYTE: |
| // TypeClass_MODULE: |
| throw CannotConvertException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" |
| "There is no conversion for this UNO type to a 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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" |
| "The provided any of type\" ") + rAny.getValueType().getTypeName() + |
| OUSTR("\" is unappropriate for conversion!"), Reference<XInterface>(), -1); |
| |
| } |
| } |
| catch (CannotConvertException & ) |
| { |
| throw; |
| } |
| catch (IllegalArgumentException & ) |
| { |
| throw; |
| } |
| catch(BridgeRuntimeError&) |
| { |
| throw; |
| } |
| catch(Exception & e) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Unexpected exception occurred. Message: ") + e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" |
| "Unexpected exception occurred. ") ); |
| } |
| } |
| |
| // Creates an SAFEARRAY of the specified element and if necessary |
| // creates a SAFEARRAY whith 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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n" |
| "The any does not contain a sequence!"), 0, 0); |
| if (elemtype == VT_NULL || elemtype == VT_EMPTY) |
| throw IllegalArgumentException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n" |
| "No element type supplied!"),0, -1); |
| SAFEARRAY* pArray= NULL; |
| // 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 |
| scoped_array<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= *(uno_Sequence* const*) rSeq.getValue(); |
| sal_Int32 dimsSeq= dims - 1; |
| |
| // arDimSeqIndizes 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 ar 0 based |
| scoped_array<sal_Int32> sarDimsSeqIndices; |
| sal_Int32* arDimsSeqIndices= NULL; |
| if( dimsSeq > 0) |
| { |
| sarDimsSeqIndices.reset(new sal_Int32[dimsSeq]); |
| arDimsSeqIndices = sarDimsSeqIndices.get(); |
| memset( arDimsSeqIndices, 0, sizeof( sal_Int32 ) * dimsSeq); |
| } |
| |
| char* psaCurrentData= (char*)pSAData; |
| |
| do |
| { |
| // Get the Sequence at the current index , see arDimsSeqIndices |
| uno_Sequence * pCurrentSeq= pMultiSeq; |
| sal_Int32 curDim=1; // 1 based |
| sal_Bool skipSeq= sal_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= *(uno_Sequence**) &pCurrentSeq->elements[ offset]; |
| curDim++; |
| } |
| else |
| { |
| // There is no Sequence at this index, so skip this index |
| skipSeq= sal_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_Int16 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= (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 an 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( ( 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> |
| sal_Bool UnoConversionUtilities<T>::incrementMultidimensionalIndex(sal_Int32 dimensions, |
| const sal_Int32 * parDimensionLengths, |
| sal_Int32 * parMultidimensionalIndex) |
| { |
| if( dimensions < 1) |
| return sal_False; |
| |
| sal_Bool ret= sal_True; |
| sal_Bool carry= sal_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= sal_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= sal_False; |
| ret= sal_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 - countains 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= (*(uno_Sequence* const *) 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= NULL; |
| rSeq.getValueTypeDescription( &pSeqDesc); |
| typelib_TypeDescriptionReference* pElementDescRef= ((typelib_IndirectTypeDescription*)pSeqDesc)->pType; |
| |
| // if the elements are Sequences than do recursion |
| if( dim < seqElementCounts.getLength() ) |
| { |
| uno_Sequence* pSeq = *(uno_Sequence* const*) rSeq.getValue(); |
| uno_Sequence** arSequences= (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 = NULL; |
| sal_uInt32 n = 0; |
| |
| if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE ) |
| throw IllegalArgumentException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper\n" |
| "The UNO argument is not a sequence"), 0, -1); |
| |
| uno_Sequence * punoSeq= *(uno_Sequence**) rSeq.getValue(); |
| |
| typelib_TypeDescriptionReference* pSeqTypeRef= rSeq.getValueTypeRef(); |
| typelib_TypeDescription* pSeqType= NULL; |
| TYPELIB_DANGER_GET( &pSeqType, pSeqTypeRef); |
| typelib_IndirectTypeDescription * pSeqIndDec= (typelib_IndirectTypeDescription*) pSeqType; |
| |
| |
| typelib_TypeDescriptionReference * pSeqElementTypeRef= pSeqIndDec->pType; |
| TYPELIB_DANGER_RELEASE( pSeqType); |
| |
| typelib_TypeDescription* pSeqElementDesc= NULL; |
| 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; |
| sal_uInt8 * pSeqData= (sal_uInt8*) 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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createUnoObjectWrapper \n" |
| "Cannot create an Automation interface for a UNO type which is not " |
| "a struct or interface!"), 0, -1); |
| |
| if (rObj.getValueTypeClass() == TypeClass_INTERFACE) |
| { |
| if (! (rObj >>= xInt)) |
| throw IllegalArgumentException( |
| OUSTR("[automation bridge] UnoConversionUtilities<T>::createUnoObjectWrapper\n " |
| "Could not create wrapper object for UNO object!"), 0, -1); |
| //If XInterface is NULL, which is a valid value, then simply return NULL. |
| if ( ! xInt.is()) |
| { |
| pVar->vt = VT_UNKNOWN; |
| pVar->punkVal = NULL; |
| return; |
| } |
| //make sure we have the main XInterface which is used with a map |
| xInt = Reference<XInterface>(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 ? |
| IT_Uno it_uno = UnoObjToWrapperMap.find( (sal_uInt32) 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 suppy an IDispatch by its own ? |
| else |
| { |
| Reference<XInterface> xIntComWrapper = xInt; |
| typedef hash_map<sal_uInt32,sal_uInt32>::iterator _IT; |
| // Adapter? then get the COM wrapper to which the adapter delegates its calls |
| _IT it= AdapterToWrapperMap.find( (sal_uInt32) 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 from the UNO object. |
| |
| // get an XInvocation or create one using the invocation service |
| Reference<XInvocation> xInv(xInt, UNO_QUERY); |
| if ( ! xInv.is()) |
| { |
| Reference<XSingleServiceFactory> xInvFactory= getInvocationFactory(rObj); |
| if (xInvFactory.is()) |
| { |
| Sequence<Any> params(1); |
| params.getArray()[0] = rObj; |
| Reference<XInterface> xInt = xInvFactory->createInstanceWithArguments(params); |
| xInv= Reference<XInvocation>(xInt, UNO_QUERY); |
| } |
| } |
| |
| if (xInv.is()) |
| { |
| Reference<XInterface> xNewWrapper = createUnoWrapperInstance(); |
| Reference<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[(sal_uInt32) xInt.get()]= xNewWrapper; |
| convertSelfToCom(xNewWrapper, pVar); |
| return; |
| } |
| } |
| } |
| |
| template<class T> |
| void UnoConversionUtilities<T>::variantToAny( const VARIANT* pVariant, Any& rAny, |
| sal_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, const_cast<VARIANTARG*>(pVariant)))) // remove VT_BYREF |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] UnoConversionUtilities<T>::variantToAny \n" |
| "VariantCopyInd failed for reason : ") + OUString::valueOf(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, getCppuType( &unoSeq)); |
| } |
| else |
| { |
| switch (var.vt) |
| { |
| case VT_EMPTY: |
| rAny.setValue(NULL, Type()); |
| break; |
| case VT_NULL: |
| rAny.setValue(NULL, Type()); |
| break; |
| case VT_I2: |
| rAny.setValue( & var.iVal, getCppuType( (sal_Int16*)0)); |
| break; |
| case VT_I4: |
| rAny.setValue( & var.lVal, getCppuType( (sal_Int32*)0)); |
| // necessary for use in JavaScript ( see "reduceRange") |
| if( bReduceValueRange) |
| reduceRange(rAny); |
| break; |
| case VT_R4: |
| rAny.setValue( & var.fltVal, getCppuType( (float*)0)); |
| break; |
| case VT_R8: |
| rAny.setValue(& var.dblVal, getCppuType( (double*)0)); |
| 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(reinterpret_cast<const sal_Unicode*>(var.bstrVal)); |
| rAny.setValue( &b, getCppuType( &b)); |
| break; |
| } |
| case VT_UNKNOWN: |
| case VT_DISPATCH: |
| { |
| //check if it is a UNO type |
| #ifdef __MINGW32__ |
| CComQIPtr<IUnoTypeWrapper, &__uuidof(IUnoTypeWrapper)> spType((IUnknown*) var.byref); |
| #else |
| CComQIPtr<IUnoTypeWrapper> spType((IUnknown*) var.byref); |
| #endif |
| if (spType) |
| { |
| CComBSTR sName; |
| if (FAILED(spType->get_Name(&sName))) |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::variantToAny \n" |
| "Failed to get the type name from a UnoTypeWrapper!")); |
| Type type; |
| if (getType(sName, type) == false) |
| { |
| throw CannotConvertException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::variantToAny \n" |
| "A UNO type with the name: ") + OUString(reinterpret_cast<const sal_Unicode*>(LPCOLESTR(sName))) + |
| OUSTR("does not exist!"), |
| 0, 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: |
| { |
| sal_Bool b= var.boolVal == VARIANT_TRUE; |
| rAny.setValue( &b, getCppuType( &b)); |
| break; |
| } |
| case VT_I1: |
| rAny.setValue( & var.cVal, getCppuType((sal_Int8*)0)); |
| break; |
| case VT_UI1: // there is no unsigned char in UNO |
| rAny.setValue( & var.bVal, getCppuType( (sal_Int8*)0)); |
| break; |
| case VT_UI2: |
| rAny.setValue( & var.uiVal, getCppuType( (sal_uInt16*)0)); |
| break; |
| case VT_UI4: |
| rAny.setValue( & var.ulVal, getCppuType( (sal_uInt32*)0)); |
| break; |
| case VT_INT: |
| rAny.setValue( & var.intVal, getCppuType( (sal_Int32*)0)); |
| break; |
| case VT_UINT: |
| rAny.setValue( & var.uintVal, getCppuType( (sal_uInt32*)0)); |
| break; |
| case VT_VOID: |
| rAny.setValue( NULL, 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 (IllegalArgumentException & ) |
| { |
| throw; |
| } |
| catch (CannotConvertException &) |
| { |
| throw; |
| } |
| catch (BridgeRuntimeError & ) |
| { |
| throw; |
| } |
| catch (Exception & e) |
| { |
| throw BridgeRuntimeError(OUSTR("[automation bridge] unexpected exception in " |
| "UnoConversionUtilities<T>::variantToAny ! Message : \n") + |
| e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] unexpected exception in " |
| "UnoConversionUtilities<T>::variantToAny !")); |
| } |
| |
| } |
| // The function converts an IUnknown* into an UNO interface or struct. The |
| // IUnknown pointer can constitute different kind of objects: |
| // 1. a wrapper of an UNO struct (the wrapper was created by this bridge) |
| // 2. a wrapper of an 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_Impl. 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 then 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 an 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> |
| #ifdef __MINGW32__ |
| Any UnoConversionUtilities<T>::createOleObjectWrapper(VARIANT* pVar, const Type& aType) |
| #else |
| Any UnoConversionUtilities<T>::createOleObjectWrapper(VARIANT* pVar, const Type& aType= Type()) |
| #endif |
| { |
| //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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" |
| "The VARIANT does not contain an object type! "), 0, -1); |
| |
| MutexGuard guard( getBridgeMutex()); |
| |
| CComPtr<IUnknown> spUnknown; |
| CComPtr<IDispatch> spDispatch; |
| |
| if (pVar->vt == VT_UNKNOWN) |
| { |
| spUnknown = pVar->punkVal; |
| if (spUnknown) |
| #ifdef __MINGW32__ |
| spUnknown->QueryInterface( IID_IDispatch, reinterpret_cast<LPVOID*>( & spDispatch.p)); |
| #else |
| spUnknown.QueryInterface( & spDispatch.p); |
| #endif |
| } |
| else if (pVar->vt == VT_DISPATCH && pVar->pdispVal != NULL) |
| { |
| CComPtr<IDispatch> spDispatch(pVar->pdispVal); |
| if (spDispatch) |
| #ifdef __MINGW32__ |
| spDispatch->QueryInterface( IID_IUnknown, reinterpret_cast<LPVOID*>( & spUnknown.p)); |
| #else |
| spDispatch.QueryInterface( & spUnknown.p); |
| #endif |
| } |
| |
| static Type VOID_TYPE= 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 = getCppuType((Reference<XInterface>*) 0); |
| break; |
| case VT_DISPATCH: |
| desiredType = getCppuType((Reference<XInvocation>*) 0); |
| break; |
| default: |
| desiredType = aType; |
| } |
| } |
| |
| // COM pointer are NULL, no wrapper required |
| if (spUnknown == NULL) |
| { |
| Reference<XInterface> xInt; |
| if( aType.getTypeClass() == TypeClass_INTERFACE) |
| ret.setValue( &xInt, aType); |
| else if( aType.getTypeClass() == TypeClass_STRUCT) |
| ret.setValue( NULL, aType); |
| else |
| ret <<= xInt; |
| return ret; |
| } |
| |
| |
| // Check if "spUnknown" is a UNO wrapper, that is an UNO object that has been |
| // passed to COM. Then it supports IUnoObjectWrapper |
| // and we extract the original UNO object. |
| #ifdef __MINGW32__ |
| CComQIPtr<IUnoObjectWrapper, &__uuidof(IUnoObjectWrapper)> spUno( spUnknown); |
| #else |
| CComQIPtr<IUnoObjectWrapper> spUno( spUnknown); |
| #endif |
| 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; |
| CIT_Com cit_currWrapper= ComPtrToWrapperMap.find( reinterpret_cast<sal_uInt32>(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. |
| CIT_Wrap it = WrapperToAdapterMap.find((sal_uInt32) 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( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" |
| "The COM object is not suitable for the UNO type: ") + |
| desiredType.getTypeName(), 0, -1); |
| } |
| else |
| { |
| //There is an adapter available |
| Reference<XInterface> xIntAdapter((XInterface*) it->second); |
| ret = xIntAdapter->queryInterface( desiredType); |
| if ( ! ret.hasValue()) |
| throw IllegalArgumentException( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" |
| "The COM object is not suitable for the UNO type: ") + |
| desiredType.getTypeName(), 0, -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 != getCppuType((Reference<XInvocation>*)0)) |
| { |
| 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( |
| OUSTR("[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]; |
| #ifdef __MINGW32__ |
| params[0] <<= reinterpret_cast<sal_uInt32>( spUnknown.p ); |
| #else |
| params[0] <<= (sal_uInt32) spUnknown.p; |
| #endif |
| sal_Bool bDisp = pVar->vt == VT_DISPATCH ? sal_True : sal_False; |
| params[1].setValue( & bDisp, getBooleanCppuType()); |
| params[2] <<= seqTypes; |
| |
| xInit->initialize( Sequence<Any>( params, 3)); |
| #ifdef __MINGW32__ |
| ComPtrToWrapperMap[reinterpret_cast<sal_uInt32>( spUnknown.p )]= xIntNewProxy; |
| #else |
| ComPtrToWrapperMap[reinterpret_cast<sal_uInt32>(spUnknown.p)]= xIntNewProxy; |
| #endif |
| |
| // 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 == getCppuType((Reference<XInvocation>*)0) || |
| (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. An 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()) |
| { |
| // 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 hash_map<sal_uInt32,sal_uInt32>::value_type VALUE; |
| AdapterToWrapperMap.insert( VALUE( (sal_uInt32) xIntAdapted.get(), (sal_uInt32) receiver.get())); |
| WrapperToAdapterMap.insert( VALUE( (sal_uInt32) receiver.get(), (sal_uInt32) xIntAdapted.get())); |
| } |
| else |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" |
| "Could not create a proxy for COM object! Creation of adapter failed.")); |
| } |
| 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 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( |
| OUSTR("[automation bridge] Conversion of ValueObject failed ")); |
| } |
| catch (BridgeRuntimeError &) |
| { |
| throw; |
| } |
| catch (Exception & e) |
| { |
| throw BridgeRuntimeError(OUSTR("[automation bridge] unexpected exception in " |
| "UnoConversionUtilities<T>::convertValueObject ! Message : \n") + |
| e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[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 |
| { |
| bool bFail = false; |
| if( pvar->vt != VT_DISPATCH) |
| throw BridgeRuntimeError(OUSTR("[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(OUSTR("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" |
| "Conversion of dispatch object to Sequence failed!")); |
| |
| DISPID dispid; |
| OUString sindex; |
| DISPPARAMS param= {0,0,0,0}; |
| CComVariant result; |
| |
| OLECHAR* sLength= L"length"; |
| |
| // Get the length of the array. Can also be obtained throu GetNextDispID. The |
| // method only returns DISPIDs of the array data. Their names are like "0", "1" etc. |
| if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sLength , 1, LOCALE_USER_DEFAULT, &dispid))) |
| throw BridgeRuntimeError(OUSTR("[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, NULL, NULL))) |
| throw BridgeRuntimeError(OUSTR("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" |
| "Conversion of dispatch object to Sequence failed!")); |
| if( FAILED( VariantChangeType( &result, &result, 0, VT_I4))) |
| throw BridgeRuntimeError(OUSTR("[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= NULL; |
| 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=NULL; |
| 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, NULL, 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::valueOf( i); |
| OLECHAR* sindex = (OLECHAR*)ousIndex.getStr(); |
| |
| if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sindex , 1, LOCALE_USER_DEFAULT, &dispid))) |
| { |
| throw BridgeRuntimeError(OUSTR("[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, NULL, NULL))) |
| { |
| throw BridgeRuntimeError(OUSTR("[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= (void*)(pArray + (i * nelementSize)); |
| |
| if( result.vt & VT_DISPATCH && typeElement == typelib_TypeClass_SEQUENCE) |
| { |
| variantToAny( &result, any, elemType, sal_False); |
| // copy the converted VARIANT, that is a Sequence to the Sequence |
| uno_Sequence * p_unoSeq= *(uno_Sequence**)any.getValue(); |
| // just copy the pointer of the uno_Sequence |
| // nelementSize should be 4 !!!! |
| memcpy( pDest, &p_unoSeq, nelementSize); |
| osl_incrementInterlockedCount( &p_unoSeq->nRefCount); |
| } |
| else // Element type is no Sequence -> do one conversion |
| { |
| variantToAny( &result, any, elemType, sal_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() == 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); |
| |
| if (bFail) |
| throw BridgeRuntimeError( |
| OUSTR("[automation bridge] Conversion of ValueObject failed ")); |
| } |
| catch (BridgeRuntimeError & ) |
| { |
| throw; |
| } |
| catch (Exception & e) |
| { |
| throw BridgeRuntimeError(OUSTR("[automation bridge] unexpected exception in " |
| "UnoConversionUtilities<T>::convertValueObject ! Message : \n") + |
| e.Message); |
| } |
| catch(...) |
| { |
| throw BridgeRuntimeError( |
| OUSTR("[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 recursivly 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) |
| { |
| HRESULT hr= S_OK; |
| 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, getCppuType(&element)); |
| } |
| 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], sal_False); |
| else |
| variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], |
| getElementTypeOfSequence(unotype), sal_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_IndirectTypeDescription* pDescSeq= NULL; |
| seqType.getDescription((typelib_TypeDescription** ) & pDescSeq); |
| retValue = Type(pDescSeq->pType); |
| typelib_typedescription_release( (typelib_TypeDescription*) 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) |
| { |
| scoped_array<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 an VARIANT has the type VT_DISPATCH it can either be an 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. |
| // An JScript has property like "0", "1", "2" etc. which represent the |
| // value at the corresponding index of the array |
| template<class T> |
| sal_Bool UnoConversionUtilities<T>::isJScriptArray(const VARIANT* rvar) |
| { |
| OSL_ENSURE( rvar->vt == VT_DISPATCH, "param is not a VT_DISPATCH"); |
| HRESULT hr; |
| OLECHAR* sindex= L"0"; |
| DISPID id; |
| if ( rvar->vt == VT_DISPATCH && rvar->pdispVal ) |
| { |
| hr= rvar->pdispVal->GetIDsOfNames( IID_NULL, &sindex, 1, |
| LOCALE_USER_DEFAULT, &id); |
| |
| if( SUCCEEDED ( hr) ) |
| return sal_True; |
| } |
| |
| return sal_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 exspect an array( SafeArray or IDispatch) of Strings. |
| Any anyNames; |
| variantToAny( &var, anyNames, getCppuType( (Sequence<Any>*) 0)); |
| Sequence<Any> 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<class T> |
| Reference<XTypeConverter> UnoConversionUtilities<T>::getTypeConverter() |
| { |
| if ( ! m_typeConverter.is()) |
| { |
| MutexGuard guard(getBridgeMutex()); |
| if ( ! m_typeConverter.is()) |
| { |
| Reference<XInterface> xIntConverter = |
| m_smgr->createInstance(OUSTR("com.sun.star.script.Converter")); |
| if (xIntConverter.is()) |
| m_typeConverter = Reference<XTypeConverter>(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. |
| |
| #ifndef _REDUCE_RANGE |
| #define _REDUCE_RANGE |
| inline void reduceRange( Any& any) |
| { |
| OSL_ASSERT( any.getValueTypeClass() == TypeClass_LONG); |
| |
| sal_Int32 value= *(sal_Int32*)any.getValue(); |
| if( value <= 0x7f && value >= -0x80) |
| {// -128 bis 127 |
| sal_Int8 charVal= static_cast<sal_Int8>( value); |
| any.setValue( &charVal, getCppuType( (sal_Int8*)0)); |
| } |
| else if( value <= 0x7fff && value >= -0x8000) |
| {// -32768 bis 32767 |
| sal_Int16 shortVal= static_cast<sal_Int16>( value); |
| any.setValue( &shortVal, getCppuType( (sal_Int16*)0)); |
| } |
| } |
| #endif |
| |
| |
| |
| } // end namespace |
| #endif |
| |