/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/ #pragma once
// for some reason DECIMAL_NEG (wtypes.h) which contains BYTE is not resolved. typedefunsignedchar BYTE; // classes for wrapping uno objects #define INTERFACE_OLE_WRAPPER_IMPL 1 #define UNO_OBJECT_WRAPPER_REMOTE_OPT 2
// classes for wrapping ole objects #define IUNKNOWN_WRAPPER_IMPL 1
#define INTERFACE_ADAPTER_FACTORY "com.sun.star.script.InvocationAdapterFactory" // COM or JScript objects implementing UNO interfaces have to implement this property #define SUPPORTED_INTERFACES_PROP L"_implementedInterfaces" // Second property without leading underscore for use in VB #define SUPPORTED_INTERFACES_PROP2 L"Bridge_ImplementedInterfaces"
//Maps IUnknown pointers to a weak reference of the respective wrapper class (e.g. // IUnknownWrapperImpl. It is the responsibility of the wrapper to remove the entry when // it is being destroyed. // Used to ensure that an Automation object is always mapped to the same UNO objects. extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap;
// Maps XInterface pointers to a weak reference of its wrapper class (i.e. // InterfaceOleWrapper). It is the responsibility of the wrapper to remove the entry when // it is being destroyed. It is used to ensure the identity of objects. That is, a UNO interface // is mapped to IDispatch which is kept alive in the COM environment. If the same // UNO interface is mapped again to COM then the IDispach of the first mapped instance // must be returned. extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap;
// This function tries to the change the type of a value (contained in the Any) // to the smallest possible that can hold the value. This is actually done only // for types of VT_I4 (see o2u_variantToAny). The reason is the following: // JavaScript passes integer values always as VT_I4. If there is a parameter or // property of type any then the bridge converts the any's content according // to "o2u_variantToAny". Because the VARTYPE is VT_I4 the value would be converted // to TypeClass_LONG. Say the method XPropertySet::setPropertyValue( string name, any value) // would be called on an object and the property actually is of TypeClass_SHORT. // After conversion of the VARIANT parameter the Any would contain type // TypeClass_LONG. Because the corereflection does not cast from long to short // the "setPropertValue" would fail as the value has not the right type.
// The corereflection does convert small integer types to bigger types. // Therefore we can reduce the type if possible and avoid the above mentioned // problem.
// The function is not used when elements are to be converted for Sequences.
sal_Int32 value= *o3tl::doAccess<sal_Int32>(any); if( value <= 0x7f && value >= -0x80)
{// -128 bis 127
sal_Int8 charVal= static_cast<sal_Int8>( value);
any.setValue( &charVal, cppu::UnoType<sal_Int8>::get());
} elseif( value <= 0x7fff && value >= -0x8000)
{// -32768 bis 32767
sal_Int16 shortVal= static_cast<sal_Int16>( value);
any.setValue( &shortVal, cppu::UnoType<sal_Int16>::get());
}
}
// createUnoObjectWrapper gets a wrapper instance by calling createUnoWrapperInstance // and initializes it via XInitialization. The wrapper object is required to implement // XBridgeSupplier so that it can convert itself to IDispatch. // class T: Deriving class ( must implement XInterface ) /** All methods are allowed to throw at least a BridgeRuntimeError.
*/ template< class > class UnoConversionUtilities
{ public: explicit UnoConversionUtilities( const Reference<XMultiServiceFactory> & smgr):
m_nUnoWrapperClass( INTERFACE_OLE_WRAPPER_IMPL),
m_nComWrapperClass( IUNKNOWN_WRAPPER_IMPL),
m_smgr( smgr)
{}
virtual ~UnoConversionUtilities() {} /** converts only into oleautomation types, that is there is no VT_I1, VT_UI2, VT_UI4 a sal_Unicode character is converted into a BSTR. @exception com.sun.star.lang.IllegalArgumentException If the any was inappropriate for conversion. @exception com.sun.star.script.CannotConvertException The any contains a type class for which no conversion is provided.
*/ void anyToVariant(VARIANT* pVariant, const Any& rAny); void anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type);
/** @exception com.sun.star.lang.IllegalArgumentException If rSeq does not contain a sequence then the exception is thrown.
*/
SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq); /** @exception com.sun.star.lang.IllegalArgumentException If rSeq does not contain a sequence or elemtype has no proper value then the exception is thrown.
*/
SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype); /** @exception com.sun.star.lang.IllegalArgumentException If rObj does not contain a struct or interface
*/ void createUnoObjectWrapper(const Any & rObj, VARIANT * pVar); /** @exception CannotConvertException Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. ArgumentIndex is 0. @IllegalArgumentException Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1,
*/ void variantToAny(const VARIANT* pVariant, Any& rAny, bool bReduceValueRange = true); /** This method converts variants arguments in calls from COM -> UNO. Only then the expected UNO type is known. @exception CannotConvertException Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. ArgumentIndex is 0. @IllegalArgumentException Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1,
*/ void variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange = true);
/** @exception IllegalArgumentException -if pVar does not contain VT_UNKNOWN or VT_DISPATCH or pVar is used for a particular UNO type which is not supported by pVar
*/
Any createOleObjectWrapper(VARIANT* pVar, const Type& aType= Type());
/* Return true means var contained a ValueObject, and it was successfully converted. The result is in any. It an error occurred a BridgeRuntimeError will be thrown.
*/ bool convertValueObject( const VARIANTARG *var, Any& any); void dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type);
// helper function for Sequence conversion void getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc); // helper function for Sequence conversion staticbool incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLength,
sal_Int32 * parMultidimensionalIndex); // helper function for Sequence conversion static size_t getOleElementSize( VARTYPE type);
static Type getElementTypeOfSequence( const Type& seqType);
//Provides a typeconverter
Reference<XTypeConverter> getTypeConverter();
// This member determines what class is used to convert a UNO object // or struct to a COM object. It is passed along to the anyToVariant // function in the createBridge function implementation const sal_uInt8 m_nUnoWrapperClass; const sal_uInt8 m_nComWrapperClass;
// The servicemanager is either a local smgr or remote when the service // com.sun.star.bridge.OleBridgeSupplierVar1 is used. This service can be // created by createInstanceWithArguments where one can supply a service // manager that is to be used. // Local service manager as supplied by the loader when the creator function // of the service is being called.
Reference<XMultiServiceFactory> m_smgr; // An explicitly supplied service manager when the service // com.sun.star.bridge.OleBridgeSupplierVar1 is used. That can be a remote // manager.
Reference<XMultiServiceFactory> m_smgrRemote;
Reference<XSingleServiceFactory> m_xInvocationFactoryLocal;
Reference<XSingleServiceFactory> m_xInvocationFactoryRemote;
private: // Holds the type converter which is used for sequence conversion etc. // Use the getTypeConverter function to obtain the interface.
Reference<XTypeConverter> m_typeConverter;
};
// ask the object for XBridgeSupplier2 and on success bridges // the uno object to IUnknown or IDispatch. // return true the UNO object supports template < class T > bool convertSelfToCom( T& unoInterface, VARIANT * pVar)
{ bool ret = false;
Reference< XInterface > xInt( unoInterface, UNO_QUERY); if( xInt.is())
{
Reference< css::bridge::XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY); if( xSupplier.is())
{
sal_Int8 arId[16];
rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(arId));
Sequence<sal_Int8> seqId( arId, 16);
Any anySource;
anySource <<= xInt;
Any anyDisp = xSupplier->createBridge(
anySource, seqId, css::bridge::ModelDependent::UNO,
css::bridge::ModelDependent::OLE);
// due to global-process-id check this must be in-process pointer if (auto v = o3tl::tryAccess<sal_uIntPtr>(anyDisp))
{
VARIANT* pvariant= reinterpret_cast<VARIANT*>(*v);
HRESULT hr; if (FAILED(hr = VariantCopy(pVar, pvariant))) throw BridgeRuntimeError( "[automation bridge] convertSelfToCom\n" "VariantCopy failed! Error: " +
OUString::number(hr));
VariantClear( pvariant);
CoTaskMemFree( pvariant);
ret = true;
}
}
} return ret;
}
// Gets the invocation factory depending on the Type in the Any. // The factory can be created by a local or remote multi service factory. // In case there is a remote multi service factory available there are // some services or types for which the local factory is used. The exceptions // are: all structs. // Param anyObject - contains the object ( interface, struct) for what we need an invocation object.
// There is no need to support indirect values, since they're not supported by UNO if( FAILED(hr= VariantCopyInd( &var, pArg))) // remove VT_BYREF throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::variantToAny \n" "VariantCopyInd failed for reason : " + OUString::number(hr)); bool bHandled = convertValueObject( & var, rAny); if( bHandled)
OSL_ENSURE( rAny.getValueType() == ptype, "type in Value Object must match the type parameter");
if( ! bHandled)
{ // convert into a variant type that is the equivalent to the type // the sequence expects. Thus variantToAny produces the correct type // E.g. An Array object contains VT_I4 and the sequence expects shorts // than the vartype must be changed. The reason is, you can't specify the // type in JavaScript and the script engine determines the type being used. switch( ptype.getTypeClass())
{ case TypeClass_CHAR: // could be: new Array( 12, 'w', "w") if( var.vt == VT_BSTR)
{ if(SUCCEEDED( hr= VariantChangeType( &var, &var, 0, VT_BSTR)))
rAny.setValue( V_BSTR( &var), ptype); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true;
} else
{ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2)))
rAny.setValue(& var.iVal, ptype); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true;
} break; case TypeClass_INTERFACE: // could also be an IUnknown case TypeClass_STRUCT:
{
rAny = createOleObjectWrapper( & var, ptype); break;
} case TypeClass_ENUM: if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I4)))
rAny.setValue(& var.lVal, ptype); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_SEQUENCE: // There are different ways of receiving a sequence: // 1: JScript, VARTYPE: VT_DISPATCH // 2. VBScript simple arraysVT_VARIANT|VT_BYREF the referenced VARIANT contains // a VT_ARRAY| <type> // 3. VBScript multi dimensional arrays: VT_ARRAY|VT_BYREF if( pArg->vt == VT_DISPATCH)
{
dispatchExObject2Sequence( pArg, rAny, ptype);
} else
{ if ((var.vt & VT_ARRAY) != 0)
{
VARTYPE oleType = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY );
Sequence<Any> unoSeq = createOleArrayWrapper( var.parray, oleType, ptype);
Reference<XTypeConverter> conv = getTypeConverter(); if (conv.is())
{ try
{
Any anySeq(unoSeq);
Any convAny = conv->convertTo(anySeq, ptype);
rAny = convAny;
} catch (const IllegalArgumentException& e)
{ throw BridgeRuntimeError( "[automation bridge]com.sun.star.lang.IllegalArgumentException " "in UnoConversionUtilities::variantToAny! Message: " +
e.Message);
} catch (const CannotConvertException& e)
{ throw BridgeRuntimeError( "[automation bridge]com.sun.star.script.CannotConvertException " "in UnoConversionUtilities::variantToAny! Message: " +
e.Message);
}
}
}
} break; case TypeClass_VOID:
rAny.setValue(nullptr,Type()); break; case TypeClass_ANY: // Any // There could be a JScript Array that needs special handling // If an Any is expected and this Any must contain a Sequence // then we cannot figure out what element type is required. // Therefore we convert to Sequence< Any > if( pArg->vt == VT_DISPATCH && isJScriptArray( pArg))
{
dispatchExObject2Sequence( pArg, rAny,
cppu::UnoType<Sequence<Any>>::get());
} elseif (pArg->vt == VT_DECIMAL)
{ //Decimal maps to hyper in calls from COM -> UNO // It does not matter if we create a sal_uInt64 or sal_Int64, // because the UNO object is called through invocation which //will do a type conversion if necessary if (var.decVal.sign == 0)
{ // positive value
variantToAny( & var, rAny, cppu::UnoType<sal_uInt64>::get(),
bReduceValueRange);
} else
{ //negative value
variantToAny( & var, rAny, cppu::UnoType<sal_Int64>::get(),
bReduceValueRange);
}
} else
{
variantToAny( & var, rAny);
} break; case TypeClass_BOOLEAN: // VARIANT could be VARIANT_BOOL or other if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BOOL)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_STRING: // UString if(var.vt == VT_NULL)
var = CComBSTR(""); if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BSTR)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_FLOAT: // float if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R4)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_DOUBLE: // double if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R8)))
variantToAny(& var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_BYTE: // BYTE if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I1)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_SHORT: // INT16 if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_LONG: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_I4)))
variantToAny( & var, rAny, bReduceValueRange); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_HYPER: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL)))
{ if (var.decVal.Lo64 > SAL_CONST_UINT64(0x8000000000000000)
|| var.decVal.Hi32 > 0
|| var.decVal.scale > 0)
{
bFail = true; break;
}
sal_Int64 value = var.decVal.Lo64; if (var.decVal.sign == DECIMAL_NEG)
value |= SAL_CONST_UINT64(0x8000000000000000);
rAny <<= value;
} elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_UNSIGNED_SHORT: // UINT16 if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI2)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_UNSIGNED_LONG: if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI4)))
variantToAny( & var, rAny, bReduceValueRange); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_UNSIGNED_HYPER: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL)))
{ if (var.decVal.Hi32 > 0 || var.decVal.scale > 0)
{
bFail = true; break;
}
rAny <<= var.decVal.Lo64;
} elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; case TypeClass_TYPE: if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_UNKNOWN)))
variantToAny( & var, rAny); elseif (hr == DISP_E_TYPEMISMATCH)
bCannotConvert = true; else
bFail = true; break; default:
bCannotConvert = true; break;
}
} if (bCannotConvert) throw CannotConvertException( "[automation bridge]UnoConversionUtilities::variantToAny \n" "Cannot convert the value of vartype :\"" +
OUString::number(static_cast<sal_Int32>(var.vt)) + "\" to the expected UNO type of type class: " +
OUString::number(static_cast<sal_Int32>(ptype.getTypeClass())),
nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
// The function only converts Sequences to SAFEARRAYS with elements of the type // specified by the parameter type. Everything else is forwarded to // anyToVariant(VARIANT* pVariant, const Any& rAny) // Param type must not be VT_BYREF template<class T> void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type)
{ try
{
HRESULT hr= S_OK;
OSL_ASSERT( (type & VT_BYREF) == 0); if (type & VT_ARRAY)
{
type ^= VT_ARRAY;
SAFEARRAY* ar= createUnoSequenceWrapper( rAny, type); if( ar)
{
VariantClear( pVariant);
pVariant->vt= ::sal::static_int_cast< VARTYPE, int >( VT_ARRAY | type );
pVariant->byref= ar;
}
} elseif(type == VT_VARIANT)
{
anyToVariant(pVariant, rAny);
} else
{
CComVariant var;
anyToVariant( &var, rAny); if(FAILED(hr = VariantChangeType(&var, &var, 0, type)))
{ if (hr == DISP_E_TYPEMISMATCH) throw CannotConvertException( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Cannot convert the value of type :\"" +
rAny.getValueTypeName() + "\" to the expected Automation type of VARTYPE: " +
OUString::number(static_cast<sal_Int32>(type)),
nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::anyToVariant \n" "Conversion of any with " +
rAny.getValueTypeName() + " to VARIANT with type: " + OUString::number(static_cast<sal_Int32>(type)) + " failed! Error code: " + OUString::number(hr));
sal_uInt64 value;
rAny >>= value;
pVariant->decVal.Lo64 = value; break;
} case TypeClass_TYPE:
{
Type type;
rAny >>= type;
CComVariant var; if (!createUnoTypeWrapper(type.getTypeName(), & var)) throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::anyToVariant \n" "Error during conversion of UNO type to Automation object!");
if (FAILED(VariantCopy(pVariant, &var))) throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::anyToVariant \n" "Unexpected error!"); break;
} default: //TypeClass_SERVICE: //TypeClass_EXCEPTION: //When an InvocationTargetException is thrown when calling XInvocation::invoke //on a UNO object, then the target exception is directly used to create a //EXEPINFO structure //TypeClass_TYPEDEF //TypeClass_ANY: //TypeClass_UNKNOWN: //TypeClass_MODULE: throw CannotConvertException( "[automation bridge]UnoConversionUtilities::anyToVariant\n" "There is no conversion for this UNO type to an Automation type." "The destination type class is the type class of the UNO " "argument which was to be converted.",
Reference<XInterface>(), rAny.getValueTypeClass(),
FailReason::TYPE_NOT_SUPPORTED, 0);
break;
} if (bIllegal)
{ throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::anyToVariant\n" "The provided any of type\"" + rAny.getValueTypeName() + "\" is unappropriate for conversion!", Reference(), -1);
// Creates an SAFEARRAY of the specified element and if necessary // creates a SAFEARRAY with multiple dimensions. // Used by sal_Bool anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type); template<class T>
SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype)
{ if (rSeq.getValueTypeClass() != TypeClass_SEQUENCE) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper \n" "The any does not contain a sequence!", nullptr, 0); if (elemtype == VT_NULL || elemtype == VT_EMPTY) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper \n" "No element type supplied!",nullptr, -1);
SAFEARRAY* pArray= nullptr; // Get the dimensions. This is done by examining the type name string // The count of brackets determines the dimensions.
OUString sTypeName= rSeq.getValueTypeName();
sal_Int32 dims=0; for(sal_Int32 lastIndex=0;(lastIndex= sTypeName.indexOf( L'[', lastIndex)) != -1; lastIndex++,dims++);
//get the maximum number of elements per dimensions and the typedescription of the elements
Sequence<sal_Int32> seqElementCounts( dims);
TypeDescription elementTypeDesc;
getElementCountAndTypeOfSequence( rSeq, 1, seqElementCounts, elementTypeDesc );
if( elementTypeDesc.is() )
{ // set up the SAFEARRAY
std::unique_ptr<SAFEARRAYBOUND[]> sarSafeArrayBound(new SAFEARRAYBOUND[dims]);
SAFEARRAYBOUND* prgsabound= sarSafeArrayBound.get(); for( sal_Int32 i=0; i < dims; i++)
{ //prgsabound[0] is the right most dimension
prgsabound[dims - i - 1].lLbound = 0;
prgsabound[dims - i - 1].cElements = seqElementCounts[i];
}
typelib_TypeDescription* rawTypeDesc= elementTypeDesc.get();
sal_Int32 elementSize= rawTypeDesc->nSize;
size_t oleElementSize= getOleElementSize( elemtype); // SafeArrayCreate clears the memory for the data itself.
pArray = SafeArrayCreate(elemtype, dims, prgsabound);
// convert the Sequence's elements and populate the SAFEARRAY if( pArray)
{ // Iterate over every Sequence that contains the actual elements void* pSAData; if( SUCCEEDED( SafeArrayAccessData( pArray, &pSAData)))
{ const sal_Int32* parElementCount= seqElementCounts.getConstArray();
uno_Sequence * pMultiSeq= *static_cast<uno_Sequence* const*>(rSeq.getValue());
sal_Int32 dimsSeq= dims - 1;
// arDimSeqIndices contains the current index of a block of data. // E.g. Sequence<Sequence<sal_Int32>> , the index would refer to Sequence<sal_Int32> // In this case arDimSeqIndices would have the size 1. That is the elements are not counted // but the Sequences that contain those elements. // The indices are 0 based
std::unique_ptr<sal_Int32[]> sarDimsSeqIndices;
sal_Int32* arDimsSeqIndices= nullptr; if( dimsSeq > 0)
{
sarDimsSeqIndices.reset(new sal_Int32[dimsSeq]);
arDimsSeqIndices = sarDimsSeqIndices.get();
memset( arDimsSeqIndices, 0, sizeof( sal_Int32 ) * dimsSeq);
}
do
{ // Get the Sequence at the current index , see arDimsSeqIndices
uno_Sequence * pCurrentSeq= pMultiSeq;
sal_Int32 curDim=1; // 1 based bool skipSeq= false; while( curDim <= dimsSeq )
{ // get the Sequence at the index if valid if( pCurrentSeq->nElements > arDimsSeqIndices[ curDim - 1] ) // don't point to Nirvana
{ // size of Sequence is 4
sal_Int32 offset= arDimsSeqIndices[ curDim - 1] * 4;
pCurrentSeq= *reinterpret_cast<uno_Sequence**>(&pCurrentSeq->elements[ offset]);
curDim++;
} else
{ // There is no Sequence at this index, so skip this index
skipSeq= true; break;
}
}
if( skipSeq) continue;
// Calculate the current position within the datablock of the SAFEARRAY // for the next Sequence.
sal_Int32 memOffset= 0;
sal_Int32 dimWeight= parElementCount[ dims - 1]; // size of the rightmost dimension for(sal_Int32 idims=0; idims < dimsSeq; idims++ )
{
memOffset+= arDimsSeqIndices[dimsSeq - 1 - idims] * dimWeight; // now determine the weight of the dimension to the left of the current. if( dims - 2 - idims >=0)
dimWeight*= parElementCount[dims - 2 - idims];
}
psaCurrentData= static_cast<char*>(pSAData) + memOffset * oleElementSize; // convert the Sequence and put the elements into the Safearray for( sal_Int32 i= 0; i < pCurrentSeq->nElements; i++)
{
Any unoElement( pCurrentSeq->elements + i * elementSize, rawTypeDesc ); // The any is being converted into a VARIANT which value is then copied // to the SAFEARRAY's data block. When copying one has to follow the rules for // copying certain types, as are VT_DISPATCH, VT_UNKNOWN, VT_VARIANT, VT_BSTR. // To increase performance, we just do a memcpy of VARIANT::byref. This is possible // because anyToVariant has already followed the copying rules. To make this // work there must not be a VariantClear. // One Exception is VARIANT because I don't know how VariantCopy works.
// Increments a multi dimensional index. // Returns true as long as the index has been successfully incremented, false otherwise. // False is also returned if an overflow of the most significant dimension occurs. E.g. // assume an array with the dimensions (2,2), then the lowest index is (0,0) and the highest // index is (1,1). If the function is being called with the index (1,1) then the overflow would // occur, with the result (0,0) and a sal_False as return value. // Param dimensions - number of dimensions // Param parDimensionsLength - The array contains the size of each dimension, that is the // size of the array equals the parameter dimensions. // The rightmost dimensions is the least significant one // ( parDimensionsLengths[ dimensions -1 ] ). // Param parMultiDimensionalIndex - The array contains the index. Each dimension index is // 0 based. template<class T> bool UnoConversionUtilities<T>::incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLengths,
sal_Int32 * parMultidimensionalIndex)
{ if( dimensions < 1) returnfalse;
bool ret= true; bool carry= true; // to get into the while loop
sal_Int32 currentDimension= dimensions; //most significant is 1 while( carry)
{
parMultidimensionalIndex[ currentDimension - 1]++; // if carryover, set index to 0 and handle carry on a level above if( parMultidimensionalIndex[ currentDimension - 1] > (parDimensionLengths[ currentDimension - 1] - 1))
parMultidimensionalIndex[ currentDimension - 1]= 0; else
carry= false;
currentDimension --; // if dimensions drops below 1 and carry is set than then all indices are 0 again // this is signalled by returning sal_False if( currentDimension < 1 && carry)
{
carry= false;
ret= false;
}
} return ret;
}
// Determines the size of a certain OLE type. The function takes // only those types into account which are oleautomation types and // can have a value ( unless VT_NULL, VT_EMPTY, VT_ARRAY, VT_BYREF). // Currently used in createUnoSequenceWrapper to calculate addresses // for data within a SAFEARRAY. template<class T>
size_t UnoConversionUtilities<T>::getOleElementSize( VARTYPE type)
{
size_t size; switch( type)
{ case VT_BOOL: size= sizeof( VARIANT_BOOL);break; case VT_UI1: size= sizeof( unsignedchar);break; case VT_R8: size= sizeof( double);break; case VT_R4: size= sizeof( float);break; case VT_I2: size= sizeof( short);break; case VT_I4: size= sizeof( long);break; case VT_BSTR: size= sizeof( BSTR); break; case VT_ERROR: size= sizeof( SCODE); break; case VT_DISPATCH: case VT_UNKNOWN: size= sizeof( IUnknown*); break; case VT_VARIANT: size= sizeof( VARIANT);break; default: size= 0;
} return size;
}
//If a Sequence is being converted into a SAFEARRAY then we possibly have // to create a SAFEARRAY with multiple dimensions. This is the case when a // Sequence contains Sequences ( Sequence< Sequence < XXX > > ). The leftmost // Sequence in the declaration is assumed to represent dimension 1. Because // all Sequence elements of a Sequence can have different length, we have to // determine the maximum length which is then the length of the respective // dimension. // getElementCountAndTypeOfSequence determines the length of each dimension and calls itself recursively // in the process. // param rSeq - an Any that has to contain a Sequence // param dim - the dimension for which the number of elements is being determined, // must be one. // param seqElementCounts - contains the maximum number of elements for each // dimension. Index 0 contains the number of dimension one. // After return the Sequence contains the maximum number of // elements for each dimension. // The length of the Sequence must equal the number of dimensions. // param typeClass - TypeClass of the element type that is no Sequence, e.g. // Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32) template<class T> void UnoConversionUtilities<T>::getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim,
Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc)
{
sal_Int32 dimCount= (*static_cast<uno_Sequence* const *>(rSeq.getValue()))->nElements; if( dimCount > seqElementCounts[ dim-1])
seqElementCounts.getArray()[ dim-1]= dimCount;
// we need the element type to construct the any that is // passed into getElementCountAndTypeOfSequence again
typelib_TypeDescription* pSeqDesc= nullptr;
rSeq.getValueTypeDescription( &pSeqDesc);
typelib_TypeDescriptionReference* pElementDescRef= reinterpret_cast<typelib_IndirectTypeDescription*>(pSeqDesc)->pType;
// if the elements are Sequences then do recursion if( dim < seqElementCounts.getLength() )
{
uno_Sequence* pSeq = *static_cast<uno_Sequence* const*>(rSeq.getValue());
uno_Sequence** arSequences= reinterpret_cast<uno_Sequence**>(pSeq->elements); for( sal_Int32 i=0; i < dimCount; i++)
{
uno_Sequence* arElement= arSequences[ i];
getElementCountAndTypeOfSequence( Any( &arElement, pElementDescRef), dim + 1 , seqElementCounts, typeDesc);
}
} else
{ // determine the element type ( e.g. Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32)
typeDesc= pElementDescRef;
}
typelib_typedescription_release( pSeqDesc);
}
if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE ) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoSequenceWrapper\n" "The UNO argument is not a sequence", nullptr, -1);
/* The argument rObj can contain - UNO struct - UNO interface - UNO interface created by this bridge (adapter factory) - UNO interface created by this bridge ( COM Wrapper)
pVar must be initialized.
*/ template<class T> void UnoConversionUtilities<T>::createUnoObjectWrapper(const Any & rObj, VARIANT * pVar)
{
MutexGuard guard(getBridgeMutex());
Reference<XInterface> xInt;
TypeClass tc = rObj.getValueTypeClass(); if (tc != TypeClass_INTERFACE && tc != TypeClass_STRUCT) throw IllegalArgumentException( "[automation bridge]UnoConversionUtilities::createUnoObjectWrapper \n" "Cannot create an Automation interface for a UNO type which is not " "a struct or interface!", nullptr, -1);
if (rObj.getValueTypeClass() == TypeClass_INTERFACE)
{ if (! (rObj >>= xInt)) throw IllegalArgumentException( "[automation bridge] UnoConversionUtilities::createUnoObjectWrapper\n " "Could not create wrapper object for UNO object!", nullptr, -1); //If XInterface is NULL, which is a valid value, then simply return NULL. if ( ! xInt.is())
{
pVar->vt = VT_UNKNOWN;
pVar->punkVal = nullptr; return;
} //make sure we have the main XInterface which is used with a map
xInt.set(xInt, UNO_QUERY); //If there is already a wrapper for the UNO object then use it
Reference<XInterface> xIntWrapper; // Does a UNO wrapper exist already ? auto it_uno = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get())); if(it_uno != UnoObjToWrapperMap.end())
{
xIntWrapper = it_uno->second; if (xIntWrapper.is())
{
convertSelfToCom(xIntWrapper, pVar); return;
}
} // Is the object a COM wrapper ( either XInvocation, or Adapter object) // or does it supply an IDispatch by its own ? else
{
Reference<XInterface> xIntComWrapper = xInt;
// Adapter? then get the COM wrapper to which the adapter delegates its calls auto it = AdapterToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get())); if( it != AdapterToWrapperMap.end() )
xIntComWrapper= reinterpret_cast<XInterface*>(it->second);
if (convertSelfToCom(xIntComWrapper, pVar)) return;
}
} // If we have no UNO wrapper nor the IDispatch yet then we have to create // a wrapper. For that we need an XInvocation.
// create an XInvocation using the invocation service
Reference<XInvocation> xInv;
Reference<XSingleServiceFactory> xInvFactory= getInvocationFactory(rObj); if (xInvFactory.is())
{
Sequence<Any> params(2);
params.getArray()[0] = rObj;
params.getArray()[1] <<= OUString("FromOLE");
Reference<XInterface> xInt2 = xInvFactory->createInstanceWithArguments(params);
xInv.set(xInt2, UNO_QUERY);
}
if (xInv.is())
{
Reference<XInterface> xNewWrapper = createUnoWrapperInstance();
Reference<css::lang::XInitialization> xInitWrapper(xNewWrapper, UNO_QUERY); if (xInitWrapper.is())
{
VARTYPE vartype= getVarType( rObj);
// put the newly created object into a map. If the same object will // be mapped again and there is already a wrapper then the old wrapper // will be used. if(xInt.is()) // only interfaces
UnoObjToWrapperMap[reinterpret_cast<sal_uIntPtr>(xInt.get())]= xNewWrapper;
convertSelfToCom(xNewWrapper, pVar); return;
}
}
}
// There is no need to support indirect values, since they're not supported by UNO if( FAILED(hr= VariantCopyInd( &var, pVariant))) // remove VT_BYREF throw BridgeRuntimeError( "[automation bridge] UnoConversionUtilities::variantToAny \n" "VariantCopyInd failed for reason : " + OUString::number(hr));
if ( ! convertValueObject( & var, rAny))
{ if ((var.vt & VT_ARRAY) > 0)
{
VARTYPE oleTypeFlags = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY );
Sequence<Any> unoSeq = createOleArrayWrapper(var.parray, oleTypeFlags);
rAny.setValue( &unoSeq, cppu::UnoType<decltype(unoSeq)>::get());
} else
{ switch (var.vt)
{ case VT_EMPTY:
rAny.setValue(nullptr, Type()); break; case VT_NULL:
rAny.setValue(nullptr, Type()); break; case VT_I2:
rAny.setValue( & var.iVal, cppu::UnoType<sal_Int16>::get()); break; case VT_I4:
rAny.setValue( & var.lVal, cppu::UnoType<sal_Int32>::get()); // necessary for use in JavaScript ( see "reduceRange") if( bReduceValueRange)
reduceRange(rAny); break; case VT_R4:
rAny.setValue( & var.fltVal, cppu::UnoType<float>::get()); break; case VT_R8:
rAny.setValue(& var.dblVal, cppu::UnoType<double>::get()); break; case VT_CY:
{
Currency cy(var.cyVal.int64);
rAny <<= cy; break;
} case VT_DATE:
{
Date d(var.date);
rAny <<= d; break;
} case VT_BSTR:
{
OUString b(o3tl::toU(var.bstrVal));
rAny.setValue( &b, cppu::UnoType<decltype(b)>::get()); break;
} case VT_UNKNOWN: case VT_DISPATCH:
{ //check if it is a UNO type
CComQIPtr<IUnoTypeWrapper> spType(static_cast<IUnknown*>(var.byref)); if (spType)
{
CComBSTR sName; if (FAILED(spType->get_Name(&sName))) throw BridgeRuntimeError( "[automation bridge]UnoConversionUtilities::variantToAny \n" "Failed to get the type name from a UnoTypeWrapper!");
Type type; if (!getType(sName, type))
{ throw CannotConvertException(
OUString::Concat("[automation bridge]UnoConversionUtilities::variantToAny \n" "A UNO type with the name: ") + o3tl::toU(LPCOLESTR(sName)) + "does not exist!",
nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
}
rAny <<= type;
} else
{
rAny = createOleObjectWrapper( & var);
} break;
} case VT_ERROR:
{
SCode scode(var.scode);
rAny <<= scode; break;
} case VT_BOOL:
{
rAny <<= (var.boolVal == VARIANT_TRUE); break;
} case VT_I1:
rAny.setValue( & var.cVal, cppu::UnoType<sal_Int8>::get()); break; case VT_UI1: // there is no unsigned char in UNO
rAny <<= sal_Int8(var.bVal); break; case VT_UI2:
rAny.setValue( & var.uiVal, cppu::UnoType<cppu::UnoUnsignedShortType>::get() ); break; case VT_UI4:
rAny.setValue( & var.ulVal, cppu::UnoType<sal_uInt32>::get()); break; case VT_INT:
rAny.setValue( & var.intVal, cppu::UnoType<sal_Int32>::get()); break; case VT_UINT:
rAny.setValue( & var.uintVal, cppu::UnoType<sal_uInt32>::get()); break; case VT_VOID:
rAny.setValue( nullptr, Type()); break; case VT_DECIMAL:
{
Decimal dec;
dec.Scale = var.decVal.scale;
dec.Sign = var.decVal.sign;
dec.LowValue = var.decVal.Lo32;
dec.MiddleValue = var.decVal.Mid32;
dec.HighValue = var.decVal.Hi32;
rAny <<= dec; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.