/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using com::sun::star::uno::Reference; using namespace com::sun::star::uno; using namespace com::sun::star::lang; using namespace com::sun::star::reflection; using namespace com::sun::star::beans; using namespace com::sun::star::script; using namespace com::sun::star::container; using namespace com::sun::star::bridge; using namespace cppu; // Identifiers for creating the strings for dbg_Properties constexpr OUString ID_DBG_SUPPORTEDINTERFACES = u"Dbg_SupportedInterfaces"_ustr; constexpr OUString ID_DBG_PROPERTIES = u"Dbg_Properties"_ustr; constexpr OUString ID_DBG_METHODS = u"Dbg_Methods"_ustr; char const aSeqLevelStr[] = "[]"; // Gets the default property for a uno object. Note: There is some // redirection built in. The property name specifies the name // of the default property. bool SbUnoObject::getDefaultPropName( SbUnoObject const * pUnoObj, OUString& sDfltProp ) { bool bResult = false; Reference< XDefaultProperty> xDefaultProp( pUnoObj->maTmpUnoObj, UNO_QUERY ); if ( xDefaultProp.is() ) { sDfltProp = xDefaultProp->getDefaultPropertyName(); if ( !sDfltProp.isEmpty() ) bResult = true; } return bResult; } SbxVariable* getDefaultProp( SbxVariable* pRef ) { SbxVariable* pDefaultProp = nullptr; if ( pRef->GetType() == SbxOBJECT ) { SbxObject* pObj = dynamic_cast(pRef); if (!pObj) { SbxBase* pObjVarObj = pRef->GetObject(); pObj = dynamic_cast( pObjVarObj ); } if (SbUnoObject* pUnoObj = dynamic_cast(pObj)) { pDefaultProp = pUnoObj->GetDfltProperty(); } } return pDefaultProp; } void SetSbUnoObjectDfltPropName( SbxObject* pObj ) { SbUnoObject* pUnoObj = dynamic_cast( pObj ); if ( pUnoObj ) { OUString sDfltPropName; if ( SbUnoObject::getDefaultPropName( pUnoObj, sDfltPropName ) ) { pUnoObj->SetDfltProperty( sDfltPropName ); } } } // save CoreReflection statically static Reference< XIdlReflection > getCoreReflection_Impl() { return css::reflection::theCoreReflection::get( comphelper::getProcessComponentContext()); } // save CoreReflection statically static Reference< XHierarchicalNameAccess > const & getCoreReflection_HierarchicalNameAccess_Impl() { static Reference< XHierarchicalNameAccess > xCoreReflection_HierarchicalNameAccess; if( !xCoreReflection_HierarchicalNameAccess.is() ) { Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); if( xCoreReflection.is() ) { xCoreReflection_HierarchicalNameAccess = Reference< XHierarchicalNameAccess >( xCoreReflection, UNO_QUERY ); } } return xCoreReflection_HierarchicalNameAccess; } // Hold TypeProvider statically static Reference< XHierarchicalNameAccess > const & getTypeProvider_Impl() { static Reference< XHierarchicalNameAccess > xAccess; // Do we have already CoreReflection; if not obtain it if( !xAccess.is() ) { Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); if( xContext.is() ) { xContext->getValueByName( "/singletons/com.sun.star.reflection.theTypeDescriptionManager" ) >>= xAccess; OSL_ENSURE( xAccess.is(), "### TypeDescriptionManager singleton not accessible!?" ); } if( !xAccess.is() ) { throw DeploymentException( "/singletons/com.sun.star.reflection.theTypeDescriptionManager singleton not accessible" ); } } return xAccess; } // Hold TypeConverter statically static Reference< XTypeConverter > const & getTypeConverter_Impl() { static Reference< XTypeConverter > xTypeConverter; // Do we have already CoreReflection; if not obtain it if( !xTypeConverter.is() ) { Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); if( xContext.is() ) { xTypeConverter = Converter::create(xContext); } if( !xTypeConverter.is() ) { throw DeploymentException( "com.sun.star.script.Converter service not accessible" ); } } return xTypeConverter; } // #111851 factory function to create an OLE object SbUnoObject* createOLEObject_Impl( const OUString& aType ) { static const Reference xOLEFactory = [] { Reference xFactory; Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); if( xContext.is() ) { Reference xSMgr = xContext->getServiceManager(); xFactory.set( xSMgr->createInstanceWithContext( "com.sun.star.bridge.OleObjectFactory", xContext ), UNO_QUERY ); } return xFactory; }(); SbUnoObject* pUnoObj = nullptr; if( xOLEFactory.is() ) { // some type names available in VBA can not be directly used in COM OUString aOLEType = aType; if ( aOLEType == "SAXXMLReader30" ) { aOLEType = "Msxml2.SAXXMLReader.3.0"; } Reference< XInterface > xOLEObject = xOLEFactory->createInstance( aOLEType ); if( xOLEObject.is() ) { pUnoObj = new SbUnoObject( aType, Any(xOLEObject) ); OUString sDfltPropName; if ( SbUnoObject::getDefaultPropName( pUnoObj, sDfltPropName ) ) pUnoObj->SetDfltProperty( sDfltPropName ); } } return pUnoObj; } namespace { void lcl_indent( OUStringBuffer& _inout_rBuffer, sal_Int32 _nLevel ) { while ( _nLevel-- > 0 ) { _inout_rBuffer.append( " " ); } } } static void implAppendExceptionMsg( OUStringBuffer& _inout_rBuffer, const Exception& _e, std::u16string_view _rExceptionType, sal_Int32 _nLevel ) { _inout_rBuffer.append( "\n" ); lcl_indent( _inout_rBuffer, _nLevel ); _inout_rBuffer.append( "Type: " ); if ( _rExceptionType.empty() ) _inout_rBuffer.append( "Unknown" ); else _inout_rBuffer.append( _rExceptionType ); _inout_rBuffer.append( "\n" ); lcl_indent( _inout_rBuffer, _nLevel ); _inout_rBuffer.append( "Message: " ); _inout_rBuffer.append( _e.Message ); } // construct an error message for the exception static OUString implGetExceptionMsg( const Exception& e, std::u16string_view aExceptionType_ ) { OUStringBuffer aMessageBuf; implAppendExceptionMsg( aMessageBuf, e, aExceptionType_, 0 ); return aMessageBuf.makeStringAndClear(); } static OUString implGetExceptionMsg( const Any& _rCaughtException ) { auto e = o3tl::tryAccess(_rCaughtException); OSL_PRECOND( e, "implGetExceptionMsg: illegal argument!" ); if ( !e ) { return OUString(); } return implGetExceptionMsg( *e, _rCaughtException.getValueTypeName() ); } static Any convertAny( const Any& rVal, const Type& aDestType ) { Any aConvertedVal; const Reference< XTypeConverter >& xConverter = getTypeConverter_Impl(); try { aConvertedVal = xConverter->convertTo( rVal, aDestType ); } catch( const IllegalArgumentException& ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( ::cppu::getCaughtException() ) ); return aConvertedVal; } catch( const CannotConvertException& e2 ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e2, u"com.sun.star.lang.IllegalArgumentException" ) ); return aConvertedVal; } return aConvertedVal; } // #105565 Special Object to wrap a strongly typed Uno Any // TODO: source out later static Reference TypeToIdlClass( const Type& rType ) { return getCoreReflection_Impl()->forName(rType.getTypeName()); } // Exception type unknown template< class EXCEPTION > static OUString implGetExceptionMsg( const EXCEPTION& e ) { return implGetExceptionMsg( e, cppu::UnoType::get().getTypeName() ); } static void implHandleBasicErrorException( BasicErrorException const & e ) { ErrCode nError = StarBASIC::GetSfxFromVBError( static_cast(e.ErrorCode) ); StarBASIC::Error( nError, e.ErrorMessageArgument ); } static void implHandleWrappedTargetException( const Any& _rWrappedTargetException ) { Any aExamine( _rWrappedTargetException ); // completely strip the first InvocationTargetException, its error message isn't of any // interest to the user, it just says something like "invoking the UNO method went wrong.". InvocationTargetException aInvocationError; if ( aExamine >>= aInvocationError ) aExamine = aInvocationError.TargetException; BasicErrorException aBasicError; ErrCode nError( ERRCODE_BASIC_EXCEPTION ); OUStringBuffer aMessageBuf; // strip any other WrappedTargetException instances, but this time preserve the error messages. WrappedTargetException aWrapped; sal_Int32 nLevel = 0; while ( aExamine >>= aWrapped ) { // special handling for BasicErrorException errors if ( aWrapped.TargetException >>= aBasicError ) { nError = StarBASIC::GetSfxFromVBError( static_cast(aBasicError.ErrorCode) ); aMessageBuf.append( aBasicError.ErrorMessageArgument ); aExamine.clear(); break; } // append this round's message implAppendExceptionMsg( aMessageBuf, aWrapped, aExamine.getValueTypeName(), nLevel ); if ( aWrapped.TargetException.getValueTypeClass() == TypeClass_EXCEPTION ) // there is a next chain element aMessageBuf.append( "\nTargetException:" ); // next round aExamine = aWrapped.TargetException; ++nLevel; } if ( auto e = o3tl::tryAccess(aExamine) ) { // the last element in the chain is still an exception, but no WrappedTargetException implAppendExceptionMsg( aMessageBuf, *e, aExamine.getValueTypeName(), nLevel ); } StarBASIC::Error( nError, aMessageBuf.makeStringAndClear() ); } static void implHandleAnyException( const Any& _rCaughtException ) { BasicErrorException aBasicError; WrappedTargetException aWrappedError; if ( _rCaughtException >>= aBasicError ) { implHandleBasicErrorException( aBasicError ); } else if ( _rCaughtException >>= aWrappedError ) { implHandleWrappedTargetException( _rCaughtException ); } else { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( _rCaughtException ) ); } } namespace { // NativeObjectWrapper handling struct ObjectItem { SbxObjectRef m_xNativeObj; explicit ObjectItem( SbxObject* pNativeObj ) : m_xNativeObj( pNativeObj ) {} }; } typedef std::vector< ObjectItem > NativeObjectWrapperVector; namespace { NativeObjectWrapperVector gaNativeObjectWrapperVector; } void clearNativeObjectWrapperVector() { gaNativeObjectWrapperVector.clear(); } static sal_uInt32 lcl_registerNativeObjectWrapper( SbxObject* pNativeObj ) { sal_uInt32 nIndex = gaNativeObjectWrapperVector.size(); gaNativeObjectWrapperVector.emplace_back( pNativeObj ); return nIndex; } static SbxObject* lcl_getNativeObject( sal_uInt32 nIndex ) { SbxObjectRef xRetObj; if( nIndex < gaNativeObjectWrapperVector.size() ) { ObjectItem& rItem = gaNativeObjectWrapperVector[ nIndex ]; xRetObj = rItem.m_xNativeObj; } return xRetObj.get(); } // convert from Uno to Sbx static SbxDataType unoToSbxType( TypeClass eType ) { SbxDataType eRetType = SbxVOID; switch( eType ) { case TypeClass_INTERFACE: case TypeClass_TYPE: case TypeClass_STRUCT: case TypeClass_EXCEPTION: eRetType = SbxOBJECT; break; case TypeClass_ENUM: eRetType = SbxLONG; break; case TypeClass_SEQUENCE: eRetType = SbxDataType( SbxOBJECT | SbxARRAY ); break; case TypeClass_ANY: eRetType = SbxVARIANT; break; case TypeClass_BOOLEAN: eRetType = SbxBOOL; break; case TypeClass_CHAR: eRetType = SbxCHAR; break; case TypeClass_STRING: eRetType = SbxSTRING; break; case TypeClass_FLOAT: eRetType = SbxSINGLE; break; case TypeClass_DOUBLE: eRetType = SbxDOUBLE; break; case TypeClass_BYTE: eRetType = SbxINTEGER; break; case TypeClass_SHORT: eRetType = SbxINTEGER; break; case TypeClass_LONG: eRetType = SbxLONG; break; case TypeClass_HYPER: eRetType = SbxSALINT64; break; case TypeClass_UNSIGNED_SHORT: eRetType = SbxUSHORT; break; case TypeClass_UNSIGNED_LONG: eRetType = SbxULONG; break; case TypeClass_UNSIGNED_HYPER: eRetType = SbxSALUINT64;break; default: break; } return eRetType; } static SbxDataType unoToSbxType( const Reference< XIdlClass >& xIdlClass ) { SbxDataType eRetType = SbxVOID; if( xIdlClass.is() ) { TypeClass eType = xIdlClass->getTypeClass(); eRetType = unoToSbxType( eType ); } return eRetType; } static void implSequenceToMultiDimArray( SbxDimArray*& pArray, Sequence< sal_Int32 >& indices, Sequence< sal_Int32 >& sizes, const Any& aValue, sal_Int32 dimension, bool bIsZeroIndex, Type const * pType ) { const Type& aType = aValue.getValueType(); TypeClass eTypeClass = aType.getTypeClass(); sal_Int32 dimCopy = dimension; if ( eTypeClass == TypeClass_SEQUENCE ) { Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aType ); Reference< XIdlArray > xIdlArray = xIdlTargetClass->getArray(); typelib_TypeDescription * pTD = nullptr; aType.getDescription( &pTD ); Type aElementType( reinterpret_cast(pTD)->pType ); ::typelib_typedescription_release( pTD ); sal_Int32 nLen = xIdlArray->getLen( aValue ); for ( sal_Int32 index = 0; index < nLen; ++index ) { auto pindices = indices.getArray(); Any aElementAny = xIdlArray->get( aValue, static_cast(index) ); // This detects the dimension were currently processing if ( dimCopy == dimension ) { ++dimCopy; if ( sizes.getLength() < dimCopy ) { sizes.realloc( sizes.getLength() + 1 ); sizes.getArray()[ sizes.getLength() - 1 ] = nLen; indices.realloc( indices.getLength() + 1 ); pindices = indices.getArray(); } } if ( bIsZeroIndex ) pindices[ dimCopy - 1 ] = index; else pindices[ dimCopy - 1] = index + 1; implSequenceToMultiDimArray( pArray, indices, sizes, aElementAny, dimCopy, bIsZeroIndex, &aElementType ); } } else { if ( !indices.hasElements() ) { // Should never ever get here ( indices.getLength() // should equal number of dimensions in the array ) // And that should at least be 1 ! // #QUESTION is there a better error? StarBASIC::Error( ERRCODE_BASIC_INVALID_OBJECT ); return; } SbxDataType eSbxElementType = unoToSbxType( pType ? pType->getTypeClass() : aValue.getValueTypeClass() ); if ( !pArray ) { pArray = new SbxDimArray( eSbxElementType ); sal_Int32 nIndexLen = indices.getLength(); // Dimension the array for ( sal_Int32 index = 0; index < nIndexLen; ++index ) { if ( bIsZeroIndex ) pArray->unoAddDim(0, sizes[index] - 1); else pArray->unoAddDim(1, sizes[index]); } } if ( pArray ) { auto xVar = tools::make_ref( eSbxElementType ); unoToSbxValue( xVar.get(), aValue ); sal_Int32* pIndices = indices.getArray(); pArray->Put(xVar.get(), pIndices); } } } void unoToSbxValue( SbxVariable* pVar, const Any& aValue ) { const Type& aType = aValue.getValueType(); TypeClass eTypeClass = aType.getTypeClass(); switch( eTypeClass ) { case TypeClass_TYPE: { // Map Type to IdlClass Type aType_; aValue >>= aType_; Reference xClass = TypeToIdlClass( aType_ ); Any aClassAny; aClassAny <<= xClass; // instantiate SbUnoObject SbUnoObject* pSbUnoObject = new SbUnoObject( OUString(), aClassAny ); SbxObjectRef xWrapper = static_cast(pSbUnoObject); // If the object is invalid deliver null if( !pSbUnoObject->getUnoAny().hasValue() ) { pVar->PutObject( nullptr ); } else { pVar->PutObject( xWrapper.get() ); } } break; // Interfaces and Structs must be wrapped in a SbUnoObject case TypeClass_INTERFACE: case TypeClass_STRUCT: case TypeClass_EXCEPTION: { if( eTypeClass == TypeClass_STRUCT ) { ArrayWrapper aWrap; NativeObjectWrapper aNativeObjectWrapper; if ( aValue >>= aWrap ) { SbxDimArray* pArray = nullptr; Sequence< sal_Int32 > indices; Sequence< sal_Int32 > sizes; implSequenceToMultiDimArray( pArray, indices, sizes, aWrap.Array, /*dimension*/0, aWrap.IsZeroIndex, nullptr ); if ( pArray ) { SbxDimArrayRef xArray = pArray; SbxFlagBits nFlags = pVar->GetFlags(); pVar->ResetFlag( SbxFlagBits::Fixed ); pVar->PutObject( xArray.get() ); pVar->SetFlags( nFlags ); } else pVar->PutEmpty(); break; } else if ( aValue >>= aNativeObjectWrapper ) { sal_uInt32 nIndex = 0; if( aNativeObjectWrapper.ObjectId >>= nIndex ) { SbxObject* pObj = lcl_getNativeObject( nIndex ); pVar->PutObject( pObj ); } else pVar->PutEmpty(); break; } else { SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) { oleautomation::Date aDate; if( aValue >>= aDate ) { pVar->PutDate( aDate.Value ); break; } else { oleautomation::Decimal aDecimal; if( aValue >>= aDecimal ) { pVar->PutDecimal( aDecimal ); break; } else { oleautomation::Currency aCurrency; if( aValue >>= aCurrency ) { pVar->PutCurrency( aCurrency.Value ); break; } } } } } } // instantiate a SbUnoObject SbUnoObject* pSbUnoObject = new SbUnoObject( OUString(), aValue ); //If this is called externally e.g. from the scripting //framework then there is no 'active' runtime the default property will not be set up //only a vba object will have XDefaultProp set anyway so... this //test seems a bit of overkill //if ( SbiRuntime::isVBAEnabled() ) { OUString sDfltPropName; if ( SbUnoObject::getDefaultPropName( pSbUnoObject, sDfltPropName ) ) { pSbUnoObject->SetDfltProperty( sDfltPropName ); } } SbxObjectRef xWrapper = static_cast(pSbUnoObject); // If the object is invalid deliver null if( !pSbUnoObject->getUnoAny().hasValue() ) { pVar->PutObject( nullptr ); } else { pVar->PutObject( xWrapper.get() ); } } break; case TypeClass_ENUM: { sal_Int32 nEnum = 0; enum2int( nEnum, aValue ); pVar->PutLong( nEnum ); } break; case TypeClass_SEQUENCE: { Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aType ); Reference< XIdlArray > xIdlArray = xIdlTargetClass->getArray(); sal_Int32 i, nLen = xIdlArray->getLen( aValue ); typelib_TypeDescription * pTD = nullptr; aType.getDescription( &pTD ); assert( pTD && pTD->eTypeClass == typelib_TypeClass_SEQUENCE ); Type aElementType( reinterpret_cast(pTD)->pType ); ::typelib_typedescription_release( pTD ); // build an Array in Basic SbxDimArrayRef xArray; SbxDataType eSbxElementType = unoToSbxType( aElementType.getTypeClass() ); xArray = new SbxDimArray( eSbxElementType ); if( nLen > 0 ) { xArray->unoAddDim(0, nLen - 1); // register the elements as variables for( i = 0 ; i < nLen ; i++ ) { // convert elements Any aElementAny = xIdlArray->get( aValue, static_cast(i) ); auto xVar = tools::make_ref( eSbxElementType ); unoToSbxValue( xVar.get(), aElementAny ); // put into the Array xArray->Put(xVar.get(), &i); } } else { xArray->unoAddDim(0, -1); } // return the Array SbxFlagBits nFlags = pVar->GetFlags(); pVar->ResetFlag( SbxFlagBits::Fixed ); pVar->PutObject( xArray.get() ); pVar->SetFlags( nFlags ); } break; case TypeClass_BOOLEAN: pVar->PutBool( *o3tl::forceAccess(aValue) ); break; case TypeClass_CHAR: { pVar->PutChar( *o3tl::forceAccess(aValue) ); break; } case TypeClass_STRING: { OUString val; aValue >>= val; pVar->PutString( val ); } break; case TypeClass_FLOAT: { float val = 0; aValue >>= val; pVar->PutSingle( val ); } break; case TypeClass_DOUBLE: { double val = 0; aValue >>= val; pVar->PutDouble( val ); } break; case TypeClass_BYTE: { sal_Int8 val = 0; aValue >>= val; pVar->PutInteger( val ); } break; case TypeClass_SHORT: { sal_Int16 val = 0; aValue >>= val; pVar->PutInteger( val ); } break; case TypeClass_LONG: { sal_Int32 val = 0; aValue >>= val; pVar->PutLong( val ); } break; case TypeClass_HYPER: { sal_Int64 val = 0; aValue >>= val; pVar->PutInt64( val ); } break; case TypeClass_UNSIGNED_SHORT: { sal_uInt16 val = 0; aValue >>= val; pVar->PutUShort( val ); } break; case TypeClass_UNSIGNED_LONG: { sal_uInt32 val = 0; aValue >>= val; pVar->PutULong( val ); } break; case TypeClass_UNSIGNED_HYPER: { sal_uInt64 val = 0; aValue >>= val; pVar->PutUInt64( val ); } break; default: pVar->PutEmpty(); break; } } // Deliver the reflection for Sbx types static Type getUnoTypeForSbxBaseType( SbxDataType eType ) { Type aRetType = cppu::UnoType::get(); switch( eType ) { case SbxNULL: aRetType = cppu::UnoType::get(); break; case SbxINTEGER: aRetType = cppu::UnoType::get(); break; case SbxLONG: aRetType = cppu::UnoType::get(); break; case SbxSINGLE: aRetType = cppu::UnoType::get(); break; case SbxDOUBLE: aRetType = cppu::UnoType::get(); break; case SbxCURRENCY: aRetType = cppu::UnoType::get(); break; case SbxDECIMAL: aRetType = cppu::UnoType::get(); break; case SbxDATE: { SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) aRetType = cppu::UnoType::get(); else aRetType = cppu::UnoType::get(); } break; case SbxSTRING: aRetType = cppu::UnoType::get(); break; case SbxBOOL: aRetType = cppu::UnoType::get(); break; case SbxVARIANT: aRetType = cppu::UnoType::get(); break; case SbxCHAR: aRetType = cppu::UnoType::get(); break; case SbxBYTE: aRetType = cppu::UnoType::get(); break; case SbxUSHORT: aRetType = cppu::UnoType::get(); break; case SbxULONG: aRetType = ::cppu::UnoType::get(); break; // map machine-dependent ones to long for consistency case SbxINT: aRetType = ::cppu::UnoType::get(); break; case SbxUINT: aRetType = ::cppu::UnoType::get(); break; default: break; } return aRetType; } // Converting of Sbx to Uno without a know target class for TypeClass_ANY static Type getUnoTypeForSbxValue( const SbxValue* pVal ) { Type aRetType = cppu::UnoType::get(); if( !pVal ) return aRetType; // convert SbxType to Uno SbxDataType eBaseType = pVal->SbxValue::GetType(); if( eBaseType == SbxOBJECT ) { SbxBaseRef xObj = pVal->GetObject(); if( !xObj.is() ) { aRetType = cppu::UnoType::get(); return aRetType; } if( auto pArray = dynamic_cast( xObj.get() ) ) { sal_Int32 nDims = pArray->GetDims(); Type aElementType = getUnoTypeForSbxBaseType( static_cast(pArray->GetType() & 0xfff) ); TypeClass eElementTypeClass = aElementType.getTypeClass(); // Normal case: One dimensional array sal_Int32 nLower, nUpper; if (nDims == 1 && pArray->GetDim(1, nLower, nUpper)) { if( eElementTypeClass == TypeClass_VOID || eElementTypeClass == TypeClass_ANY ) { // If all elements of the arrays are from the same type, take // this one - otherwise the whole will be considered as Any-Sequence bool bNeedsInit = true; for (sal_Int32 aIdx[1] = { nLower }; aIdx[0] <= nUpper; ++aIdx[0]) { SbxVariableRef xVar = pArray->Get(aIdx); Type aType = getUnoTypeForSbxValue( xVar.get() ); if( bNeedsInit ) { if( aType.getTypeClass() == TypeClass_VOID ) { // if only first element is void: different types -> []any // if all elements are void: []void is not allowed -> []any aElementType = cppu::UnoType::get(); break; } aElementType = aType; bNeedsInit = false; } else if( aElementType != aType ) { // different types -> AnySequence aElementType = cppu::UnoType::get(); break; } } } OUString aSeqTypeName = aSeqLevelStr + aElementType.getTypeName(); aRetType = Type( TypeClass_SEQUENCE, aSeqTypeName ); } // #i33795 Map also multi dimensional arrays to corresponding sequences else if( nDims > 1 ) { if( eElementTypeClass == TypeClass_VOID || eElementTypeClass == TypeClass_ANY ) { // For this check the array's dim structure does not matter sal_uInt32 nFlatArraySize = pArray->Count(); bool bNeedsInit = true; for( sal_uInt32 i = 0 ; i < nFlatArraySize ; i++ ) { SbxVariableRef xVar = pArray->SbxArray::Get(i); Type aType = getUnoTypeForSbxValue( xVar.get() ); if( bNeedsInit ) { if( aType.getTypeClass() == TypeClass_VOID ) { // if only first element is void: different types -> []any // if all elements are void: []void is not allowed -> []any aElementType = cppu::UnoType::get(); break; } aElementType = aType; bNeedsInit = false; } else if( aElementType != aType ) { // different types -> AnySequence aElementType = cppu::UnoType::get(); break; } } } OUStringBuffer aSeqTypeName; for(sal_Int32 iDim = 0 ; iDim < nDims ; iDim++ ) { aSeqTypeName.append(aSeqLevelStr); } aSeqTypeName.append(aElementType.getTypeName()); aRetType = Type( TypeClass_SEQUENCE, aSeqTypeName.makeStringAndClear() ); } } // No array, but ... else if( auto obj = dynamic_cast( xObj.get() ) ) { aRetType = obj->getUnoAny().getValueType(); } // SbUnoAnyObject? else if( auto any = dynamic_cast( xObj.get() ) ) { aRetType = any->getValue().getValueType(); } // Otherwise it is a No-Uno-Basic-Object -> default==deliver void } // No object, convert basic type else { if (eBaseType == SbxBYTE && pVal->GetByte() > 127) { // Basic Byte type is unsigned; cppu::UnoType corresponds to UNO boolean, // so values 128-255 are only representable starting with UNO short types eBaseType = SbxUSHORT; } aRetType = getUnoTypeForSbxBaseType( eBaseType ); } return aRetType; } // converting of Sbx to Uno without known target class for TypeClass_ANY static Any sbxToUnoValueImpl( const SbxValue* pVar, bool bBlockConversionToSmallestType = false ) { SbxDataType eBaseType = pVar->SbxValue::GetType(); if( eBaseType == SbxOBJECT ) { SbxBaseRef xObj = pVar->GetObject(); if( xObj.is() ) { if( auto obj = dynamic_cast( xObj.get() ) ) return obj->getValue(); if( auto pClassModuleObj = dynamic_cast( xObj.get() ) ) { Any aRetAny; SbModule* pClassModule = pClassModuleObj->getClassModule(); if( pClassModule->createCOMWrapperForIface( aRetAny, pClassModuleObj ) ) return aRetAny; } if( dynamic_cast( xObj.get() ) == nullptr ) { // Create NativeObjectWrapper to identify object in case of callbacks SbxObject* pObj = dynamic_cast( pVar->GetObject() ); if( pObj != nullptr ) { NativeObjectWrapper aNativeObjectWrapper; sal_uInt32 nIndex = lcl_registerNativeObjectWrapper( pObj ); aNativeObjectWrapper.ObjectId <<= nIndex; Any aRetAny; aRetAny <<= aNativeObjectWrapper; return aRetAny; } } } } Type aType = getUnoTypeForSbxValue( pVar ); TypeClass eType = aType.getTypeClass(); if( !bBlockConversionToSmallestType ) { // #79615 Choose "smallest" representation for int values // because up cast is allowed, downcast not switch( eType ) { case TypeClass_FLOAT: case TypeClass_DOUBLE: { double d = pVar->GetDouble(); if( rtl::math::approxEqual(d, floor( d )) ) { if( d >= -128 && d <= 127 ) aType = ::cppu::UnoType::get(); else if( d >= SbxMININT && d <= SbxMAXINT ) aType = ::cppu::UnoType::get(); else if( d >= -SbxMAXLNG && d <= SbxMAXLNG ) aType = ::cppu::UnoType::get(); } break; } case TypeClass_SHORT: { sal_Int16 n = pVar->GetInteger(); if( n >= -128 && n <= 127 ) aType = ::cppu::UnoType::get(); break; } case TypeClass_LONG: { sal_Int32 n = pVar->GetLong(); if( n >= -128 && n <= 127 ) aType = ::cppu::UnoType::get(); else if( n >= SbxMININT && n <= SbxMAXINT ) aType = ::cppu::UnoType::get(); break; } case TypeClass_UNSIGNED_LONG: { sal_uInt32 n = pVar->GetLong(); if( n <= SbxMAXUINT ) aType = cppu::UnoType::get(); break; } // TODO: need to add hyper types ? default: break; } } return sbxToUnoValue( pVar, aType ); } // Helper function for StepREDIMP static Any implRekMultiDimArrayToSequence( SbxDimArray* pArray, const Type& aElemType, sal_Int32 nMaxDimIndex, sal_Int32 nActualDim, sal_Int32* pActualIndices, sal_Int32* pLowerBounds, sal_Int32* pUpperBounds ) { sal_Int32 nSeqLevel = nMaxDimIndex - nActualDim + 1; OUStringBuffer aSeqTypeName; sal_Int32 i; for( i = 0 ; i < nSeqLevel ; i++ ) { aSeqTypeName.append(aSeqLevelStr); } aSeqTypeName.append(aElemType.getTypeName()); Type aSeqType( TypeClass_SEQUENCE, aSeqTypeName.makeStringAndClear() ); // Create Sequence instance Any aRetVal; Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aSeqType ); xIdlTargetClass->createObject( aRetVal ); // Alloc sequence according to array bounds sal_Int32 nUpper = pUpperBounds[nActualDim]; sal_Int32 nLower = pLowerBounds[nActualDim]; sal_Int32 nSeqSize = nUpper - nLower + 1; Reference< XIdlArray > xArray = xIdlTargetClass->getArray(); xArray->realloc( aRetVal, nSeqSize ); sal_Int32& ri = pActualIndices[nActualDim]; for( ri = nLower,i = 0 ; ri <= nUpper ; ri++,i++ ) { Any aElementVal; if( nActualDim < nMaxDimIndex ) { aElementVal = implRekMultiDimArrayToSequence( pArray, aElemType, nMaxDimIndex, nActualDim + 1, pActualIndices, pLowerBounds, pUpperBounds ); } else { SbxVariable* pSource = pArray->Get(pActualIndices); aElementVal = sbxToUnoValue( pSource, aElemType ); } try { // transfer to the sequence xArray->set( aRetVal, i, aElementVal ); } catch( const IllegalArgumentException& ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( ::cppu::getCaughtException() ) ); } catch (const IndexOutOfBoundsException&) { StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); } } return aRetVal; } // Map old interface Any sbxToUnoValue( const SbxValue* pVar ) { return sbxToUnoValueImpl( pVar ); } // function to find a global identifier in // the UnoScope and to wrap it for Sbx static bool implGetTypeByName( const OUString& rName, Type& rRetType ) { bool bSuccess = false; const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); if( xTypeAccess->hasByHierarchicalName( rName ) ) { Any aRet = xTypeAccess->getByHierarchicalName( rName ); Reference< XTypeDescription > xTypeDesc; aRet >>= xTypeDesc; if( xTypeDesc.is() ) { rRetType = Type( xTypeDesc->getTypeClass(), xTypeDesc->getName() ); bSuccess = true; } } return bSuccess; } // converting of Sbx to Uno with known target class Any sbxToUnoValue( const SbxValue* pVar, const Type& rType, Property const * pUnoProperty ) { Any aRetVal; // #94560 No conversion of empty/void for MAYBE_VOID properties if( pUnoProperty && pUnoProperty->Attributes & PropertyAttribute::MAYBEVOID ) { if( pVar->IsEmpty() ) return aRetVal; } SbxDataType eBaseType = pVar->SbxValue::GetType(); if( eBaseType == SbxOBJECT ) { SbxBaseRef xObj = pVar->GetObject(); if ( auto obj = dynamic_cast( xObj.get() ) ) { return obj->getValue(); } } TypeClass eType = rType.getTypeClass(); switch( eType ) { case TypeClass_INTERFACE: case TypeClass_STRUCT: case TypeClass_EXCEPTION: { Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( rType ); // null reference? if( pVar->IsNull() && eType == TypeClass_INTERFACE ) { Reference< XInterface > xRef; OUString aClassName = xIdlTargetClass->getName(); Type aClassType( xIdlTargetClass->getTypeClass(), aClassName ); aRetVal.setValue( &xRef, aClassType ); } else { // #112368 Special conversion for Decimal, Currency and Date if( eType == TypeClass_STRUCT ) { SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) { if( rType == cppu::UnoType::get()) { oleautomation::Decimal aDecimal; pVar->fillAutomationDecimal( aDecimal ); aRetVal <<= aDecimal; break; } else if( rType == cppu::UnoType::get()) { // assumes per previous code that ole Currency is Int64 aRetVal <<= pVar->GetInt64(); break; } else if( rType == cppu::UnoType::get()) { oleautomation::Date aDate; aDate.Value = pVar->GetDate(); aRetVal <<= aDate; break; } } } SbxBaseRef pObj = pVar->GetObject(); if( auto obj = dynamic_cast( pObj.get() ) ) { aRetVal = obj->getUnoAny(); } else if( auto structRef = dynamic_cast( pObj.get() ) ) { aRetVal = structRef->getUnoAny(); } else { // null object -> null XInterface Reference xInt; aRetVal <<= xInt; } } } break; case TypeClass_TYPE: { if( eBaseType == SbxOBJECT ) { // XIdlClass? Reference< XIdlClass > xIdlClass; SbxBaseRef pObj = pVar->GetObject(); if( auto obj = dynamic_cast( pObj.get() ) ) { Any aUnoAny = obj->getUnoAny(); aUnoAny >>= xIdlClass; } if( xIdlClass.is() ) { OUString aClassName = xIdlClass->getName(); Type aType( xIdlClass->getTypeClass(), aClassName ); aRetVal <<= aType; } } else if( eBaseType == SbxSTRING ) { OUString aTypeName = pVar->GetOUString(); Type aType; bool bSuccess = implGetTypeByName( aTypeName, aType ); if( bSuccess ) { aRetVal <<= aType; } } } break; case TypeClass_ENUM: { aRetVal = int2enum( pVar->GetLong(), rType ); } break; case TypeClass_SEQUENCE: { SbxBaseRef xObj = pVar->GetObject(); if( auto pArray = dynamic_cast( xObj.get() ) ) { sal_Int32 nDims = pArray->GetDims(); // Normal case: One dimensional array sal_Int32 nLower, nUpper; if (nDims == 1 && pArray->GetDim(1, nLower, nUpper)) { sal_Int32 nSeqSize = nUpper - nLower + 1; // create the instance of the required sequence Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( rType ); xIdlTargetClass->createObject( aRetVal ); Reference< XIdlArray > xArray = xIdlTargetClass->getArray(); xArray->realloc( aRetVal, nSeqSize ); // Element-Type OUString aClassName = xIdlTargetClass->getName(); typelib_TypeDescription * pSeqTD = nullptr; typelib_typedescription_getByName( &pSeqTD, aClassName.pData ); assert( pSeqTD ); Type aElemType( reinterpret_cast(pSeqTD)->pType ); // convert all array member and register them sal_Int32 aIdx[1]; aIdx[0] = nLower; for (sal_Int32 i = 0 ; i < nSeqSize; ++i, ++aIdx[0]) { SbxVariableRef xVar = pArray->Get(aIdx); // Convert the value of Sbx to Uno Any aAnyValue = sbxToUnoValue( xVar.get(), aElemType ); try { // insert in the sequence xArray->set( aRetVal, i, aAnyValue ); } catch( const IllegalArgumentException& ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( ::cppu::getCaughtException() ) ); } catch (const IndexOutOfBoundsException&) { StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); } } } // #i33795 Map also multi dimensional arrays to corresponding sequences else if( nDims > 1 ) { // Element-Type typelib_TypeDescription * pSeqTD = nullptr; Type aCurType( rType ); sal_Int32 nSeqLevel = 0; Type aElemType; do { OUString aTypeName = aCurType.getTypeName(); typelib_typedescription_getByName( &pSeqTD, aTypeName.pData ); assert( pSeqTD ); if( pSeqTD->eTypeClass == typelib_TypeClass_SEQUENCE ) { aCurType = Type( reinterpret_cast(pSeqTD)->pType ); nSeqLevel++; } else { aElemType = aCurType; break; } } while( true ); if( nSeqLevel == nDims ) { std::unique_ptr pLowerBounds(new sal_Int32[nDims]); std::unique_ptr pUpperBounds(new sal_Int32[nDims]); std::unique_ptr pActualIndices(new sal_Int32[nDims]); for(sal_Int32 i = 1 ; i <= nDims ; i++ ) { sal_Int32 lBound, uBound; pArray->GetDim(i, lBound, uBound); sal_Int32 j = i - 1; pActualIndices[j] = pLowerBounds[j] = lBound; pUpperBounds[j] = uBound; } aRetVal = implRekMultiDimArrayToSequence( pArray, aElemType, nDims - 1, 0, pActualIndices.get(), pLowerBounds.get(), pUpperBounds.get() ); } } } } break; // for Any use the class independent converting routine case TypeClass_ANY: { aRetVal = sbxToUnoValueImpl( pVar ); } break; case TypeClass_BOOLEAN: { aRetVal <<= pVar->GetBool(); break; } case TypeClass_CHAR: { aRetVal <<= pVar->GetChar(); break; } case TypeClass_STRING: aRetVal <<= pVar->GetOUString(); break; case TypeClass_FLOAT: aRetVal <<= pVar->GetSingle(); break; case TypeClass_DOUBLE: aRetVal <<= pVar->GetDouble(); break; case TypeClass_BYTE: { sal_Int16 nVal = pVar->GetInteger(); bool bOverflow = false; if( nVal < -128 ) { bOverflow = true; nVal = -128; } else if( nVal > 255 ) // 128..255 map to -128..-1 { bOverflow = true; nVal = 127; } if( bOverflow ) StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW ); sal_Int8 nByteVal = static_cast(nVal); aRetVal <<= nByteVal; break; } case TypeClass_SHORT: aRetVal <<= pVar->GetInteger(); break; case TypeClass_LONG: aRetVal <<= pVar->GetLong(); break; case TypeClass_HYPER: aRetVal <<= pVar->GetInt64(); break; case TypeClass_UNSIGNED_SHORT: aRetVal <<= pVar->GetUShort(); break; case TypeClass_UNSIGNED_LONG: aRetVal <<= pVar->GetULong(); break; case TypeClass_UNSIGNED_HYPER: aRetVal <<= pVar->GetUInt64(); break; default: break; } return aRetVal; } static void processAutomationParams( SbxArray* pParams, Sequence< Any >& args, sal_uInt32 nParamCount ) { AutomationNamedArgsSbxArray* pArgNamesArray = dynamic_cast( pParams ); args.realloc( nParamCount ); Any* pAnyArgs = args.getArray(); bool bBlockConversionToSmallestType = GetSbData()->pInst->IsCompatibility(); sal_uInt32 i = 0; if( pArgNamesArray ) { Sequence< OUString >& rNameSeq = pArgNamesArray->getNames(); OUString* pNames = rNameSeq.getArray(); Any aValAny; for( i = 0 ; i < nParamCount ; i++ ) { sal_uInt32 iSbx = i + 1; aValAny = sbxToUnoValueImpl(pParams->Get(iSbx), bBlockConversionToSmallestType ); OUString aParamName = pNames[iSbx]; if( !aParamName.isEmpty() ) { oleautomation::NamedArgument aNamedArgument; aNamedArgument.Name = aParamName; aNamedArgument.Value = aValAny; pAnyArgs[i] <<= aNamedArgument; } else { pAnyArgs[i] = aValAny; } } } else { for( i = 0 ; i < nParamCount ; i++ ) { pAnyArgs[i] = sbxToUnoValueImpl(pParams->Get(i + 1), bBlockConversionToSmallestType ); } } } namespace { enum class INVOKETYPE { GetProp = 0, Func }; } static Any invokeAutomationMethod( const OUString& Name, Sequence< Any > const & args, SbxArray* pParams, sal_uInt32 nParamCount, Reference< XInvocation > const & rxInvocation, INVOKETYPE invokeType ) { Sequence< sal_Int16 > OutParamIndex; Sequence< Any > OutParam; Any aRetAny; switch( invokeType ) { case INVOKETYPE::Func: aRetAny = rxInvocation->invoke( Name, args, OutParamIndex, OutParam ); break; case INVOKETYPE::GetProp: { Reference< XAutomationInvocation > xAutoInv( rxInvocation, UNO_QUERY ); aRetAny = xAutoInv->invokeGetProperty( Name, args, OutParamIndex, OutParam ); break; } default: assert(false); break; } const sal_Int16* pIndices = OutParamIndex.getConstArray(); sal_uInt32 nLen = OutParamIndex.getLength(); if( nLen ) { const Any* pNewValues = OutParam.getConstArray(); for( sal_uInt32 j = 0 ; j < nLen ; j++ ) { sal_Int16 iTarget = pIndices[ j ]; if( o3tl::make_unsigned(iTarget) >= nParamCount ) break; unoToSbxValue(pParams->Get(j + 1), pNewValues[j]); } } return aRetAny; } // Debugging help method to readout the implemented interfaces of an object static OUString Impl_GetInterfaceInfo( const Reference< XInterface >& x, const Reference< XIdlClass >& xClass, sal_uInt16 nRekLevel ) { Type aIfaceType = cppu::UnoType::get(); static Reference< XIdlClass > xIfaceClass = TypeToIdlClass( aIfaceType ); OUStringBuffer aRetStr; for( sal_uInt16 i = 0 ; i < nRekLevel ; i++ ) aRetStr.append( " " ); aRetStr.append( xClass->getName() ); OUString aClassName = xClass->getName(); Type aClassType( xClass->getTypeClass(), aClassName ); // checking if the interface is really supported if( !x->queryInterface( aClassType ).hasValue() ) { aRetStr.append( " (ERROR: Not really supported!)\n" ); } // Are there super interfaces? else { aRetStr.append( "\n" ); // get the super interfaces Sequence< Reference< XIdlClass > > aSuperClassSeq = xClass->getSuperclasses(); const Reference< XIdlClass >* pClasses = aSuperClassSeq.getConstArray(); sal_uInt32 nSuperIfaceCount = aSuperClassSeq.getLength(); for( sal_uInt32 j = 0 ; j < nSuperIfaceCount ; j++ ) { const Reference< XIdlClass >& rxIfaceClass = pClasses[j]; if( !rxIfaceClass->equals( xIfaceClass ) ) aRetStr.append( Impl_GetInterfaceInfo( x, rxIfaceClass, nRekLevel + 1 ) ); } } return aRetStr.makeStringAndClear(); } static OUString getDbgObjectNameImpl(SbUnoObject& rUnoObj) { OUString aName = rUnoObj.GetClassName(); if( aName.isEmpty() ) { Any aToInspectObj = rUnoObj.getUnoAny(); Reference< XInterface > xObj(aToInspectObj, css::uno::UNO_QUERY); if( xObj.is() ) { Reference< XServiceInfo > xServiceInfo( xObj, UNO_QUERY ); if( xServiceInfo.is() ) aName = xServiceInfo->getImplementationName(); } } return aName; } static OUString getDbgObjectName(SbUnoObject& rUnoObj) { OUString aName = getDbgObjectNameImpl(rUnoObj); if( aName.isEmpty() ) aName += "Unknown"; OUStringBuffer aRet; if( aName.getLength() > 20 ) { aRet.append( "\n" ); } aRet.append( "\"" + aName + "\":" ); return aRet.makeStringAndClear(); } OUString getBasicObjectTypeName( SbxObject* pObj ) { if (pObj) { if (SbUnoObject* pUnoObj = dynamic_cast(pObj)) { return getDbgObjectNameImpl(*pUnoObj); } else if (SbUnoStructRefObject* pUnoStructObj = dynamic_cast(pObj)) { return pUnoStructObj->GetClassName(); } } return OUString(); } namespace { bool matchesBasicTypeName( css::uno::Reference const & unoType, OUString const & basicTypeName) { if (unoType->getName().endsWithIgnoreAsciiCase(basicTypeName)) { return true; } auto const sups = unoType->getSuperclasses(); return std::any_of( sups.begin(), sups.end(), [&basicTypeName](auto const & t) { return matchesBasicTypeName(t, basicTypeName); }); } } bool checkUnoObjectType(SbUnoObject& rUnoObj, const OUString& rClass) { Any aToInspectObj = rUnoObj.getUnoAny(); // Return true for XInvocation based objects as interface type names don't count then Reference< XInvocation > xInvocation( aToInspectObj, UNO_QUERY ); if( xInvocation.is() ) { return true; } bool bResult = false; Reference< XTypeProvider > xTypeProvider( aToInspectObj, UNO_QUERY ); if( xTypeProvider.is() ) { /* Although interfaces in the ooo.vba namespace obey the IDL rules and have a leading 'X', in Basic we want to be able to do something like 'Dim wb As Workbooks' or 'Dim lb As MSForms.Label'. Here we add a leading 'X' to the class name and a leading dot to the entire type name. This results e.g. in '.XWorkbooks' or '.MSForms.XLabel' which matches the interface names 'ooo.vba.excel.XWorkbooks' or 'ooo.vba.msforms.XLabel'. */ OUString aClassName; if ( SbiRuntime::isVBAEnabled() ) { aClassName = "."; sal_Int32 nClassNameDot = rClass.lastIndexOf( '.' ); if( nClassNameDot >= 0 ) { aClassName += OUString::Concat(rClass.subView( 0, nClassNameDot + 1 )) + "X" + rClass.subView( nClassNameDot + 1 ); } else { aClassName += "X" + rClass; } } else // assume extended type declaration support for basic ( can't get here // otherwise. aClassName = rClass; Sequence< Type > aTypeSeq = xTypeProvider->getTypes(); const Type* pTypeArray = aTypeSeq.getConstArray(); sal_uInt32 nIfaceCount = aTypeSeq.getLength(); for( sal_uInt32 j = 0 ; j < nIfaceCount ; j++ ) { const Type& rType = pTypeArray[j]; Reference xClass = TypeToIdlClass( rType ); if( !xClass.is() ) { OSL_FAIL("failed to get XIdlClass for type"); break; } OUString aInterfaceName = xClass->getName(); if ( aInterfaceName == "com.sun.star.bridge.oleautomation.XAutomationObject" ) { // there is a hack in the extensions/source/ole/oleobj.cxx to return the typename of the automation object, lets check if it // matches Reference< XInvocation > xInv( aToInspectObj, UNO_QUERY ); if ( xInv.is() ) { OUString sTypeName; xInv->getValue( "$GetTypeName" ) >>= sTypeName; if ( sTypeName.isEmpty() || sTypeName == "IDispatch" ) { // can't check type, leave it pass bResult = true; } else { bResult = sTypeName == rClass; } } break; // finished checking automation object } if ( matchesBasicTypeName(xClass, aClassName) ) { bResult = true; break; } } } return bResult; } // Debugging help method to readout the implemented interfaces of an object static OUString Impl_GetSupportedInterfaces(SbUnoObject& rUnoObj) { Any aToInspectObj = rUnoObj.getUnoAny(); // allow only TypeClass interface OUStringBuffer aRet; auto x = o3tl::tryAccess>(aToInspectObj); if( !x ) { aRet.append( ID_DBG_SUPPORTEDINTERFACES + " not available.\n(TypeClass is not TypeClass_INTERFACE)\n" ); } else { Reference< XTypeProvider > xTypeProvider( *x, UNO_QUERY ); aRet.append( "Supported interfaces by object " + getDbgObjectName(rUnoObj) + "\n" ); if( xTypeProvider.is() ) { // get the interfaces of the implementation Sequence< Type > aTypeSeq = xTypeProvider->getTypes(); const Type* pTypeArray = aTypeSeq.getConstArray(); sal_uInt32 nIfaceCount = aTypeSeq.getLength(); for( sal_uInt32 j = 0 ; j < nIfaceCount ; j++ ) { const Type& rType = pTypeArray[j]; Reference xClass = TypeToIdlClass( rType ); if( xClass.is() ) { aRet.append( Impl_GetInterfaceInfo( *x, xClass, 1 ) ); } else { typelib_TypeDescription * pTD = nullptr; rType.getDescription( &pTD ); aRet.append( OUString::Concat("*** ERROR: No IdlClass for type \"") + OUString::unacquired(&pTD->pTypeName) + "\"\n*** Please check type library\n" ); } } } } return aRet.makeStringAndClear(); } // Debugging help method SbxDataType -> String static OUString Dbg_SbxDataType2String( SbxDataType eType ) { OUStringBuffer aRet; switch( +eType ) { case SbxEMPTY: aRet.append("SbxEMPTY"); break; case SbxNULL: aRet.append("SbxNULL"); break; case SbxINTEGER: aRet.append("SbxINTEGER"); break; case SbxLONG: aRet.append("SbxLONG"); break; case SbxSINGLE: aRet.append("SbxSINGLE"); break; case SbxDOUBLE: aRet.append("SbxDOUBLE"); break; case SbxCURRENCY: aRet.append("SbxCURRENCY"); break; case SbxDECIMAL: aRet.append("SbxDECIMAL"); break; case SbxDATE: aRet.append("SbxDATE"); break; case SbxSTRING: aRet.append("SbxSTRING"); break; case SbxOBJECT: aRet.append("SbxOBJECT"); break; case SbxERROR: aRet.append("SbxERROR"); break; case SbxBOOL: aRet.append("SbxBOOL"); break; case SbxVARIANT: aRet.append("SbxVARIANT"); break; case SbxDATAOBJECT: aRet.append("SbxDATAOBJECT"); break; case SbxCHAR: aRet.append("SbxCHAR"); break; case SbxBYTE: aRet.append("SbxBYTE"); break; case SbxUSHORT: aRet.append("SbxUSHORT"); break; case SbxULONG: aRet.append("SbxULONG"); break; case SbxSALINT64: aRet.append("SbxINT64"); break; case SbxSALUINT64: aRet.append("SbxUINT64"); break; case SbxINT: aRet.append("SbxINT"); break; case SbxUINT: aRet.append("SbxUINT"); break; case SbxVOID: aRet.append("SbxVOID"); break; case SbxHRESULT: aRet.append("SbxHRESULT"); break; case SbxPOINTER: aRet.append("SbxPOINTER"); break; case SbxDIMARRAY: aRet.append("SbxDIMARRAY"); break; case SbxCARRAY: aRet.append("SbxCARRAY"); break; case SbxUSERDEF: aRet.append("SbxUSERDEF"); break; case SbxLPSTR: aRet.append("SbxLPSTR"); break; case SbxLPWSTR: aRet.append("SbxLPWSTR"); break; case SbxCoreSTRING: aRet.append("SbxCoreSTRING"); break; case SbxOBJECT | SbxARRAY: aRet.append("SbxARRAY"); break; default: aRet.append("Unknown Sbx-Type!");break; } return aRet.makeStringAndClear(); } // Debugging help method to display the properties of a SbUnoObjects static OUString Impl_DumpProperties(SbUnoObject& rUnoObj) { OUStringBuffer aRet("Properties of object " + getDbgObjectName(rUnoObj)); // analyse the Uno-Infos to recognise the arrays Reference< XIntrospectionAccess > xAccess = rUnoObj.getIntrospectionAccess(); if( !xAccess.is() ) { Reference< XInvocation > xInvok = rUnoObj.getInvocation(); if( xInvok.is() ) xAccess = xInvok->getIntrospection(); } if( !xAccess.is() ) { aRet.append( "\nUnknown, no introspection available\n" ); return aRet.makeStringAndClear(); } Sequence props = xAccess->getProperties( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); sal_uInt32 nUnoPropCount = props.getLength(); const Property* pUnoProps = props.getConstArray(); SbxArray* pProps = rUnoObj.GetProperties(); sal_uInt32 nPropCount = pProps->Count(); sal_uInt32 nPropsPerLine = 1 + nPropCount / 30; for( sal_uInt32 i = 0; i < nPropCount; i++ ) { SbxVariable* pVar = pProps->Get(i); if( pVar ) { OUStringBuffer aPropStr; if( (i % nPropsPerLine) == 0 ) aPropStr.append( "\n" ); // output the type and name // Is it in Uno a sequence? SbxDataType eType = pVar->GetFullType(); bool bMaybeVoid = false; if( i < nUnoPropCount ) { const Property& rProp = pUnoProps[ i ]; // For MAYBEVOID freshly convert the type from Uno, // so not just SbxEMPTY is returned. if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) { eType = unoToSbxType( rProp.Type.getTypeClass() ); bMaybeVoid = true; } if( eType == SbxOBJECT ) { Type aType = rProp.Type; if( aType.getTypeClass() == TypeClass_SEQUENCE ) eType = SbxDataType( SbxOBJECT | SbxARRAY ); } } aPropStr.append( Dbg_SbxDataType2String( eType ) ); if( bMaybeVoid ) aPropStr.append( "/void" ); aPropStr.append( " " + pVar->GetName() ); if( i == nPropCount - 1 ) aPropStr.append( "\n" ); else aPropStr.append( "; " ); aRet.append( aPropStr ); } } return aRet.makeStringAndClear(); } // Debugging help method to display the methods of an SbUnoObjects static OUString Impl_DumpMethods(SbUnoObject& rUnoObj) { OUStringBuffer aRet("Methods of object " + getDbgObjectName(rUnoObj)); // XIntrospectionAccess, so that the types of the parameter could be outputted Reference< XIntrospectionAccess > xAccess = rUnoObj.getIntrospectionAccess(); if( !xAccess.is() ) { Reference< XInvocation > xInvok = rUnoObj.getInvocation(); if( xInvok.is() ) xAccess = xInvok->getIntrospection(); } if( !xAccess.is() ) { aRet.append( "\nUnknown, no introspection available\n" ); return aRet.makeStringAndClear(); } Sequence< Reference< XIdlMethod > > methods = xAccess->getMethods ( MethodConcept::ALL - MethodConcept::DANGEROUS ); const Reference< XIdlMethod >* pUnoMethods = methods.getConstArray(); SbxArray* pMethods = rUnoObj.GetMethods(); sal_uInt32 nMethodCount = pMethods->Count(); if( !nMethodCount ) { aRet.append( "\nNo methods found\n" ); return aRet.makeStringAndClear(); } sal_uInt32 nPropsPerLine = 1 + nMethodCount / 30; for( sal_uInt32 i = 0; i < nMethodCount; i++ ) { SbxVariable* pVar = pMethods->Get(i); if( pVar ) { if( (i % nPropsPerLine) == 0 ) aRet.append( "\n" ); // address the method const Reference< XIdlMethod >& rxMethod = pUnoMethods[i]; // Is it in Uno a sequence? SbxDataType eType = pVar->GetFullType(); if( eType == SbxOBJECT ) { Reference< XIdlClass > xClass = rxMethod->getReturnType(); if( xClass.is() && xClass->getTypeClass() == TypeClass_SEQUENCE ) eType = SbxDataType( SbxOBJECT | SbxARRAY ); } // output the name and the type aRet.append( Dbg_SbxDataType2String( eType ) + " " + pVar->GetName() + " ( " ); // the get-method mustn't have a parameter Sequence< Reference< XIdlClass > > aParamsSeq = rxMethod->getParameterTypes(); sal_uInt32 nParamCount = aParamsSeq.getLength(); const Reference< XIdlClass >* pParams = aParamsSeq.getConstArray(); if( nParamCount > 0 ) { for( sal_uInt32 j = 0; j < nParamCount; j++ ) { aRet.append ( Dbg_SbxDataType2String( unoToSbxType( pParams[ j ] ) ) ); if( j < nParamCount - 1 ) aRet.append( ", " ); } } else aRet.append( "void" ); aRet.append( " ) " ); if( i == nMethodCount - 1 ) aRet.append( "\n" ); else aRet.append( "; " ); } } return aRet.makeStringAndClear(); } // Implementation SbUnoObject void SbUnoObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) { if( bNeedIntrospection ) doIntrospection(); const SbxHint* pHint = dynamic_cast(&rHint); if( !pHint ) return; SbxVariable* pVar = pHint->GetVar(); SbxArray* pParams = pVar->GetParameters(); SbUnoProperty* pProp = dynamic_cast( pVar ); SbUnoMethod* pMeth = dynamic_cast( pVar ); if( pProp ) { bool bInvocation = pProp->isInvocationBased(); if( pHint->GetId() == SfxHintId::BasicDataWanted ) { // Test-Properties sal_Int32 nId = pProp->nId; if( nId < 0 ) { // Id == -1: Display implemented interfaces according the ClassProvider if( nId == -1 ) // Property ID_DBG_SUPPORTEDINTERFACES" { OUString aRetStr = Impl_GetSupportedInterfaces(*this); pVar->PutString( aRetStr ); } // Id == -2: output properties else if( nId == -2 ) // Property ID_DBG_PROPERTIES { // now all properties must be created implCreateAll(); OUString aRetStr = Impl_DumpProperties(*this); pVar->PutString( aRetStr ); } // Id == -3: output the methods else if( nId == -3 ) // Property ID_DBG_METHODS { // now all properties must be created implCreateAll(); OUString aRetStr = Impl_DumpMethods(*this); pVar->PutString( aRetStr ); } return; } if( !bInvocation && mxUnoAccess.is() ) { try { if ( maStructInfo ) { StructRefInfo aMember = maStructInfo->getStructMember( pProp->GetName() ); if ( aMember.isEmpty() ) { StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); } else { if ( pProp->isUnoStruct() ) { SbUnoStructRefObject* pSbUnoObject = new SbUnoStructRefObject( pProp->GetName(), std::move(aMember) ); SbxObjectRef xWrapper = static_cast(pSbUnoObject); pVar->PutObject( xWrapper.get() ); } else { Any aRetAny = aMember.getValue(); // take over the value from Uno to Sbx unoToSbxValue( pVar, aRetAny ); } return; } } // get the value Reference< XPropertySet > xPropSet( mxUnoAccess->queryAdapter( cppu::UnoType::get()), UNO_QUERY ); Any aRetAny = xPropSet->getPropertyValue( pProp->GetName() ); // The use of getPropertyValue (instead of using the index) is // suboptimal, but the refactoring to XInvocation is already pending // Otherwise it is possible to use FastPropertySet // take over the value from Uno to Sbx unoToSbxValue( pVar, aRetAny ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } } else if( bInvocation && mxInvocation.is() ) { try { sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; bool bCanBeConsideredAMethod = mxInvocation->hasMethod( pProp->GetName() ); Any aRetAny; if ( bCanBeConsideredAMethod && nParamCount ) { // Automation properties have methods, so... we need to invoke this through // XInvocation Sequence args; processAutomationParams( pParams, args, nParamCount ); aRetAny = invokeAutomationMethod( pProp->GetName(), args, pParams, nParamCount, mxInvocation, INVOKETYPE::GetProp ); } else aRetAny = mxInvocation->getValue( pProp->GetName() ); // take over the value from Uno to Sbx unoToSbxValue( pVar, aRetAny ); if( pParams && bCanBeConsideredAMethod ) pVar->SetParameters( nullptr ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } } } else if( pHint->GetId() == SfxHintId::BasicDataChanged ) { if( !bInvocation && mxUnoAccess.is() ) { if( pProp->aUnoProp.Attributes & PropertyAttribute::READONLY ) { StarBASIC::Error( ERRCODE_BASIC_PROP_READONLY ); return; } if ( maStructInfo ) { StructRefInfo aMember = maStructInfo->getStructMember( pProp->GetName() ); if ( aMember.isEmpty() ) { StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); } else { Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); aMember.setValue( aAnyValue ); } return; } // take over the value from Uno to Sbx Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); try { // set the value Reference< XPropertySet > xPropSet( mxUnoAccess->queryAdapter( cppu::UnoType::get()), UNO_QUERY ); xPropSet->setPropertyValue( pProp->GetName(), aAnyValue ); // The use of getPropertyValue (instead of using the index) is // suboptimal, but the refactoring to XInvocation is already pending // Otherwise it is possible to use FastPropertySet } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } } else if( bInvocation && mxInvocation.is() ) { // take over the value from Uno to Sbx Any aAnyValue = sbxToUnoValueImpl( pVar ); try { // set the value mxInvocation->setValue( pProp->GetName(), aAnyValue ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } } } } else if( pMeth ) { bool bInvocation = pMeth->isInvocationBased(); if( pHint->GetId() == SfxHintId::BasicDataWanted ) { // number of Parameter -1 because of Param0 == this sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; Sequence args; bool bOutParams = false; if( !bInvocation && mxUnoAccess.is() ) { // get info const Sequence& rInfoSeq = pMeth->getParamInfos(); const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); sal_uInt32 nUnoParamCount = rInfoSeq.getLength(); sal_uInt32 nAllocParamCount = nParamCount; // ignore surplus parameter; alternative: throw an error if( nParamCount > nUnoParamCount ) { nParamCount = nUnoParamCount; nAllocParamCount = nParamCount; } else if( nParamCount < nUnoParamCount ) { SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) { // Check types bool bError = false; for( sal_uInt32 i = nParamCount ; i < nUnoParamCount ; i++ ) { const ParamInfo& rInfo = pParamInfos[i]; const Reference< XIdlClass >& rxClass = rInfo.aType; if( rxClass->getTypeClass() != TypeClass_ANY ) { bError = true; StarBASIC::Error( ERRCODE_BASIC_NOT_OPTIONAL ); } } if( !bError ) nAllocParamCount = nUnoParamCount; } } if( nAllocParamCount > 0 ) { args.realloc( nAllocParamCount ); Any* pAnyArgs = args.getArray(); for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) { const ParamInfo& rInfo = pParamInfos[i]; const Reference< XIdlClass >& rxClass = rInfo.aType; css::uno::Type aType( rxClass->getTypeClass(), rxClass->getName() ); // ATTENTION: Don't forget for Sbx-Parameter the offset! pAnyArgs[i] = sbxToUnoValue(pParams->Get(i + 1), aType); // If it is not certain check whether the out-parameter are available. if( !bOutParams ) { ParamMode aParamMode = rInfo.aMode; if( aParamMode != ParamMode_IN ) bOutParams = true; } } } } else if( bInvocation && pParams && mxInvocation.is() ) { processAutomationParams( pParams, args, nParamCount ); } // call the method GetSbData()->bBlockCompilerError = true; // #106433 Block compiler errors for API calls try { if( !bInvocation && mxUnoAccess.is() ) { Any aRetAny = pMeth->m_xUnoMethod->invoke( getUnoAny(), args ); // take over the value from Uno to Sbx unoToSbxValue( pVar, aRetAny ); // Did we to copy back the Out-Parameter? if( bOutParams ) { const Any* pAnyArgs = args.getConstArray(); // get info const Sequence& rInfoSeq = pMeth->getParamInfos(); const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); sal_uInt32 j; for( j = 0 ; j < nParamCount ; j++ ) { const ParamInfo& rInfo = pParamInfos[j]; ParamMode aParamMode = rInfo.aMode; if( aParamMode != ParamMode_IN ) unoToSbxValue(pParams->Get(j + 1), pAnyArgs[j]); } } } else if( bInvocation && mxInvocation.is() ) { Any aRetAny = invokeAutomationMethod( pMeth->GetName(), args, pParams, nParamCount, mxInvocation, INVOKETYPE::Func ); unoToSbxValue( pVar, aRetAny ); } // remove parameter here, because this was not done anymore in unoToSbxValue() // for arrays if( pParams ) pVar->SetParameters( nullptr ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } GetSbData()->bBlockCompilerError = false; // #106433 Unblock compiler errors } } else SbxObject::Notify( rBC, rHint ); } SbUnoObject::SbUnoObject( const OUString& aName_, const Any& aUnoObj_ ) : SbxObject( aName_ ) , bNeedIntrospection( true ) , bNativeCOMObject( false ) { // beat out again the default properties of Sbx Remove( "Name", SbxClassType::DontCare ); Remove( "Parent", SbxClassType::DontCare ); // check the type of the objects TypeClass eType = aUnoObj_.getValueType().getTypeClass(); Reference< XInterface > x; if( eType == TypeClass_INTERFACE ) { // get the interface from the Any aUnoObj_ >>= x; if( !x.is() ) return; } // Did the object have an invocation itself? mxInvocation.set( x, UNO_QUERY ); if( mxInvocation.is() ) { // get the ExactName mxExactNameInvocation.set( mxInvocation, UNO_QUERY ); // The remainder refers only to the introspection Reference< XTypeProvider > xTypeProvider( x, UNO_QUERY ); if( !xTypeProvider.is() ) { bNeedIntrospection = false; return; } // Ignore introspection based members for COM objects to avoid // hiding of equally named COM symbols, e.g. XInvocation::getValue Reference< oleautomation::XAutomationObject > xAutomationObject( aUnoObj_, UNO_QUERY ); if( xAutomationObject.is() ) bNativeCOMObject = true; } maTmpUnoObj = aUnoObj_; //*** Define the name *** bool bFatalError = true; // Is it an interface or a struct? bool bSetClassName = false; OUString aClassName_; if( eType == TypeClass_STRUCT || eType == TypeClass_EXCEPTION ) { // Struct is Ok bFatalError = false; // insert the real name of the class if( aName_.isEmpty() ) { aClassName_ = aUnoObj_.getValueType().getTypeName(); bSetClassName = true; } StructRefInfo aThisStruct( maTmpUnoObj, maTmpUnoObj.getValueType(), 0 ); maStructInfo = std::make_shared( GetName(), aThisStruct ); } else if( eType == TypeClass_INTERFACE ) { // Interface works always through the type in the Any bFatalError = false; } if( bSetClassName ) SetClassName( aClassName_ ); // Neither interface nor Struct -> FatalError if( bFatalError ) { StarBASIC::FatalError( ERRCODE_BASIC_EXCEPTION ); return; } // pass the introspection primal on demand } SbUnoObject::~SbUnoObject() { } // pass the introspection on Demand void SbUnoObject::doIntrospection() { if( !bNeedIntrospection ) return; Reference xContext = comphelper::getProcessComponentContext(); if (!xContext.is()) return; // get the introspection service Reference xIntrospection; try { xIntrospection = theIntrospection::get(xContext); } catch ( const css::uno::DeploymentException& ) { } if (!xIntrospection.is()) return; bNeedIntrospection = false; // pass the introspection try { mxUnoAccess = xIntrospection->inspect( maTmpUnoObj ); } catch( const RuntimeException& e ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); } if( !mxUnoAccess.is() ) { // #51475 mark to indicate an invalid object (no mxMaterialHolder) return; } // get MaterialHolder from access mxMaterialHolder.set( mxUnoAccess, UNO_QUERY ); // get ExactName from access mxExactName.set( mxUnoAccess, UNO_QUERY ); } // Start of a list of all SbUnoMethod-Instances static SbUnoMethod* pFirst = nullptr; void clearUnoMethodsForBasic( StarBASIC const * pBasic ) { SbUnoMethod* pMeth = pFirst; while( pMeth ) { SbxObject* pObject = pMeth->GetParent(); if ( pObject ) { StarBASIC* pModBasic = dynamic_cast< StarBASIC* >( pObject->GetParent() ); if ( pModBasic == pBasic ) { // for now the solution is to remove the method from the list and to clear it, // but in case the element should be correctly transferred to another StarBASIC, // we should either set module parent to NULL without clearing it, or even // set the new StarBASIC as the parent of the module // pObject->SetParent( NULL ); if( pMeth == pFirst ) pFirst = pMeth->pNext; else if( pMeth->pPrev ) pMeth->pPrev->pNext = pMeth->pNext; if( pMeth->pNext ) pMeth->pNext->pPrev = pMeth->pPrev; pMeth->pPrev = nullptr; pMeth->pNext = nullptr; pMeth->SbxValue::Clear(); pObject->SbxValue::Clear(); // start from the beginning after object clearing, the cycle will end since the method is removed each time pMeth = pFirst; } else pMeth = pMeth->pNext; } else pMeth = pMeth->pNext; } } void clearUnoMethods() { SbUnoMethod* pMeth = pFirst; while( pMeth ) { pMeth->SbxValue::Clear(); pMeth = pMeth->pNext; } } SbUnoMethod::SbUnoMethod ( const OUString& aName_, SbxDataType eSbxType, Reference< XIdlMethod > const & xUnoMethod_, bool bInvocation ) : SbxMethod( aName_, eSbxType ) , mbInvocation( bInvocation ) { m_xUnoMethod = xUnoMethod_; pParamInfoSeq = nullptr; // enregister the method in a list pNext = pFirst; pPrev = nullptr; pFirst = this; if( pNext ) pNext->pPrev = this; } SbUnoMethod::~SbUnoMethod() { pParamInfoSeq.reset(); if( this == pFirst ) pFirst = pNext; else if( pPrev ) pPrev->pNext = pNext; if( pNext ) pNext->pPrev = pPrev; } SbxInfo* SbUnoMethod::GetInfo() { if( !pInfo.is() && m_xUnoMethod.is() ) { SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) { pInfo = new SbxInfo(); const Sequence& rInfoSeq = getParamInfos(); const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); sal_uInt32 nParamCount = rInfoSeq.getLength(); for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) { const ParamInfo& rInfo = pParamInfos[i]; OUString aParamName = rInfo.aName; pInfo->AddParam( aParamName, SbxVARIANT, SbxFlagBits::Read ); } } } return pInfo.get(); } const Sequence& SbUnoMethod::getParamInfos() { if (!pParamInfoSeq) { Sequence aTmp; if (m_xUnoMethod.is()) aTmp = m_xUnoMethod->getParameterInfos(); pParamInfoSeq.reset( new Sequence(aTmp) ); } return *pParamInfoSeq; } SbUnoProperty::SbUnoProperty ( const OUString& aName_, SbxDataType eSbxType, SbxDataType eRealSbxType, Property aUnoProp_, sal_Int32 nId_, bool bInvocation, bool bUnoStruct ) : SbxProperty( aName_, eSbxType ) , aUnoProp(std::move( aUnoProp_ )) , nId( nId_ ) , mbInvocation( bInvocation ) , mRealType( eRealSbxType ) , mbUnoStruct( bUnoStruct ) { // as needed establish a dummy array so that SbiRuntime::CheckArray() works static SbxArrayRef xDummyArray = new SbxArray( SbxVARIANT ); if( eSbxType & SbxARRAY ) PutObject( xDummyArray.get() ); } SbUnoProperty::~SbUnoProperty() {} SbxVariable* SbUnoObject::Find( const OUString& rName, SbxClassType t ) { static Reference< XIdlMethod > xDummyMethod; static Property aDummyProp; SbxVariable* pRes = SbxObject::Find( rName, t ); if( bNeedIntrospection ) doIntrospection(); // New 1999-03-04: Create properties on demand. Therefore search now via // IntrospectionAccess if a property or a method of the required name exist if( !pRes ) { OUString aUName( rName ); if( mxUnoAccess.is() && !bNativeCOMObject ) { if( mxExactName.is() ) { OUString aUExactName = mxExactName->getExactName( aUName ); if( !aUExactName.isEmpty() ) { aUName = aUExactName; } } if( mxUnoAccess->hasProperty( aUName, PropertyConcept::ALL - PropertyConcept::DANGEROUS ) ) { const Property& rProp = mxUnoAccess-> getProperty( aUName, PropertyConcept::ALL - PropertyConcept::DANGEROUS ); // If the property could be void the type had to be set to Variant SbxDataType eSbxType; if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) eSbxType = SbxVARIANT; else eSbxType = unoToSbxType( rProp.Type.getTypeClass() ); SbxDataType eRealSbxType = ( ( rProp.Attributes & PropertyAttribute::MAYBEVOID ) ? unoToSbxType( rProp.Type.getTypeClass() ) : eSbxType ); // create the property and superimpose it auto pProp = tools::make_ref( rProp.Name, eSbxType, eRealSbxType, rProp, 0, false, ( rProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT ) ); QuickInsert( pProp.get() ); pRes = pProp.get(); } else if( mxUnoAccess->hasMethod( aUName, MethodConcept::ALL - MethodConcept::DANGEROUS ) ) { // address the method const Reference< XIdlMethod >& rxMethod = mxUnoAccess-> getMethod( aUName, MethodConcept::ALL - MethodConcept::DANGEROUS ); // create SbUnoMethod and superimpose it auto xMethRef = tools::make_ref( rxMethod->getName(), unoToSbxType( rxMethod->getReturnType() ), rxMethod, false ); QuickInsert( xMethRef.get() ); pRes = xMethRef.get(); } // If nothing was found check via XNameAccess if( !pRes ) { try { Reference< XNameAccess > xNameAccess( mxUnoAccess->queryAdapter( cppu::UnoType::get()), UNO_QUERY ); if( xNameAccess.is() && xNameAccess->hasByName( rName ) ) { Any aAny = xNameAccess->getByName( rName ); // ATTENTION: Because of XNameAccess, the variable generated here // may not be included as a fixed property in the object and therefore // won't be stored anywhere. // If this leads to problems, it has to be created // synthetically or a class SbUnoNameAccessProperty, // which checks the existence on access and which // is disposed if the name is not found anymore. pRes = new SbxVariable( SbxVARIANT ); unoToSbxValue( pRes, aAny ); } } catch( const NoSuchElementException& e ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); } catch( const Exception& ) { // Establish so that the exception error will not be overwritten if( !pRes ) pRes = new SbxVariable( SbxVARIANT ); implHandleAnyException( ::cppu::getCaughtException() ); } } } if( !pRes && mxInvocation.is() ) { if( mxExactNameInvocation.is() ) { OUString aUExactName = mxExactNameInvocation->getExactName( aUName ); if( !aUExactName.isEmpty() ) { aUName = aUExactName; } } try { if( mxInvocation->hasProperty( aUName ) ) { // create a property and superimpose it auto xVarRef = tools::make_ref( aUName, SbxVARIANT, SbxVARIANT, aDummyProp, 0, true, false ); QuickInsert( xVarRef.get() ); pRes = xVarRef.get(); } else if( mxInvocation->hasMethod( aUName ) ) { // create SbUnoMethode and superimpose it auto xMethRef = tools::make_ref( aUName, SbxVARIANT, xDummyMethod, true ); QuickInsert( xMethRef.get() ); pRes = xMethRef.get(); } else { Reference< XDirectInvocation > xDirectInvoke( mxInvocation, UNO_QUERY ); if ( xDirectInvoke.is() && xDirectInvoke->hasMember( aUName ) ) { auto xMethRef = tools::make_ref( aUName, SbxVARIANT, xDummyMethod, true ); QuickInsert( xMethRef.get() ); pRes = xMethRef.get(); } } } catch( const RuntimeException& e ) { // Establish so that the exception error will not be overwritten if( !pRes ) pRes = new SbxVariable( SbxVARIANT ); StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); } } } // At the very end checking if the Dbg_-Properties are meant if( !pRes ) { if( rName.equalsIgnoreAsciiCase(ID_DBG_SUPPORTEDINTERFACES) || rName.equalsIgnoreAsciiCase(ID_DBG_PROPERTIES) || rName.equalsIgnoreAsciiCase(ID_DBG_METHODS) ) { // Create implCreateDbgProperties(); // Now they have to be found regular pRes = SbxObject::Find( rName, SbxClassType::DontCare ); } } return pRes; } // help method to create the dbg_-Properties void SbUnoObject::implCreateDbgProperties() { Property aProp; // Id == -1: display the implemented interfaces corresponding the ClassProvider auto xVarRef = tools::make_ref( ID_DBG_SUPPORTEDINTERFACES, SbxSTRING, SbxSTRING, aProp, -1, false, false ); QuickInsert( xVarRef.get() ); // Id == -2: output the properties xVarRef = tools::make_ref( ID_DBG_PROPERTIES, SbxSTRING, SbxSTRING, aProp, -2, false, false ); QuickInsert( xVarRef.get() ); // Id == -3: output the Methods xVarRef = tools::make_ref( ID_DBG_METHODS, SbxSTRING, SbxSTRING, aProp, -3, false, false ); QuickInsert( xVarRef.get() ); } void SbUnoObject::implCreateAll() { // throw away all existing methods and properties pMethods = tools::make_ref(); pProps = tools::make_ref(); if( bNeedIntrospection ) doIntrospection(); // get introspection Reference< XIntrospectionAccess > xAccess = mxUnoAccess; if( !xAccess.is() || bNativeCOMObject ) { if( mxInvocation.is() ) xAccess = mxInvocation->getIntrospection(); else if( bNativeCOMObject ) return; } if( !xAccess.is() ) return; // Establish properties Sequence props = xAccess->getProperties( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); sal_uInt32 nPropCount = props.getLength(); const Property* pProps_ = props.getConstArray(); sal_uInt32 i; for( i = 0 ; i < nPropCount ; i++ ) { const Property& rProp = pProps_[ i ]; // If the property could be void the type had to be set to Variant SbxDataType eSbxType; if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) eSbxType = SbxVARIANT; else eSbxType = unoToSbxType( rProp.Type.getTypeClass() ); SbxDataType eRealSbxType = ( ( rProp.Attributes & PropertyAttribute::MAYBEVOID ) ? unoToSbxType( rProp.Type.getTypeClass() ) : eSbxType ); // Create property and superimpose it auto xVarRef = tools::make_ref( rProp.Name, eSbxType, eRealSbxType, rProp, i, false, ( rProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT ) ); QuickInsert( xVarRef.get() ); } // Create Dbg_-Properties implCreateDbgProperties(); // Create methods Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods ( MethodConcept::ALL - MethodConcept::DANGEROUS ); sal_uInt32 nMethCount = aMethodSeq.getLength(); const Reference< XIdlMethod >* pMethods_ = aMethodSeq.getConstArray(); for( i = 0 ; i < nMethCount ; i++ ) { // address method const Reference< XIdlMethod >& rxMethod = pMethods_[i]; // Create SbUnoMethod and superimpose it auto xMethRef = tools::make_ref ( rxMethod->getName(), unoToSbxType( rxMethod->getReturnType() ), rxMethod, false ); QuickInsert( xMethRef.get() ); } } // output the value Any SbUnoObject::getUnoAny() { Any aRetAny; if( bNeedIntrospection ) doIntrospection(); if ( maStructInfo ) aRetAny = maTmpUnoObj; else if( mxMaterialHolder.is() ) aRetAny = mxMaterialHolder->getMaterial(); else if( mxInvocation.is() ) aRetAny <<= mxInvocation; return aRetAny; } // help method to create a Uno-Struct per CoreReflection static SbUnoObjectRef Impl_CreateUnoStruct( const OUString& aClassName ) { // get CoreReflection Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); if( !xCoreReflection.is() ) return nullptr; // search for the class Reference< XIdlClass > xClass; const Reference< XHierarchicalNameAccess >& xHarryName = getCoreReflection_HierarchicalNameAccess_Impl(); if( xHarryName.is() && xHarryName->hasByHierarchicalName( aClassName ) ) xClass = xCoreReflection->forName( aClassName ); if( !xClass.is() ) return nullptr; // Is it really a struct? TypeClass eType = xClass->getTypeClass(); if ( ( eType != TypeClass_STRUCT ) && ( eType != TypeClass_EXCEPTION ) ) return nullptr; // create an instance Any aNewAny; xClass->createObject( aNewAny ); // make a SbUnoObject out of it SbUnoObjectRef pUnoObj = new SbUnoObject( aClassName, aNewAny ); return pUnoObj; } // Factory-Class to create Uno-Structs per DIM AS NEW SbxBaseRef SbUnoFactory::Create( sal_uInt16, sal_uInt32 ) { // Via SbxId nothing works in Uno return nullptr; } SbxObjectRef SbUnoFactory::CreateObject( const OUString& rClassName ) { return Impl_CreateUnoStruct( rClassName ).get(); } // Provisional interface for the UNO-Connection // Deliver a SbxObject, that wrap a Uno-Interface SbxObjectRef GetSbUnoObject( const OUString& aName, const Any& aUnoObj_ ) { return new SbUnoObject( aName, aUnoObj_ ); } // Force creation of all properties for debugging void createAllObjectProperties( SbxObject* pObj ) { if( !pObj ) return; SbUnoObject* pUnoObj = dynamic_cast( pObj ); SbUnoStructRefObject* pUnoStructObj = dynamic_cast( pObj ); if( pUnoObj ) { pUnoObj->createAllProperties(); } else if ( pUnoStructObj ) { pUnoStructObj->createAllProperties(); } } void RTL_Impl_CreateUnoStruct( SbxArray& rPar ) { // We need 1 parameter minimum if (rPar.Count() < 2) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // get the name of the class of the struct OUString aClassName = rPar.Get(1)->GetOUString(); // try to create Struct with the same name SbUnoObjectRef xUnoObj = Impl_CreateUnoStruct( aClassName ); if( !xUnoObj.is() ) { return; } // return the object SbxVariableRef refVar = rPar.Get(0); refVar->PutObject( xUnoObj.get() ); } void RTL_Impl_CreateUnoService( SbxArray& rPar ) { // We need 1 Parameter minimum if (rPar.Count() < 2) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // get the name of the class of the struct OUString aServiceName = rPar.Get(1)->GetOUString(); // search for the service and instantiate it Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); Reference< XInterface > xInterface; try { xInterface = xFactory->createInstance( aServiceName ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } SbxVariableRef refVar = rPar.Get(0); if( xInterface.is() ) { // Create a SbUnoObject out of it and return it SbUnoObjectRef xUnoObj = new SbUnoObject( aServiceName, Any(xInterface) ); if( xUnoObj->getUnoAny().hasValue() ) { // return the object refVar->PutObject( xUnoObj.get() ); } else { refVar->PutObject( nullptr ); } } else { refVar->PutObject( nullptr ); } } void RTL_Impl_CreateUnoServiceWithArguments( SbxArray& rPar ) { // We need 2 parameter minimum if (rPar.Count() < 3) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // get the name of the class of the struct OUString aServiceName = rPar.Get(1)->GetOUString(); Any aArgAsAny = sbxToUnoValue(rPar.Get(2), cppu::UnoType>::get() ); Sequence< Any > aArgs; aArgAsAny >>= aArgs; // search for the service and instantiate it Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); Reference< XInterface > xInterface; try { xInterface = xFactory->createInstanceWithArguments( aServiceName, aArgs ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } SbxVariableRef refVar = rPar.Get(0); if( xInterface.is() ) { // Create a SbUnoObject out of it and return it SbUnoObjectRef xUnoObj = new SbUnoObject( aServiceName, Any(xInterface) ); if( xUnoObj->getUnoAny().hasValue() ) { // return the object refVar->PutObject( xUnoObj.get() ); } else { refVar->PutObject( nullptr ); } } else { refVar->PutObject( nullptr ); } } void RTL_Impl_GetProcessServiceManager( SbxArray& rPar ) { SbxVariableRef refVar = rPar.Get(0); // get the global service manager Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); // Create a SbUnoObject out of it and return it SbUnoObjectRef xUnoObj = new SbUnoObject( "ProcessServiceManager", Any(xFactory) ); refVar->PutObject( xUnoObj.get() ); } void RTL_Impl_HasInterfaces( SbxArray& rPar ) { // We need 2 parameter minimum sal_uInt32 nParCount = rPar.Count(); if( nParCount < 3 ) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // variable for the return value SbxVariableRef refVar = rPar.Get(0); refVar->PutBool( false ); // get the Uno-Object SbxBaseRef pObj = rPar.Get(1)->GetObject(); auto obj = dynamic_cast( pObj.get() ); if( obj == nullptr ) { return; } Any aAny = obj->getUnoAny(); auto x = o3tl::tryAccess>(aAny); if( !x ) { return; } // get CoreReflection Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); if( !xCoreReflection.is() ) { return; } for( sal_uInt32 i = 2 ; i < nParCount ; i++ ) { // get the name of the interface of the struct OUString aIfaceName = rPar.Get(i)->GetOUString(); // search for the class Reference< XIdlClass > xClass = xCoreReflection->forName( aIfaceName ); if( !xClass.is() ) { return; } // check if the interface will be supported OUString aClassName = xClass->getName(); Type aClassType( xClass->getTypeClass(), aClassName ); if( !(*x)->queryInterface( aClassType ).hasValue() ) { return; } } // Everything works; then return TRUE refVar->PutBool( true ); } void RTL_Impl_IsUnoStruct( SbxArray& rPar ) { // We need 1 parameter minimum if (rPar.Count() < 2) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // variable for the return value SbxVariableRef refVar = rPar.Get(0); refVar->PutBool( false ); // get the Uno-Object SbxVariableRef xParam = rPar.Get(1); if( !xParam->IsObject() ) { return; } SbxBaseRef pObj = xParam->GetObject(); auto obj = dynamic_cast( pObj.get() ); if( obj == nullptr ) { return; } Any aAny = obj->getUnoAny(); TypeClass eType = aAny.getValueType().getTypeClass(); if( eType == TypeClass_STRUCT ) { refVar->PutBool( true ); } } void RTL_Impl_EqualUnoObjects( SbxArray& rPar ) { if (rPar.Count() < 3) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // variable for the return value SbxVariableRef refVar = rPar.Get(0); refVar->PutBool( false ); // get the Uno-Objects SbxVariableRef xParam1 = rPar.Get(1); if( !xParam1->IsObject() ) { return; } SbxBaseRef pObj1 = xParam1->GetObject(); auto obj1 = dynamic_cast( pObj1.get() ); if( obj1 == nullptr ) { return; } Any aAny1 = obj1->getUnoAny(); TypeClass eType1 = aAny1.getValueType().getTypeClass(); if( eType1 != TypeClass_INTERFACE ) { return; } Reference< XInterface > x1; aAny1 >>= x1; SbxVariableRef xParam2 = rPar.Get(2); if( !xParam2->IsObject() ) { return; } SbxBaseRef pObj2 = xParam2->GetObject(); auto obj2 = dynamic_cast( pObj2.get() ); if( obj2 == nullptr ) { return; } Any aAny2 = obj2->getUnoAny(); TypeClass eType2 = aAny2.getValueType().getTypeClass(); if( eType2 != TypeClass_INTERFACE ) { return; } Reference< XInterface > x2; aAny2 >>= x2; if( x1 == x2 ) { refVar->PutBool( true ); } } // helper wrapper function to interact with TypeProvider and // XTypeDescriptionEnumerationAccess. // if it fails for whatever reason // returned Reference<> be null e.g. .is() will be false static Reference< XTypeDescriptionEnumeration > getTypeDescriptorEnumeration( const OUString& sSearchRoot, const Sequence< TypeClass >& types, TypeDescriptionSearchDepth depth ) { Reference< XTypeDescriptionEnumeration > xEnum; Reference< XTypeDescriptionEnumerationAccess> xTypeEnumAccess( getTypeProvider_Impl(), UNO_QUERY ); if ( xTypeEnumAccess.is() ) { try { xEnum = xTypeEnumAccess->createTypeDescriptionEnumeration( sSearchRoot, types, depth ); } catch(const NoSuchTypeNameException& /*nstne*/ ) {} catch(const InvalidTypeNameException& /*nstne*/ ) {} } return xEnum; } VBAConstantHelper& VBAConstantHelper::instance() { static VBAConstantHelper aHelper; return aHelper; } void VBAConstantHelper::init() { if ( isInited ) return; Reference< XTypeDescriptionEnumeration > xEnum = getTypeDescriptorEnumeration( "ooo.vba", {TypeClass_CONSTANTS}, TypeDescriptionSearchDepth_INFINITE ); if ( !xEnum.is()) { return; //NULL; } while ( xEnum->hasMoreElements() ) { Reference< XConstantsTypeDescription > xConstants( xEnum->nextElement(), UNO_QUERY ); if ( xConstants.is() ) { // store constant group name OUString sFullName = xConstants->getName(); sal_Int32 indexLastDot = sFullName.lastIndexOf('.'); OUString sLeafName( sFullName ); if ( indexLastDot > -1 ) { sLeafName = sFullName.copy( indexLastDot + 1); } aConstCache.push_back( sLeafName ); // assume constant group names are unique const Sequence< Reference< XConstantTypeDescription > > aConsts = xConstants->getConstants(); for (const auto& ctd : aConsts) { // store constant member name sFullName = ctd->getName(); indexLastDot = sFullName.lastIndexOf('.'); sLeafName = sFullName; if ( indexLastDot > -1 ) { sLeafName = sFullName.copy( indexLastDot + 1); } aConstHash[ sLeafName.toAsciiLowerCase() ] = ctd->getConstantValue(); } } } isInited = true; } bool VBAConstantHelper::isVBAConstantType( std::u16string_view rName ) { init(); bool bConstant = false; for (auto const& elem : aConstCache) { if( o3tl::equalsIgnoreAsciiCase(rName, elem) ) { bConstant = true; break; } } return bConstant; } SbxVariable* VBAConstantHelper::getVBAConstant( const OUString& rName ) { SbxVariable* pConst = nullptr; init(); auto it = aConstHash.find( rName.toAsciiLowerCase() ); if ( it != aConstHash.end() ) { pConst = new SbxVariable( SbxVARIANT ); pConst->SetName( rName ); unoToSbxValue( pConst, it->second ); } return pConst; } // Function to search for a global identifier in the // UnoScope and to wrap it for Sbx SbUnoClass* findUnoClass( const OUString& rName ) { // #105550 Check if module exists SbUnoClass* pUnoClass = nullptr; const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); if( xTypeAccess->hasByHierarchicalName( rName ) ) { Any aRet = xTypeAccess->getByHierarchicalName( rName ); Reference< XTypeDescription > xTypeDesc; aRet >>= xTypeDesc; if( xTypeDesc.is() ) { TypeClass eTypeClass = xTypeDesc->getTypeClass(); if( eTypeClass == TypeClass_MODULE || eTypeClass == TypeClass_CONSTANTS ) { pUnoClass = new SbUnoClass( rName ); } } } return pUnoClass; } SbxVariable* SbUnoClass::Find( const OUString& rName, SbxClassType ) { SbxVariable* pRes = SbxObject::Find( rName, SbxClassType::Variable ); // If nothing were located the submodule isn't known yet if( !pRes ) { // If it is already a class, ask for the field if( m_xClass.is() ) { // Is it a field(?) Reference< XIdlField > xField = m_xClass->getField( rName ); if( xField.is() ) { try { Any aAny = xField->get( {} ); //TODO: does this make sense? // Convert to Sbx pRes = new SbxVariable( SbxVARIANT ); pRes->SetName( rName ); unoToSbxValue( pRes, aAny ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } } } else { // expand fully qualified name OUString aNewName = GetName() + "." + rName; // get CoreReflection Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); if( xCoreReflection.is() ) { // Is it a constant? Reference< XHierarchicalNameAccess > xHarryName( xCoreReflection, UNO_QUERY ); if( xHarryName.is() ) { try { Any aValue = xHarryName->getByHierarchicalName( aNewName ); TypeClass eType = aValue.getValueType().getTypeClass(); // Interface located? Then it is a class if( eType == TypeClass_INTERFACE ) { Reference< XIdlClass > xClass( aValue, UNO_QUERY ); if( xClass.is() ) { pRes = new SbxVariable( SbxVARIANT ); SbxObjectRef xWrapper = static_cast(new SbUnoClass( aNewName, xClass )); pRes->PutObject( xWrapper.get() ); } } else { pRes = new SbxVariable( SbxVARIANT ); unoToSbxValue( pRes, aValue ); } } catch( const NoSuchElementException& ) { } } // Otherwise take it again as class if( !pRes ) { SbUnoClass* pNewClass = findUnoClass( aNewName ); if( pNewClass ) { pRes = new SbxVariable( SbxVARIANT ); SbxObjectRef xWrapper = static_cast(pNewClass); pRes->PutObject( xWrapper.get() ); } } // A UNO service? if( !pRes ) { SbUnoService* pUnoService = findUnoService( aNewName ); if( pUnoService ) { pRes = new SbxVariable( SbxVARIANT ); SbxObjectRef xWrapper = static_cast(pUnoService); pRes->PutObject( xWrapper.get() ); } } // A UNO singleton? if( !pRes ) { SbUnoSingleton* pUnoSingleton = findUnoSingleton( aNewName ); if( pUnoSingleton ) { pRes = new SbxVariable( SbxVARIANT ); SbxObjectRef xWrapper = static_cast(pUnoSingleton); pRes->PutObject( xWrapper.get() ); } } } } if( pRes ) { pRes->SetName( rName ); // Insert variable, so that it could be found later QuickInsert( pRes ); // Take us out as listener at once, // the values are all constant if( pRes->IsBroadcaster() ) EndListening( pRes->GetBroadcaster(), true ); } } return pRes; } SbUnoService* findUnoService( const OUString& rName ) { SbUnoService* pSbUnoService = nullptr; const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); if( xTypeAccess->hasByHierarchicalName( rName ) ) { Any aRet = xTypeAccess->getByHierarchicalName( rName ); Reference< XTypeDescription > xTypeDesc; aRet >>= xTypeDesc; if( xTypeDesc.is() ) { TypeClass eTypeClass = xTypeDesc->getTypeClass(); if( eTypeClass == TypeClass_SERVICE ) { Reference< XServiceTypeDescription2 > xServiceTypeDesc( xTypeDesc, UNO_QUERY ); if( xServiceTypeDesc.is() ) pSbUnoService = new SbUnoService( rName, xServiceTypeDesc ); } } } return pSbUnoService; } SbxVariable* SbUnoService::Find( const OUString& rName, SbxClassType ) { SbxVariable* pRes = SbxObject::Find( rName, SbxClassType::Method ); if( !pRes ) { // If it is already a class ask for a field if( m_bNeedsInit && m_xServiceTypeDesc.is() ) { m_bNeedsInit = false; Sequence< Reference< XServiceConstructorDescription > > aSCDSeq = m_xServiceTypeDesc->getConstructors(); const Reference< XServiceConstructorDescription >* pCtorSeq = aSCDSeq.getConstArray(); int nCtorCount = aSCDSeq.getLength(); for( int i = 0 ; i < nCtorCount ; ++i ) { Reference< XServiceConstructorDescription > xCtor = pCtorSeq[i]; OUString aName( xCtor->getName() ); if( aName.isEmpty() ) { if( xCtor->isDefaultConstructor() ) { aName = "create"; } } if( !aName.isEmpty() ) { // Create and insert SbUnoServiceCtor SbxVariableRef xSbCtorRef = new SbUnoServiceCtor( aName, xCtor ); QuickInsert( xSbCtorRef.get() ); } } pRes = SbxObject::Find( rName, SbxClassType::Method ); } } return pRes; } void SbUnoService::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) { const SbxHint* pHint = dynamic_cast(&rHint); if( !pHint ) return; SbxVariable* pVar = pHint->GetVar(); SbxArray* pParams = pVar->GetParameters(); SbUnoServiceCtor* pUnoCtor = dynamic_cast( pVar ); if( pUnoCtor && pHint->GetId() == SfxHintId::BasicDataWanted ) { // Parameter count -1 because of Param0 == this sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; Sequence args; Reference< XServiceConstructorDescription > xCtor = pUnoCtor->getServiceCtorDesc(); Sequence< Reference< XParameter > > aParameterSeq = xCtor->getParameters(); const Reference< XParameter >* pParameterSeq = aParameterSeq.getConstArray(); sal_uInt32 nUnoParamCount = aParameterSeq.getLength(); // Default: Ignore not needed parameters bool bParameterError = false; // Is the last parameter a rest parameter? bool bRestParameterMode = false; if( nUnoParamCount > 0 ) { Reference< XParameter > xLastParam = pParameterSeq[ nUnoParamCount - 1 ]; if( xLastParam.is() ) { if( xLastParam->isRestParameter() ) bRestParameterMode = true; } } // Too many parameters with context as first parameter? sal_uInt32 nSbxParameterOffset = 1; sal_uInt32 nParameterOffsetByContext = 0; Reference < XComponentContext > xFirstParamContext; if( nParamCount > nUnoParamCount ) { // Check if first parameter is a context and use it // then in createInstanceWithArgumentsAndContext Any aArg0 = sbxToUnoValue(pParams->Get(nSbxParameterOffset)); if( (aArg0 >>= xFirstParamContext) && xFirstParamContext.is() ) nParameterOffsetByContext = 1; } sal_uInt32 nEffectiveParamCount = nParamCount - nParameterOffsetByContext; sal_uInt32 nAllocParamCount = nEffectiveParamCount; if( nEffectiveParamCount > nUnoParamCount ) { if( !bRestParameterMode ) { nEffectiveParamCount = nUnoParamCount; nAllocParamCount = nUnoParamCount; } } // Not enough parameters? else if( nUnoParamCount > nEffectiveParamCount ) { // RestParameterMode only helps if one (the last) parameter is missing int nDiff = nUnoParamCount - nEffectiveParamCount; if( !bRestParameterMode || nDiff > 1 ) { bParameterError = true; StarBASIC::Error( ERRCODE_BASIC_NOT_OPTIONAL ); } } if( !bParameterError ) { bool bOutParams = false; if( nAllocParamCount > 0 ) { args.realloc( nAllocParamCount ); Any* pAnyArgs = args.getArray(); for( sal_uInt32 i = 0 ; i < nEffectiveParamCount ; i++ ) { sal_uInt32 iSbx = i + nSbxParameterOffset + nParameterOffsetByContext; // bRestParameterMode allows nEffectiveParamCount > nUnoParamCount Reference< XParameter > xParam; if( i < nUnoParamCount ) { xParam = pParameterSeq[i]; if( !xParam.is() ) continue; Reference< XTypeDescription > xParamTypeDesc = xParam->getType(); if( !xParamTypeDesc.is() ) continue; css::uno::Type aType( xParamTypeDesc->getTypeClass(), xParamTypeDesc->getName() ); // sbx parameter needs offset 1 pAnyArgs[i] = sbxToUnoValue(pParams->Get(iSbx), aType); // Check for out parameter if not already done if( !bOutParams && xParam->isOut() ) bOutParams = true; } else { pAnyArgs[i] = sbxToUnoValue(pParams->Get(iSbx)); } } } // "Call" ctor using createInstanceWithArgumentsAndContext Reference < XComponentContext > xContext( xFirstParamContext.is() ? xFirstParamContext : comphelper::getProcessComponentContext() ); Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); Any aRetAny; OUString aServiceName = GetName(); Reference < XInterface > xRet; try { xRet = xServiceMgr->createInstanceWithArgumentsAndContext( aServiceName, args, xContext ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } aRetAny <<= xRet; unoToSbxValue( pVar, aRetAny ); // Copy back out parameters? if( bOutParams ) { const Any* pAnyArgs = args.getConstArray(); for( sal_uInt32 j = 0 ; j < nUnoParamCount ; j++ ) { Reference< XParameter > xParam = pParameterSeq[j]; if( !xParam.is() ) continue; if( xParam->isOut() ) unoToSbxValue(pParams->Get(j + 1), pAnyArgs[j]); } } } } else SbxObject::Notify( rBC, rHint ); } SbUnoServiceCtor::SbUnoServiceCtor( const OUString& aName_, Reference< XServiceConstructorDescription > const & xServiceCtorDesc ) : SbxMethod( aName_, SbxOBJECT ) , m_xServiceCtorDesc( xServiceCtorDesc ) { } SbUnoServiceCtor::~SbUnoServiceCtor() { } SbxInfo* SbUnoServiceCtor::GetInfo() { return nullptr; } SbUnoSingleton* findUnoSingleton( const OUString& rName ) { SbUnoSingleton* pSbUnoSingleton = nullptr; const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); if( xTypeAccess->hasByHierarchicalName( rName ) ) { Any aRet = xTypeAccess->getByHierarchicalName( rName ); Reference< XTypeDescription > xTypeDesc; aRet >>= xTypeDesc; if( xTypeDesc.is() ) { TypeClass eTypeClass = xTypeDesc->getTypeClass(); if( eTypeClass == TypeClass_SINGLETON ) { Reference< XSingletonTypeDescription > xSingletonTypeDesc( xTypeDesc, UNO_QUERY ); if( xSingletonTypeDesc.is() ) pSbUnoSingleton = new SbUnoSingleton( rName ); } } } return pSbUnoSingleton; } SbUnoSingleton::SbUnoSingleton( const OUString& aName_ ) : SbxObject( aName_ ) { SbxVariableRef xGetMethodRef = new SbxMethod( "get", SbxOBJECT ); QuickInsert( xGetMethodRef.get() ); } void SbUnoSingleton::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) { const SbxHint* pHint = dynamic_cast(&rHint); if( pHint ) { SbxVariable* pVar = pHint->GetVar(); SbxArray* pParams = pVar->GetParameters(); sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; sal_uInt32 nAllowedParamCount = 1; Reference < XComponentContext > xContextToUse; if( nParamCount > 0 ) { // Check if first parameter is a context and use it then Reference < XComponentContext > xFirstParamContext; Any aArg1 = sbxToUnoValue(pParams->Get(1)); if( (aArg1 >>= xFirstParamContext) && xFirstParamContext.is() ) xContextToUse = xFirstParamContext; } if( !xContextToUse.is() ) { xContextToUse = comphelper::getProcessComponentContext(); --nAllowedParamCount; } if( nParamCount > nAllowedParamCount ) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } Any aRetAny; if( xContextToUse.is() ) { OUString aSingletonName = "/singletons/" + GetName(); Reference < XInterface > xRet; xContextToUse->getValueByName( aSingletonName ) >>= xRet; aRetAny <<= xRet; } unoToSbxValue( pVar, aRetAny ); } else { SbxObject::Notify( rBC, rHint ); } } namespace { // Implementation of an EventAttacher-drawn AllListener, which // solely transmits several events to a general AllListener class BasicAllListener_Impl : public WeakImplHelper< XAllListener > { void firing_impl(const AllEventObject& Event, Any* pRet); public: SbxObjectRef xSbxObj; OUString aPrefixName; explicit BasicAllListener_Impl( OUString aPrefixName ); // Methods of XAllListener virtual void SAL_CALL firing(const AllEventObject& Event) override; virtual Any SAL_CALL approveFiring(const AllEventObject& Event) override; // Methods of XEventListener virtual void SAL_CALL disposing(const EventObject& Source) override; }; } BasicAllListener_Impl::BasicAllListener_Impl(OUString aPrefixName_) : aPrefixName(std::move( aPrefixName_ )) { } void BasicAllListener_Impl::firing_impl( const AllEventObject& Event, Any* pRet ) { SolarMutexGuard guard; if( !xSbxObj.is() ) return; OUString aMethodName = aPrefixName + Event.MethodName; SbxVariable * pP = xSbxObj.get(); while( pP->GetParent() ) { pP = pP->GetParent(); StarBASIC * pLib = dynamic_cast( pP ); if( pLib ) { // Create in a Basic Array SbxArrayRef xSbxArray = new SbxArray( SbxVARIANT ); const Any * pArgs = Event.Arguments.getConstArray(); sal_Int32 nCount = Event.Arguments.getLength(); for( sal_Int32 i = 0; i < nCount; i++ ) { // Convert elements SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); unoToSbxValue( xVar.get(), pArgs[i] ); xSbxArray->Put(xVar.get(), i + 1); } pLib->Call( aMethodName, xSbxArray.get() ); // get the return value from the Param-Array, if requested if( pRet ) { SbxVariable* pVar = xSbxArray->Get(0); if( pVar ) { // #95792 Avoid a second call SbxFlagBits nFlags = pVar->GetFlags(); pVar->SetFlag( SbxFlagBits::NoBroadcast ); *pRet = sbxToUnoValueImpl( pVar ); pVar->SetFlags( nFlags ); } } break; } } } // Methods of Listener void BasicAllListener_Impl::firing( const AllEventObject& Event ) { firing_impl( Event, nullptr ); } Any BasicAllListener_Impl::approveFiring( const AllEventObject& Event ) { Any aRetAny; firing_impl( Event, &aRetAny ); return aRetAny; } // Methods of XEventListener void BasicAllListener_Impl ::disposing(const EventObject& ) { SolarMutexGuard guard; xSbxObj.clear(); } // class InvocationToAllListenerMapper // helper class to map XInvocation to XAllListener (also in project eventattacher!) namespace { class InvocationToAllListenerMapper : public WeakImplHelper< XInvocation > { public: InvocationToAllListenerMapper( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ); // XInvocation virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override; virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override; virtual Any SAL_CALL getValue(const OUString& PropertyName) override; virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override; virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override; private: Reference< XAllListener > m_xAllListener; Reference< XIdlClass > m_xListenerType; Any m_Helper; }; } // Function to replace AllListenerAdapterService::createAllListerAdapter static Reference< XInterface > createAllListenerAdapter ( const Reference< XInvocationAdapterFactory2 >& xInvocationAdapterFactory, const Reference< XIdlClass >& xListenerType, const Reference< XAllListener >& xListener, const Any& Helper ) { Reference< XInterface > xAdapter; if( xInvocationAdapterFactory.is() && xListenerType.is() && xListener.is() ) { Reference< XInvocation > xInvocationToAllListenerMapper = new InvocationToAllListenerMapper(xListenerType, xListener, Helper); Type aListenerType( xListenerType->getTypeClass(), xListenerType->getName() ); xAdapter = xInvocationAdapterFactory->createAdapter( xInvocationToAllListenerMapper, {aListenerType} ); } return xAdapter; } // InvocationToAllListenerMapper InvocationToAllListenerMapper::InvocationToAllListenerMapper ( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ) : m_xAllListener( AllListener ) , m_xListenerType( ListenerType ) , m_Helper(std::move( Helper )) { } Reference< XIntrospectionAccess > SAL_CALL InvocationToAllListenerMapper::getIntrospection() { return Reference< XIntrospectionAccess >(); } Any SAL_CALL InvocationToAllListenerMapper::invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >&, Sequence< Any >&) { Any aRet; // Check if to firing or approveFiring has to be called Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( FunctionName ); bool bApproveFiring = false; if( !xMethod.is() ) return aRet; Reference< XIdlClass > xReturnType = xMethod->getReturnType(); Sequence< Reference< XIdlClass > > aExceptionSeq = xMethod->getExceptionTypes(); if( ( xReturnType.is() && xReturnType->getTypeClass() != TypeClass_VOID ) || aExceptionSeq.hasElements() ) { bApproveFiring = true; } else { Sequence< ParamInfo > aParamSeq = xMethod->getParameterInfos(); sal_uInt32 nParamCount = aParamSeq.getLength(); if( nParamCount > 1 ) { const ParamInfo* pInfo = aParamSeq.getConstArray(); for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) { if( pInfo[ i ].aMode != ParamMode_IN ) { bApproveFiring = true; break; } } } } AllEventObject aAllEvent; aAllEvent.Source = getXWeak(); aAllEvent.Helper = m_Helper; aAllEvent.ListenerType = Type(m_xListenerType->getTypeClass(), m_xListenerType->getName() ); aAllEvent.MethodName = FunctionName; aAllEvent.Arguments = Params; if( bApproveFiring ) aRet = m_xAllListener->approveFiring( aAllEvent ); else m_xAllListener->firing( aAllEvent ); return aRet; } void SAL_CALL InvocationToAllListenerMapper::setValue(const OUString&, const Any&) {} Any SAL_CALL InvocationToAllListenerMapper::getValue(const OUString&) { return Any(); } sal_Bool SAL_CALL InvocationToAllListenerMapper::hasMethod(const OUString& Name) { Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( Name ); return xMethod.is(); } sal_Bool SAL_CALL InvocationToAllListenerMapper::hasProperty(const OUString& Name) { Reference< XIdlField > xField = m_xListenerType->getField( Name ); return xField.is(); } // create Uno-Service // 1. Parameter == Prefix-Name of the macro // 2. Parameter == fully qualified name of the listener void SbRtl_CreateUnoListener(StarBASIC * pBasic, SbxArray & rPar, bool) { // We need 2 parameters if (rPar.Count() != 3) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // get the name of the class of the struct OUString aPrefixName = rPar.Get(1)->GetOUString(); OUString aListenerClassName = rPar.Get(2)->GetOUString(); // get the CoreReflection Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); if( !xCoreReflection.is() ) return; // get the AllListenerAdapterService Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); // search the class Reference< XIdlClass > xClass = xCoreReflection->forName( aListenerClassName ); if( !xClass.is() ) return; // From 1999-11-30: get the InvocationAdapterFactory Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = InvocationAdapterFactory::create( xContext ); rtl::Reference xAllLst = new BasicAllListener_Impl( aPrefixName ); Any aTmp; Reference< XInterface > xLst = createAllListenerAdapter( xInvocationAdapterFactory, xClass, xAllLst, aTmp ); if( !xLst.is() ) return; OUString aClassName = xClass->getName(); Type aClassType( xClass->getTypeClass(), aClassName ); aTmp = xLst->queryInterface( aClassType ); if( !aTmp.hasValue() ) return; SbUnoObject* pUnoObj = new SbUnoObject( aListenerClassName, aTmp ); xAllLst->xSbxObj = pUnoObj; xAllLst->xSbxObj->SetParent( pBasic ); // #100326 Register listener object to set Parent NULL in Dtor SbxArrayRef xBasicUnoListeners = pBasic->getUnoListeners(); xBasicUnoListeners->Insert(pUnoObj, xBasicUnoListeners->Count()); // return the object SbxVariableRef refVar = rPar.Get(0); refVar->PutObject( xAllLst->xSbxObj.get() ); } // Represents the DefaultContext property of the ProcessServiceManager // in the Basic runtime system. void RTL_Impl_GetDefaultContext( SbxArray& rPar ) { SbxVariableRef refVar = rPar.Get(0); Any aContextAny( comphelper::getProcessComponentContext() ); SbUnoObjectRef xUnoObj = new SbUnoObject( "DefaultContext", aContextAny ); refVar->PutObject( xUnoObj.get() ); } // Creates a Basic wrapper object for a strongly typed Uno value // 1. parameter: Uno type as full qualified type name, e.g. "byte[]" void RTL_Impl_CreateUnoValue( SbxArray& rPar ) { // 2 parameters needed if (rPar.Count() != 3) { StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); return; } // get the name of the class of the struct OUString aTypeName = rPar.Get(1)->GetOUString(); SbxVariable* pVal = rPar.Get(2); if( aTypeName == "type" ) { SbxDataType eBaseType = pVal->SbxValue::GetType(); OUString aValTypeName; if( eBaseType == SbxSTRING ) { aValTypeName = pVal->GetOUString(); } else if( eBaseType == SbxOBJECT ) { // XIdlClass? Reference< XIdlClass > xIdlClass; SbxBaseRef pObj = pVal->GetObject(); if( auto obj = dynamic_cast( pObj.get() ) ) { Any aUnoAny = obj->getUnoAny(); aUnoAny >>= xIdlClass; } if( xIdlClass.is() ) { aValTypeName = xIdlClass->getName(); } } Type aType; bool bSuccess = implGetTypeByName( aValTypeName, aType ); if( bSuccess ) { Any aTypeAny( aType ); SbxVariableRef refVar = rPar.Get(0); SbxObjectRef xUnoAnyObject = new SbUnoAnyObject( aTypeAny ); refVar->PutObject( xUnoAnyObject.get() ); } return; } // Check the type const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); Any aRet; try { aRet = xTypeAccess->getByHierarchicalName( aTypeName ); } catch( const NoSuchElementException& e1 ) { StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e1, u"com.sun.star.container.NoSuchElementException" ) ); return; } Reference< XTypeDescription > xTypeDesc; aRet >>= xTypeDesc; TypeClass eTypeClass = xTypeDesc->getTypeClass(); Type aDestType( eTypeClass, aTypeName ); // Preconvert value Any aVal = sbxToUnoValueImpl( pVal ); Any aConvertedVal = convertAny( aVal, aDestType ); SbxVariableRef refVar = rPar.Get(0); SbxObjectRef xUnoAnyObject = new SbUnoAnyObject( aConvertedVal ); refVar->PutObject( xUnoAnyObject.get() ); } namespace { class ModuleInvocationProxy : public WeakImplHelper< XInvocation, XComponent > { std::mutex m_aMutex; OUString m_aPrefix; SbxObjectRef m_xScopeObj; bool m_bProxyIsClassModuleObject; ::comphelper::OInterfaceContainerHelper4 m_aListeners; public: ModuleInvocationProxy( std::u16string_view aPrefix, SbxObjectRef const & xScopeObj ); // XInvocation virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; virtual void SAL_CALL setValue( const OUString& rProperty, const Any& rValue ) override; virtual Any SAL_CALL getValue( const OUString& rProperty ) override; virtual sal_Bool SAL_CALL hasMethod( const OUString& rName ) override; virtual sal_Bool SAL_CALL hasProperty( const OUString& rProp ) override; virtual Any SAL_CALL invoke( const OUString& rFunction, const Sequence< Any >& rParams, Sequence< sal_Int16 >& rOutParamIndex, Sequence< Any >& rOutParam ) override; // XComponent virtual void SAL_CALL dispose() override; virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override; virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override; }; } ModuleInvocationProxy::ModuleInvocationProxy( std::u16string_view aPrefix, SbxObjectRef const & xScopeObj ) : m_aPrefix( OUString::Concat(aPrefix) + "_" ) , m_xScopeObj( xScopeObj ) { m_bProxyIsClassModuleObject = xScopeObj.is() && dynamic_cast( xScopeObj.get() ) != nullptr; } Reference< XIntrospectionAccess > SAL_CALL ModuleInvocationProxy::getIntrospection() { return Reference< XIntrospectionAccess >(); } void SAL_CALL ModuleInvocationProxy::setValue(const OUString& rProperty, const Any& rValue) { if( !m_bProxyIsClassModuleObject ) throw UnknownPropertyException(); SolarMutexGuard guard; OUString aPropertyFunctionName = "Property Set " + m_aPrefix + rProperty; SbxVariable* p = m_xScopeObj->Find( aPropertyFunctionName, SbxClassType::Method ); SbMethod* pMeth = dynamic_cast( p ); if( pMeth == nullptr ) { // TODO: Check vba behavior concerning missing function //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); throw UnknownPropertyException(aPropertyFunctionName); } // Setup parameter SbxArrayRef xArray = new SbxArray; SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); unoToSbxValue( xVar.get(), rValue ); xArray->Put(xVar.get(), 1); // Call property method SbxVariableRef xValue = new SbxVariable; pMeth->SetParameters( xArray.get() ); pMeth->Call( xValue.get() ); pMeth->SetParameters( nullptr ); // TODO: OutParameter? } Any SAL_CALL ModuleInvocationProxy::getValue(const OUString& rProperty) { if( !m_bProxyIsClassModuleObject ) { throw UnknownPropertyException(); } SolarMutexGuard guard; OUString aPropertyFunctionName = "Property Get " + m_aPrefix + rProperty; SbxVariable* p = m_xScopeObj->Find( aPropertyFunctionName, SbxClassType::Method ); SbMethod* pMeth = dynamic_cast( p ); if( pMeth == nullptr ) { // TODO: Check vba behavior concerning missing function //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); throw UnknownPropertyException(aPropertyFunctionName); } // Call method SbxVariableRef xValue = new SbxVariable; pMeth->Call( xValue.get() ); Any aRet = sbxToUnoValue( xValue.get() ); return aRet; } sal_Bool SAL_CALL ModuleInvocationProxy::hasMethod( const OUString& ) { return false; } sal_Bool SAL_CALL ModuleInvocationProxy::hasProperty( const OUString& ) { return false; } Any SAL_CALL ModuleInvocationProxy::invoke( const OUString& rFunction, const Sequence< Any >& rParams, Sequence< sal_Int16 >&, Sequence< Any >& ) { SolarMutexGuard guard; Any aRet; SbxObjectRef xScopeObj = m_xScopeObj; if( !xScopeObj.is() ) { return aRet; } OUString aFunctionName = m_aPrefix + rFunction; bool bOldReschedule = false; SbiInstance* pInst = GetSbData()->pInst; if( pInst && pInst->IsCompatibility() ) { bOldReschedule = pInst->IsReschedule(); if ( bOldReschedule ) pInst->EnableReschedule( false ); } SbxVariable* p = xScopeObj->Find( aFunctionName, SbxClassType::Method ); SbMethod* pMeth = dynamic_cast( p ); if( pMeth == nullptr ) { // TODO: Check vba behavior concerning missing function //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); return aRet; } // Setup parameters SbxArrayRef xArray; sal_Int32 nParamCount = rParams.getLength(); if( nParamCount ) { xArray = new SbxArray; const Any *pArgs = rParams.getConstArray(); for( sal_Int32 i = 0 ; i < nParamCount ; i++ ) { SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); unoToSbxValue( xVar.get(), pArgs[i] ); xArray->Put(xVar.get(), sal::static_int_cast(i + 1)); } } // Call method SbxVariableRef xValue = new SbxVariable; if( xArray.is() ) pMeth->SetParameters( xArray.get() ); pMeth->Call( xValue.get() ); aRet = sbxToUnoValue( xValue.get() ); pMeth->SetParameters( nullptr ); if (bOldReschedule) pInst->EnableReschedule( bOldReschedule ); // TODO: OutParameter? return aRet; } void SAL_CALL ModuleInvocationProxy::dispose() { std::unique_lock aGuard( m_aMutex ); EventObject aEvent( static_cast(this) ); m_aListeners.disposeAndClear( aGuard, aEvent ); m_xScopeObj = nullptr; } void SAL_CALL ModuleInvocationProxy::addEventListener( const Reference< XEventListener >& xListener ) { std::unique_lock aGuard( m_aMutex ); m_aListeners.addInterface( aGuard, xListener ); } void SAL_CALL ModuleInvocationProxy::removeEventListener( const Reference< XEventListener >& xListener ) { std::unique_lock aGuard( m_aMutex ); m_aListeners.removeInterface( aGuard, xListener ); } Reference< XInterface > createComListener( const Any& aControlAny, const OUString& aVBAType, std::u16string_view aPrefix, const SbxObjectRef& xScopeObj ) { Reference< XInterface > xRet; Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); Reference< XInvocation > xProxy = new ModuleInvocationProxy( aPrefix, xScopeObj ); Sequence args{ aControlAny, Any(aVBAType), Any(xProxy) }; try { xRet = xServiceMgr->createInstanceWithArgumentsAndContext( "com.sun.star.custom.UnoComListener", args, xContext ); } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } return xRet; } typedef std::vector< WeakReference< XComponent > > ComponentRefVector; namespace { struct StarBasicDisposeItem { StarBASIC* m_pBasic; SbxArrayRef m_pRegisteredVariables; ComponentRefVector m_vComImplementsObjects; explicit StarBasicDisposeItem( StarBASIC* pBasic ) : m_pBasic( pBasic ) , m_pRegisteredVariables(new SbxArray()) { } }; } typedef std::vector< StarBasicDisposeItem* > DisposeItemVector; static DisposeItemVector GaDisposeItemVector; static DisposeItemVector::iterator lcl_findItemForBasic( StarBASIC const * pBasic ) { return std::find_if(GaDisposeItemVector.begin(), GaDisposeItemVector.end(), [&pBasic](StarBasicDisposeItem* pItem) { return pItem->m_pBasic == pBasic; }); } static StarBasicDisposeItem* lcl_getOrCreateItemForBasic( StarBASIC* pBasic ) { DisposeItemVector::iterator it = lcl_findItemForBasic( pBasic ); StarBasicDisposeItem* pItem = (it != GaDisposeItemVector.end()) ? *it : nullptr; if( pItem == nullptr ) { pItem = new StarBasicDisposeItem( pBasic ); GaDisposeItemVector.push_back( pItem ); } return pItem; } void registerComponentToBeDisposedForBasic ( const Reference< XComponent >& xComponent, StarBASIC* pBasic ) { StarBasicDisposeItem* pItem = lcl_getOrCreateItemForBasic( pBasic ); pItem->m_vComImplementsObjects.emplace_back(xComponent ); } void registerComListenerVariableForBasic( SbxVariable* pVar, StarBASIC* pBasic ) { StarBasicDisposeItem* pItem = lcl_getOrCreateItemForBasic( pBasic ); SbxArray* pArray = pItem->m_pRegisteredVariables.get(); pArray->Put(pVar, pArray->Count()); } void disposeComVariablesForBasic( StarBASIC const * pBasic ) { DisposeItemVector::iterator it = lcl_findItemForBasic( pBasic ); if( it == GaDisposeItemVector.end() ) return; StarBasicDisposeItem* pItem = *it; SbxArray* pArray = pItem->m_pRegisteredVariables.get(); sal_uInt32 nCount = pArray->Count(); for( sal_uInt32 i = 0 ; i < nCount ; ++i ) { SbxVariable* pVar = pArray->Get(i); pVar->ClearComListener(); } ComponentRefVector& rv = pItem->m_vComImplementsObjects; for (auto const& elem : rv) { Reference< XComponent > xComponent( elem.get(), UNO_QUERY ); if (xComponent.is()) xComponent->dispose(); } delete pItem; GaDisposeItemVector.erase( it ); } // Handle module implements mechanism for OLE types bool SbModule::createCOMWrapperForIface( Any& o_rRetAny, SbClassModuleObject* pProxyClassModuleObject ) { // For now: Take first interface that allows to instantiate COM wrapper // TODO: Check if support for multiple interfaces is needed Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); Reference< XSingleServiceFactory > xComImplementsFactory ( xServiceMgr->createInstanceWithContext( "com.sun.star.custom.ComImplementsFactory", xContext ), UNO_QUERY ); if( !xComImplementsFactory.is() ) return false; bool bSuccess = false; SbxArray* pModIfaces = pClassData->mxIfaces.get(); sal_uInt32 nCount = pModIfaces->Count(); for( sal_uInt32 i = 0 ; i < nCount ; ++i ) { SbxVariable* pVar = pModIfaces->Get(i); const OUString& aIfaceName = pVar->GetName(); if( !aIfaceName.isEmpty() ) { OUString aPureIfaceName = aIfaceName; sal_Int32 indexLastDot = aIfaceName.lastIndexOf('.'); if ( indexLastDot > -1 ) { aPureIfaceName = aIfaceName.copy( indexLastDot + 1 ); } Reference< XInvocation > xProxy = new ModuleInvocationProxy( aPureIfaceName, pProxyClassModuleObject ); Sequence args{ Any(aIfaceName), Any(xProxy) }; Reference< XInterface > xRet; try { xRet = xComImplementsFactory->createInstanceWithArguments( args ); bSuccess = true; } catch( const Exception& ) { implHandleAnyException( ::cppu::getCaughtException() ); } if( bSuccess ) { Reference< XComponent > xComponent( xProxy, UNO_QUERY ); if( xComponent.is() ) { StarBASIC* pParentBasic = nullptr; SbxObject* pCurObject = this; do { SbxObject* pObjParent = pCurObject->GetParent(); pParentBasic = dynamic_cast( pObjParent ); pCurObject = pObjParent; } while( pParentBasic == nullptr && pCurObject != nullptr ); assert( pParentBasic != nullptr ); registerComponentToBeDisposedForBasic( xComponent, pParentBasic ); } o_rRetAny <<= xRet; break; } } } return bSuccess; } // Due to an incorrect behavior IE returns an object instead of a string // in some scenarios. Calling toString at the object may correct this. // Helper function used in sbxvalue.cxx bool handleToStringForCOMObjects( SbxObject* pObj, SbxValue* pVal ) { bool bSuccess = false; if( auto pUnoObj = dynamic_cast( pObj) ) { // Only for native COM objects if( pUnoObj->isNativeCOMObject() ) { SbxVariableRef pMeth = pObj->Find( "toString", SbxClassType::Method ); if ( pMeth.is() ) { SbxValues aRes; pMeth->Get( aRes ); pVal->Put( aRes ); bSuccess = true; } } } return bSuccess; } Any StructRefInfo::getValue() { Any aRet; uno_any_destruct( &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); typelib_TypeDescription * pTD = nullptr; maType.getDescription(&pTD); uno_any_construct( &aRet, getInst(), pTD, reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); typelib_typedescription_release(pTD); return aRet; } void StructRefInfo::setValue( const Any& rValue ) { bool bSuccess = uno_type_assignData( getInst(), maType.getTypeLibType(), const_cast(rValue.getValue()), rValue.getValueTypeRef(), reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface), reinterpret_cast< uno_AcquireFunc >(cpp_acquire), reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); OSL_ENSURE(bSuccess, "StructRefInfo::setValue: ooops... the value could not be assigned!"); } OUString StructRefInfo::getTypeName() const { return maType.getTypeName(); } void* StructRefInfo::getInst() { return const_cast(static_cast(maAny.getValue()) + mnPos); } TypeClass StructRefInfo::getTypeClass() const { return maType.getTypeClass(); } SbUnoStructRefObject::SbUnoStructRefObject( const OUString& aName_, StructRefInfo aMemberInfo ) : SbxObject( aName_ ), maMemberInfo(std::move( aMemberInfo )), mbMemberCacheInit( false ) { SetClassName( maMemberInfo.getTypeName() ); } SbUnoStructRefObject::~SbUnoStructRefObject() { } void SbUnoStructRefObject::initMemberCache() { if ( mbMemberCacheInit ) return; typelib_TypeDescription * pTD = nullptr; maMemberInfo.getType().getDescription(&pTD); for ( typelib_CompoundTypeDescription * pCompTypeDescr = reinterpret_cast(pTD); pCompTypeDescr; pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) { typelib_TypeDescriptionReference ** ppTypeRefs = pCompTypeDescr->ppTypeRefs; rtl_uString ** ppNames = pCompTypeDescr->ppMemberNames; sal_Int32 * pMemberOffsets = pCompTypeDescr->pMemberOffsets; for ( sal_Int32 nPos = pCompTypeDescr->nMembers; nPos--; ) { OUString aName( ppNames[nPos] ); maFields[ aName ] = std::make_unique( maMemberInfo.getRootAnyRef(), ppTypeRefs[nPos], maMemberInfo.getPos() + pMemberOffsets[nPos] ); } } typelib_typedescription_release(pTD); mbMemberCacheInit = true; } SbxVariable* SbUnoStructRefObject::Find( const OUString& rName, SbxClassType t ) { SbxVariable* pRes = SbxObject::Find( rName, t ); if ( !pRes ) { if ( !mbMemberCacheInit ) initMemberCache(); StructFieldInfo::iterator it = maFields.find( rName ); if ( it != maFields.end() ) { SbxDataType eSbxType; eSbxType = unoToSbxType( it->second->getTypeClass() ); SbxDataType eRealSbxType = eSbxType; Property aProp; aProp.Name = rName; aProp.Type = css::uno::Type( it->second->getTypeClass(), it->second->getTypeName() ); const bool bIsStruct = aProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT; SbUnoProperty* pProp = new SbUnoProperty( rName, eSbxType, eRealSbxType, std::move(aProp), 0, false, bIsStruct ); SbxVariableRef xVarRef = pProp; QuickInsert( xVarRef.get() ); pRes = xVarRef.get(); } } if( !pRes ) { if( rName.equalsIgnoreAsciiCase(ID_DBG_SUPPORTEDINTERFACES) || rName.equalsIgnoreAsciiCase(ID_DBG_PROPERTIES) || rName.equalsIgnoreAsciiCase(ID_DBG_METHODS) ) { // Create implCreateDbgProperties(); // Now they have to be found regular pRes = SbxObject::Find( rName, SbxClassType::DontCare ); } } return pRes; } // help method to create the dbg_-Properties void SbUnoStructRefObject::implCreateDbgProperties() { Property aProp; // Id == -1: display the implemented interfaces corresponding the ClassProvider SbxVariableRef xVarRef = new SbUnoProperty( ID_DBG_SUPPORTEDINTERFACES, SbxSTRING, SbxSTRING, aProp, -1, false, false ); QuickInsert( xVarRef.get() ); // Id == -2: output the properties xVarRef = new SbUnoProperty( ID_DBG_PROPERTIES, SbxSTRING, SbxSTRING, aProp, -2, false, false ); QuickInsert( xVarRef.get() ); // Id == -3: output the Methods xVarRef = new SbUnoProperty( ID_DBG_METHODS, SbxSTRING, SbxSTRING, std::move(aProp), -3, false, false ); QuickInsert( xVarRef.get() ); } void SbUnoStructRefObject::implCreateAll() { // throw away all existing methods and properties pMethods = new SbxArray; pProps = new SbxArray; if (!mbMemberCacheInit) initMemberCache(); for (auto const& field : maFields) { const OUString& rName = field.first; SbxDataType eSbxType; eSbxType = unoToSbxType( field.second->getTypeClass() ); SbxDataType eRealSbxType = eSbxType; Property aProp; aProp.Name = rName; aProp.Type = css::uno::Type( field.second->getTypeClass(), field.second->getTypeName() ); const bool bIsStruct = aProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT; SbUnoProperty* pProp = new SbUnoProperty( rName, eSbxType, eRealSbxType, std::move(aProp), 0, false, bIsStruct ); SbxVariableRef xVarRef = pProp; QuickInsert( xVarRef.get() ); } // Create Dbg_-Properties implCreateDbgProperties(); } // output the value Any SbUnoStructRefObject::getUnoAny() { return maMemberInfo.getValue(); } OUString SbUnoStructRefObject::Impl_DumpProperties() { OUStringBuffer aRet("Properties of object " + getDbgObjectName() ); sal_uInt32 nPropCount = pProps->Count(); sal_uInt32 nPropsPerLine = 1 + nPropCount / 30; for( sal_uInt32 i = 0; i < nPropCount; i++ ) { SbxVariable* pVar = pProps->Get(i); if( pVar ) { OUStringBuffer aPropStr; if( (i % nPropsPerLine) == 0 ) { aPropStr.append( "\n" ); } // output the type and name // Is it in Uno a sequence? SbxDataType eType = pVar->GetFullType(); const OUString& aName( pVar->GetName() ); StructFieldInfo::iterator it = maFields.find( aName ); if ( it != maFields.end() ) { const StructRefInfo& rPropInfo = *it->second; if( eType == SbxOBJECT ) { if( rPropInfo.getTypeClass() == TypeClass_SEQUENCE ) { eType = SbxDataType( SbxOBJECT | SbxARRAY ); } } } aPropStr.append( Dbg_SbxDataType2String( eType ) + " " + pVar->GetName() ); if( i == nPropCount - 1 ) { aPropStr.append( "\n" ); } else { aPropStr.append( "; " ); } aRet.append( aPropStr ); } } return aRet.makeStringAndClear(); } void SbUnoStructRefObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) { if ( !mbMemberCacheInit ) initMemberCache(); const SbxHint* pHint = dynamic_cast(&rHint); if( !pHint ) return; SbxVariable* pVar = pHint->GetVar(); SbUnoProperty* pProp = dynamic_cast( pVar ); if( pProp ) { StructFieldInfo::iterator it = maFields.find( pProp->GetName() ); // handle get/set of members of struct if( pHint->GetId() == SfxHintId::BasicDataWanted ) { // Test-Properties sal_Int32 nId = pProp->nId; if( nId < 0 ) { // Id == -1: Display implemented interfaces according the ClassProvider if( nId == -1 ) // Property ID_DBG_SUPPORTEDINTERFACES" { OUString aRet = OUString::Concat( ID_DBG_SUPPORTEDINTERFACES ) + " not available.\n(TypeClass is not TypeClass_INTERFACE)\n"; pVar->PutString( aRet ); } // Id == -2: output properties else if( nId == -2 ) // Property ID_DBG_PROPERTIES { // by now all properties must be established implCreateAll(); OUString aRetStr = Impl_DumpProperties(); pVar->PutString( aRetStr ); } // Id == -3: output the methods else if( nId == -3 ) // Property ID_DBG_METHODS { // by now all properties must be established implCreateAll(); OUString aRet = "Methods of object " + getDbgObjectName() + "\nNo methods found\n"; pVar->PutString( aRet ); } return; } if ( it != maFields.end() ) { Any aRetAny = it->second->getValue(); unoToSbxValue( pVar, aRetAny ); } else StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); } else if( pHint->GetId() == SfxHintId::BasicDataChanged ) { if ( it != maFields.end() ) { // take over the value from Uno to Sbx Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); it->second->setValue( aAnyValue ); } else StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); } } else SbxObject::Notify( rBC, rHint ); } StructRefInfo SbUnoStructRefObject::getStructMember( const OUString& rMemberName ) { if (!mbMemberCacheInit) { initMemberCache(); } StructFieldInfo::iterator it = maFields.find( rMemberName ); css::uno::Type aFoundType; sal_Int32 nFoundPos = -1; if ( it != maFields.end() ) { aFoundType = it->second->getType(); nFoundPos = it->second->getPos(); } StructRefInfo aRet( maMemberInfo.getRootAnyRef(), aFoundType, nFoundPos ); return aRet; } OUString SbUnoStructRefObject::getDbgObjectName() const { OUString aName = GetClassName(); if( aName.isEmpty() ) { aName += "Unknown"; } OUStringBuffer aRet; if( aName.getLength() > 20 ) { aRet.append( "\n" ); } aRet.append( "\"" + aName + "\":" ); return aRet.makeStringAndClear(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */