diff options
Diffstat (limited to 'extensions/source/ole')
-rw-r--r-- | extensions/source/ole/comifaces.hxx | 62 | ||||
-rw-r--r-- | extensions/source/ole/jscriptclasses.cxx | 312 | ||||
-rw-r--r-- | extensions/source/ole/jscriptclasses.hxx | 147 | ||||
-rw-r--r-- | extensions/source/ole/ole2uno.cxx | 47 | ||||
-rw-r--r-- | extensions/source/ole/ole2uno.hxx | 70 | ||||
-rw-r--r-- | extensions/source/ole/oleautobridge.component | 37 | ||||
-rw-r--r-- | extensions/source/ole/oledll.cxx | 69 | ||||
-rw-r--r-- | extensions/source/ole/oleobjw.cxx | 2513 | ||||
-rw-r--r-- | extensions/source/ole/oleobjw.hxx | 244 | ||||
-rw-r--r-- | extensions/source/ole/olethread.cxx | 69 | ||||
-rw-r--r-- | extensions/source/ole/servprov.cxx | 547 | ||||
-rw-r--r-- | extensions/source/ole/servprov.hxx | 184 | ||||
-rw-r--r-- | extensions/source/ole/servreg.cxx | 94 | ||||
-rw-r--r-- | extensions/source/ole/unoconversionutilities.hxx | 2363 | ||||
-rw-r--r-- | extensions/source/ole/unoobjw.cxx | 3436 | ||||
-rw-r--r-- | extensions/source/ole/unoobjw.hxx | 268 | ||||
-rw-r--r-- | extensions/source/ole/unotypewrapper.cxx | 160 | ||||
-rw-r--r-- | extensions/source/ole/unotypewrapper.hxx | 81 | ||||
-rw-r--r-- | extensions/source/ole/wincrap.hxx | 63 | ||||
-rw-r--r-- | extensions/source/ole/windata.hxx | 195 |
20 files changed, 10961 insertions, 0 deletions
diff --git a/extensions/source/ole/comifaces.hxx b/extensions/source/ole/comifaces.hxx new file mode 100644 index 0000000000..51e955dd60 --- /dev/null +++ b/extensions/source/ole/comifaces.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <com/sun/star/uno/XInterface.hpp> + +using namespace com::sun::star::uno; + +MIDL_INTERFACE("e40a2331-3bc1-11d4-8321-005004526ab4") +IJScriptValueObject: public IUnknown +{ + STDMETHOD( Set)( VARIANT type, VARIANT value)= 0; + STDMETHOD( Get)( VARIANT *val)= 0; + STDMETHOD( InitOutParam)()= 0; + STDMETHOD( InitInOutParam)( VARIANT type, VARIANT value)= 0; + STDMETHOD( IsOutParam)( VARIANT_BOOL * flag)= 0; + STDMETHOD( IsInOutParam)( VARIANT_BOOL * flag)= 0; + STDMETHOD( GetValue)( BSTR* type, VARIANT *value)= 0; + +protected: + ~IJScriptValueObject() {} +}; + +MIDL_INTERFACE("7B5C3410-66FA-11d4-832A-005004526AB4") +IUnoObjectWrapper: public IUnknown +{ + STDMETHOD( getWrapperXInterface)( Reference<XInterface>* pInt)=0; + STDMETHOD( getOriginalUnoObject)( Reference<XInterface>* pInt)=0; + STDMETHOD( getOriginalUnoStruct)( Any * pStruct)=0; + +protected: + ~IUnoObjectWrapper() {} +}; + +MIDL_INTERFACE("8BB66591-A544-4de9-822C-57AB57BCED1C") +IUnoTypeWrapper: public IUnknown +{ + STDMETHOD(put_Name)(BSTR val) = 0; + STDMETHOD(get_Name)(BSTR* pVal) = 0; + +protected: + ~IUnoTypeWrapper() {} +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/jscriptclasses.cxx b/extensions/source/ole/jscriptclasses.cxx new file mode 100644 index 0000000000..8fc371c4c2 --- /dev/null +++ b/extensions/source/ole/jscriptclasses.cxx @@ -0,0 +1,312 @@ +/* -*- 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 "jscriptclasses.hxx" + + +// JScriptValue + +JScriptValue::JScriptValue(): m_bOutParam(false), m_bInOutParam(false) +{ +} + +JScriptValue::~JScriptValue() +{ +} + + +// JScriptValue, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetTypeInfoCount(UINT* /*pctinfo*/) +{ + return E_NOTIMPL; +} + +// JScriptValue, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetTypeInfo( UINT /*iTInfo*/, + LCID /*lcid*/, + ITypeInfo** /*ppTInfo*/) +{ + return E_NOTIMPL; +} + +// JScriptValue, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetIDsOfNames( REFIID /*riid*/, + LPOLESTR *rgszNames, + UINT /*cNames*/, + LCID /*lcid*/, + DISPID *rgDispId) +{ + if( !rgDispId) + return E_POINTER; + + + HRESULT ret= S_OK; + CComBSTR name(*rgszNames); + name.ToLower(); + + if( name == CComBSTR( L"set") ) + *rgDispId= 1; + else if( name == CComBSTR( L"get") ) + *rgDispId= 2; + else if( name == CComBSTR( L"initoutparam") ) + *rgDispId= 3; + else if( name == CComBSTR( L"initinoutparam") ) + *rgDispId= 4; + else + ret= DISP_E_UNKNOWNNAME; + + return ret; +} + +// JScriptValue, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Invoke( DISPID dispIdMember, + REFIID /*riid*/, + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO* /*pExcepInfo*/, + UINT* /*puArgErr*/) +{ + if( pDispParams->cNamedArgs) + return DISP_E_NONAMEDARGS; + + + HRESULT ret= S_OK; + switch( dispIdMember) + { + case 0: // DISPID_VALUE + if( wFlags & DISPATCH_PROPERTYGET && pVarResult) + { + if( FAILED( VariantCopy( pVarResult, &m_varValue))) + ret= E_FAIL; + } + else + ret= E_POINTER; + break; + case 1: + if( wFlags & DISPATCH_METHOD) + ret= Set( pDispParams->rgvarg[1], pDispParams->rgvarg[0]); + if( FAILED( ret)) + ret= DISP_E_EXCEPTION; + break; + case 2: + if( wFlags & DISPATCH_METHOD) + ret= Get( pVarResult); + if( FAILED( ret)) + ret= DISP_E_EXCEPTION; + break; + case 3: + if( wFlags & DISPATCH_METHOD) + ret= InitOutParam(); + if( FAILED( ret)) + ret= DISP_E_EXCEPTION; + break; + case 4: + if( wFlags & DISPATCH_METHOD) + ret= InitInOutParam( pDispParams->rgvarg[1], pDispParams->rgvarg[0]); + if( FAILED( ret)) + ret= DISP_E_EXCEPTION; + break; + default: + ret= DISP_E_MEMBERNOTFOUND; + break; + } + + return ret; +} + +// JScriptValue, IScriptOutParam----------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Set( VARIANT type, VARIANT value) +{ + Lock(); + m_varValue.Clear(); + HRESULT hr= VariantCopyInd( &m_varValue, &value); + VARIANT var; + VariantInit( &var); + if( SUCCEEDED( hr= VariantChangeType( &var, &type, 0, VT_BSTR))) + m_bstrType= var.bstrVal; + Unlock(); + return hr; +} +// JScriptValue, IScriptOutParam----------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Get( VARIANT *val) +{ + Lock(); + if( !val) + return E_POINTER; + HRESULT hr= VariantCopy( val, &m_varValue); + Unlock(); + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::InitOutParam() +{ + Lock(); + m_varValue.Clear(); + m_bOutParam= true; + m_bInOutParam= false; + Unlock(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::InitInOutParam( VARIANT type, VARIANT value) +{ + Lock(); + m_bInOutParam= true; + m_bOutParam= false; + Unlock(); + return Set( type, value); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::IsOutParam( VARIANT_BOOL * flag) +{ + Lock(); + if( !flag) + return E_POINTER; + *flag= m_bOutParam ? VARIANT_TRUE : VARIANT_FALSE; + Unlock(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::IsInOutParam( VARIANT_BOOL * flag) +{ + Lock(); + if( !flag) + return E_POINTER; + *flag= m_bInOutParam ? VARIANT_TRUE : VARIANT_FALSE; + Unlock(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetValue( BSTR* type, VARIANT *value) +{ + Lock(); + if( !type || !value) + return E_POINTER; + HRESULT hr; + if( SUCCEEDED( hr= m_bstrType.CopyTo( type))) + hr= VariantCopy( value, &m_varValue); + Unlock(); + return hr; +} + + +// JScriptOutValue + + +JScriptOutParam::JScriptOutParam() +{ +} + +JScriptOutParam::~JScriptOutParam() +{ +} + + +// JScriptOutParam, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetTypeInfoCount(UINT* /*pctinfo*/) +{ + return E_NOTIMPL; +} + +// JScriptOutParam, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetTypeInfo( UINT /*iTInfo*/, + LCID /*lcid*/, + ITypeInfo** /*ppTInfo*/) +{ + return E_NOTIMPL; +} + +// JScriptOutParam, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetIDsOfNames( REFIID /*riid*/, + LPOLESTR *rgszNames, + UINT /*cNames*/, + LCID /*lcid*/, + DISPID *rgDispId) +{ + if( !rgDispId) + return E_POINTER; + + + HRESULT ret= S_OK; + CComBSTR name(*rgszNames); + name.ToLower(); + + if( name == CComBSTR( L"0") ) + *rgDispId= 1; + else + ret= DISP_E_UNKNOWNNAME; + + return ret; +} + +// JScriptOutParam, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::Invoke( DISPID dispIdMember, + REFIID /*riid*/, + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO* /*pExcepInfo*/, + UINT* /*puArgErr*/) +{ + HRESULT ret= S_OK; + switch( dispIdMember) + { + case 0: // DISPID_VALUE + if( wFlags & DISPATCH_PROPERTYGET && pVarResult) + { + if( FAILED( VariantCopy( pVarResult, &m_varValue))) + ret= E_FAIL; + } + else if( wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF) + { + m_varValue.Clear(); + if( FAILED( VariantCopyInd( &m_varValue, &pDispParams->rgvarg[0]))) + ret= E_FAIL; + } + else + ret= E_POINTER; + break; + case 1: + if( wFlags & DISPATCH_PROPERTYGET && pVarResult) + { + if( FAILED( VariantCopy( pVarResult, &m_varValue))) + ret= E_FAIL; + } + else if( wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF) + { + m_varValue.Clear(); + if( FAILED( VariantCopyInd( &m_varValue, &pDispParams->rgvarg[0]))) + ret= E_FAIL; + } + else + ret= E_POINTER; + break; + + default: + ret= DISP_E_MEMBERNOTFOUND; + break; + } + + return ret; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/jscriptclasses.hxx b/extensions/source/ole/jscriptclasses.hxx new file mode 100644 index 0000000000..cef993ed04 --- /dev/null +++ b/extensions/source/ole/jscriptclasses.hxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "wincrap.hxx" + +#include "comifaces.hxx" + + +// Sequences are represented by prepending "[]", e.g. []char, [][]byte, [][][]object, etc. + +// To make a JScriptValue object to an out parameter, call +// "InitOutParam" and to make it a in/out parameter call +// "InitInOutParam" + +// If the object represents an out parameter then the value can after the call +// be retrieved by "Get". + +// From JavaScript the functions Get, Set, InitOutParam and InitInOutParam are +// used, that is they are accessible through IDispatch. The functions are used +// by the bridge. + +class JScriptValue: + public CComObjectRootEx<CComMultiThreadModel>, + public IJScriptValueObject, + public IDispatch +{ +public: + JScriptValue(); + virtual ~JScriptValue(); + + BEGIN_COM_MAP(JScriptValue) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IJScriptValueObject) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + // IDispatch ------------------------------------------- + STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override; + + STDMETHOD( GetTypeInfo)( UINT iTInfo, + LCID lcid, + ITypeInfo **ppTInfo) override; + + STDMETHOD( GetIDsOfNames)( REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId) override; + + STDMETHOD( Invoke)( DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) override; + // IJScriptOutParam -------------------------------------- + + STDMETHOD( Set)( VARIANT type, VARIANT value) override; + STDMETHOD( Get)( VARIANT *val) override; + STDMETHOD( InitOutParam)() override; + STDMETHOD( InitInOutParam)( VARIANT type, VARIANT value) override; + STDMETHOD( IsOutParam)( VARIANT_BOOL * flag) override; + STDMETHOD( IsInOutParam)( VARIANT_BOOL * flag) override; + STDMETHOD( GetValue)( BSTR* type, VARIANT *value) override; + + + CComVariant m_varValue; + CComBSTR m_bstrType; + bool m_bOutParam: 1; + bool m_bInOutParam: 1; + +}; + +// If a class is implemented in JScript, then its method +class JScriptOutParam: + public CComObjectRootEx<CComMultiThreadModel>, + public IDispatch +{ +public: + JScriptOutParam(); + virtual ~JScriptOutParam(); + + BEGIN_COM_MAP(JScriptOutParam) + COM_INTERFACE_ENTRY(IDispatch) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + // IDispatch ------------------------------------------- + STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override; + + STDMETHOD( GetTypeInfo)( UINT iTInfo, + LCID lcid, + ITypeInfo **ppTInfo) override; + + STDMETHOD( GetIDsOfNames)( REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId) override; + + STDMETHOD( Invoke)( DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) override; + + +private: + CComVariant m_varValue; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/ole2uno.cxx b/extensions/source/ole/ole2uno.cxx new file mode 100644 index 0000000000..f9eef5125e --- /dev/null +++ b/extensions/source/ole/ole2uno.cxx @@ -0,0 +1,47 @@ +/* -*- 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 <osl/getglobalmutex.hxx> +#include <rtl/instance.hxx> +#include "ole2uno.hxx" + +using namespace osl; + +namespace { + +struct MutexInit +{ + Mutex * operator () () + { + static Mutex aInstance; + return &aInstance; + } +}; + +} + +Mutex * getBridgeMutex() +{ + return rtl_Instance< Mutex, MutexInit, ::osl::MutexGuard, + ::osl::GetGlobalMutex >::create( + MutexInit(), ::osl::GetGlobalMutex()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/ole2uno.hxx b/extensions/source/ole/ole2uno.hxx new file mode 100644 index 0000000000..5fcf2fd96a --- /dev/null +++ b/extensions/source/ole/ole2uno.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "wincrap.hxx" + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/bridge/XBridgeSupplier2.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> +#include <com/sun/star/reflection/InvocationTargetException.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <rtl/process.h> +#include <rtl/uuid.h> + +#define UNO_2_OLE_EXCEPTIONCODE 1001 +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::registry; +using namespace com::sun::star::reflection; +using namespace com::sun::star::beans; +using namespace osl; + +VARTYPE getVarType(const Any& val); +/* creates a Type object for a given type name. + + The function returns false if the name does not represent + a valid type. +*/ +bool getType(BSTR name, Type& type); +void o2u_attachCurrentThread(); + +class BridgeRuntimeError +{ +public: + explicit BridgeRuntimeError(const OUString& sMessage) + : message(sMessage) + { + } + OUString message; +}; + +Mutex* getBridgeMutex(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/oleautobridge.component b/extensions/source/ole/oleautobridge.component new file mode 100644 index 0000000000..09f7621c23 --- /dev/null +++ b/extensions/source/ole/oleautobridge.component @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="oleautobridge" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.ole.OleClient"> + <service name="com.sun.star.bridge.OleObjectFactory"/> + <service name="com.sun.star.bridge.oleautomation.Factory"/> + </implementation> + <implementation name="com.sun.star.comp.ole.OleConverter2"> + <service name="com.sun.star.bridge.OleBridgeSupplier2"/> + <service name="com.sun.star.bridge.oleautomation.BridgeSupplier"/> + </implementation> + <implementation name="com.sun.star.comp.ole.OleConverterVar1"> + <service name="com.sun.star.bridge.OleBridgeSupplierVar1"/> + </implementation> + <implementation name="com.sun.star.comp.ole.OleServer"> + <service name="com.sun.star.bridge.OleApplicationRegistration"/> + <service name="com.sun.star.bridge.oleautomation.ApplicationRegistration"/> + </implementation> +</component> diff --git a/extensions/source/ole/oledll.cxx b/extensions/source/ole/oledll.cxx new file mode 100644 index 0000000000..9ed73a3304 --- /dev/null +++ b/extensions/source/ole/oledll.cxx @@ -0,0 +1,69 @@ +/* -*- 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 . + */ + +#define STRICT +#define _WIN32_DCOM + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wattributes" +#pragma clang diagnostic ignored "-Wdelete-incomplete" +#pragma clang diagnostic ignored "-Wextra" +#pragma clang diagnostic ignored "-Wint-to-pointer-cast" +#pragma clang diagnostic ignored "-Winvalid-noreturn" +#pragma clang diagnostic ignored "-Wmicrosoft" +#pragma clang diagnostic ignored "-Wnon-pod-varargs" +#endif + +#include <atlbase.h> +static CComModule _Module; +#include <atlcom.h> + +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +BEGIN_OBJECT_MAP(ObjectMap) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif +END_OBJECT_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +// DLL Entry Point + +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + _Module.Init(ObjectMap, hInstance); + DisableThreadLibraryCalls(hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + _Module.Term(); + } + return TRUE; // ok +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/oleobjw.cxx b/extensions/source/ole/oleobjw.cxx new file mode 100644 index 0000000000..85f410c546 --- /dev/null +++ b/extensions/source/ole/oleobjw.cxx @@ -0,0 +1,2513 @@ +/* -*- 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 "ole2uno.hxx" +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <osl/diagnose.h> +#include <osl/doublecheckedlocking.h> +#include <osl/thread.h> + +#include <memory> +#include <string_view> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/FailReason.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> + +#include <com/sun/star/bridge/oleautomation/NamedArgument.hpp> +#include <com/sun/star/bridge/oleautomation/PropertyPutArgument.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include <typelib/typedescription.hxx> +#include <rtl/uuid.h> +#include <rtl/ustring.hxx> + +#include "jscriptclasses.hxx" + +#include "oleobjw.hxx" +#include "unoobjw.hxx" +#include <stdio.h> +using namespace osl; +using namespace cppu; +using namespace com::sun::star::script; +using namespace com::sun::star::lang; +using namespace com::sun::star::bridge; +using namespace com::sun::star::bridge::oleautomation; +using namespace com::sun::star::bridge::ModelDependent; +using namespace ::com::sun::star; + + +#define JSCRIPT_ID_PROPERTY L"_environment" +#define JSCRIPT_ID L"jscript" + +// key: XInterface pointer created by Invocation Adapter Factory +// value: XInterface pointer to the wrapper class. +// Entries to the map are made within +// Any createOleObjectWrapper(IUnknown* pUnknown, const Type& aType); +// Entries are being deleted if the wrapper class's destructor has been +// called. +// Before UNO object is wrapped to COM object this map is checked +// to see if the UNO object is already a wrapper. +std::unordered_map<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap; +// key: XInterface of the wrapper object. +// value: XInterface of the Interface created by the Invocation Adapter Factory. +// A COM wrapper is responsible for removing the corresponding entry +// in AdapterToWrapperMap if it is being destroyed. Because the wrapper does not +// know about its adapted interface it uses WrapperToAdapterMap to get the +// adapted interface which is then used to locate the entry in AdapterToWrapperMap. +std::unordered_map<sal_uIntPtr,sal_uIntPtr> WrapperToAdapterMap; + +std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap; + +IUnknownWrapper::IUnknownWrapper( Reference<XMultiServiceFactory> const & xFactory, + sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass): + UnoConversionUtilities<IUnknownWrapper>( xFactory, unoWrapperClass, comWrapperClass), + m_pxIdlClass( nullptr), m_eJScript( JScriptUndefined), + m_bComTlbIndexInit(false), m_bHasDfltMethod(false), m_bHasDfltProperty(false) +{ +} + + +IUnknownWrapper::~IUnknownWrapper() +{ + o2u_attachCurrentThread(); + MutexGuard guard(getBridgeMutex()); + XInterface * xIntRoot = static_cast<OWeakObject *>(this); +#if OSL_DEBUG_LEVEL > 0 + acquire(); // make sure we don't delete us twice because of Reference + OSL_ASSERT( Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY).get() == xIntRoot ); +#endif + + // remove entries in global maps + auto it= WrapperToAdapterMap.find( reinterpret_cast<sal_uIntPtr>(xIntRoot)); + if( it != WrapperToAdapterMap.end()) + { + sal_uIntPtr adapter= it->second; + + AdapterToWrapperMap.erase( adapter); + WrapperToAdapterMap.erase( it); + } + + auto it_c= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_spUnknown.p)); + if(it_c != ComPtrToWrapperMap.end()) + ComPtrToWrapperMap.erase(it_c); +} + +Any IUnknownWrapper::queryInterface(const Type& t) +{ + if (t == cppu::UnoType<XDefaultMethod>::get() && !m_bHasDfltMethod ) + return Any(); + if (t == cppu::UnoType<XDefaultProperty>::get() && !m_bHasDfltProperty ) + return Any(); + if ( ( t == cppu::UnoType<XInvocation>::get() || t == cppu::UnoType<XAutomationInvocation>::get() ) && !m_spDispatch) + return Any(); + // XDirectInvocation seems to be an oracle replacement for XAutomationInvocation, however it is flawed especially wrt. assumptions about whether to invoke a + // Put or Get property, the implementation code has no business guessing that, it's up to the caller to decide that. Worse XDirectInvocation duplicates lots of code. + // XAutomationInvocation provides separate calls for put& get + // properties. Note: Currently the basic runtime doesn't call put properties directly, it should... after all the basic runtime should know whether it is calling a put or get property. + // For the moment for ease of merging we will let the XDirectInvoke and XAuthomationInvocation interfaces stay side by side (and for the moment at least I would prefer the basic + // runtime to call XAutomationInvocation instead of XDirectInvoke + return WeakImplHelper<XBridgeSupplier2, + XInitialization, XAutomationObject, XDefaultProperty, XDefaultMethod, XDirectInvocation, XAutomationInvocation >::queryInterface(t); +} + +Reference<XIntrospectionAccess> SAL_CALL IUnknownWrapper::getIntrospection() +{ + Reference<XIntrospectionAccess> ret; + + return ret; +} + +Any SAL_CALL IUnknownWrapper::invokeGetProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) +{ + Any aResult; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( !aDescGet ) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + aResult = invokeWithDispIdComTlb( aDescGet, aPropertyName, aParams, aOutParamIndex, aOutParam ); + } + catch ( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invokeGetProperty ! Message : \n " + + e.Message, + nullptr, anyEx ); + } + return aResult; +} + +Any SAL_CALL IUnknownWrapper::invokePutProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) +{ + Any aResult; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( !aDescPut ) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + aResult = invokeWithDispIdComTlb( aDescPut, aPropertyName, aParams, aOutParamIndex, aOutParam ); + } + catch ( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invokePutProperty ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + return aResult; +} + + +Any SAL_CALL IUnknownWrapper::invoke( const OUString& aFunctionName, + const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, + Sequence< Any >& aOutParam ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + Any ret; + + try + { + o2u_attachCurrentThread(); + + TypeDescription methodDesc; + getMethodInfo(aFunctionName, methodDesc); + if( methodDesc.is()) + { + ret = invokeWithDispIdUnoTlb(aFunctionName, + aParams, + aOutParamIndex, + aOutParam); + } + else + { + ret= invokeWithDispIdComTlb( aFunctionName, + aParams, + aOutParamIndex, + aOutParam); + } + } + catch (const IllegalArgumentException &) + { + throw; + } + catch (const CannotConvertException &) + { + throw; + } + catch (const BridgeRuntimeError & e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::invoke ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch(...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::Invoke !"); + } + return ret; +} + +void SAL_CALL IUnknownWrapper::setValue( const OUString& aPropertyName, + const Any& aValue ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + try + { + o2u_attachCurrentThread(); + + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + //check if there is such a property at all or if it is read only + if ( ! aDescPut && ! aDescGet && ! aVarDesc) + { + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + + if ( (! aDescPut && aDescGet) + || (aVarDesc && aVarDesc->wVarFlags == VARFLAG_FREADONLY) ) + { + //read-only + SAL_WARN( "extensions.olebridge", "[automation bridge] Property " << aPropertyName << " is read-only"); + // ignore silently + return; + } + + HRESULT hr= S_OK; + DISPPARAMS dispparams; + CComVariant varArg; + CComVariant varRefArg; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + + // converting UNO value to OLE variant + DISPID dispidPut= DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = &dispidPut; + dispparams.cArgs = 1; + dispparams.cNamedArgs = 1; + dispparams.rgvarg = & varArg; + + OSL_ASSERT(aDescPut || aVarDesc); + + VARTYPE vt = 0; + DISPID dispid = 0; + INVOKEKIND invkind = INVOKE_PROPERTYPUT; + //determine the expected type, dispid, invoke kind (DISPATCH_PROPERTYPUT, + //DISPATCH_PROPERTYPUTREF) + if (aDescPut) + { + vt = getElementTypeDesc(& aDescPut->lprgelemdescParam[0].tdesc); + dispid = aDescPut->memid; + invkind = aDescPut->invkind; + } + else + { + vt = getElementTypeDesc( & aVarDesc->elemdescVar.tdesc); + dispid = aVarDesc->memid; + if (vt == VT_UNKNOWN || vt == VT_DISPATCH || + (vt & VT_ARRAY) || (vt & VT_BYREF)) + { + invkind = INVOKE_PROPERTYPUTREF; + } + } + + // convert the uno argument + if (vt & VT_BYREF) + { + anyToVariant( & varRefArg, aValue, ::sal::static_int_cast< VARTYPE, int >( vt ^ VT_BYREF ) ); + varArg.vt = vt; + if( (vt & VT_TYPEMASK) == VT_VARIANT) + varArg.byref = & varRefArg; + else if ((vt & VT_TYPEMASK) == VT_DECIMAL) + varArg.byref = & varRefArg.decVal; + else + varArg.byref = & varRefArg.byref; + } + else + { + anyToVariant(& varArg, aValue, vt); + } + // call to IDispatch + hr = m_spDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, ::sal::static_int_cast< WORD, INVOKEKIND >( invkind ), + &dispparams, & varResult, & excepinfo, &uArgErr); + + // lookup error code + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw RuntimeException(); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException(); + break; + case DISP_E_EXCEPTION: + throw InvocationTargetException(); + break; + case DISP_E_MEMBERNOTFOUND: + throw UnknownPropertyException(); + break; + case DISP_E_NONAMEDARGS: + throw RuntimeException(); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )) ; + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::UNKNOWN, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException(); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException(); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + } + catch (const CannotConvertException &) + { + throw; + } + catch (const UnknownPropertyException &) + { + throw; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::setValue ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch (...) + { + throw RuntimeException( + "[automation bridge] unexpected exception in " + "IUnknownWrapper::setValue !"); + } +} + +Any SAL_CALL IUnknownWrapper::getValue( const OUString& aPropertyName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + Any ret; + try + { + o2u_attachCurrentThread(); + ITypeInfo * pInfo = getTypeInfo(); + // I was going to implement an XServiceInfo interface to allow the type + // of the automation object to be exposed... but it seems + // from looking at comments in the code that it is possible for + // this object to actually wrap a UNO object ( I guess if automation is + // used from MSO to create Openoffice objects ) Therefore, those objects + // will more than likely already have their own XServiceInfo interface. + // Instead here I chose a name that should be illegal both in COM and + // UNO ( from an IDL point of view ) therefore I think this is a safe + // hack + if ( aPropertyName == "$GetTypeName" ) + { + if ( pInfo && m_sTypeName.getLength() == 0 ) + { + m_sTypeName = "IDispatch"; + CComBSTR sName; + + if ( SUCCEEDED( pInfo->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) ) + { + OUString sTmp( o3tl::toU(LPCOLESTR(sName))); + if ( sTmp.startsWith("_") ) + sTmp = sTmp.copy(1); + // do we own the memory for pTypeLib, msdn doc is vague + // I'll assume we do + CComPtr< ITypeLib > pTypeLib; + unsigned int index; + if ( SUCCEEDED( pInfo->GetContainingTypeLib( &pTypeLib.p, &index )) ) + { + if ( SUCCEEDED( pTypeLib->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) ) + { + OUString sLibName( o3tl::toU(LPCOLESTR(sName))); + m_sTypeName = sLibName + "." + sTmp; + + } + } + } + + } + ret <<= m_sTypeName; + return ret; + } + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc); + if ( ! aDescGet && ! aDescPut && ! aVarDesc) + { + //property not found + OUString msg("[automation bridge]Property \"" + aPropertyName + + "\" is not supported"); + throw UnknownPropertyException(msg); + } + // write-only should not be possible + OSL_ASSERT( aDescGet || ! aDescPut); + + HRESULT hr; + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + DISPID dispid; + if (aDescGet) + dispid = aDescGet->memid; + else if (aVarDesc) + dispid = aVarDesc->memid; + else + dispid = aDescPut->memid; + + hr = m_spDispatch->Invoke(dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_PROPERTYGET, + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (hr == S_OK) + { + // If the com object implements uno interfaces then we have + // to convert the attribute into the expected type. + TypeDescription attrInfo; + getAttributeInfo(aPropertyName, attrInfo); + if( attrInfo.is() ) + variantToAny( &varResult, ret, Type( attrInfo.get()->pWeakRef)); + else + variantToAny(&varResult, ret); + } + + // lookup error code + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + case DISP_E_BADVARTYPE: + case DISP_E_EXCEPTION: + throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + case DISP_E_MEMBERNOTFOUND: + throw UnknownPropertyException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + default: + throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription))); + break; + } + } + catch ( const UnknownPropertyException& ) + { + throw; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::getValue ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + catch (...) + { + throw RuntimeException( + "[automation bridge] unexpected exception in " + "IUnknownWrapper::getValue !"); + } + return ret; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasMethod( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + bool ret = false; + + try + { + o2u_attachCurrentThread(); + ITypeInfo* pInfo = getTypeInfo(); + FuncDesc aDesc(pInfo); + getFuncDesc(aName, & aDesc); + // Automation properties can have arguments. Those are treated as methods and + //are called through XInvocation::invoke. + if ( ! aDesc) + { + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc( aName, & aDescGet, & aDescPut, & aVarDesc); + if ((aDescGet && aDescGet->cParams > 0) + || (aDescPut && aDescPut->cParams > 0)) + ret = true; + } + else + ret = true; + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasMethod ! Message : \n" + + e.Message, + nullptr, anyEx ); + } + catch (...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasMethod !"); + } + return ret; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasProperty( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException("[automation bridge] The object does not have an " + "IDispatch interface"); + } + bool ret = false; + try + { + o2u_attachCurrentThread(); + + ITypeInfo * pInfo = getTypeInfo(); + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(aName, & aDescGet, & aDescPut, & aVarDesc); + + // we should probably just check the func kind + // basic has been modified to handle properties ( 'get' ) props at + // least with parameters + // additionally you can call invoke(Get|Set)Property on the bridge + // you can determine if a property has parameter is hasMethod + // returns true for the name + if (aVarDesc + || aDescPut + || aDescGet ) + { + ret = true; + } + } + catch (const BridgeRuntimeError& e) + { + throw RuntimeException(e.message); + } + catch (const Exception & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasProperty ! Message : \n" + + e.Message, + nullptr, anyEx ); + + } + catch (...) + { + throw RuntimeException("[automation bridge] unexpected exception in " + "IUnknownWrapper::hasProperty !"); + } + return ret; +} + +Any SAL_CALL IUnknownWrapper::createBridge( const Any& modelDepObject, + const Sequence< sal_Int8 >& /*aProcessId*/, sal_Int16 sourceModelType, + sal_Int16 destModelType ) +{ + Any ret; + o2u_attachCurrentThread(); + + if ( + (sourceModelType == UNO) && + (destModelType == OLE) && + (modelDepObject.getValueTypeClass() == TypeClass_INTERFACE) + ) + { + Reference<XInterface> xInt( *static_cast<XInterface* const *>(modelDepObject.getValue())); + Reference<XInterface> xSelf( static_cast<OWeakObject*>(this)); + + if (xInt == xSelf) + { + VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT))); + + VariantInit(pVariant); + if (m_bOriginalDispatch) + { + pVariant->vt = VT_DISPATCH; + pVariant->pdispVal = m_spDispatch; + pVariant->pdispVal->AddRef(); + } + else + { + pVariant->vt = VT_UNKNOWN; + pVariant->punkVal = m_spUnknown; + pVariant->punkVal->AddRef(); + } + + ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get()); + } + } + + return ret; +} +/** @internal + @exception IllegalArgumentException + @exception CannotConvertException + @exception InvocationTargetException + @RuntimeException +*/ +Any IUnknownWrapper::invokeWithDispIdUnoTlb(const OUString& sFunctionName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + Any ret; + HRESULT hr= S_OK; + + sal_Int32 parameterCount= Params.getLength(); + sal_Int32 outParameterCount= 0; + typelib_InterfaceMethodTypeDescription* pMethod= nullptr; + TypeDescription methodDesc; + getMethodInfo(sFunctionName, methodDesc); + + // We need to know whether the IDispatch is from a JScript object. + // Then out and in/out parameters have to be treated differently than + // with common COM objects. + bool bJScriptObject= isJScriptObject(); + std::unique_ptr<CComVariant[]> sarParams; + std::unique_ptr<CComVariant[]> sarParamsRef; + CComVariant *pVarParams= nullptr; + CComVariant *pVarParamsRef= nullptr; + bool bConvRet= true; + + if( methodDesc.is()) + { + pMethod = reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(methodDesc.get()); + parameterCount = pMethod->nParams; + // Create the Array for the array being passed in DISPPARAMS + // the array also contains the outparameter (but not the values) + if( pMethod->nParams > 0) + { + sarParams.reset(new CComVariant[ parameterCount]); + pVarParams = sarParams.get(); + } + + // Create the Array for the out an in/out parameter. These values + // are referenced by the VT_BYREF VARIANTs in DISPPARAMS. + // We need to find out the number of out and in/out parameter. + for( sal_Int32 i=0; i < parameterCount; i++) + { + if( pMethod->pParams[i].bOut) + outParameterCount++; + } + + if( !bJScriptObject) + { + sarParamsRef.reset(new CComVariant[outParameterCount]); + pVarParamsRef = sarParamsRef.get(); + // build up the parameters for IDispatch::Invoke + sal_Int32 outParamIndex=0; + int i = 0; + try + { + for( i= 0; i < parameterCount; i++) + { + // In parameter + if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut) + { + anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]); + } + // Out parameter + in/out parameter + else if( pMethod->pParams[i].bOut ) + { + CComVariant var; + if(pMethod->pParams[i].bIn) + { + anyToVariant( & var,Params[i]); + pVarParamsRef[outParamIndex] = var; + } + + switch( pMethod->pParams[i].pTypeRef->eTypeClass) + { + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_STRUCT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt= VT_DISPATCH; + pVarParamsRef[ outParamIndex].pdispVal= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_DISPATCH | VT_BYREF; + pVarParams[parameterCount - i -1].ppdispVal= &pVarParamsRef[outParamIndex].pdispVal; + break; + case typelib_TypeClass_ENUM: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_I4; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_I4 | VT_BYREF; + pVarParams[parameterCount - i -1].plVal= &pVarParamsRef[outParamIndex].lVal; + break; + case typelib_TypeClass_SEQUENCE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_ARRAY| VT_VARIANT; + pVarParamsRef[ outParamIndex].parray= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_ARRAY| VT_BYREF | VT_VARIANT; + pVarParams[parameterCount - i -1].pparray= &pVarParamsRef[outParamIndex].parray; + break; + case typelib_TypeClass_ANY: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_EMPTY; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF; + pVarParams[parameterCount - i -1].pvarVal = &pVarParamsRef[outParamIndex]; + break; + case typelib_TypeClass_BOOLEAN: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_BOOL; + pVarParamsRef[ outParamIndex].boolVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_BOOL| VT_BYREF; + pVarParams[parameterCount - i -1].pboolVal = + & pVarParamsRef[outParamIndex].boolVal; + break; + + case typelib_TypeClass_STRING: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_BSTR; + pVarParamsRef[ outParamIndex].bstrVal= nullptr; + } + pVarParams[parameterCount - i -1].vt = VT_BSTR| VT_BYREF; + pVarParams[parameterCount - i -1].pbstrVal= + & pVarParamsRef[outParamIndex].bstrVal; + break; + + case typelib_TypeClass_FLOAT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_R4; + pVarParamsRef[ outParamIndex].fltVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_R4| VT_BYREF; + pVarParams[parameterCount - i -1].pfltVal = + & pVarParamsRef[outParamIndex].fltVal; + break; + case typelib_TypeClass_DOUBLE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_R8; + pVarParamsRef[ outParamIndex].dblVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_R8| VT_BYREF; + pVarParams[parameterCount - i -1].pdblVal= + & pVarParamsRef[outParamIndex].dblVal; + break; + case typelib_TypeClass_BYTE: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_UI1; + pVarParamsRef[ outParamIndex].bVal= 0; + } + pVarParams[parameterCount - i -1].vt = VT_UI1| VT_BYREF; + pVarParams[parameterCount - i -1].pbVal= + & pVarParamsRef[outParamIndex].bVal; + break; + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_I2; + pVarParamsRef[ outParamIndex].iVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_I2| VT_BYREF; + pVarParams[parameterCount - i -1].piVal= + & pVarParamsRef[outParamIndex].iVal; + break; + + default: + if( ! pMethod->pParams[i].bIn) + { + pVarParamsRef[ outParamIndex].vt = VT_EMPTY; + pVarParamsRef[ outParamIndex].lVal = 0; + } + pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF; + pVarParams[parameterCount - i -1].pvarVal = + & pVarParamsRef[outParamIndex]; + } + outParamIndex++; + } // end else if + } // end for + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + else // it is a JScriptObject + { + int i = 0; + try + { + for( ; i< parameterCount; i++) + { + // In parameter + if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut) + { + anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]); + } + // Out parameter + in/out parameter + else if( pMethod->pParams[i].bOut ) + { + CComObject<JScriptOutParam>* pParamObject; + if( !SUCCEEDED( CComObject<JScriptOutParam>::CreateInstance( &pParamObject))) + { + throw BridgeRuntimeError( + "[automation bridge]IUnknownWrapper::" + "invokeWithDispIdUnoTlb\n" + "Could not create out parameter at index: " + + OUString::number(static_cast<sal_Int32>(i))); + } + + CComPtr<IUnknown> pUnk(pParamObject->GetUnknown()); + CComQIPtr<IDispatch> pDisp( pUnk); + + pVarParams[ parameterCount - i -1].vt= VT_DISPATCH; + pVarParams[ parameterCount - i -1].pdispVal= pDisp; + pVarParams[ parameterCount - i -1].pdispVal->AddRef(); + // if the param is in/out then put the parameter on index 0 + if( pMethod->pParams[i].bIn ) // in / out + { + CComVariant varParam; + anyToVariant( &varParam, Params.getConstArray()[i]); + CComDispatchDriver dispDriver( pDisp); + if(FAILED( dispDriver.PutPropertyByName( L"0", &varParam))) + throw BridgeRuntimeError( + "[automation bridge]IUnknownWrapper::" + "invokeWithDispIdUnoTlb\n" + "Could not set property \"0\" for the in/out " + "param!"); + + } + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + } + // No type description Available, that is we have to deal with a COM component, + // that does not implements UNO interfaces ( IDispatch based) + else + { + //We should not run into this block, because invokeWithDispIdComTlb should + //have been called instead. + OSL_ASSERT(false); + } + + + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + DISPPARAMS dispparams= { pVarParams, nullptr, static_cast<UINT>(parameterCount), 0}; + + // Get the DISPID + FuncDesc aDesc(getTypeInfo()); + getFuncDesc(sFunctionName, & aDesc); + // invoking OLE method + hr = m_spDispatch->Invoke(aDesc->memid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_METHOD, + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (hr == S_OK) + { + if( outParameterCount && pMethod) + { + OutParamIndex.realloc( outParameterCount); + auto pOutParamIndex = OutParamIndex.getArray(); + OutParam.realloc( outParameterCount); + auto pOutParam = OutParam.getArray(); + sal_Int32 outIndex=0; + int i = 0; + try + { + for( ; i < parameterCount; i++) + { + if( pMethod->pParams[i].bOut ) + { + pOutParamIndex[outIndex]= static_cast<sal_Int16>(i); + Any outAny; + if( !bJScriptObject) + { + variantToAny( &pVarParamsRef[outIndex], outAny, + Type(pMethod->pParams[i].pTypeRef), false); + pOutParam[outIndex++]= outAny; + } + else //JScriptObject + { + if( pVarParams[i].vt == VT_DISPATCH) + { + CComDispatchDriver pDisp( pVarParams[i].pdispVal); + if( pDisp) + { + CComVariant varOut; + if( SUCCEEDED( pDisp.GetPropertyByName( L"0", &varOut))) + { + variantToAny( &varOut, outAny, + Type(pMethod->pParams[parameterCount - 1 - i].pTypeRef), false); + pOutParam[outParameterCount - 1 - outIndex++]= outAny; + } + else + bConvRet= false; + } + else + bConvRet= false; + } + else + bConvRet= false; + } + } + if( !bConvRet) break; + } + } + catch(IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i ); + throw; + } + catch(CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + } + // return value, no type information available + if ( bConvRet) + { + try + { + if( pMethod ) + variantToAny(&varResult, ret, Type( pMethod->pReturnTypeRef), false); + else + variantToAny(&varResult, ret, false); + } + catch (IllegalArgumentException & e) + { + e.Message = + "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n" + "Could not convert return value! \n Message: \n" + e.Message; + throw; + } + catch (CannotConvertException & e) + { + e.Message = + "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n" + "Could not convert return value! \n Message: \n" + e.Message; + throw; + } + } + } + + if( !bConvRet) // conversion of return or out parameter failed + throw CannotConvertException("Call to COM object failed. Conversion of return or out value failed", + Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY ), TypeClass_UNKNOWN, + FailReason::UNKNOWN, 0);// lookup error code + // conversion of return or out parameter failed + switch (hr) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException(); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException(); + break; + case DISP_E_EXCEPTION: + throw InvocationTargetException(); + break; + case DISP_E_MEMBERNOTFOUND: + throw IllegalArgumentException(); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException(); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException() ; + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException() ; + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + + return ret; +} + + +// XInitialization +void SAL_CALL IUnknownWrapper::initialize( const Sequence< Any >& aArguments ) +{ + // 1.parameter is IUnknown + // 2.parameter is a boolean which indicates if the COM pointer was an IUnknown or IDispatch + // 3.parameter is a Sequence<Type> + o2u_attachCurrentThread(); + OSL_ASSERT(aArguments.getLength() == 3); + + m_spUnknown= *static_cast<IUnknown* const *>(aArguments[0].getValue()); + m_spUnknown.QueryInterface( & m_spDispatch.p); + + aArguments[1] >>= m_bOriginalDispatch; + aArguments[2] >>= m_seqTypes; + + ITypeInfo* pType = nullptr; + try + { + // a COM object implementation that has no TypeInfo is still a legal COM object; + // such objects can at least be transported through UNO using the bridge + // so we should allow to create wrappers for them as well + pType = getTypeInfo(); + } + catch( const BridgeRuntimeError& ) + {} + catch( const Exception& ) + {} + + if ( pType ) + { + try + { + // Get Default member + CComBSTR defaultMemberName; + if ( SUCCEEDED( pType->GetDocumentation(0, &defaultMemberName, nullptr, nullptr, nullptr ) ) ) + { + OUString usName(o3tl::toU(LPCOLESTR(defaultMemberName))); + FuncDesc aDescGet(pType); + FuncDesc aDescPut(pType); + VarDesc aVarDesc(pType); + // see if this is a property first ( more likely to be a property then a method ) + getPropDesc( usName, & aDescGet, & aDescPut, & aVarDesc); + + if ( !aDescGet && !aDescPut ) + { + getFuncDesc( usName, &aDescGet ); + if ( !aDescGet ) + throw BridgeRuntimeError( "[automation bridge]IUnknownWrapper::initialize() Failed to get Function or Property desc. for " + usName ); + } + // now for some funny heuristics to make basic understand what to do + // a single aDescGet ( that doesn't take any params ) would be + // a read only ( defaultmember ) property e.g. this object + // should implement XDefaultProperty + // a single aDescGet ( that *does* ) take params is basically a + // default method e.g. implement XDefaultMethod + + // a DescPut ( I guess we only really support a default param with '1' param ) as a setValue ( but I guess we can leave it through, the object will fail if we don't get it right anyway ) + if ( aDescPut || ( aDescGet && aDescGet->cParams == 0 ) ) + m_bHasDfltProperty = true; + if ( aDescGet->cParams > 0 ) + m_bHasDfltMethod = true; + if ( m_bHasDfltProperty || m_bHasDfltMethod ) + m_sDefaultMember = usName; + } + } + catch ( const BridgeRuntimeError & e ) + { + throw RuntimeException( e.message ); + } + catch( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "[automation bridge] unexpected exception in IUnknownWrapper::initialize() error message: \n" + e.Message, + nullptr, anyEx ); + } + } +} + + +// XDirectInvocation +uno::Any SAL_CALL IUnknownWrapper::directInvoke( const OUString& aName, const uno::Sequence< uno::Any >& aParams ) +{ + Any aResult; + + if ( !m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + o2u_attachCurrentThread(); + DISPID dispid; + if ( !getDispid( aName, &dispid ) ) + throw IllegalArgumentException( + "[automation bridge] The object does not have a function or property " + + aName, Reference<XInterface>(), 0); + + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr = 0; + INVOKEKIND pInvkinds[2]; + pInvkinds[0] = INVOKE_FUNC; + pInvkinds[1] = aParams.getLength() ? INVOKE_PROPERTYPUT : INVOKE_PROPERTYGET; + HRESULT hInvRes = E_FAIL; + + // try Invoke first, if it does not work, try put/get property + for ( sal_Int32 nStep = 0; FAILED( hInvRes ) && nStep < 2; nStep++ ) + { + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + + std::unique_ptr<DISPID[]> arDispidNamedArgs; + std::unique_ptr<CComVariant[]> ptrArgs; + std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments + CComVariant * arArgs = nullptr; + CComVariant * arRefArgs = nullptr; + + dispparams.cArgs = aParams.getLength(); + + // Determine the number of named arguments + for ( uno::Any const & any : aParams ) + if ( any.getValueType() == cppu::UnoType<NamedArgument>::get() ) + dispparams.cNamedArgs ++; + + // fill the named arguments + if ( dispparams.cNamedArgs > 0 + && ( dispparams.cNamedArgs != 1 || pInvkinds[nStep] != INVOKE_PROPERTYPUT ) ) + { + int nSizeAr = dispparams.cNamedArgs + 1; + if ( pInvkinds[nStep] == INVOKE_PROPERTYPUT ) + nSizeAr = dispparams.cNamedArgs; + + std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]); + OLECHAR ** pNames = saNames.get(); + pNames[0] = const_cast<OLECHAR*>(o3tl::toW(aName.getStr())); + + int cNamedArg = 0; + for ( size_t nInd = 0; nInd < dispparams.cArgs; nInd++ ) + { + if (auto v = o3tl::tryAccess<NamedArgument>(aParams[nInd])) + { + const NamedArgument& arg = *v; + + //We put the parameter names in reverse order into the array, + //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs + //The first name in the array is the method name + pNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr())); + } + } + + arDispidNamedArgs.reset( new DISPID[nSizeAr] ); + HRESULT hr = getTypeInfo()->GetIDsOfNames( pNames, nSizeAr, arDispidNamedArgs.get() ); + if ( hr == E_NOTIMPL ) + hr = m_spDispatch->GetIDsOfNames(IID_NULL, pNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() ); + + if ( SUCCEEDED( hr ) ) + { + if ( pInvkinds[nStep] == DISPATCH_PROPERTYPUT ) + { + DISPID* arIDs = arDispidNamedArgs.get(); + arIDs[0] = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = arIDs; + } + else + { + DISPID* arIDs = arDispidNamedArgs.get(); + dispparams.rgdispidNamedArgs = & arIDs[1]; + } + } + else if (hr == DISP_E_UNKNOWNNAME) + { + throw IllegalArgumentException( + "[automation bridge]One of the named arguments is wrong!", + Reference<XInterface>(), 0); + } + else + { + throw InvocationTargetException( + "[automation bridge] ITypeInfo::GetIDsOfNames returned error " + + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any()); + } + } + + //Convert arguments + ptrArgs.reset(new CComVariant[dispparams.cArgs]); + ptrRefArgs.reset(new CComVariant[dispparams.cArgs]); + arArgs = ptrArgs.get(); + arRefArgs = ptrRefArgs.get(); + + sal_Int32 nInd = 0; + try + { + sal_Int32 revIndex = 0; + for ( nInd = 0; nInd < sal_Int32(dispparams.cArgs); nInd++) + { + revIndex = dispparams.cArgs - nInd - 1; + arRefArgs[revIndex].byref = nullptr; + Any anyArg; + if ( nInd < aParams.getLength() ) + anyArg = aParams.getConstArray()[nInd]; + + // Property Put arguments + if ( anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get() ) + { + PropertyPutArgument arg; + anyArg >>= arg; + anyArg = arg.Value; + } + // named argument + if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + { + NamedArgument aNamedArgument; + anyArg >>= aNamedArgument; + anyArg = aNamedArgument.Value; + } + + if ( nInd < aParams.getLength() && anyArg.getValueTypeClass() != TypeClass_VOID ) + { + anyToVariant( &arArgs[revIndex], anyArg, VT_VARIANT ); + } + else + { + arArgs[revIndex].vt = VT_ERROR; + arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( nInd ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = nInd; + throw; + } + + dispparams.rgvarg = arArgs; + // invoking OLE method + hInvRes = m_spDispatch->Invoke( dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + ::sal::static_int_cast< WORD, INVOKEKIND >( pInvkinds[nStep] ), + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + } + + // converting return value and out parameter back to UNO + if ( SUCCEEDED( hInvRes ) ) + variantToAny( &varResult, aResult, false ); + else + { + // map error codes to exceptions + OUString message; + switch ( hInvRes ) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException("[automation bridge] Wrong " + "number of arguments. Object returned DISP_E_BADPARAMCOUNT.", + nullptr, 0); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException("[automation bridge] One or more " + "arguments have the wrong type. Object returned " + "DISP_E_BADVARTYPE.", nullptr); + break; + case DISP_E_EXCEPTION: + message = OUString::Concat("[automation bridge]: ") + + std::u16string_view(o3tl::toU(excepinfo.bstrDescription), + ::SysStringLen(excepinfo.bstrDescription)); + throw InvocationTargetException(message, Reference<XInterface>(), Any()); + break; + case DISP_E_MEMBERNOTFOUND: + message = "[automation bridge]: A function with the name \"" + + aName + "\" is not supported. Object returned " + "DISP_E_MEMBERNOTFOUND."; + throw IllegalArgumentException(message, nullptr, 0); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException("[automation bridge] Object " + "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("[automation bridge] Call failed.", + static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("[automation bridge]Call failed." + "Object returned DISP_E_PARAMNOTFOUND.", + nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("[automation bridge] Call failed. " + "Object returned DISP_E_TYPEMISMATCH", + static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNLCID.",nullptr); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("[automation bridge] Call failed." + "Object returned DISP_E_PARAMNOTOPTIONAL", + static_cast<XInterface*>(static_cast<XWeak*>(this)), + TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + } + + return aResult; +} + +sal_Bool SAL_CALL IUnknownWrapper::hasMember( const OUString& aName ) +{ + if ( ! m_spDispatch ) + { + throw RuntimeException( + "[automation bridge] The object does not have an IDispatch interface"); + } + + o2u_attachCurrentThread(); + DISPID dispid; + return getDispid( aName, &dispid ); +} + + +// UnoConversionUtilities -------------------------------------------------------------------------------- +Reference< XInterface > IUnknownWrapper::createUnoWrapperInstance() +{ + if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else + return Reference<XInterface>(); +} +Reference<XInterface> IUnknownWrapper::createComWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + + +void IUnknownWrapper::getMethodInfo(std::u16string_view sName, TypeDescription& methodInfo) +{ + TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName); + if( desc.is()) + { + typelib_TypeDescription* pMember= desc.get(); + if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_METHOD ) + methodInfo= pMember; + } +} + +void IUnknownWrapper::getAttributeInfo(std::u16string_view sName, TypeDescription& attributeInfo) +{ + TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName); + if( desc.is()) + { + typelib_TypeDescription* pMember= desc.get(); + if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE ) + { + attributeInfo= reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(pMember)->pAttributeTypeRef; + } + } +} +TypeDescription IUnknownWrapper::getInterfaceMemberDescOfCurrentCall(std::u16string_view sName) +{ + TypeDescription ret; + + for( auto const & rType : std::as_const(m_seqTypes) ) + { + TypeDescription _curDesc( rType ); + _curDesc.makeComplete(); + typelib_InterfaceTypeDescription * pInterface= reinterpret_cast<typelib_InterfaceTypeDescription*>(_curDesc.get()); + if( pInterface) + { + typelib_InterfaceMemberTypeDescription* pMember= nullptr; + //find the member description of the current call + for( int j=0; j < pInterface->nAllMembers; j++) + { + typelib_TypeDescriptionReference* pTypeRefMember = pInterface->ppAllMembers[j]; + typelib_TypeDescription* pDescMember= nullptr; + TYPELIB_DANGER_GET( &pDescMember, pTypeRefMember); + + typelib_InterfaceMemberTypeDescription* pInterfaceMember= + reinterpret_cast<typelib_InterfaceMemberTypeDescription*>(pDescMember); + if( OUString( pInterfaceMember->pMemberName) == sName) + { + pMember= pInterfaceMember; + break; + } + TYPELIB_DANGER_RELEASE( pDescMember); + } + + if( pMember) + { + ret= &pMember->aBase; + TYPELIB_DANGER_RELEASE( &pMember->aBase); + } + } + if( ret.is()) + break; + } + return ret; +} + +bool IUnknownWrapper::isJScriptObject() +{ + if( m_eJScript == JScriptUndefined) + { + CComDispatchDriver disp( m_spDispatch); + if( disp) + { + CComVariant result; + if( SUCCEEDED( disp.GetPropertyByName( JSCRIPT_ID_PROPERTY, &result))) + { + if(result.vt == VT_BSTR) + { + CComBSTR name( result.bstrVal); + name.ToLower(); + if( name == CComBSTR(JSCRIPT_ID)) + m_eJScript= IsJScript; + } + } + } + if( m_eJScript == JScriptUndefined) + m_eJScript= NoJScript; + } + + return m_eJScript != NoJScript; +} + + +/** @internal + The function ultimately calls IDispatch::Invoke on the wrapped COM object. + The COM object does not implement UNO Interfaces ( via IDispatch). This + is the case when the OleObjectFactory service has been used to create a + component. + @exception IllegalArgumentException + @exception CannotConvertException + @InvocationTargetException + @RuntimeException + @BridgeRuntimeError +*/ +Any IUnknownWrapper::invokeWithDispIdComTlb(const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + // Get type info for the call. It can be a method call or property put or + // property get operation. + FuncDesc aFuncDesc(getTypeInfo()); + getFuncDescForInvoke(sFuncName, Params, & aFuncDesc); + return invokeWithDispIdComTlb( aFuncDesc, sFuncName, Params, OutParamIndex, OutParam ); +} + +Any IUnknownWrapper::invokeWithDispIdComTlb(FuncDesc& aFuncDesc, + const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam) +{ + Any ret; + HRESULT result; + + DISPPARAMS dispparams = {nullptr, nullptr, 0, 0}; + CComVariant varResult; + ExcepInfo excepinfo; + unsigned int uArgErr; + sal_Int32 i = 0; + sal_Int32 nUnoArgs = Params.getLength(); + DISPID idPropertyPut = DISPID_PROPERTYPUT; + std::unique_ptr<DISPID[]> arDispidNamedArgs; + std::unique_ptr<CComVariant[]> ptrArgs; + std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments + CComVariant * arArgs = nullptr; + CComVariant * arRefArgs = nullptr; + sal_Int32 revIndex = 0; + + //Set the array of DISPIDs for named args if it is a property put operation. + //If there are other named arguments another array is set later on. + if (aFuncDesc->invkind == INVOKE_PROPERTYPUT + || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + dispparams.rgdispidNamedArgs = & idPropertyPut; + + //Determine the number of named arguments + for (int iParam = 0; iParam < nUnoArgs; iParam ++) + { + const Any & curArg = Params[iParam]; + if (curArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + dispparams.cNamedArgs ++; + } + //In a property put operation a property value is a named argument (DISPID_PROPERTYPUT). + //Therefore the number of named arguments is increased by one. + //Although named, the argument is not named in an actual language, such as Basic, + //therefore it is never a com.sun.star.bridge.oleautomation.NamedArgument + if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT + || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF) + dispparams.cNamedArgs ++; + + //Determine the number of all arguments and named arguments + if (aFuncDesc->cParamsOpt == -1) + { + //Attribute vararg is set on this method. "Unlimited" number of args + //supported. There can be no optional or defaultvalue on any of the arguments. + dispparams.cArgs = nUnoArgs; + } + else + { + //If there are named arguments, then the dispparams.cArgs + //is the number of supplied args, otherwise it is the expected number. + if (dispparams.cNamedArgs) + dispparams.cArgs = nUnoArgs; + else + dispparams.cArgs = aFuncDesc->cParams; + } + + //check if there are not too many arguments supplied + if (::sal::static_int_cast< sal_uInt32, int >( nUnoArgs ) > dispparams.cArgs) + { + throw IllegalArgumentException( + "[automation bridge] There are too many arguments for this method", + Reference<XInterface>(), static_cast<sal_Int16>(dispparams.cArgs)); + } + + //Set up the array of DISPIDs (DISPPARAMS::rgdispidNamedArgs) + //for the named arguments. + //If there is only one named arg and if it is because of a property put + //operation, then we need not set up the DISPID array. + if (dispparams.cNamedArgs > 0 && + ! (dispparams.cNamedArgs == 1 && + (aFuncDesc->invkind == INVOKE_PROPERTYPUT || + aFuncDesc->invkind == INVOKE_PROPERTYPUTREF))) + { + //set up an array containing the member and parameter names + //which is then used in ITypeInfo::GetIDsOfNames + //First determine the size of the array of names which is passed to + //ITypeInfo::GetIDsOfNames. It must hold the method names + the named + //args. + int nSizeAr = dispparams.cNamedArgs + 1; + if (aFuncDesc->invkind == INVOKE_PROPERTYPUT + || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + { + nSizeAr = dispparams.cNamedArgs; //counts the DISID_PROPERTYPUT + } + + std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]); + OLECHAR ** arNames = saNames.get(); + arNames[0] = const_cast<OLECHAR*>(o3tl::toW(sFuncName.getStr())); + + int cNamedArg = 0; + for (size_t iParams = 0; iParams < dispparams.cArgs; iParams ++) + { + const Any & curArg = Params[iParams]; + if (auto v = o3tl::tryAccess<NamedArgument>(curArg)) + { + const NamedArgument& arg = *v; + //We put the parameter names in reverse order into the array, + //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs + //The first name in the array is the method name + arNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr())); + } + } + + //Prepare the array of DISPIDs for ITypeInfo::GetIDsOfNames + //it must be big enough to contain the DISPIDs of the member + parameters + arDispidNamedArgs.reset(new DISPID[nSizeAr]); + HRESULT hr = getTypeInfo()->GetIDsOfNames(arNames, nSizeAr, + arDispidNamedArgs.get()); + if ( hr == E_NOTIMPL ) + hr = m_spDispatch->GetIDsOfNames(IID_NULL, arNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() ); + + if (hr == S_OK) + { + // In a "property put" operation, the property value is a named param with the + //special DISPID DISPID_PROPERTYPUT + if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT + || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF) + { + //Element at index 0 in the DISPID array must be DISPID_PROPERTYPUT + //The first item in the array arDispidNamedArgs is the DISPID for + //the method. We replace it with DISPID_PROPERTYPUT. + DISPID* arIDs = arDispidNamedArgs.get(); + arIDs[0] = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = arIDs; + } + else + { + //The first item in the array arDispidNamedArgs is the DISPID for + //the method. It must be removed + DISPID* arIDs = arDispidNamedArgs.get(); + dispparams.rgdispidNamedArgs = & arIDs[1]; + } + } + else if (hr == DISP_E_UNKNOWNNAME) + { + throw IllegalArgumentException( + "[automation bridge]One of the named arguments is wrong!", + Reference<XInterface>(), 0); + } + else + { + throw InvocationTargetException( + "[automation bridge] ITypeInfo::GetIDsOfNames returned error " + + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any()); + } + } + + //Convert arguments + ptrArgs.reset(new CComVariant[dispparams.cArgs]); + ptrRefArgs.reset(new CComVariant[dispparams.cArgs]); + arArgs = ptrArgs.get(); + arRefArgs = ptrRefArgs.get(); + try + { + for (i = 0; i < static_cast<sal_Int32>(dispparams.cArgs); i++) + { + revIndex= dispparams.cArgs - i -1; + arRefArgs[revIndex].byref=nullptr; + Any anyArg; + if ( i < nUnoArgs) + anyArg= Params.getConstArray()[i]; + + unsigned short paramFlags = PARAMFLAG_FOPT | PARAMFLAG_FIN; + VARTYPE varType = VT_VARIANT; + if (aFuncDesc->cParamsOpt != -1 || aFuncDesc->cParams != (i + 1)) + { + paramFlags = aFuncDesc->lprgelemdescParam[i].paramdesc.wParamFlags; + varType = getElementTypeDesc(&aFuncDesc->lprgelemdescParam[i].tdesc); + } + + // Make sure that there is a UNO parameter for every + // expected parameter. If there is no UNO parameter where the + // called function expects one, then it must be optional. Otherwise + // it's a UNO programming error. + if (i >= nUnoArgs && !(paramFlags & PARAMFLAG_FOPT)) + { + throw IllegalArgumentException( + ("ole automation bridge: The called function expects an argument at position: " + + OUString::number(i) + " (index starting at 0)."), + Reference<XInterface>(), static_cast<sal_Int16>(i)); + } + + // Property Put arguments + if (anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get()) + { + PropertyPutArgument arg; + anyArg >>= arg; + anyArg = arg.Value; + } + // named argument + if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get()) + { + NamedArgument aNamedArgument; + anyArg >>= aNamedArgument; + anyArg = aNamedArgument.Value; + } + // out param + if (paramFlags & PARAMFLAG_FOUT && + ! (paramFlags & PARAMFLAG_FIN) ) + { + VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF ); + if (i < nUnoArgs) + { + arRefArgs[revIndex].vt= type; + } + else + { + //optional arg + arRefArgs[revIndex].vt = VT_ERROR; + arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + if( type == VT_VARIANT ) + { + arArgs[revIndex].vt= VT_VARIANT | VT_BYREF; + arArgs[revIndex].byref= &arRefArgs[revIndex]; + } + else + { + arArgs[revIndex].vt= varType; + if (type == VT_DECIMAL) + arArgs[revIndex].byref= & arRefArgs[revIndex].decVal; + else + arArgs[revIndex].byref= & arRefArgs[revIndex].byref; + } + } + // in/out + in byref params + else if (varType & VT_BYREF) + { + VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF ); + CComVariant var; + + if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID) + { + anyToVariant( & arRefArgs[revIndex], anyArg, type); + } + else if (paramFlags & PARAMFLAG_FHASDEFAULT) + { + //optional arg with default + VariantCopy( & arRefArgs[revIndex], + & aFuncDesc->lprgelemdescParam[i].paramdesc. + pparamdescex->varDefaultValue); + } + else + { + //optional arg + //e.g: call func(x) in basic : func() ' no arg supplied + OSL_ASSERT(paramFlags & PARAMFLAG_FOPT); + arRefArgs[revIndex].vt = VT_ERROR; + arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + + // Set the converted arguments in the array which will be + // DISPPARAMS::rgvarg + // byref arg VT_XXX |VT_BYREF + arArgs[revIndex].vt = varType; + if (revIndex == 0 && aFuncDesc->invkind == INVOKE_PROPERTYPUT) + { + arArgs[revIndex] = arRefArgs[revIndex]; + } + else if (type == VT_DECIMAL) + { + arArgs[revIndex].byref= & arRefArgs[revIndex].decVal; + } + else if (type == VT_VARIANT) + { + if ( ! (paramFlags & PARAMFLAG_FOUT)) + arArgs[revIndex] = arRefArgs[revIndex]; + else + arArgs[revIndex].byref = & arRefArgs[revIndex]; + } + else + { + arArgs[revIndex].byref = & arRefArgs[revIndex].byref; + arArgs[revIndex].vt = ::sal::static_int_cast< VARTYPE, int >( arRefArgs[revIndex].vt | VT_BYREF ); + } + + } + // in parameter no VT_BYREF except for array, interfaces + else + { // void any stands for optional param + if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID) + { + anyToVariant( & arArgs[revIndex], anyArg, varType); + } + //optional arg but no void any supplied + //Basic: obj.func() ' first parameter left out because it is optional + else if (paramFlags & PARAMFLAG_FHASDEFAULT) + { + //optional arg with default either as direct arg : VT_XXX or + VariantCopy( & arArgs[revIndex], + & aFuncDesc->lprgelemdescParam[i].paramdesc. + pparamdescex->varDefaultValue); + } + else if (paramFlags & PARAMFLAG_FOPT) + { + arArgs[revIndex].vt = VT_ERROR; + arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND; + } + else + { + arArgs[revIndex].vt = VT_EMPTY; + arArgs[revIndex].lVal = 0; + } + } + } + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( i ); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = i; + throw; + } + dispparams.rgvarg= arArgs; + // invoking OLE method + result = m_spDispatch->Invoke(aFuncDesc->memid, + IID_NULL, + LOCALE_USER_DEFAULT, + ::sal::static_int_cast< WORD, INVOKEKIND >( aFuncDesc->invkind ), + &dispparams, + &varResult, + &excepinfo, + &uArgErr); + + // converting return value and out parameter back to UNO + if (result == S_OK) + { + + // allocate space for the out param Sequence and indices Sequence + int outParamsCount= 0; // includes in/out parameter + for (int j = 0; j < aFuncDesc->cParams; j++) + { + if (aFuncDesc->lprgelemdescParam[j].paramdesc.wParamFlags & + PARAMFLAG_FOUT) + outParamsCount++; + } + + OutParamIndex.realloc(outParamsCount); + OutParam.realloc(outParamsCount); + // Convert out params + if (outParamsCount) + { + auto pOutParamIndex = OutParamIndex.getArray(); + auto pOutParam = OutParam.getArray(); + int outParamIndex=0; + for (int paramIndex = 0; paramIndex < nUnoArgs; paramIndex ++) + { + //Determine the index within the method signature + int realParamIndex = paramIndex; + int revParamIndex = dispparams.cArgs - paramIndex - 1; + if (Params[paramIndex].getValueType() + == cppu::UnoType<NamedArgument>::get()) + { + //dispparams.rgdispidNamedArgs contains the mapping from index + //of named args list to index of parameter list + realParamIndex = dispparams.rgdispidNamedArgs[revParamIndex]; + } + + // no named arg, always come before named args + if (! (aFuncDesc->lprgelemdescParam[realParamIndex].paramdesc.wParamFlags + & PARAMFLAG_FOUT)) + continue; + Any outAny; + // variantToAny is called with the "reduce range" parameter set to sal_False. + // That causes VT_I4 values not to be converted down to a "lower" type. That + // feature exist for JScript only because it only uses VT_I4 for integer types. + try + { + variantToAny( & arRefArgs[revParamIndex], outAny, false ); + } + catch (IllegalArgumentException & e) + { + e.ArgumentPosition = static_cast<sal_Int16>(paramIndex); + throw; + } + catch (CannotConvertException & e) + { + e.ArgumentIndex = paramIndex; + throw; + } + pOutParam[outParamIndex] = outAny; + pOutParamIndex[outParamIndex] = ::sal::static_int_cast< sal_Int16, int >( paramIndex ); + outParamIndex++; + } + OutParam.realloc(outParamIndex); + OutParamIndex.realloc(outParamIndex); + } + // Return value + variantToAny(&varResult, ret, false); + } + + // map error codes to exceptions + OUString message; + switch (result) + { + case S_OK: + break; + case DISP_E_BADPARAMCOUNT: + throw IllegalArgumentException("[automation bridge] Wrong " + "number of arguments. Object returned DISP_E_BADPARAMCOUNT.", + nullptr, 0); + break; + case DISP_E_BADVARTYPE: + throw RuntimeException("[automation bridge] One or more " + "arguments have the wrong type. Object returned " + "DISP_E_BADVARTYPE.", nullptr); + break; + case DISP_E_EXCEPTION: + message = OUString::Concat("[automation bridge]: ") + + std::u16string_view(o3tl::toU(excepinfo.bstrDescription), + ::SysStringLen(excepinfo.bstrDescription)); + + throw InvocationTargetException(message, Reference<XInterface>(), Any()); + break; + case DISP_E_MEMBERNOTFOUND: + message = "[automation bridge]: A function with the name \"" + + sFuncName + "\" is not supported. Object returned " + "DISP_E_MEMBERNOTFOUND."; + throw IllegalArgumentException(message, nullptr, 0); + break; + case DISP_E_NONAMEDARGS: + throw IllegalArgumentException("[automation bridge] Object " + "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_OVERFLOW: + throw CannotConvertException("[automation bridge] Call failed.", + static_cast<XInterface*>( + static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr); + break; + case DISP_E_PARAMNOTFOUND: + throw IllegalArgumentException("[automation bridge]Call failed." + "Object returned DISP_E_PARAMNOTFOUND.", + nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )); + break; + case DISP_E_TYPEMISMATCH: + throw CannotConvertException("[automation bridge] Call failed. " + "Object returned DISP_E_TYPEMISMATCH", + static_cast<XInterface*>( + static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr); + break; + case DISP_E_UNKNOWNINTERFACE: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr); + break; + case DISP_E_UNKNOWNLCID: + throw RuntimeException("[automation bridge] Call failed. " + "Object returned DISP_E_UNKNOWNLCID.",nullptr); + break; + case DISP_E_PARAMNOTOPTIONAL: + throw CannotConvertException("[automation bridge] Call failed." + "Object returned DISP_E_PARAMNOTOPTIONAL", + static_cast<XInterface*>(static_cast<XWeak*>(this)), + TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr); + break; + default: + throw RuntimeException(); + break; + } + + return ret; +} + +void IUnknownWrapper::getFuncDescForInvoke(const OUString & sFuncName, + const Sequence<Any> & seqArgs, + FUNCDESC** pFuncDesc) +{ + int nUnoArgs = seqArgs.getLength(); + const Any * arArgs = seqArgs.getConstArray(); + ITypeInfo* pInfo = getTypeInfo(); + + //If the last of the positional arguments is a PropertyPutArgument + //then obtain the type info for the property put operation. + + //The property value is always the last argument, in a positional argument list + //or in a list of named arguments. A PropertyPutArgument is actually a named argument + //hence it must not be put in an extra NamedArgument structure + if (nUnoArgs > 0 && + arArgs[nUnoArgs - 1].getValueType() == cppu::UnoType<PropertyPutArgument>::get()) + { + // DISPATCH_PROPERTYPUT + FuncDesc aDescGet(pInfo); + FuncDesc aDescPut(pInfo); + VarDesc aVarDesc(pInfo); + getPropDesc(sFuncName, & aDescGet, & aDescPut, & aVarDesc); + if ( ! aDescPut) + { + throw IllegalArgumentException( + "[automation bridge] The object does not have a writeable property: " + + sFuncName, Reference<XInterface>(), 0); + } + *pFuncDesc = aDescPut.Detach(); + } + else + { // DISPATCH_METHOD + FuncDesc aFuncDesc(pInfo); + getFuncDesc(sFuncName, & aFuncDesc); + if ( ! aFuncDesc) + { + // Fallback: DISPATCH_PROPERTYGET can mostly be called as + // DISPATCH_METHOD + ITypeInfo * pTypeInfo = getTypeInfo(); + FuncDesc aDescPut(pTypeInfo); + VarDesc aVarDesc(pTypeInfo); + getPropDesc(sFuncName, & aFuncDesc, & aDescPut, & aVarDesc); + if ( ! aFuncDesc ) + { + throw IllegalArgumentException( + "[automation bridge] The object does not have a function" + " or readable property \"" + + sFuncName + "\"", Reference<XInterface>(), 0); + } + } + *pFuncDesc = aFuncDesc.Detach(); + } +} +bool IUnknownWrapper::getDispid(const OUString& sFuncName, DISPID * id) +{ + OSL_ASSERT(m_spDispatch); + LPOLESTR lpsz = const_cast<LPOLESTR> (o3tl::toW(sFuncName.getStr())); + HRESULT hr = m_spDispatch->GetIDsOfNames(IID_NULL, &lpsz, 1, LOCALE_USER_DEFAULT, id); + return hr == S_OK; +} +void IUnknownWrapper::getFuncDesc(const OUString & sFuncName, FUNCDESC ** pFuncDesc) + +{ + OSL_ASSERT( * pFuncDesc == nullptr); + buildComTlbIndex(); + typedef TLBFuncIndexMap::const_iterator cit; + //We assume there is only one entry with the function name. A property + //would have two entries. + cit itIndex= m_mapComFunc.find(sFuncName); + if (itIndex == m_mapComFunc.end()) + { + //try case insensitive with IDispatch::GetIDsOfNames + DISPID id; + if (getDispid(sFuncName, &id)) + { + CComBSTR memberName; + unsigned int pcNames=0; + // get the case sensitive name + if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames))) + { + //get the associated index and add an entry to the map + //with the name sFuncName which differs in the casing of the letters to + //the actual name as obtained from ITypeInfo + OUString sRealName(o3tl::toU(LPCOLESTR(memberName))); + cit itOrg = m_mapComFunc.find(sRealName); + OSL_ASSERT(itOrg != m_mapComFunc.end()); + // maybe this is a property, if so we need + // to store either both id's ( put/get ) or + // just the get. Storing both is more consistent + std::pair<cit, cit> pItems = m_mapComFunc.equal_range( sRealName ); + for ( ;pItems.first != pItems.second; ++pItems.first ) + m_mapComFunc.insert( TLBFuncIndexMap::value_type ( std::make_pair(sFuncName, pItems.first->second ) )); + itIndex = + m_mapComFunc.find( sFuncName ); + } + } + } + +#if OSL_DEBUG_LEVEL >= 1 + // There must only be one entry if sFuncName represents a function or two + // if it is a property + std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName.toAsciiLowerCase()); + int numEntries = 0; + for ( ;p.first != p.second; p.first ++, numEntries ++); + OSL_ASSERT( ! (numEntries > 3) ); +#endif + if( itIndex != m_mapComFunc.end()) + { + ITypeInfo* pType= getTypeInfo(); + FUNCDESC * pDesc = nullptr; + if (!SUCCEEDED(pType->GetFuncDesc(itIndex->second, & pDesc))) + { + throw BridgeRuntimeError("[automation bridge] Could not get " + "FUNCDESC for " + sFuncName); + } + if (pDesc->invkind == INVOKE_FUNC) + { + (*pFuncDesc) = pDesc; + } + else + { + pType->ReleaseFuncDesc(pDesc); + } + } + //else no entry found for sFuncName, pFuncDesc will not be filled in +} + +void IUnknownWrapper::getPropDesc(const OUString & sFuncName, FUNCDESC ** pFuncDescGet, + FUNCDESC** pFuncDescPut, VARDESC** pVarDesc) +{ + OSL_ASSERT( * pFuncDescGet == nullptr && * pFuncDescPut == nullptr); + buildComTlbIndex(); + typedef TLBFuncIndexMap::const_iterator cit; + std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName); + if (p.first == m_mapComFunc.end()) + { + //try case insensitive with IDispatch::GetIDsOfNames + DISPID id; + if (getDispid(sFuncName, &id)) + { + CComBSTR memberName; + unsigned int pcNames=0; + // get the case sensitive name + if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames))) + { + //As opposed to getFuncDesc, we do not add the value because we would + // need to find the get and set description for the property. This would + //mean to iterate over all FUNCDESCs again. + p = m_mapComFunc.equal_range(OUString(o3tl::toU(LPCOLESTR(memberName)))); + } + } + } + + for ( int i = 0 ;p.first != p.second; p.first ++, i ++) + { + // There are a maximum of two entries, property put and property get + OSL_ASSERT( ! (i > 2) ); + ITypeInfo* pType= getTypeInfo(); + FUNCDESC * pFuncDesc = nullptr; + if (SUCCEEDED( pType->GetFuncDesc(p.first->second, & pFuncDesc))) + { + if (pFuncDesc->invkind == INVOKE_PROPERTYGET) + { + (*pFuncDescGet) = pFuncDesc; + } + else if (pFuncDesc->invkind == INVOKE_PROPERTYPUT || + pFuncDesc->invkind == INVOKE_PROPERTYPUTREF) + { + //a property can have 3 entries, put, put ref, get + // If INVOKE_PROPERTYPUTREF or INVOKE_PROPERTYPUT is used + //depends on what is found first. + if ( * pFuncDescPut) + { + //we already have found one + pType->ReleaseFuncDesc(pFuncDesc); + } + else + { + (*pFuncDescPut) = pFuncDesc; + } + } + else + { + pType->ReleaseFuncDesc(pFuncDesc); + } + } + //ITypeInfo::GetFuncDesc may even provide a funcdesc for a VARDESC + // with invkind = INVOKE_FUNC. Since this function should only return + //a value for a real property (XInvokation::hasMethod, ..::hasProperty + //we need to make sure that sFuncName represents a real property. + VARDESC * pVD = nullptr; + if (SUCCEEDED(pType->GetVarDesc(p.first->second, & pVD))) + (*pVarDesc) = pVD; + } + //else no entry for sFuncName, pFuncDesc will not be filled in +} + +VARTYPE IUnknownWrapper::getUserDefinedElementType( ITypeInfo* pTypeInfo, const DWORD nHrefType ) +{ + VARTYPE _type( VT_NULL ); + if ( pTypeInfo ) + { + CComPtr<ITypeInfo> spRefInfo; + pTypeInfo->GetRefTypeInfo( nHrefType, &spRefInfo.p ); + if ( spRefInfo ) + { + TypeAttr attr( spRefInfo ); + spRefInfo->GetTypeAttr( &attr ); + if ( attr->typekind == TKIND_ENUM ) + { + // We use the type of the first enum value. + if ( attr->cVars == 0 ) + { + throw BridgeRuntimeError("[automation bridge] Could not obtain type description"); + } + VarDesc var( spRefInfo ); + spRefInfo->GetVarDesc( 0, &var ); + _type = var->lpvarValue->vt; + } + else if ( attr->typekind == TKIND_INTERFACE ) + { + _type = VT_UNKNOWN; + } + else if ( attr->typekind == TKIND_DISPATCH ) + { + _type = VT_DISPATCH; + } + else if ( attr->typekind == TKIND_ALIAS ) + { + // TKIND_ALIAS is a type that is an alias for another type. So get that alias type. + _type = getUserDefinedElementType( pTypeInfo, attr->tdescAlias.hreftype ); + } + else + { + throw BridgeRuntimeError( "[automation bridge] Unhandled user defined type." ); + } + } + } + return _type; +} + +VARTYPE IUnknownWrapper::getElementTypeDesc(const TYPEDESC *desc) +{ + VARTYPE _type( VT_NULL ); + + if (desc->vt == VT_PTR) + { + _type = getElementTypeDesc(desc->lptdesc); + _type |= VT_BYREF; + } + else if (desc->vt == VT_SAFEARRAY) + { + _type = getElementTypeDesc(desc->lptdesc); + _type |= VT_ARRAY; + } + else if (desc->vt == VT_USERDEFINED) + { + ITypeInfo* thisInfo = getTypeInfo(); //kept by this instance + _type = getUserDefinedElementType( thisInfo, desc->hreftype ); + } + else + { + _type = desc->vt; + } + return _type; +} + +void IUnknownWrapper::buildComTlbIndex() +{ + if ( ! m_bComTlbIndexInit) + { + MutexGuard guard(getBridgeMutex()); + { + if ( ! m_bComTlbIndexInit) + { + OUString sError; + ITypeInfo* pType= getTypeInfo(); + TypeAttr typeAttr(pType); + if( SUCCEEDED( pType->GetTypeAttr( &typeAttr))) + { + for( WORD i= 0; i < typeAttr->cFuncs; i++) + { + FuncDesc funcDesc(pType); + if( SUCCEEDED( pType->GetFuncDesc( i, &funcDesc))) + { + CComBSTR memberName; + unsigned int pcNames=0; + if( SUCCEEDED(pType->GetNames( funcDesc->memid, & memberName, 1, &pcNames))) + { + OUString usName(o3tl::toU(LPCOLESTR(memberName))); + m_mapComFunc.emplace(usName, i); + } + else + { + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetNames failed."; + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetFuncDesc failed."; + } + + //If we create an Object in JScript and a property then it + //has VARDESC instead of FUNCDESC + for (WORD i = 0; i < typeAttr->cVars; i++) + { + VarDesc varDesc(pType); + if (SUCCEEDED(pType->GetVarDesc(i, & varDesc))) + { + CComBSTR memberName; + unsigned int pcNames = 0; + if (SUCCEEDED(pType->GetNames(varDesc->memid, & memberName, 1, &pcNames))) + { + if (varDesc->varkind == VAR_DISPATCH) + { + OUString usName(o3tl::toU(LPCOLESTR(memberName))); + m_mapComFunc.emplace(usName, i); + } + } + else + { + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetNames failed."; + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetVarDesc failed."; + + } + } + else + sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, " + "ITypeInfo::GetTypeAttr failed."; + + if (sError.getLength()) + { + throw BridgeRuntimeError(sError); + } + + m_bComTlbIndexInit = true; + } + } + } +} + +ITypeInfo* IUnknownWrapper::getTypeInfo() +{ + if( !m_spDispatch) + { + throw BridgeRuntimeError("The object has no IDispatch interface!"); + } + + if( !m_spTypeInfo ) + { + MutexGuard guard(getBridgeMutex()); + if( ! m_spTypeInfo) + { + CComPtr< ITypeInfo > spType; + if( !SUCCEEDED( m_spDispatch->GetTypeInfo( 0, LOCALE_USER_DEFAULT, &spType.p))) + { + throw BridgeRuntimeError("[automation bridge]The dispatch object does not " + "support ITypeInfo!"); + } + + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + + //If this is a dual interface then TYPEATTR::typekind is usually TKIND_INTERFACE + //We need to get the type description for TKIND_DISPATCH + TypeAttr typeAttr(spType.p); + if( SUCCEEDED(spType->GetTypeAttr( &typeAttr))) + { + if (typeAttr->typekind == TKIND_INTERFACE && + typeAttr->wTypeFlags & TYPEFLAG_FDUAL) + { + HREFTYPE refDispatch; + if (!SUCCEEDED(spType->GetRefTypeOfImplType(::sal::static_int_cast< UINT, int >( -1 ), &refDispatch))) + { + throw BridgeRuntimeError( + "[automation bridge] Could not obtain type information " + "for dispatch interface." ); + } + CComPtr<ITypeInfo> spTypeDisp; + if (SUCCEEDED(spType->GetRefTypeInfo(refDispatch, & spTypeDisp))) + m_spTypeInfo= spTypeDisp; + } + else if (typeAttr->typekind == TKIND_DISPATCH) + { + m_spTypeInfo= spType; + } + else + { + throw BridgeRuntimeError( + "[automation bridge] Automation object does not " + "provide type information."); + } + } + } + } + return m_spTypeInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/oleobjw.hxx b/extensions/source/ole/oleobjw.hxx new file mode 100644 index 0000000000..d1a1d0ed81 --- /dev/null +++ b/extensions/source/ole/oleobjw.hxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ole2uno.hxx" +#include "wincrap.hxx" + +#include <string_view> +#include <unordered_map> +#include <vector> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp> +#include <com/sun/star/script/XAutomationInvocation.hpp> +#include <rtl/ustring.hxx> + +#include <com/sun/star/script/XDefaultProperty.hpp> +#include <com/sun/star/script/XDefaultMethod.hpp> +#include <com/sun/star/script/XDirectInvocation.hpp> + +#include <typelib/typedescription.hxx> +#include "unoconversionutilities.hxx" +#include "windata.hxx" +using namespace cppu; +using namespace com::sun::star::lang; +using namespace com::sun::star::bridge; +using namespace com::sun::star::bridge::oleautomation; + +typedef std::unordered_map<OUString, std::pair<DISPID, unsigned short>> DispIdMap; + +typedef std::unordered_multimap<OUString, unsigned int> TLBFuncIndexMap; + +// This class wraps an IDispatch and maps XInvocation calls to IDispatch calls on the wrapped object. +// If m_TypeDescription is set then this class represents a UNO interface implemented in a COM component. +// The interface is not a real interface in terms of an abstract class but is realized through IDispatch. +class IUnknownWrapper : public WeakImplHelper< XBridgeSupplier2, XInitialization, XAutomationObject, XDefaultProperty, XDefaultMethod, XDirectInvocation, XAutomationInvocation >, + + public UnoConversionUtilities<IUnknownWrapper> + +{ +public: + IUnknownWrapper(Reference<XMultiServiceFactory> const &xFactory, + sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass); + + ~IUnknownWrapper() override; + + //XInterface + Any SAL_CALL queryInterface(const Type& t) override; + + // XInvokation + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection( ) override; + virtual Any SAL_CALL invoke( const OUString& aFunctionName, + const Sequence< Any >& aParams, + Sequence< sal_Int16 >& aOutParamIndex, + Sequence< Any >& aOutParam ) override; + virtual void SAL_CALL setValue( const OUString& aPropertyName, + const Any& aValue ) override; + virtual Any SAL_CALL getValue( const OUString& aPropertyName ) override; + virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override; + + // XBridgeSupplier2 + // This interface is implemented to provide a safe way to obtain the original + // IUnknown or IDispatch within the function anyToVariant. The function asks + // every UNO object for its XBridgeSupplier2 and if it is available uses it to convert + // the object with its own supplier. + virtual Any SAL_CALL createBridge( const Any& modelDepObject, + const Sequence< sal_Int8 >& aProcessId, + sal_Int16 sourceModelType, + sal_Int16 destModelType ) override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XDefaultProperty + virtual OUString SAL_CALL getDefaultPropertyName( ) override { return m_sDefaultMember; } + + // XDefaultMethod + virtual OUString SAL_CALL getDefaultMethodName( ) override { return m_sDefaultMember; } + + virtual css::uno::Any SAL_CALL invokeGetProperty( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override; + virtual css::uno::Any SAL_CALL invokePutProperty( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override; + + // XDirectInvocation + virtual css::uno::Any SAL_CALL directInvoke( const OUString& aName, const css::uno::Sequence< css::uno::Any >& aParams ) override; + virtual sal_Bool SAL_CALL hasMember( const OUString& aName ) override; + + + Any invokeWithDispIdComTlb(FuncDesc& aFuncDesc, + const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam); + + +protected: + + virtual Any invokeWithDispIdUnoTlb(const OUString& sFunctionName, + const Sequence< Any >& Params, + Sequence<sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam); + // Is used for OleObjectFactory service + virtual Any invokeWithDispIdComTlb(const OUString& sFuncName, + const Sequence< Any >& Params, + Sequence< sal_Int16 >& OutParamIndex, + Sequence< Any >& OutParam); + + // UnoConversionUtilities ------------------------------------------------------------------------------- + virtual Reference<XInterface> createUnoWrapperInstance() override; + virtual Reference<XInterface> createComWrapperInstance() override; + + /**Obtains a FUNCDESC structure for a function. + Fills the FUNCDESC structure if ITypeInfo provides information for + the function of name sFuncName or pFuncDesc will not be filled in. + May throw a BridgeRuntimeError. + */ + void getFuncDesc(const OUString & sFuncName, FUNCDESC ** pFuncDesc); + /**Obtains a FUNCDESC structures or a VARDESC structure + for a property. pFuncDescPut may also contain + a structure for a "propertyputref" operation. If pFuncDesc contains a + "put ref" or "put" FUNCDESC depends on what was found first in the type + description. + Fills the FUNCDESC structure if ITypeInfo provides information for + the respective property functions or the structures will not be filled in. + May throw a BridgeRuntimeError. + */ + void getPropDesc(const OUString & sFuncName, FUNCDESC ** pFuncDescGet, + FUNCDESC** pFuncDescPut, VARDESC ** pVarDesc); + // These functions are for the case if an object of this class wraps an IDispatch + // object that implements UNO interfaces. In that case the member m_seqTypes + // is set through XInitialization::initialize. + void getMethodInfo(std::u16string_view sName, TypeDescription& methodDescription); + // After return attributInfo contains typelib_InterfaceAttributeTypeDescription::pAttributeTypeRef + void getAttributeInfo(std::u16string_view sName, TypeDescription& attributeInfo); + // used by get MethodInfo + TypeDescription getInterfaceMemberDescOfCurrentCall(std::u16string_view sName); + /** Returns always a valid ITypeInfo interface or throws a BridgeRuntimeError. + The returned interface does not need to be AddRef'ed as long as it is locally + used. The interface is kept in the instance of this class. + */ + ITypeInfo* getTypeInfo(); + + /** Returns the DISPID for a function or property name. If true is returned then + id contains a valid DISPID. + */ + + bool getDispid(const OUString& sFuncName, DISPID * id); + + VARTYPE getUserDefinedElementType( ITypeInfo* pTypeInfo, const DWORD nHrefType ); + + /** Gets the element type in a VARIANT like style. E.g. if desc->lptdesc contains + a VT_PTR than it is replaced by VT_BYREF and VT_SAFEARRAY is replaced by VT_ARRAY + If the TYPEDESC describes an SAFEARRAY then varType is a combination of VT_ARRAY + and the element type. + The argument desc must be obtained from FUNCDESC::lprgelemdescParam[i].tdesc where + FUNCDESC was obtained from the ITypeInfo belonging to wrapped IDispatch. + */ + VARTYPE getElementTypeDesc( const TYPEDESC *desc); + /** Iterates over all functions and put the names and indices into the map + m_mapComFunc of type TLBFuncIndexMap. + Call the function every time before accessing the map. + Throws a BridgeRuntimeError on failure. + */ + void buildComTlbIndex(); + + /** Returns a FUNCDESC structure which contains type information about the + current XInvocation::invoke call. The FUNCDESC either describes a method, + a property put or a property get operation. + It uses the types com.sun.star.bridge.oleautomation.PropertyPutArgument + which can be + contained in the sequence of in-arguments of invoke to determine if the call is + a property put or property get operation. + If no adequate FUNCDESC was found, an IllegalArgumentException is thrown. + Therefore it is safe to assume that the returned FUNCDESC* is not NULL. + + @exception IllegalArgumentException + Thrown if no adequate FUNCDESC could be found. + */ + void getFuncDescForInvoke(const OUString & sFuncName, + const Sequence<Any> & seqArgs, FUNCDESC** pFuncDesc); + + // Finds out whether the wrapped IDispatch is a JScript Object. This is + // done by + // asking for the property "_environment". If it has the value "JScript" + // (case insensitive) then the IDispatch is considered a JScript object. + bool isJScriptObject(); + + + // If UNO interfaces are implemented in JScript objects, VB or C++ COM objects + // and those are passed as parameter to a UNO interface function, then + // the IDispatch* are wrapped by objects of this class. Assuming that the functions + // implemented by the IDispatch object returns another UNO interface then + // it has to be wrapped to this type. But this is only possible if an object of this + // wrapper class knows what type it is represting. The member m_TypeDescription holds this + // information. + // m_TypeDescription is only useful when an object wraps an IDispatch object that implements + // a UNO interface. The value is set during a call to XInitialization::initialize. + Sequence<Type> m_seqTypes; + CComPtr<IUnknown> m_spUnknown; + CComPtr<IDispatch> m_spDispatch; + OUString m_sTypeName; // is "" ( not initialised ), "IDispatch" ( we have no idea ) or "SomeLibrary.SomeTypeName" if we managed to get a type + /** This value is set during XInitialization::initialize. It indicates that the COM interface + was transported as VT_DISPATCH in a VARIANT rather than a VT_UNKNOWN + */ + bool m_bOriginalDispatch; + DispIdMap m_dispIdMap; + Reference<XIdlClass>* m_pxIdlClass; + + + // used by isJScriptObject + enum JScriptDetermination{ JScriptUndefined=0, NoJScript, IsJScript}; + JScriptDetermination m_eJScript; + // The map is filled by buildComTlbIndex + // It maps Uno Function names to an index which is used in ITypeInfo::GetFuncDesc + TLBFuncIndexMap m_mapComFunc; + // used for synchronizing the computation of the content for m_mapComFunc + bool m_bComTlbIndexInit; + // Keeps the ITypeInfo obtained from IDispatch::GetTypeInfo + CComPtr< ITypeInfo > m_spTypeInfo; + OUString m_sDefaultMember; + bool m_bHasDfltMethod; + bool m_bHasDfltProperty; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/olethread.cxx b/extensions/source/ole/olethread.cxx new file mode 100644 index 0000000000..503f8bc096 --- /dev/null +++ b/extensions/source/ole/olethread.cxx @@ -0,0 +1,69 @@ +/* -*- 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 "ole2uno.hxx" + +#include <comphelper/windowserrorstring.hxx> +#include <osl/thread.hxx> +#include <sal/log.hxx> + +void o2u_attachCurrentThread() +{ + static osl::ThreadData oleThreadData; + + if (!bool(reinterpret_cast<sal_IntPtr>(oleThreadData.getData()))) + { + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (!SUCCEEDED(hr)) + { // FIXME: is it a problem that this ends up in STA currently? + assert(RPC_E_CHANGED_MODE == hr); + // Let's find out explicitly what apartment mode we are in. + if (hr == RPC_E_CHANGED_MODE) + SAL_INFO("extensions.olebridge", "CoInitializeEx failed (expectedly): " + << WindowsErrorStringFromHRESULT(hr)); + else + SAL_WARN("extensions.olebridge", + "CoInitializeEx failed: " << WindowsErrorStringFromHRESULT(hr)); + APTTYPE nAptType; + APTTYPEQUALIFIER nAptTypeQualifier; + if (SUCCEEDED(CoGetApartmentType(&nAptType, &nAptTypeQualifier))) + { + SAL_INFO("extensions.olebridge", + " Thread is in a " + << (nAptType == APTTYPE_STA ? OUString("single-threaded") : + (nAptType == APTTYPE_MTA ? OUString("multi-threaded") : + (nAptType == APTTYPE_NA ? OUString("neutral") : + (nAptType == APTTYPE_MAINSTA ? OUString("main single-threaded") : + ("unknown (") + OUString::number(nAptType) + ")")))) + << " apartment" + << (nAptTypeQualifier == APTTYPEQUALIFIER_NONE ? OUString() : + (nAptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA ? OUString(" (implicit)") : + (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MTA ? OUString(" (on MTA)") : + (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA ? OUString(" (on STA)") : + (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA ? OUString(" (on implicit MTA)") : + (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA ? OUString(" (on main STA)") : + (" (with unknown qualifier (" + OUString::number(nAptTypeQualifier) + "))"))))))) + << "."); + } + } + oleThreadData.setData(reinterpret_cast<void*>(true)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/servprov.cxx b/extensions/source/ole/servprov.cxx new file mode 100644 index 0000000000..f5a9323d36 --- /dev/null +++ b/extensions/source/ole/servprov.cxx @@ -0,0 +1,547 @@ +/* -*- 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 <vector> + +#include "ole2uno.hxx" +#include "unoconversionutilities.hxx" +#include "servprov.hxx" +#include "unoobjw.hxx" +#include "oleobjw.hxx" + +#include <com/sun/star/script/CannotConvertException.hpp> +#include <comphelper/automationinvokedzone.hxx> +#include <comphelper/windowsdebugoutput.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/any.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <ooo/vba/XHelperInterface.hpp> +#include <sal/log.hxx> + +using namespace cppu; +using namespace osl; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::bridge; +using namespace com::sun::star::bridge::ModelDependent; + +#include <initguid.h> + +// GUID used since 5.2 ( src569 m) +// {82154420-0FBF-11d4-8313-005004526AB4} +DEFINE_GUID(OID_ServiceManager, 0x82154420, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4); + +// FIXME: This GUID is just the above OID_ServiceManager with the +// initial part bumped by one. Is that good enough? +// {82154421-0FBF-11d4-8313-005004526AB4} +DEFINE_GUID(OID_LibreOfficeWriterApplication, 0x82154421, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4); + +// For Calc +// {82154425-0FBF-11d4-8313-005004526AB4} +DEFINE_GUID(OID_LibreOfficeCalcApplication, 0x82154425, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4); + +OneInstanceOleWrapper::OneInstanceOleWrapper( const Reference<XMultiServiceFactory>& smgr, + std::function<const Reference<XInterface>()> xInstFunction ) + : m_refCount(0) + , m_xInstFunction(xInstFunction) + , m_factoryHandle(0) + , m_smgr(smgr) +{ + Reference<XInterface> xInt = m_smgr->createInstance("com.sun.star.bridge.oleautomation.BridgeSupplier"); + + if (xInt.is()) + { + Any a= xInt->queryInterface( cppu::UnoType<XBridgeSupplier2>::get() ); + a >>= m_bridgeSupplier; + } +} + +OneInstanceOleWrapper::~OneInstanceOleWrapper() +{ +} + +bool OneInstanceOleWrapper::registerClass(GUID const * pGuid) +{ + HRESULT hresult; + + o2u_attachCurrentThread(); + + hresult = CoRegisterClassObject( + *pGuid, + this, + CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &m_factoryHandle); + + SAL_INFO("extensions.olebridge", "CoRegisterClassObject(" << *pGuid << "): " << WindowsErrorStringFromHRESULT(hresult)); + + return (hresult == NOERROR); +} + +bool OneInstanceOleWrapper::deregisterClass() +{ + return CoRevokeClassObject(m_factoryHandle) == NOERROR; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::QueryInterface(REFIID riid, void ** ppv) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown*>(static_cast<IClassFactory*>(this)); + return NOERROR; + } + else if (IsEqualIID(riid, IID_IClassFactory)) + { + AddRef(); + *ppv = static_cast<IClassFactory*>(this); + return NOERROR; + } + + *ppv = nullptr; + return ResultFromScode(E_NOINTERFACE); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) OneInstanceOleWrapper::AddRef() +{ + return osl_atomic_increment( &m_refCount); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) OneInstanceOleWrapper::Release() +{ + MutexGuard oGuard( Mutex::getGlobalMutex()); + ULONG refCount = --m_refCount; + if ( m_refCount == 0) + { + delete this; + } + + return refCount; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::CreateInstance(IUnknown*, + REFIID riid, void** ppv) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::CreateInstance(" << riid << ")"); + + HRESULT ret = ResultFromScode(E_UNEXPECTED); + + const Reference<XInterface>& xInst = m_xInstFunction(); + if (xInst.is()) + { + Any usrAny(&xInst, cppu::UnoType<decltype(xInst)>::get()); + sal_uInt8 arId[16]; + rtl_getGlobalProcessId( arId); + Any oleAny = m_bridgeSupplier->createBridge(usrAny, + Sequence<sal_Int8>( reinterpret_cast<sal_Int8*>(arId), 16), + UNO, + OLE); + + + if (auto v = o3tl::tryAccess<sal_uIntPtr>(oleAny)) + { + VARIANT* pVariant = reinterpret_cast<VARIANT*>(*v); + + if ((pVariant->vt == VT_UNKNOWN) || (pVariant->vt == VT_DISPATCH)) + { + SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::Createbridge: punkVal=" << pVariant->punkVal); + ret = pVariant->punkVal->QueryInterface(riid, ppv); + } + + VariantClear(pVariant); + CoTaskMemFree(pVariant); + } + } + + return ret; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::LockServer(BOOL /*fLock*/) +{ + return NOERROR; +} + +OleConverter::OleConverter( const Reference<XMultiServiceFactory> &smgr): + UnoConversionUtilities<OleConverter>( smgr) + +{ +} + +// The XMultiServiceFactory is later set by XInitialization +OleConverter::OleConverter( const Reference<XMultiServiceFactory>& smgr, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ): + UnoConversionUtilities<OleConverter>( smgr, unoWrapperClass, comWrapperClass ) + +{ +} + +OleConverter::~OleConverter() +{ +} + +// XBridgeSupplier -------------------------------------------------------------- +Any SAL_CALL OleConverter::createBridge(const Any& modelDepObject, + const Sequence< sal_Int8 >& ProcessId, + sal_Int16 sourceModelType, + sal_Int16 destModelType) +{ + Any ret; + sal_uInt8 arId[16]; + rtl_getGlobalProcessId( arId ); + + Sequence< sal_Int8 > seqProcessId( reinterpret_cast<sal_Int8*>(arId), 16); + + if ( seqProcessId == ProcessId) + { + if (sourceModelType == UNO) + { + if (destModelType == UNO) + { + // same model -> copy value only + ret = modelDepObject; + } + else if (destModelType == OLE) + { + // convert UNO any into variant + VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT))); + VariantInit( pVariant); + try + { + anyToVariant( pVariant, modelDepObject); + } + catch(...) + { + CoTaskMemFree(pVariant); + throw IllegalArgumentException(); + } + ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get()); + } + else + throw IllegalArgumentException(); + } + else if (sourceModelType == OLE) + { + auto v = o3tl::tryAccess<sal_uIntPtr>(modelDepObject); + if (!v) + { + throw IllegalArgumentException(); + } + else if (destModelType == OLE) + { + // same model -> copy value only + VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT))); + + if (NOERROR != VariantCopy(pVariant, reinterpret_cast<VARIANT*>(*v))) + { + CoTaskMemFree(pVariant); + throw(IllegalArgumentException()); + } + else + { + ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get()); + } + } + else if (destModelType == UNO) + { + // convert variant into UNO any + VARIANT* pVariant = reinterpret_cast<VARIANT*>(*v); + try + { + variantToAny(pVariant, ret); + } + catch (const CannotConvertException & e) + { + throw IllegalArgumentException( + e.Message, nullptr, -1); + } + } + else + throw IllegalArgumentException(); + + } + else + throw IllegalArgumentException(); + } + + return ret; +} + +OUString OleConverter::getImplementationName() +{ + return m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL + ? OUString("com.sun.star.comp.ole.OleConverter2") + : OUString("com.sun.star.comp.ole.OleConverterVar1"); +} + +sal_Bool OleConverter::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> OleConverter::getSupportedServiceNames() +{ + if (m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL) + { + return css::uno::Sequence<OUString>{ + "com.sun.star.bridge.OleBridgeSupplier2", + "com.sun.star.bridge.oleautomation.BridgeSupplier"}; + } + return {"com.sun.star.bridge.OleBridgeSupplierVar1"}; +} + +// XInitialize ------------------------------------------------------------------------------ +// the first argument is an XMultiServiceFactory if at all +void SAL_CALL OleConverter::initialize( const Sequence< Any >& aArguments ) +{ + if( aArguments.getLength() == 1 && aArguments[0].getValueTypeClass() == TypeClass_INTERFACE) + { + Reference < XInterface > xInt; + aArguments[0] >>= xInt; + Reference <XMultiServiceFactory> xMulti( xInt, UNO_QUERY); + m_smgrRemote= xMulti; + } +} + +// UnoConversionUtilities ------------------------------------------------------------------- +Reference< XInterface > OleConverter::createUnoWrapperInstance() +{ + if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else + return Reference<XInterface>(); +} + +Reference< XInterface > OleConverter::createComWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + +OleClient::OleClient( const Reference<XMultiServiceFactory>& smgr): + UnoConversionUtilities<OleClient>( smgr) +{ + Reference<XInterface> xInt;// = m_smgr->createInstance(L"com.sun.star.bridge.OleBridgeSupplier2"); + + if (xInt.is()) + { + Any a= xInt->queryInterface(cppu::UnoType<XBridgeSupplier2>::get() ); + a >>= m_bridgeSupplier; + } +} + +OleClient::~OleClient() +{ +} + +Sequence< OUString > SAL_CALL OleClient::getAvailableServiceNames() +{ + Sequence< OUString > ret; + + return ret; +} + +OUString OleClient::getImplementationName() +{ + return "com.sun.star.comp.ole.OleClient"; +} + +sal_Bool OleClient::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> OleClient::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.bridge.OleObjectFactory", + "com.sun.star.bridge.oleautomation.Factory"}; +} + +Reference<XInterface> SAL_CALL OleClient::createInstance(const OUString& ServiceSpecifier) +{ + Reference<XInterface> ret; + HRESULT result; + IUnknown* pUnknown = nullptr; + CLSID classId; + + o2u_attachCurrentThread(); + + result = CLSIDFromProgID( + o3tl::toW(ServiceSpecifier.getStr()), //Pointer to the ProgID + &classId); //Pointer to the CLSID + + + if (result == NOERROR) + { + result = CoCreateInstance( + classId, //Class identifier (CLSID) of the object + nullptr, //Pointer to whether object is or isn't part of an aggregate + CLSCTX_SERVER, //Context for running executable code + IID_IUnknown, //Reference to the identifier of the interface + reinterpret_cast<void**>(&pUnknown)); //Address of output variable that receives + // the interface pointer requested in riid + } + + if (pUnknown != nullptr) + { + Any any; + CComVariant variant; + + V_VT(&variant) = VT_UNKNOWN; + V_UNKNOWN(&variant) = pUnknown; + // AddRef for Variant + pUnknown->AddRef(); + + // When the object is wrapped, then its refcount is increased + variantToAny(&variant, any); + if (any.getValueTypeClass() == TypeClass_INTERFACE) + { + any >>= ret; + } + pUnknown->Release(); // CoCreateInstance + } + + return ret; +} + +Reference<XInterface> SAL_CALL OleClient::createInstanceWithArguments(const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/) +{ + return createInstance( ServiceSpecifier); +} + +// UnoConversionUtilities ----------------------------------------------------------------------------- +Reference< XInterface > OleClient::createUnoWrapperInstance() +{ + if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT) + { + Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); + } + else + return Reference< XInterface>(); +} +// UnoConversionUtilities ----------------------------------------------------------------------------- +Reference< XInterface > OleClient::createComWrapperInstance( ) +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + +OleServer::OleServer( const Reference<XMultiServiceFactory>& smgr): + m_smgr( smgr) +{ + Reference<XInterface> xInt = m_smgr->createInstance("com.sun.star.bridge.oleautomation.BridgeSupplier"); + + if (xInt.is()) + { + Any a= xInt->queryInterface( cppu::UnoType<XBridgeSupplier2>::get() ); + a >>= m_bridgeSupplier; + } + + (void) provideInstance( [&] + { + return m_smgr; + }, + &OID_ServiceManager ); + + (void) provideInstance( [&] + { + // We want just one SwVbaGlobals for all Automation clients + static const Reference<XInterface> xWordGlobals = m_smgr->createInstance("ooo.vba.word.Globals"); + const Reference<ooo::vba::XHelperInterface> xHelperInterface(xWordGlobals, UNO_QUERY); + Any aApplication = xHelperInterface->Application(); + Reference<XInterface> xApplication; + aApplication >>= xApplication; + return xApplication; + }, + &OID_LibreOfficeWriterApplication ); + + (void) provideInstance( [&] + { + // Ditto for sc + static const Reference<XInterface> xCalcGlobals = m_smgr->createInstance("ooo.vba.excel.Globals"); + const Reference<ooo::vba::XHelperInterface> xHelperInterface(xCalcGlobals, UNO_QUERY); + Any aApplication = xHelperInterface->Application(); + Reference<XInterface> xApplication; + aApplication >>= xApplication; + return xApplication; + }, + &OID_LibreOfficeCalcApplication ); +} + +OleServer::~OleServer() +{ + for (auto const& elem : m_wrapperList) + { + elem->deregisterClass(); + elem->Release(); + } + m_wrapperList.clear(); +} + +OUString OleServer::getImplementationName() +{ + return "com.sun.star.comp.ole.OleServer"; +} + +sal_Bool OleServer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> OleServer::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.bridge.OleApplicationRegistration", + "com.sun.star.bridge.oleautomation.ApplicationRegistration"}; +} + +bool OleServer::provideInstance(std::function<const Reference<XInterface>()> xInstFunction, GUID const * guid) +{ + OneInstanceOleWrapper* pWrapper = new OneInstanceOleWrapper( m_smgr, xInstFunction ); + + pWrapper->AddRef(); + m_wrapperList.push_back(pWrapper); + + return pWrapper->registerClass(guid); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/servprov.hxx b/extensions/source/ole/servprov.hxx new file mode 100644 index 0000000000..8871f28cf7 --- /dev/null +++ b/extensions/source/ole/servprov.hxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <functional> + +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + +#include "ole2uno.hxx" +#include "unoconversionutilities.hxx" + +using namespace com::sun::star::bridge; +using namespace cppu; + +/// @throws Exception +Reference< XInterface> ConverterProvider_CreateInstance2( const Reference<XMultiServiceFactory> & xSMgr); +/// @throws Exception +Reference< XInterface> ConverterProvider_CreateInstanceVar1( const Reference<XMultiServiceFactory> & xSMgr); +/// @throws Exception +Reference<XInterface> OleClient_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr); +/// @throws Exception +Reference<XInterface> OleServer_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr); + +/***************************************************************************** + + OneInstanceOleWrapper + + Provides a single UNO object as OLE object. + + Acts as a COM class factory. When IClassFactory::CreateInstance is being called + then it maps the XInstance member it to a COM object. + +*****************************************************************************/ + +class OneInstanceOleWrapper : public IClassFactory +{ +public: + + OneInstanceOleWrapper( const Reference<XMultiServiceFactory>& smgr, + std::function<const Reference<XInterface>()> xInstFunction ); + virtual ~OneInstanceOleWrapper(); + + bool registerClass(GUID const * pGuid); + bool deregisterClass(); + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + /* IClassFactory methods */ + STDMETHOD(CreateInstance)(IUnknown* punkOuter, REFIID riid, void** ppv) override; + STDMETHOD(LockServer)(BOOL fLock) override; + +protected: + oslInterlockedCount m_refCount; + std::function<const Reference<XInterface>()> m_xInstFunction; + DWORD m_factoryHandle; + Reference<XBridgeSupplier2> m_bridgeSupplier; + Reference<XMultiServiceFactory> m_smgr; +}; + +// Implementation of the UNO service com.sun.star.bridge.OleBridgeSupplier2. + +// This class realizes the service com.sun.star.bridge.OleBridgeSupplier2 and +// com.sun.star.bridge.OleBridgeSupplierVar1. The class implements XBridgeSupplier2 which +// interface does not need a Machine Id in its createBridge function anymore, +// If a UNO interface is to be converted then the member m_nUnoWrapperClass determines +// what wrapper class is to be used. There are currently InterfaceOleWrapper and +// UnoObjectWrapperRemoteOpt. The first is used for the OleBridgeSupplier2 and the +// latter for OleBridgeSupplierVar1. +// The m_nComWrapperClass specifies the class which is used as wrapper for COM interfaces. +// Currently there is only one class available (IUnknownWrapper). +class OleConverter : public WeakImplHelper<XBridgeSupplier2, XInitialization, css::lang::XServiceInfo>, + public UnoConversionUtilities<OleConverter> +{ +public: + explicit OleConverter( const Reference<XMultiServiceFactory>& smgr); + OleConverter( const Reference<XMultiServiceFactory>& smgr, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ); + virtual ~OleConverter() override; + + // XBridgeSupplier2 --------------------------------------------------- + + Any SAL_CALL createBridge(const Any& modelDepObject, + const Sequence<sal_Int8>& ProcessId, + sal_Int16 sourceModelType, + sal_Int16 destModelType) override; + + // XInitialization + void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // UnoConversionUtilities + Reference< XInterface > createUnoWrapperInstance() override; + Reference< XInterface > createComWrapperInstance() override; +protected: + +}; + +// Implementation of the UNO service com.sun.star.bridge.OleObjectFactory. + +class OleClient : public WeakImplHelper<XMultiServiceFactory, css::lang::XServiceInfo>, + public UnoConversionUtilities<OleClient> +{ +public: + explicit OleClient( const Reference<XMultiServiceFactory>& smgr); + ~OleClient() override; + + // XMultiServiceFactory + Reference<XInterface> SAL_CALL createInstance(const OUString& ServiceSpecifier) override; + Reference<XInterface> SAL_CALL createInstanceWithArguments(const OUString& ServiceSpecifier, const Sequence< Any >& Arguments) override; + Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // UnoConversionUtilities + Reference< XInterface > createUnoWrapperInstance() override; + Reference< XInterface > createComWrapperInstance() override; + +protected: + Reference<XBridgeSupplier2> m_bridgeSupplier; +}; + +/***************************************************************************** + + OleServer + + Implementation of the UNO service com.sun.star.bridge.OleApplicationRegistration. + Register the calling application as OLE automation server for + standard OLE object. The objects will be registered while instantiating + this implementation and deregistered, if this implementation is destroyed. + +*****************************************************************************/ + +class OleServer : public cppu::WeakImplHelper<css::lang::XServiceInfo> +{ +public: + explicit OleServer( const Reference<XMultiServiceFactory> &smgr); + ~OleServer() override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +protected: + bool provideInstance(std::function<const Reference<XInterface>()> xInstFunction, GUID const * guid); + + std::list< OneInstanceOleWrapper* > m_wrapperList; + Reference< XBridgeSupplier2 > m_bridgeSupplier; + + Reference<XMultiServiceFactory> m_smgr; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/servreg.cxx b/extensions/source/ole/servreg.cxx new file mode 100644 index 0000000000..66928137b4 --- /dev/null +++ b/extensions/source/ole/servreg.cxx @@ -0,0 +1,94 @@ +/* -*- 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 <osl/time.h> +#include "ole2uno.hxx" +#include "servprov.hxx" +#include <rtl/ustring.hxx> +#include <cppuhelper/factory.hxx> + +using namespace cppu; + +Reference<XInterface> ConverterProvider_CreateInstance2( const Reference<XMultiServiceFactory> & xSMgr) +{ + Reference<XInterface> xService = *new OleConverter( xSMgr); + return xService; +} + +Reference<XInterface> ConverterProvider_CreateInstanceVar1( const Reference<XMultiServiceFactory> & xSMgr) +{ + Reference<XInterface> xService = *new OleConverter( xSMgr, UNO_OBJECT_WRAPPER_REMOTE_OPT, IUNKNOWN_WRAPPER_IMPL); + return xService; +} + +Reference<XInterface> OleClient_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr) +{ + Reference<XInterface> xService = *new OleClient( xSMgr); + return xService; +} + +Reference<XInterface> OleServer_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr) +{ + Reference<XInterface > xService = *new OleServer(xSMgr); + return xService; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void * oleautobridge_component_getFactory( + const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void * pRet = nullptr; + + OUString aImplName( OUString::createFromAscii( pImplName ) ); + Reference< XSingleServiceFactory > xFactory; + Sequence<OUString> seqServiceNames; + if (pServiceManager && aImplName == "com.sun.star.comp.ole.OleConverter2") + { + xFactory= createSingleFactory( static_cast< XMultiServiceFactory*>(pServiceManager), + aImplName, + ConverterProvider_CreateInstance2, seqServiceNames ); + } + else if (pServiceManager && aImplName == "com.sun.star.comp.ole.OleConverterVar1") + { + xFactory= createSingleFactory( static_cast<XMultiServiceFactory*>(pServiceManager), + aImplName, + ConverterProvider_CreateInstanceVar1, seqServiceNames ); + } + else if(pServiceManager && aImplName == "com.sun.star.comp.ole.OleClient") + { + xFactory= createSingleFactory( static_cast< XMultiServiceFactory*>(pServiceManager), + aImplName, + OleClient_CreateInstance, seqServiceNames ); + } + else if(pServiceManager && aImplName == "com.sun.star.comp.ole.OleServer") + { + xFactory= createOneInstanceFactory( static_cast< XMultiServiceFactory*>(pServiceManager), + aImplName, + OleServer_CreateInstance, seqServiceNames ); + } + + if (xFactory.is()) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/unoconversionutilities.hxx b/extensions/source/ole/unoconversionutilities.hxx new file mode 100644 index 0000000000..a73a714abe --- /dev/null +++ b/extensions/source/ole/unoconversionutilities.hxx @@ -0,0 +1,2363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <memory> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory2.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/FailReason.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> +#include <com/sun/star/bridge/XBridgeSupplier2.hpp> +#include <com/sun/star/bridge/oleautomation/Date.hpp> +#include <com/sun/star/bridge/oleautomation/Currency.hpp> +#include <com/sun/star/bridge/oleautomation/SCode.hpp> +#include <com/sun/star/bridge/oleautomation/Decimal.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <typelib/typedescription.hxx> +#include <o3tl/any.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include "ole2uno.hxx" +#include <cppuhelper/weakref.hxx> + +#include "unotypewrapper.hxx" +#include <unordered_map> + +// for some reason DECIMAL_NEG (wtypes.h) which contains BYTE is not resolved. +typedef unsigned char BYTE; +// classes for wrapping uno objects +#define INTERFACE_OLE_WRAPPER_IMPL 1 +#define UNO_OBJECT_WRAPPER_REMOTE_OPT 2 + +#define INVOCATION_SERVICE "com.sun.star.script.Invocation" + + +// classes for wrapping ole objects +#define IUNKNOWN_WRAPPER_IMPL 1 + +#define INTERFACE_ADAPTER_FACTORY "com.sun.star.script.InvocationAdapterFactory" +// COM or JScript objects implementing UNO interfaces have to implement this property +#define SUPPORTED_INTERFACES_PROP L"_implementedInterfaces" +// Second property without leading underscore for use in VB +#define SUPPORTED_INTERFACES_PROP2 L"Bridge_ImplementedInterfaces" + +using namespace com::sun::star::script; +using namespace com::sun::star::beans; +using namespace com::sun::star::uno; +using namespace com::sun::star::bridge::oleautomation; + +extern std::unordered_map<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap; +extern std::unordered_map<sal_uIntPtr, sal_uIntPtr> WrapperToAdapterMap; + +//Maps IUnknown pointers to a weak reference of the respective wrapper class (e.g. +// IUnknownWrapperImpl. It is the responsibility of the wrapper to remove the entry when +// it is being destroyed. +// Used to ensure that an Automation object is always mapped to the same UNO objects. +extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap; + +// Maps XInterface pointers to a weak reference of its wrapper class (i.e. +// InterfaceOleWrapper). It is the responsibility of the wrapper to remove the entry when +// it is being destroyed. It is used to ensure the identity of objects. That is, a UNO interface +// is mapped to IDispatch which is kept alive in the COM environment. If the same +// UNO interface is mapped again to COM then the IDispach of the first mapped instance +// must be returned. +extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap; + +// This function tries to the change the type of a value (contained in the Any) +// to the smallest possible that can hold the value. This is actually done only +// for types of VT_I4 (see o2u_variantToAny). The reason is the following: +// JavaScript passes integer values always as VT_I4. If there is a parameter or +// property of type any then the bridge converts the any's content according +// to "o2u_variantToAny". Because the VARTYPE is VT_I4 the value would be converted +// to TypeClass_LONG. Say the method XPropertySet::setPropertyValue( string name, any value) +// would be called on an object and the property actually is of TypeClass_SHORT. +// After conversion of the VARIANT parameter the Any would contain type +// TypeClass_LONG. Because the corereflection does not cast from long to short +// the "setPropertValue" would fail as the value has not the right type. + +// The corereflection does convert small integer types to bigger types. +// Therefore we can reduce the type if possible and avoid the above mentioned +// problem. + +// The function is not used when elements are to be converted for Sequences. + +inline void reduceRange( Any& any) +{ + OSL_ASSERT( any.getValueTypeClass() == TypeClass_LONG); + + sal_Int32 value= *o3tl::doAccess<sal_Int32>(any); + if( value <= 0x7f && value >= -0x80) + {// -128 bis 127 + sal_Int8 charVal= static_cast<sal_Int8>( value); + any.setValue( &charVal, cppu::UnoType<sal_Int8>::get()); + } + else if( value <= 0x7fff && value >= -0x8000) + {// -32768 bis 32767 + sal_Int16 shortVal= static_cast<sal_Int16>( value); + any.setValue( &shortVal, cppu::UnoType<sal_Int16>::get()); + } +} + +// createUnoObjectWrapper gets a wrapper instance by calling createUnoWrapperInstance + // and initializes it via XInitialization. The wrapper object is required to implement + // XBridgeSupplier so that it can convert itself to IDispatch. + // class T: Deriving class ( must implement XInterface ) +/** All methods are allowed to throw at least a BridgeRuntimeError. + */ +template< class > +class UnoConversionUtilities +{ +public: + explicit UnoConversionUtilities( const Reference<XMultiServiceFactory> & smgr): + m_nUnoWrapperClass( INTERFACE_OLE_WRAPPER_IMPL), + m_nComWrapperClass( IUNKNOWN_WRAPPER_IMPL), + m_smgr( smgr) + {} + + UnoConversionUtilities( const Reference<XMultiServiceFactory> & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ) + : m_nUnoWrapperClass(unoWrapperClass), + m_nComWrapperClass(comWrapperClass), m_smgr(xFactory) + {} + + virtual ~UnoConversionUtilities() {} + /** converts only into oleautomation types, that is there is no VT_I1, VT_UI2, VT_UI4 + a sal_Unicode character is converted into a BSTR. + @exception com.sun.star.lang.IllegalArgumentException + If the any was inappropriate for conversion. + @exception com.sun.star.script.CannotConvertException + The any contains a type class for which no conversion is provided. + */ + void anyToVariant(VARIANT* pVariant, const Any& rAny); + void anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type); + + /** @exception com.sun.star.lang.IllegalArgumentException + If rSeq does not contain a sequence then the exception is thrown. + */ + SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq); + /** @exception com.sun.star.lang.IllegalArgumentException + If rSeq does not contain a sequence or elemtype has no proper value + then the exception is thrown. + */ + SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype); + /** + @exception com.sun.star.lang.IllegalArgumentException + If rObj does not contain a struct or interface + */ + void createUnoObjectWrapper(const Any & rObj, VARIANT * pVar); + /** @exception CannotConvertException + Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. + ArgumentIndex is 0. + @IllegalArgumentException + Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1, + */ + void variantToAny(const VARIANT* pVariant, Any& rAny, bool bReduceValueRange = true); + /** This method converts variants arguments in calls from COM -> UNO. Only then + the expected UNO type is known. + @exception CannotConvertException + Thrown if the VARIANT contains a type that cannot be coerced in the expected Any. + ArgumentIndex is 0. + @IllegalArgumentException + Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1, + */ + void variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange = true); + + /** + @exception IllegalArgumentException + -if pVar does not contain VT_UNKNOWN or VT_DISPATCH or + pVar is used for a particular UNO type which is not supported by pVar + */ + Any createOleObjectWrapper(VARIANT* pVar, const Type& aType= Type()); + + /* + Return true means var contained a ValueObject, and it was successfully converted. + The result is in any. It an error occurred a BridgeRuntimeError will be thrown. + */ + bool convertValueObject( const VARIANTARG *var, Any& any); + void dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type); + + Sequence<Any> createOleArrayWrapperOfDim(SAFEARRAY* pArray, unsigned int dimCount, unsigned int actDim, LONG* index, + VARTYPE type, const Type& unotype); + Sequence<Any> createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unotype= Type()); + + + VARTYPE mapTypeClassToVartype( TypeClass type); + Reference< XSingleServiceFactory > getInvocationFactory(const Any& anyObject); + + + virtual Reference< XInterface > createUnoWrapperInstance()=0; + virtual Reference< XInterface > createComWrapperInstance()=0; + + static bool isJScriptArray(const VARIANT* pvar); + + Sequence<Type> getImplementedInterfaces(IUnknown* pUnk); + +protected: + Reference<XInterface> createAdapter(const Sequence<Type>& types, const Reference<XInterface>& receiver); + + // helper function for Sequence conversion + void getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc); + // helper function for Sequence conversion + static bool incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLength, + sal_Int32 * parMultidimensionalIndex); + // helper function for Sequence conversion + static size_t getOleElementSize( VARTYPE type); + + static Type getElementTypeOfSequence( const Type& seqType); + + //Provides a typeconverter + Reference<XTypeConverter> getTypeConverter(); + + // This member determines what class is used to convert a UNO object + // or struct to a COM object. It is passed along to the anyToVariant + // function in the createBridge function implementation + const sal_uInt8 m_nUnoWrapperClass; + const sal_uInt8 m_nComWrapperClass; + + // The servicemanager is either a local smgr or remote when the service + // com.sun.star.bridge.OleBridgeSupplierVar1 is used. This service can be + // created by createInstanceWithArguments where one can supply a service + // manager that is to be used. + // Local service manager as supplied by the loader when the creator function + // of the service is being called. + Reference<XMultiServiceFactory> m_smgr; + // An explicitly supplied service manager when the service + // com.sun.star.bridge.OleBridgeSupplierVar1 is used. That can be a remote + // manager. + Reference<XMultiServiceFactory> m_smgrRemote; + Reference<XSingleServiceFactory> m_xInvocationFactoryLocal; + Reference<XSingleServiceFactory> m_xInvocationFactoryRemote; + +private: + // Holds the type converter which is used for sequence conversion etc. + // Use the getTypeConverter function to obtain the interface. + Reference<XTypeConverter> m_typeConverter; + + +}; + +// ask the object for XBridgeSupplier2 and on success bridges +// the uno object to IUnknown or IDispatch. +// return true the UNO object supports +template < class T > +bool convertSelfToCom( T& unoInterface, VARIANT * pVar) +{ + bool ret = false; + Reference< XInterface > xInt( unoInterface, UNO_QUERY); + if( xInt.is()) + { + Reference< css::bridge::XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY); + if( xSupplier.is()) + { + sal_Int8 arId[16]; + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(arId)); + Sequence<sal_Int8> seqId( arId, 16); + Any anySource; + anySource <<= xInt; + Any anyDisp = xSupplier->createBridge( + anySource, seqId, css::bridge::ModelDependent::UNO, + css::bridge::ModelDependent::OLE); + + // due to global-process-id check this must be in-process pointer + if (auto v = o3tl::tryAccess<sal_uIntPtr>(anyDisp)) + { + VARIANT* pvariant= reinterpret_cast<VARIANT*>(*v); + HRESULT hr; + if (FAILED(hr = VariantCopy(pVar, pvariant))) + throw BridgeRuntimeError( + "[automation bridge] convertSelfToCom\n" + "VariantCopy failed! Error: " + + OUString::number(hr)); + VariantClear( pvariant); + CoTaskMemFree( pvariant); + ret = true; + } + } + } + return ret; +} + + +// Gets the invocation factory depending on the Type in the Any. +// The factory can be created by a local or remote multi service factory. +// In case there is a remote multi service factory available there are +// some services or types for which the local factory is used. The exceptions +// are: all structs. +// Param anyObject - contains the object ( interface, struct) for what we need an invocation object. + +template<class T> +Reference< XSingleServiceFactory > UnoConversionUtilities<T>::getInvocationFactory(const Any& anyObject) +{ + Reference< XSingleServiceFactory > retVal; + MutexGuard guard( getBridgeMutex()); + if( anyObject.getValueTypeClass() != TypeClass_STRUCT && + m_smgrRemote.is() ) + { + if( ! m_xInvocationFactoryRemote.is() ) + m_xInvocationFactoryRemote.set(m_smgrRemote->createInstance( INVOCATION_SERVICE), UNO_QUERY); + retVal= m_xInvocationFactoryRemote; + } + else + { + if( ! m_xInvocationFactoryLocal.is() ) + m_xInvocationFactoryLocal.set(m_smgr->createInstance(INVOCATION_SERVICE ), UNO_QUERY); + retVal= m_xInvocationFactoryLocal; + } + return retVal; +} + +template<class T> +void UnoConversionUtilities<T>::variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange /* = sal_True */) +{ + try + { + HRESULT hr; + bool bFail = false; + bool bCannotConvert = false; + CComVariant var; + + // There is no need to support indirect values, since they're not supported by UNO + if( FAILED(hr= VariantCopyInd( &var, pArg))) // remove VT_BYREF + throw BridgeRuntimeError( + "[automation bridge] UnoConversionUtilities<T>::variantToAny \n" + "VariantCopyInd failed for reason : " + OUString::number(hr)); + bool bHandled = convertValueObject( & var, rAny); + if( bHandled) + OSL_ENSURE( rAny.getValueType() == ptype, "type in Value Object must match the type parameter"); + + if( ! bHandled) + { + // convert into a variant type that is the equivalent to the type + // the sequence expects. Thus variantToAny produces the correct type + // E.g. An Array object contains VT_I4 and the sequence expects shorts + // than the vartype must be changed. The reason is, you can't specify the + // type in JavaScript and the script engine determines the type being used. + switch( ptype.getTypeClass()) + { + case TypeClass_CHAR: // could be: new Array( 12, 'w', "w") + if( var.vt == VT_BSTR) + { + if(SUCCEEDED( hr= VariantChangeType( &var, &var, 0, VT_BSTR))) + rAny.setValue( V_BSTR( &var), ptype); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + } + else + { + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2))) + rAny.setValue(& var.iVal, ptype); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + } + break; + case TypeClass_INTERFACE: // could also be an IUnknown + case TypeClass_STRUCT: + { + rAny = createOleObjectWrapper( & var, ptype); + break; + } + case TypeClass_ENUM: + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I4))) + rAny.setValue(& var.lVal, ptype); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_SEQUENCE: + // There are different ways of receiving a sequence: + // 1: JScript, VARTYPE: VT_DISPATCH + // 2. VBScript simple arraysVT_VARIANT|VT_BYREF the referenced VARIANT contains + // a VT_ARRAY| <type> + // 3. VBScript multi dimensional arrays: VT_ARRAY|VT_BYREF + if( pArg->vt == VT_DISPATCH) + { + dispatchExObject2Sequence( pArg, rAny, ptype); + } + else + { + if ((var.vt & VT_ARRAY) != 0) + { + VARTYPE oleType = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY ); + Sequence<Any> unoSeq = createOleArrayWrapper( var.parray, oleType, ptype); + Reference<XTypeConverter> conv = getTypeConverter(); + if (conv.is()) + { + try + { + Any anySeq(unoSeq); + Any convAny = conv->convertTo(anySeq, ptype); + rAny = convAny; + } + catch (const IllegalArgumentException& e) + { + throw BridgeRuntimeError( + "[automation bridge]com.sun.star.lang.IllegalArgumentException " + "in UnoConversionUtilities<T>::variantToAny! Message: " + + e.Message); + } + catch (const CannotConvertException& e) + { + throw BridgeRuntimeError( + "[automation bridge]com.sun.star.script.CannotConvertException " + "in UnoConversionUtilities<T>::variantToAny! Message: " + + e.Message); + } + } + } + } + break; + case TypeClass_VOID: + rAny.setValue(nullptr,Type()); + break; + case TypeClass_ANY: // Any + // There could be a JScript Array that needs special handling + // If an Any is expected and this Any must contain a Sequence + // then we cannot figure out what element type is required. + // Therefore we convert to Sequence< Any > + if( pArg->vt == VT_DISPATCH && isJScriptArray( pArg)) + { + dispatchExObject2Sequence( pArg, rAny, + cppu::UnoType<Sequence<Any>>::get()); + } + else if (pArg->vt == VT_DECIMAL) + { + //Decimal maps to hyper in calls from COM -> UNO + // It does not matter if we create a sal_uInt64 or sal_Int64, + // because the UNO object is called through invocation which + //will do a type conversion if necessary + if (var.decVal.sign == 0) + { + // positive value + variantToAny( & var, rAny, cppu::UnoType<sal_uInt64>::get(), + bReduceValueRange); + } + else + { + //negative value + variantToAny( & var, rAny, cppu::UnoType<sal_Int64>::get(), + bReduceValueRange); + } + } + else + { + variantToAny( & var, rAny); + } + break; + case TypeClass_BOOLEAN: // VARIANT could be VARIANT_BOOL or other + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BOOL))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_STRING: // UString + if(var.vt == VT_NULL) + var = CComBSTR(""); + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BSTR))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_FLOAT: // float + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R4))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_DOUBLE: // double + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R8))) + variantToAny(& var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_BYTE: // BYTE + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I1))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_SHORT: // INT16 + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_LONG: + if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_I4))) + variantToAny( & var, rAny, bReduceValueRange); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_HYPER: + if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL))) + { + if (var.decVal.Lo64 > SAL_CONST_UINT64(0x8000000000000000) + || var.decVal.Hi32 > 0 + || var.decVal.scale > 0) + { + bFail = true; + break; + } + sal_Int64 value = var.decVal.Lo64; + if (var.decVal.sign == DECIMAL_NEG) + value |= SAL_CONST_UINT64(0x8000000000000000); + rAny <<= value; + } + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_UNSIGNED_SHORT: // UINT16 + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI2))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_UNSIGNED_LONG: + if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI4))) + variantToAny( & var, rAny, bReduceValueRange); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_UNSIGNED_HYPER: + if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL))) + { + if (var.decVal.Hi32 > 0 || var.decVal.scale > 0) + { + bFail = true; + break; + } + rAny <<= var.decVal.Lo64; + } + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + case TypeClass_TYPE: + if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_UNKNOWN))) + variantToAny( & var, rAny); + else if (hr == DISP_E_TYPEMISMATCH) + bCannotConvert = true; + else + bFail = true; + break; + default: + bCannotConvert = true; + break; + } + } + if (bCannotConvert) + throw CannotConvertException( + "[automation bridge]UnoConversionUtilities<T>::variantToAny \n" + "Cannot convert the value of vartype :\"" + + OUString::number(static_cast<sal_Int32>(var.vt)) + + "\" to the expected UNO type of type class: " + + OUString::number(static_cast<sal_Int32>(ptype.getTypeClass())), + nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); + + if (bFail) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>:variantToAny\n" + "The provided VARIANT of type\" " + OUString::number(static_cast<sal_Int32>(var.vt)) + + "\" is unappropriate for conversion!", Reference<XInterface>(), -1); + } + catch (const CannotConvertException &) + { + throw; + } + catch (const IllegalArgumentException &) + { + throw; + } + catch (const BridgeRuntimeError &) + { + throw; + } + catch (const Exception & e) + { + throw BridgeRuntimeError("[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::variantToAny ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::variantToAny !"); + } +} + +// The function only converts Sequences to SAFEARRAYS with elements of the type +// specified by the parameter type. Everything else is forwarded to +// anyToVariant(VARIANT* pVariant, const Any& rAny) +// Param type must not be VT_BYREF +template<class T> +void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type) +{ + try + { + HRESULT hr= S_OK; + + OSL_ASSERT( (type & VT_BYREF) == 0); + if (type & VT_ARRAY) + { + type ^= VT_ARRAY; + SAFEARRAY* ar= createUnoSequenceWrapper( rAny, type); + if( ar) + { + VariantClear( pVariant); + pVariant->vt= ::sal::static_int_cast< VARTYPE, int >( VT_ARRAY | type ); + pVariant->byref= ar; + } + } + else if(type == VT_VARIANT) + { + anyToVariant(pVariant, rAny); + } + else + { + CComVariant var; + anyToVariant( &var, rAny); + if(FAILED(hr = VariantChangeType(&var, &var, 0, type))) + { + if (hr == DISP_E_TYPEMISMATCH) + throw CannotConvertException( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Cannot convert the value of type :\"" + + rAny.getValueTypeName() + + "\" to the expected Automation type of VARTYPE: " + + OUString::number(static_cast<sal_Int32>(type)), + nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); + + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Conversion of any with " + + rAny.getValueType().getTypeName() + + " to VARIANT with type: " + OUString::number(static_cast<sal_Int32>(type)) + + " failed! Error code: " + OUString::number(hr)); + + } + if(FAILED(hr = VariantCopy(pVariant, &var))) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "VariantCopy failed for reason: " + OUString::number(hr)); + } + } + } + catch (const IllegalArgumentException &) + { + throw; + } + catch (const CannotConvertException &) + { + throw; + } + catch (const BridgeRuntimeError&) + { + throw; + } + catch(const Exception & e) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred. Message: " + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred."); + } +} + +template<class T> +void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny) +{ + try + { + bool bIllegal = false; + switch (rAny.getValueTypeClass()) + { + case TypeClass_INTERFACE: + { + Reference<XInterface> xInt; + if (rAny >>= xInt) + { + createUnoObjectWrapper(rAny, pVariant); + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_STRUCT: + { + if (rAny.getValueType() == cppu::UnoType<Date>::get() ) + { + Date d; + if (rAny >>= d) + { + pVariant->vt = VT_DATE; + pVariant->date = d.Value; + } + else + { + bIllegal = true; + } + } + else if(rAny.getValueType() == cppu::UnoType<Decimal>::get()) + { + Decimal d; + if (rAny >>= d) + { + pVariant->vt = VT_DECIMAL; + pVariant->decVal.scale = d.Scale; + pVariant->decVal.sign = d.Sign; + pVariant->decVal.Lo32 = d.LowValue; + pVariant->decVal.Mid32 = d.MiddleValue; + pVariant->decVal.Hi32 = d.HighValue; + } + else + { + bIllegal = true; + } + } + else if (rAny.getValueType() == cppu::UnoType<Currency>::get()) + { + Currency c; + if (rAny >>= c) + { + pVariant->vt = VT_CY; + pVariant->cyVal.int64 = c.Value; + } + else + { + bIllegal = true; + } + } + else if(rAny.getValueType() == cppu::UnoType<SCode>::get()) + { + SCode s; + if (rAny >>= s) + { + pVariant->vt = VT_ERROR; + pVariant->scode = s.Value; + } + else + { + bIllegal = true; + } + } + else + { + createUnoObjectWrapper(rAny, pVariant); + } + break; + } + case TypeClass_SEQUENCE: // sequence ??? SafeArray descriptor + { + SAFEARRAY* pArray = createUnoSequenceWrapper(rAny); + if (pArray) + { + V_VT(pVariant) = VT_ARRAY | VT_VARIANT; + V_ARRAY(pVariant) = pArray; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_VOID: + { + HRESULT hr = S_OK; + if (FAILED(hr = VariantClear(pVariant))) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" + "VariantClear failed with error:" + OUString::number(hr)); + } + break; + } + case TypeClass_BOOLEAN: + { + bool value; + if (rAny >>= value) + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = value ? VARIANT_TRUE: VARIANT_FALSE; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_CHAR: + { + // Because VT_UI2 does not conform to oleautomation we convert into VT_I2 instead + sal_uInt16 value = *o3tl::forceAccess<sal_Unicode>(rAny); + pVariant->vt = VT_I2; + pVariant->iVal = value; + break; + } + case TypeClass_STRING: + { + OUString value; + if (rAny >>= value) + { + pVariant->vt = VT_BSTR; + pVariant->bstrVal = SysAllocString(o3tl::toW(value.getStr())); + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_FLOAT: + { + float value; + if (rAny >>= value) + { + pVariant->vt = VT_R4; + pVariant->fltVal = value; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_DOUBLE: + { + double value; + if (rAny >>= value) + { + pVariant->vt = VT_R8; + pVariant->dblVal = value; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_BYTE: + { + // ole automation does not know a signed char but only unsigned char + sal_Int8 value; + if (rAny >>= value) + { + pVariant->vt = VT_UI1; + pVariant->bVal = value; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_SHORT: // INT16 + case TypeClass_UNSIGNED_SHORT: // UINT16 + { + sal_Int16 value; + if (rAny >>= value) + { + pVariant->vt = VT_I2; + pVariant->iVal = value; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_ENUM: + { + sal_Int32 value = *static_cast<sal_Int32 const *>(rAny.getValue()); + pVariant->vt = VT_I4; + pVariant->lVal= value; + break; + } + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + { + sal_Int32 value; + if (rAny >>= value) + { + pVariant->vt = VT_I4; + pVariant->lVal= value; + } + else + { + bIllegal = true; + } + break; + } + case TypeClass_HYPER: + { + + pVariant->vt = VT_DECIMAL; + pVariant->decVal.scale = 0; + pVariant->decVal.sign = 0; + pVariant->decVal.Hi32 = 0; + + sal_Int64 value; + rAny >>= value; + + if (value & SAL_CONST_UINT64(0x8000000000000000)) + pVariant->decVal.sign = DECIMAL_NEG; + + pVariant->decVal.Lo64 = value; + break; + } + case TypeClass_UNSIGNED_HYPER: + { + pVariant->vt = VT_DECIMAL; + pVariant->decVal.scale = 0; + pVariant->decVal.sign = 0; + pVariant->decVal.Hi32 = 0; + + sal_uInt64 value; + rAny >>= value; + pVariant->decVal.Lo64 = value; + break; + } + case TypeClass_TYPE: + { + Type type; + rAny >>= type; + CComVariant var; + if (!createUnoTypeWrapper(type.getTypeName(), & var)) + throw BridgeRuntimeError( + "[automation bridge] UnoConversionUtilities<T>::anyToVariant \n" + "Error during conversion of UNO type to Automation object!"); + + if (FAILED(VariantCopy(pVariant, &var))) + throw BridgeRuntimeError( + "[automation bridge] UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected error!"); + break; + } + default: + //TypeClass_SERVICE: + //TypeClass_EXCEPTION: + //When an InvocationTargetException is thrown when calling XInvocation::invoke + //on a UNO object, then the target exception is directly used to create a + //EXEPINFO structure + //TypeClass_TYPEDEF + //TypeClass_ANY: + //TypeClass_UNKNOWN: + //TypeClass_MODULE: + throw CannotConvertException( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" + "There is no conversion for this UNO type to an Automation type." + "The destination type class is the type class of the UNO " + "argument which was to be converted.", + Reference<XInterface>(), rAny.getValueTypeClass(), + FailReason::TYPE_NOT_SUPPORTED, 0); + + break; + } + if (bIllegal) + { + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n" + "The provided any of type\" " + rAny.getValueType().getTypeName() + + "\" is unappropriate for conversion!", Reference<XInterface>(), -1); + + } + } + catch (const CannotConvertException &) + { + throw; + } + catch (const IllegalArgumentException &) + { + throw; + } + catch(const BridgeRuntimeError&) + { + throw; + } + catch(const Exception & e) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred. Message: " + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n" + "Unexpected exception occurred. " ); + } +} + +// Creates an SAFEARRAY of the specified element and if necessary +// creates a SAFEARRAY with multiple dimensions. +// Used by sal_Bool anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type); +template<class T> +SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype) +{ + if (rSeq.getValueTypeClass() != TypeClass_SEQUENCE) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n" + "The any does not contain a sequence!", nullptr, 0); + if (elemtype == VT_NULL || elemtype == VT_EMPTY) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n" + "No element type supplied!",nullptr, -1); + SAFEARRAY* pArray= nullptr; + // Get the dimensions. This is done by examining the type name string + // The count of brackets determines the dimensions. + OUString sTypeName= rSeq.getValueType().getTypeName(); + sal_Int32 dims=0; + for(sal_Int32 lastIndex=0;(lastIndex= sTypeName.indexOf( L'[', lastIndex)) != -1; lastIndex++,dims++); + + //get the maximum number of elements per dimensions and the typedescription of the elements + Sequence<sal_Int32> seqElementCounts( dims); + TypeDescription elementTypeDesc; + getElementCountAndTypeOfSequence( rSeq, 1, seqElementCounts, elementTypeDesc ); + + if( elementTypeDesc.is() ) + { + // set up the SAFEARRAY + std::unique_ptr<SAFEARRAYBOUND[]> sarSafeArrayBound(new SAFEARRAYBOUND[dims]); + SAFEARRAYBOUND* prgsabound= sarSafeArrayBound.get(); + for( sal_Int32 i=0; i < dims; i++) + { + //prgsabound[0] is the right most dimension + prgsabound[dims - i - 1].lLbound = 0; + prgsabound[dims - i - 1].cElements = seqElementCounts[i]; + } + + typelib_TypeDescription* rawTypeDesc= elementTypeDesc.get(); + sal_Int32 elementSize= rawTypeDesc->nSize; + size_t oleElementSize= getOleElementSize( elemtype); + // SafeArrayCreate clears the memory for the data itself. + pArray = SafeArrayCreate(elemtype, dims, prgsabound); + + // convert the Sequence's elements and populate the SAFEARRAY + if( pArray) + { + // Iterate over every Sequence that contains the actual elements + void* pSAData; + if( SUCCEEDED( SafeArrayAccessData( pArray, &pSAData))) + { + const sal_Int32* parElementCount= seqElementCounts.getConstArray(); + uno_Sequence * pMultiSeq= *static_cast<uno_Sequence* const*>(rSeq.getValue()); + sal_Int32 dimsSeq= dims - 1; + + // arDimSeqIndices contains the current index of a block of data. + // E.g. Sequence<Sequence<sal_Int32>> , the index would refer to Sequence<sal_Int32> + // In this case arDimSeqIndices would have the size 1. That is the elements are not counted + // but the Sequences that contain those elements. + // The indices are 0 based + std::unique_ptr<sal_Int32[]> sarDimsSeqIndices; + sal_Int32* arDimsSeqIndices= nullptr; + if( dimsSeq > 0) + { + sarDimsSeqIndices.reset(new sal_Int32[dimsSeq]); + arDimsSeqIndices = sarDimsSeqIndices.get(); + memset( arDimsSeqIndices, 0, sizeof( sal_Int32 ) * dimsSeq); + } + + char* psaCurrentData= static_cast<char*>(pSAData); + + do + { + // Get the Sequence at the current index , see arDimsSeqIndices + uno_Sequence * pCurrentSeq= pMultiSeq; + sal_Int32 curDim=1; // 1 based + bool skipSeq= false; + while( curDim <= dimsSeq ) + { + // get the Sequence at the index if valid + if( pCurrentSeq->nElements > arDimsSeqIndices[ curDim - 1] ) // don't point to Nirvana + { + // size of Sequence is 4 + sal_Int32 offset= arDimsSeqIndices[ curDim - 1] * 4; + pCurrentSeq= *reinterpret_cast<uno_Sequence**>(&pCurrentSeq->elements[ offset]); + curDim++; + } + else + { + // There is no Sequence at this index, so skip this index + skipSeq= true; + break; + } + } + + if( skipSeq) + continue; + + // Calculate the current position within the datablock of the SAFEARRAY + // for the next Sequence. + sal_Int32 memOffset= 0; + sal_Int32 dimWeight= parElementCount[ dims - 1]; // size of the rightmost dimension + for(sal_Int32 idims=0; idims < dimsSeq; idims++ ) + { + memOffset+= arDimsSeqIndices[dimsSeq - 1 - idims] * dimWeight; + // now determine the weight of the dimension to the left of the current. + if( dims - 2 - idims >=0) + dimWeight*= parElementCount[dims - 2 - idims]; + } + psaCurrentData= static_cast<char*>(pSAData) + memOffset * oleElementSize; + // convert the Sequence and put the elements into the Safearray + for( sal_Int32 i= 0; i < pCurrentSeq->nElements; i++) + { + Any unoElement( pCurrentSeq->elements + i * elementSize, rawTypeDesc ); + // The any is being converted into a VARIANT which value is then copied + // to the SAFEARRAY's data block. When copying one has to follow the rules for + // copying certain types, as are VT_DISPATCH, VT_UNKNOWN, VT_VARIANT, VT_BSTR. + // To increase performance, we just do a memcpy of VARIANT::byref. This is possible + // because anyToVariant has already followed the copying rules. To make this + // work there must not be a VariantClear. + // One Exception is VARIANT because I don't know how VariantCopy works. + + VARIANT var; + VariantInit( &var); + anyToVariant( &var, unoElement); + if( elemtype == VT_VARIANT ) + { + VariantCopy( reinterpret_cast<VARIANT*>(psaCurrentData), &var); + VariantClear( &var); + } + else + memcpy( psaCurrentData, &var.byref, oleElementSize); + + psaCurrentData+= oleElementSize; + } + } + while( incrementMultidimensionalIndex( dimsSeq, parElementCount, arDimsSeqIndices)); + + SafeArrayUnaccessData( pArray); + } + } + } + return pArray; +} + +// Increments a multi dimensional index. +// Returns true as long as the index has been successfully incremented, false otherwise. +// False is also returned if an overflow of the most significant dimension occurs. E.g. +// assume an array with the dimensions (2,2), then the lowest index is (0,0) and the highest +// index is (1,1). If the function is being called with the index (1,1) then the overflow would +// occur, with the result (0,0) and a sal_False as return value. +// Param dimensions - number of dimensions +// Param parDimensionsLength - The array contains the size of each dimension, that is the +// size of the array equals the parameter dimensions. +// The rightmost dimensions is the least significant one +// ( parDimensionsLengths[ dimensions -1 ] ). +// Param parMultiDimensionalIndex - The array contains the index. Each dimension index is +// 0 based. +template<class T> +bool UnoConversionUtilities<T>::incrementMultidimensionalIndex(sal_Int32 dimensions, + const sal_Int32 * parDimensionLengths, + sal_Int32 * parMultidimensionalIndex) +{ + if( dimensions < 1) + return false; + + bool ret= true; + bool carry= true; // to get into the while loop + + sal_Int32 currentDimension= dimensions; //most significant is 1 + while( carry) + { + parMultidimensionalIndex[ currentDimension - 1]++; + // if carryover, set index to 0 and handle carry on a level above + if( parMultidimensionalIndex[ currentDimension - 1] > (parDimensionLengths[ currentDimension - 1] - 1)) + parMultidimensionalIndex[ currentDimension - 1]= 0; + else + carry= false; + + currentDimension --; + // if dimensions drops below 1 and carry is set than then all indices are 0 again + // this is signalled by returning sal_False + if( currentDimension < 1 && carry) + { + carry= false; + ret= false; + } + } + return ret; +} + +// Determines the size of a certain OLE type. The function takes +// only those types into account which are oleautomation types and +// can have a value ( unless VT_NULL, VT_EMPTY, VT_ARRAY, VT_BYREF). +// Currently used in createUnoSequenceWrapper to calculate addresses +// for data within a SAFEARRAY. +template<class T> +size_t UnoConversionUtilities<T>::getOleElementSize( VARTYPE type) +{ + size_t size; + switch( type) + { + case VT_BOOL: size= sizeof( VARIANT_BOOL);break; + case VT_UI1: size= sizeof( unsigned char);break; + case VT_R8: size= sizeof( double);break; + case VT_R4: size= sizeof( float);break; + case VT_I2: size= sizeof( short);break; + case VT_I4: size= sizeof( long);break; + case VT_BSTR: size= sizeof( BSTR); break; + case VT_ERROR: size= sizeof( SCODE); break; + case VT_DISPATCH: + case VT_UNKNOWN: size= sizeof( IUnknown*); break; + case VT_VARIANT: size= sizeof( VARIANT);break; + default: size= 0; + } + return size; +} + +//If a Sequence is being converted into a SAFEARRAY then we possibly have +// to create a SAFEARRAY with multiple dimensions. This is the case when a +// Sequence contains Sequences ( Sequence< Sequence < XXX > > ). The leftmost +// Sequence in the declaration is assumed to represent dimension 1. Because +// all Sequence elements of a Sequence can have different length, we have to +// determine the maximum length which is then the length of the respective +// dimension. +// getElementCountAndTypeOfSequence determines the length of each dimension and calls itself recursively +// in the process. +// param rSeq - an Any that has to contain a Sequence +// param dim - the dimension for which the number of elements is being determined, +// must be one. +// param seqElementCounts - contains the maximum number of elements for each +// dimension. Index 0 contains the number of dimension one. +// After return the Sequence contains the maximum number of +// elements for each dimension. +// The length of the Sequence must equal the number of dimensions. +// param typeClass - TypeClass of the element type that is no Sequence, e.g. +// Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32) +template<class T> +void UnoConversionUtilities<T>::getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, + Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc) +{ + sal_Int32 dimCount= (*static_cast<uno_Sequence* const *>(rSeq.getValue()))->nElements; + if( dimCount > seqElementCounts[ dim-1]) + seqElementCounts.getArray()[ dim-1]= dimCount; + + // we need the element type to construct the any that is + // passed into getElementCountAndTypeOfSequence again + typelib_TypeDescription* pSeqDesc= nullptr; + rSeq.getValueTypeDescription( &pSeqDesc); + typelib_TypeDescriptionReference* pElementDescRef= reinterpret_cast<typelib_IndirectTypeDescription*>(pSeqDesc)->pType; + + // if the elements are Sequences then do recursion + if( dim < seqElementCounts.getLength() ) + { + uno_Sequence* pSeq = *static_cast<uno_Sequence* const*>(rSeq.getValue()); + uno_Sequence** arSequences= reinterpret_cast<uno_Sequence**>(pSeq->elements); + for( sal_Int32 i=0; i < dimCount; i++) + { + uno_Sequence* arElement= arSequences[ i]; + getElementCountAndTypeOfSequence( Any( &arElement, pElementDescRef), dim + 1 , seqElementCounts, typeDesc); + } + } + else + { + // determine the element type ( e.g. Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32) + typeDesc= pElementDescRef; + } + typelib_typedescription_release( pSeqDesc); +} + + +template<class T> +SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq) +{ + SAFEARRAY* pArray = nullptr; + sal_uInt32 n = 0; + + if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE ) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper\n" + "The UNO argument is not a sequence", nullptr, -1); + + uno_Sequence * punoSeq= *static_cast<uno_Sequence* const *>(rSeq.getValue()); + + typelib_TypeDescriptionReference* pSeqTypeRef= rSeq.getValueTypeRef(); + typelib_TypeDescription* pSeqType= nullptr; + TYPELIB_DANGER_GET( &pSeqType, pSeqTypeRef); + typelib_IndirectTypeDescription * pSeqIndDec= reinterpret_cast<typelib_IndirectTypeDescription*>(pSeqType); + + + typelib_TypeDescriptionReference * pSeqElementTypeRef= pSeqIndDec->pType; + TYPELIB_DANGER_RELEASE( pSeqType); + + typelib_TypeDescription* pSeqElementDesc= nullptr; + TYPELIB_DANGER_GET( &pSeqElementDesc, pSeqElementTypeRef); + sal_Int32 nElementSize= pSeqElementDesc->nSize; + n= punoSeq->nElements; + + SAFEARRAYBOUND rgsabound[1]; + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = n; + VARIANT oleElement; + LONG safeI[1]; + + pArray = SafeArrayCreate(VT_VARIANT, 1, rgsabound); + + Any unoElement; + char * pSeqData= punoSeq->elements; + + for (sal_uInt32 i = 0; i < n; i++) + { + unoElement.setValue( pSeqData + i * nElementSize, pSeqElementDesc); + VariantInit(&oleElement); + + anyToVariant(&oleElement, unoElement); + + safeI[0] = i; + SafeArrayPutElement(pArray, safeI, &oleElement); + + VariantClear(&oleElement); + } + TYPELIB_DANGER_RELEASE( pSeqElementDesc); + + return pArray; +} + +/* The argument rObj can contain +- UNO struct +- UNO interface +- UNO interface created by this bridge (adapter factory) +- UNO interface created by this bridge ( COM Wrapper) + +pVar must be initialized. +*/ +template<class T> +void UnoConversionUtilities<T>::createUnoObjectWrapper(const Any & rObj, VARIANT * pVar) +{ + MutexGuard guard(getBridgeMutex()); + + Reference<XInterface> xInt; + + TypeClass tc = rObj.getValueTypeClass(); + if (tc != TypeClass_INTERFACE && tc != TypeClass_STRUCT) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createUnoObjectWrapper \n" + "Cannot create an Automation interface for a UNO type which is not " + "a struct or interface!", nullptr, -1); + + if (rObj.getValueTypeClass() == TypeClass_INTERFACE) + { + if (! (rObj >>= xInt)) + throw IllegalArgumentException( + "[automation bridge] UnoConversionUtilities<T>::createUnoObjectWrapper\n " + "Could not create wrapper object for UNO object!", nullptr, -1); + //If XInterface is NULL, which is a valid value, then simply return NULL. + if ( ! xInt.is()) + { + pVar->vt = VT_UNKNOWN; + pVar->punkVal = nullptr; + return; + } + //make sure we have the main XInterface which is used with a map + xInt.set(xInt, UNO_QUERY); + //If there is already a wrapper for the UNO object then use it + + Reference<XInterface> xIntWrapper; + // Does a UNO wrapper exist already ? + auto it_uno = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get())); + if(it_uno != UnoObjToWrapperMap.end()) + { + xIntWrapper = it_uno->second; + if (xIntWrapper.is()) + { + convertSelfToCom(xIntWrapper, pVar); + return; + } + } + // Is the object a COM wrapper ( either XInvocation, or Adapter object) + // or does it supply an IDispatch by its own ? + else + { + Reference<XInterface> xIntComWrapper = xInt; + + // Adapter? then get the COM wrapper to which the adapter delegates its calls + auto it = AdapterToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get())); + if( it != AdapterToWrapperMap.end() ) + xIntComWrapper= reinterpret_cast<XInterface*>(it->second); + + if (convertSelfToCom(xIntComWrapper, pVar)) + return; + } + } + // If we have no UNO wrapper nor the IDispatch yet then we have to create + // a wrapper. For that we need an XInvocation. + + // create an XInvocation using the invocation service + Reference<XInvocation> xInv; + Reference<XSingleServiceFactory> xInvFactory= getInvocationFactory(rObj); + if (xInvFactory.is()) + { + Sequence<Any> params(2); + params.getArray()[0] = rObj; + params.getArray()[1] <<= OUString("FromOLE"); + Reference<XInterface> xInt2 = xInvFactory->createInstanceWithArguments(params); + xInv.set(xInt2, UNO_QUERY); + } + + if (xInv.is()) + { + Reference<XInterface> xNewWrapper = createUnoWrapperInstance(); + Reference<css::lang::XInitialization> xInitWrapper(xNewWrapper, UNO_QUERY); + if (xInitWrapper.is()) + { + VARTYPE vartype= getVarType( rObj); + + if (xInt.is()) + { + Any params[3]; + params[0] <<= xInv; + params[1] <<= xInt; + params[2] <<= vartype; + xInitWrapper->initialize( Sequence<Any>(params, 3)); + } + else + { + Any params[2]; + params[0] <<= xInv; + params[1] <<= vartype; + xInitWrapper->initialize( Sequence<Any>(params, 2)); + } + + // put the newly created object into a map. If the same object will + // be mapped again and there is already a wrapper then the old wrapper + // will be used. + if(xInt.is()) // only interfaces + UnoObjToWrapperMap[reinterpret_cast<sal_uIntPtr>(xInt.get())]= xNewWrapper; + convertSelfToCom(xNewWrapper, pVar); + return; + } + } +} + +template<class T> +void UnoConversionUtilities<T>::variantToAny( const VARIANT* pVariant, Any& rAny, + bool bReduceValueRange /* = sal_True */) +{ + HRESULT hr = S_OK; + try + { + CComVariant var; + + // There is no need to support indirect values, since they're not supported by UNO + if( FAILED(hr= VariantCopyInd( &var, pVariant))) // remove VT_BYREF + throw BridgeRuntimeError( + "[automation bridge] UnoConversionUtilities<T>::variantToAny \n" + "VariantCopyInd failed for reason : " + OUString::number(hr)); + + if ( ! convertValueObject( & var, rAny)) + { + if ((var.vt & VT_ARRAY) > 0) + { + VARTYPE oleTypeFlags = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY ); + + Sequence<Any> unoSeq = createOleArrayWrapper(var.parray, oleTypeFlags); + rAny.setValue( &unoSeq, cppu::UnoType<decltype(unoSeq)>::get()); + } + else + { + switch (var.vt) + { + case VT_EMPTY: + rAny.setValue(nullptr, Type()); + break; + case VT_NULL: + rAny.setValue(nullptr, Type()); + break; + case VT_I2: + rAny.setValue( & var.iVal, cppu::UnoType<sal_Int16>::get()); + break; + case VT_I4: + rAny.setValue( & var.lVal, cppu::UnoType<sal_Int32>::get()); + // necessary for use in JavaScript ( see "reduceRange") + if( bReduceValueRange) + reduceRange(rAny); + break; + case VT_R4: + rAny.setValue( & var.fltVal, cppu::UnoType<float>::get()); + break; + case VT_R8: + rAny.setValue(& var.dblVal, cppu::UnoType<double>::get()); + break; + case VT_CY: + { + Currency cy(var.cyVal.int64); + rAny <<= cy; + break; + } + case VT_DATE: + { + Date d(var.date); + rAny <<= d; + break; + } + case VT_BSTR: + { + OUString b(o3tl::toU(var.bstrVal)); + rAny.setValue( &b, cppu::UnoType<decltype(b)>::get()); + break; + } + case VT_UNKNOWN: + case VT_DISPATCH: + { + //check if it is a UNO type + CComQIPtr<IUnoTypeWrapper> spType(static_cast<IUnknown*>(var.byref)); + if (spType) + { + CComBSTR sName; + if (FAILED(spType->get_Name(&sName))) + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::variantToAny \n" + "Failed to get the type name from a UnoTypeWrapper!"); + Type type; + if (!getType(sName, type)) + { + throw CannotConvertException( + OUString::Concat("[automation bridge]UnoConversionUtilities<T>::variantToAny \n" + "A UNO type with the name: ") + o3tl::toU(LPCOLESTR(sName)) + + "does not exist!", + nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0); + } + rAny <<= type; + } + else + { + rAny = createOleObjectWrapper( & var); + } + break; + } + case VT_ERROR: + { + SCode scode(var.scode); + rAny <<= scode; + break; + } + case VT_BOOL: + { + rAny <<= (var.boolVal == VARIANT_TRUE); + break; + } + case VT_I1: + rAny.setValue( & var.cVal, cppu::UnoType<sal_Int8>::get()); + break; + case VT_UI1: // there is no unsigned char in UNO + rAny <<= sal_Int8(var.bVal); + break; + case VT_UI2: + rAny.setValue( & var.uiVal, cppu::UnoType<cppu::UnoUnsignedShortType>::get() ); + break; + case VT_UI4: + rAny.setValue( & var.ulVal, cppu::UnoType<sal_uInt32>::get()); + break; + case VT_INT: + rAny.setValue( & var.intVal, cppu::UnoType<sal_Int32>::get()); + break; + case VT_UINT: + rAny.setValue( & var.uintVal, cppu::UnoType<sal_uInt32>::get()); + break; + case VT_VOID: + rAny.setValue( nullptr, Type()); + break; + case VT_DECIMAL: + { + Decimal dec; + dec.Scale = var.decVal.scale; + dec.Sign = var.decVal.sign; + dec.LowValue = var.decVal.Lo32; + dec.MiddleValue = var.decVal.Mid32; + dec.HighValue = var.decVal.Hi32; + rAny <<= dec; + break; + } + + default: + break; + } + } + } + } + catch (const IllegalArgumentException &) + { + throw; + } + catch (const CannotConvertException &) + { + throw; + } + catch (const BridgeRuntimeError &) + { + throw; + } + catch (const Exception & e) + { + throw BridgeRuntimeError("[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::variantToAny ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::variantToAny !"); + } + +} +// The function converts an IUnknown* into a UNO interface or struct. The +// IUnknown pointer can constitute different kind of objects: +// 1. a wrapper of a UNO struct (the wrapper was created by this bridge) +// 2. a wrapper of a UNO interface (created by this bridge) +// 3. a dispatch object that implements UNO interfaces +// 4. a dispatch object. + +// If the parameter "aType" has a value then the COM object ( pUnknown) is supposed to +// implement the interface described by "aType". Moreover it ( pUnknown) can implement +// several other +// UNO interfaces in which case it has to support the SUPPORTED_INTERFACES_PROP (see +// #define) property. That property contains all names of interfaces. +// "pUnknown" is wrapped by a COM wrapper object that implements XInvocation, e.g. +// IUnknownWrapper. Additionally an object of type "aType" is created by help +// of the INTERFACE_ADAPTER_FACTORY (see #define) service. The implementation of +// "aType" calls on the COM wrapper's XInvocation::invoke. If the COM object supports +// more than one UNO interfaces, as can be determined by the property +// SUPPORTED_INTERFACES_PROP, then the INTERFACE_ADAPTER_FACTORY creates an object that +// implements all these interfaces. +// This is only done if "pUnknown" is not already a UNO wrapper, +// that is it is actually NOT a UNO object that was converted to a COM object. If it is an +// UNO wrapper than the original UNO object is being extracted, queried for "aType" (if +// it is no struct) and returned. +template<class T> +Any UnoConversionUtilities<T>::createOleObjectWrapper(VARIANT* pVar, const Type& aType) +{ + //To allow passing "Nothing" in VS 2008 we need to accept VT_EMPTY + if (pVar->vt != VT_UNKNOWN && pVar->vt != VT_DISPATCH && pVar->vt != VT_EMPTY) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "The VARIANT does not contain an object type! ", nullptr, -1); + + MutexGuard guard( getBridgeMutex()); + + CComPtr<IUnknown> spUnknown; + CComPtr<IDispatch> spDispatch; + + if (pVar->vt == VT_UNKNOWN) + { + spUnknown = pVar->punkVal; + if (spUnknown) + spUnknown.QueryInterface( & spDispatch.p); + } + else if (pVar->vt == VT_DISPATCH && pVar->pdispVal != nullptr) + { + CComPtr<IDispatch> spDispatch2(pVar->pdispVal); + if (spDispatch2) + spDispatch2.QueryInterface( & spUnknown.p); + } + + static Type VOID_TYPE; + Any ret; + //If no Type is provided and pVar contains IUnknown then we return a XInterface. + //If pVar contains an IDispatch then we return a XInvocation. + Type desiredType = aType; + + if (aType == VOID_TYPE) + { + switch (pVar->vt) + { + case VT_EMPTY: + case VT_UNKNOWN: + desiredType = cppu::UnoType<XInterface>::get(); + break; + case VT_DISPATCH: + desiredType = cppu::UnoType<XInvocation>::get(); + break; + default: + desiredType = aType; + } + } + + // COM pointer are NULL, no wrapper required + if (spUnknown == nullptr) + { + Reference<XInterface> xInt; + if( aType.getTypeClass() == TypeClass_INTERFACE) + ret.setValue( &xInt, aType); + else if( aType.getTypeClass() == TypeClass_STRUCT) + ret.setValue( nullptr, aType); + else + ret <<= xInt; + return ret; + } + + + // Check if "spUnknown" is a UNO wrapper, that is a UNO object that has been + // passed to COM. Then it supports IUnoObjectWrapper + // and we extract the original UNO object. + CComQIPtr<IUnoObjectWrapper> spUno( spUnknown); + if( spUno) + { // it is a wrapper + Reference<XInterface> xInt; + if( SUCCEEDED( spUno->getOriginalUnoObject( &xInt))) + { + ret <<= xInt; + } + else + { + Any any; + if( SUCCEEDED( spUno->getOriginalUnoStruct(&any))) + ret= any; + } + return ret; + } + + // "spUnknown" is a real COM object. + // Before we create a new wrapper object we check if there is an existing wrapper + // There can be two kinds of wrappers, those who wrap dispatch - UNO objects, and those who + // wrap ordinary dispatch objects. The dispatch-UNO objects usually are adapted to represent + // particular UNO interfaces. + Reference<XInterface> xIntWrapper; + auto cit_currWrapper= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(spUnknown.p)); + if(cit_currWrapper != ComPtrToWrapperMap.end()) + xIntWrapper = cit_currWrapper->second; + if (xIntWrapper.is()) + { + //Try to find an adapter for the wrapper + //find the proper Adapter. The pointer in the WrapperToAdapterMap are valid as long as + //we get a pointer to the wrapper from ComPtrToWrapperMap, because the Adapter hold references + //to the wrapper. + auto it = WrapperToAdapterMap.find(reinterpret_cast<sal_uIntPtr>(xIntWrapper.get())); + if (it == WrapperToAdapterMap.end()) + { + // No adapter available. + //The COM component could be a UNO object. Then we need to provide + // a proxy that implements all interfaces + Sequence<Type> seqTypes= getImplementedInterfaces(spUnknown); + Reference<XInterface> xIntAdapter; + if (seqTypes.getLength() > 0) + { + //It is a COM UNO object + xIntAdapter = createAdapter(seqTypes, xIntWrapper); + } + else + { + // Some ordinary COM object + xIntAdapter = xIntWrapper; + } + // return the wrapper directly, return XInterface or XInvocation + ret = xIntWrapper->queryInterface(desiredType); + if ( ! ret.hasValue()) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "The COM object is not suitable for the UNO type: " + + desiredType.getTypeName(), nullptr, -1); + } + else + { + //There is an adapter available + Reference<XInterface> xIntAdapter(reinterpret_cast<XInterface*>(it->second)); + ret = xIntAdapter->queryInterface( desiredType); + if ( ! ret.hasValue()) + throw IllegalArgumentException( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "The COM object is not suitable for the UNO type: " + + desiredType.getTypeName(), nullptr, -1); + } + + return ret; + } + // No existing wrapper. Therefore create a new proxy. + // If the object implements UNO interfaces then get the types. + Sequence<Type> seqTypes = getImplementedInterfaces(spUnknown); + if (seqTypes.getLength() == 0 && + aType != VOID_TYPE && aType != cppu::UnoType<XInvocation>::get()) + { + seqTypes = Sequence<Type>( & aType, 1); + } + + //There is no existing wrapper, therefore we create one for the real COM object + Reference<XInterface> xIntNewProxy= createComWrapperInstance(); + if ( ! xIntNewProxy.is()) + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "Could not create proxy object for COM object!"); + + // initialize the COM wrapper + Reference<XInitialization> xInit( xIntNewProxy, UNO_QUERY); + OSL_ASSERT( xInit.is()); + + Any params[3]; + params[0] <<= reinterpret_cast<sal_uIntPtr>(spUnknown.p); + params[1] <<= (pVar->vt == VT_DISPATCH); + params[2] <<= seqTypes; + + xInit->initialize( Sequence<Any>( params, 3)); + ComPtrToWrapperMap[reinterpret_cast<sal_uInt64>(spUnknown.p)] = xIntNewProxy; + + // we have a wrapper object + //The wrapper implements already XInvocation and XInterface. If + //param aType is void then the object is supposed to have XInvocation. + if (aType == cppu::UnoType<XInvocation>::get()|| + (aType == VOID_TYPE && seqTypes.getLength() == 0 )) + { + ret = xIntNewProxy->queryInterface(desiredType); + } + else + { + Reference<XInterface> xIntAdapter = + createAdapter(seqTypes, xIntNewProxy); + ret = xIntAdapter->queryInterface(desiredType); + } + return ret; +} +template<class T> +Reference<XInterface> UnoConversionUtilities<T>::createAdapter(const Sequence<Type>& seqTypes, + const Reference<XInterface>& receiver) +{ + Reference< XInterface> xIntAdapterFac; + xIntAdapterFac= m_smgr->createInstance(INTERFACE_ADAPTER_FACTORY); + // We create an adapter object that does not only implement the required type but also + // all types that the COM object pretends to implement. A COM object must therefore + // support the property "_implementedInterfaces". + Reference<XInterface> xIntAdapted; + Reference<XInvocation> xInv(receiver, UNO_QUERY); + Reference<XInvocationAdapterFactory2> xAdapterFac( xIntAdapterFac, UNO_QUERY); + if( xAdapterFac.is()) + xIntAdapted= xAdapterFac->createAdapter( xInv, seqTypes); + + if( !xIntAdapted.is()) + { + throw BridgeRuntimeError( + "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n" + "Could not create a proxy for COM object! Creation of adapter failed."); + } + + // Put the pointer to the wrapper object and the interface pointer of the adapted interface + // in a global map. Thus we can determine in a call to createUnoObjectWrapper whether the UNO + // object is a wrapped COM object. In that case we extract the original COM object rather than + // creating a wrapper around the UNO object. + typedef std::unordered_map<sal_uInt64,sal_uInt64>::value_type VALUE; + AdapterToWrapperMap.insert( VALUE( reinterpret_cast<sal_uInt64>(xIntAdapted.get()), reinterpret_cast<sal_uInt64>(receiver.get()))); + WrapperToAdapterMap.insert( VALUE( reinterpret_cast<sal_uInt64>(receiver.get()), reinterpret_cast<sal_uInt64>(xIntAdapted.get()))); + + return xIntAdapted; +} +// "convertValueObject" converts a JScriptValue object contained in "var" into +// an any. The type contained in the any is stipulated by a "type value" thas +// was set within the JScript script on the value object ( see JScriptValue). +template<class T> +bool UnoConversionUtilities<T>::convertValueObject( const VARIANTARG *var, Any& any) +{ + bool ret = false; + try + { + bool bFail = false; + HRESULT hr= S_OK; + CComVariant varDisp; + + if(SUCCEEDED(hr = varDisp.ChangeType( VT_DISPATCH, var))) + { + CComPtr <IJScriptValueObject> spValue; + VARIANT_BOOL varBool; + CComBSTR bstrType; + CComVariant varValue; + CComPtr<IDispatch> spDisp( varDisp.pdispVal); + if(spDisp) + { + if(SUCCEEDED( spDisp->QueryInterface( __uuidof( IJScriptValueObject), + reinterpret_cast<void**> (&spValue)))) + { + ret = true; // is a ValueObject + //If it is an out - param then it does not need to be converted. In/out and + // in params does so. + if (SUCCEEDED(hr= spValue->IsOutParam( &varBool))) + { + // if varBool == true then no conversion needed because out param + if (varBool == VARIANT_FALSE) + { + if(SUCCEEDED(hr = spValue->GetValue( & bstrType, & varValue))) + { + Type type; + if (getType(bstrType, type)) + variantToAny( & varValue, any, type); + else + bFail = true; + } + else + bFail = true; + } + } + else + bFail = true; + } + } + } + else if( hr != DISP_E_TYPEMISMATCH && hr != E_NOINTERFACE) + bFail = true; + + if (bFail) + throw BridgeRuntimeError( + "[automation bridge] Conversion of ValueObject failed "); + } + catch (const BridgeRuntimeError &) + { + throw; + } + catch (const Exception & e) + { + throw BridgeRuntimeError("[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::convertValueObject ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::convertValueObject !"); + } + return ret; +} + +template<class T> +void UnoConversionUtilities<T>::dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type) +{ + try + { + if( pvar->vt != VT_DISPATCH) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + IDispatchEx* pdispEx; + HRESULT hr; + if( FAILED( hr= pvar->pdispVal->QueryInterface( IID_IDispatchEx, + reinterpret_cast<void**>( &pdispEx)))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + + DISPID dispid; + DISPPARAMS param= {nullptr,nullptr,0,0}; + CComVariant result; + + OLECHAR const * sLength= L"length"; + + // Get the length of the array. Can also be obtained through GetNextDispID. The + // method only returns DISPIDs of the array data. Their names are like "0", "1" etc. + if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, const_cast<OLECHAR **>(&sLength), 1, LOCALE_USER_DEFAULT, &dispid))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, + ¶m, &result, nullptr, nullptr))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + if( FAILED( VariantChangeType( &result, &result, 0, VT_I4))) + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + LONG length= result.lVal; + + result.Clear(); + + // get a few basic facts about the sequence, and reallocate: + // create the Sequences + // get the size of the elements + typelib_TypeDescription *pDesc= nullptr; + type.getDescription( &pDesc); + + typelib_IndirectTypeDescription *pSeqDesc= reinterpret_cast<typelib_IndirectTypeDescription*>(pDesc); + typelib_TypeDescriptionReference *pSeqElemDescRef= pSeqDesc->pType; // type of the Sequence' elements + Type elemType( pSeqElemDescRef); + _typelib_TypeDescription* pSeqElemDesc=nullptr; + TYPELIB_DANGER_GET( &pSeqElemDesc, pSeqElemDescRef); + sal_uInt32 nelementSize= pSeqElemDesc->nSize; + TYPELIB_DANGER_RELEASE( pSeqElemDesc); + + uno_Sequence *p_uno_Seq; + uno_sequence_construct( &p_uno_Seq, pDesc, nullptr, length, cpp_acquire); + + typelib_TypeClass typeElement= pSeqDesc->pType->eTypeClass; + char *pArray= p_uno_Seq->elements; + + // Get All properties in the object, convert their values to the expected type and + // put them into the passed in sequence + for( sal_Int32 i= 0; i< length; i++) + { + OUString ousIndex=OUString::number( i); + OLECHAR* sindex = const_cast<OLECHAR *>(o3tl::toW(ousIndex.getStr())); + + if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sindex , 1, LOCALE_USER_DEFAULT, &dispid))) + { + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + } + if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, + ¶m, &result, nullptr, nullptr))) + { + throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n" + "Conversion of dispatch object to Sequence failed!"); + } + + // If the result is VT_DISPATCH than the Sequence's element type could be Sequence + // Look that up in the CoreReflection to make clear. + // That requires a recursiv conversion + Any any; + // Destination address within the out-Sequence "anySeq" where to copy the next converted element + void* pDest= pArray + (i * nelementSize); + + if( result.vt & VT_DISPATCH && typeElement == typelib_TypeClass_SEQUENCE) + { + variantToAny( &result, any, elemType, false); + // copy the converted VARIANT, that is a Sequence to the Sequence + uno_Sequence * p_unoSeq= *static_cast<uno_Sequence* const *>(any.getValue()); + // just copy the pointer of the uno_Sequence + // nelementSize should be 4 !!!! + memcpy( pDest, &p_unoSeq, nelementSize); + osl_atomic_increment( &p_unoSeq->nRefCount); + } + else // Element type is no Sequence -> do one conversion + { + variantToAny( &result, any, elemType, false); + if( typeElement == typelib_TypeClass_ANY) + { + // copy the converted VARIANT to the Sequence + uno_type_assignData( pDest, pSeqElemDescRef , &any, pSeqElemDescRef,cpp_queryInterface, + cpp_acquire, cpp_release); + } + else + { + // type after conversion must be the element type of the sequence + OSL_ENSURE(any.getValueTypeClass() == css::uno::TypeClass(typeElement), "wrong conversion"); + uno_type_assignData( pDest, pSeqElemDescRef,const_cast<void*>( any.getValue()), any.getValueTypeRef(), + cpp_queryInterface, cpp_acquire, cpp_release); + } + } + } // else + result.Clear(); + anySeq.setValue( &p_uno_Seq, pDesc); + uno_destructData( &p_uno_Seq, pDesc, cpp_release); + typelib_typedescription_release( pDesc); + } + catch (const BridgeRuntimeError &) + { + throw; + } + catch (const Exception & e) + { + throw BridgeRuntimeError("[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::convertValueObject ! Message : \n" + + e.Message); + } + catch(...) + { + throw BridgeRuntimeError( + "[automation bridge] unexpected exception in " + "UnoConversionUtilities<T>::convertValueObject !"); + } +} + +/* The argument unotype is the type that is expected by the currently called UNO function. + For example: []long, [][]long. If the function calls itself recursively then the element type + is passed on. For example a two dimensional SAFEARRAY of type VT_I4 is to be converted. Then + unotype has to be either void or [][]long. When the function calls itself recursively then + it passes the element type which is []long. +*/ +template<class T> +Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapperOfDim(SAFEARRAY* pArray, + unsigned int dimCount, unsigned int actDim, LONG* index, VARTYPE type, const Type& unotype) +{ + LONG lBound; + LONG uBound; + LONG nCountElements; + + SafeArrayGetLBound(pArray, actDim, &lBound); + SafeArrayGetUBound(pArray, actDim, &uBound); + nCountElements= uBound - lBound +1; + + Sequence<Any> anySeq(nCountElements); + Any* pUnoArray = anySeq.getArray(); + + for (index[actDim - 1] = lBound; index[actDim - 1] <= uBound; index[actDim - 1]++) + { + if (actDim > 1 ) + { + Sequence<Any> element = createOleArrayWrapperOfDim(pArray, dimCount, + actDim - 1, index, type, getElementTypeOfSequence(unotype)); + + pUnoArray[index[actDim - 1] - lBound].setValue(&element, cppu::UnoType<decltype(element)>::get()); + } + else + { + VARIANT variant; + + VariantInit(&variant); + + V_VT(&variant) = type; + + switch (type) + { + case VT_I2: + SafeArrayGetElement(pArray, index, &V_I2(&variant)); + break; + case VT_I4: + SafeArrayGetElement(pArray, index, &V_I4(&variant)); + break; + case VT_R4: + SafeArrayGetElement(pArray, index, &V_R4(&variant)); + break; + case VT_R8: + SafeArrayGetElement(pArray, index, &V_R8(&variant)); + break; + case VT_CY: + SafeArrayGetElement(pArray, index, &V_CY(&variant)); + break; + case VT_DATE: + SafeArrayGetElement(pArray, index, &V_DATE(&variant)); + break; + case VT_BSTR: + SafeArrayGetElement(pArray, index, &V_BSTR(&variant)); + break; + case VT_DISPATCH: + SafeArrayGetElement(pArray, index, &V_DISPATCH(&variant)); + break; + case VT_ERROR: + SafeArrayGetElement(pArray, index, &V_ERROR(&variant)); + break; + case VT_BOOL: + SafeArrayGetElement(pArray, index, &V_BOOL(&variant)); + break; + case VT_VARIANT: + SafeArrayGetElement(pArray, index, &variant); + break; + case VT_UNKNOWN: + SafeArrayGetElement(pArray, index, &V_UNKNOWN(&variant)); + break; + case VT_I1: + SafeArrayGetElement(pArray, index, &V_I1(&variant)); + break; + case VT_UI1: + SafeArrayGetElement(pArray, index, &V_UI1(&variant)); + break; + case VT_UI2: + SafeArrayGetElement(pArray, index, &V_UI2(&variant)); + break; + case VT_UI4: + SafeArrayGetElement(pArray, index, &V_UI4(&variant)); + break; + default: + break; + } + + if( unotype.getTypeClass() == TypeClass_VOID) + // the function was called without specifying the destination type + variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], false); + else + variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], + getElementTypeOfSequence(unotype), false); + + VariantClear(&variant); + } + } + return anySeq; +} + +template<class T> +Type UnoConversionUtilities<T>::getElementTypeOfSequence( const Type& seqType) +{ + Type retValue; + if( seqType.getTypeClass() != TypeClass_VOID) + { + OSL_ASSERT( seqType.getTypeClass() == TypeClass_SEQUENCE); + typelib_TypeDescription* pDescSeq= nullptr; + seqType.getDescription(& pDescSeq); + retValue = Type(reinterpret_cast<typelib_IndirectTypeDescription *>(pDescSeq)->pType); + typelib_typedescription_release(pDescSeq); + } + return retValue; +} +template<class T> +Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unoType) +{ + sal_uInt32 dim = SafeArrayGetDim(pArray); + + Sequence<Any> ret; + + if (dim > 0) + { + std::unique_ptr<LONG[]> sarIndex(new LONG[dim]); + LONG * index = sarIndex.get(); + + for (unsigned int i = 0; i < dim; i++) + { + index[i] = 0; + } + + ret = createOleArrayWrapperOfDim(pArray, dim, dim, index, type, unoType); + } + + return ret; +} + +// If a VARIANT has the type VT_DISPATCH it can either be a JScript Array +// or some other object. This function finds out if it is such an array or +// not. Currently there's no way to make sure it's an array +// so we assume that when the object has a property "0" then it is an Array. +// A JScript has property like "0", "1", "2" etc. which represent the +// value at the corresponding index of the array +template<class T> +bool UnoConversionUtilities<T>::isJScriptArray(const VARIANT* rvar) +{ + OSL_ENSURE( rvar->vt == VT_DISPATCH, "param is not a VT_DISPATCH"); + HRESULT hr; + OLECHAR const * sindex= L"0"; + DISPID id; + if ( rvar->vt == VT_DISPATCH && rvar->pdispVal ) + { + hr= rvar->pdispVal->GetIDsOfNames( + IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, + &id); + + if( SUCCEEDED ( hr) ) + return true; + } + + return false; +} + +template<class T> +VARTYPE UnoConversionUtilities<T>::mapTypeClassToVartype( TypeClass type) +{ + VARTYPE ret; + switch( type) + { + case TypeClass_INTERFACE: ret= VT_DISPATCH; + break; + case TypeClass_STRUCT: ret= VT_DISPATCH; + break; + case TypeClass_ENUM: ret= VT_I4; + break; + case TypeClass_SEQUENCE: ret= VT_ARRAY; + break; + case TypeClass_ANY: ret= VT_VARIANT; + break; + case TypeClass_BOOLEAN: ret= VT_BOOL; + break; + case TypeClass_CHAR: ret= VT_I2; + break; + case TypeClass_STRING: ret= VT_BSTR; + break; + case TypeClass_FLOAT: ret= VT_R4; + break; + case TypeClass_DOUBLE: ret= VT_R8; + break; + case TypeClass_BYTE: ret= VT_UI1; + break; + case TypeClass_SHORT: ret= VT_I2; + break; + case TypeClass_LONG: ret= VT_I4; + break; + case TypeClass_UNSIGNED_SHORT: ret= VT_UI2; + break; + case TypeClass_UNSIGNED_LONG: ret= VT_UI4; + break; + default: + ret= VT_EMPTY; + } + return ret; +} + +template<class T> +Sequence<Type> UnoConversionUtilities<T>::getImplementedInterfaces(IUnknown* pUnk) +{ + Sequence<Type> seqTypes; + CComDispatchDriver disp( pUnk); + if( disp) + { + CComVariant var; + HRESULT hr= S_OK; + // There are two different property names possible. + if( FAILED( hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP, &var))) + { + hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP2, &var); + } + if (SUCCEEDED( hr)) + { + // we expect an array( SafeArray or IDispatch) of Strings. + Any anyNames; + variantToAny( &var, anyNames, cppu::UnoType<Sequence<Any>>::get()); + Sequence<Any> seqAny; + if( anyNames >>= seqAny) + { + seqTypes.realloc( seqAny.getLength()); + auto pseqTypes = seqTypes.getArray(); + for( sal_Int32 i=0; i < seqAny.getLength(); i++) + { + OUString typeName; + seqAny[i] >>= typeName; + pseqTypes[i]= Type( TypeClass_INTERFACE, typeName); + } + } + } + } + return seqTypes; +} +template<class T> +Reference<XTypeConverter> UnoConversionUtilities<T>::getTypeConverter() +{ + if ( ! m_typeConverter.is()) + { + MutexGuard guard(getBridgeMutex()); + if ( ! m_typeConverter.is()) + { + Reference<XInterface> xIntConverter = + m_smgr->createInstance("com.sun.star.script.Converter"); + if (xIntConverter.is()) + m_typeConverter.set(xIntConverter, UNO_QUERY); + } + } + return m_typeConverter; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/unoobjw.cxx b/extensions/source/ole/unoobjw.cxx new file mode 100644 index 0000000000..915ecd92f8 --- /dev/null +++ b/extensions/source/ole/unoobjw.cxx @@ -0,0 +1,3436 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 . + */ + +// Documentation pointers for recent work: +// +// https://www.codeproject.com/Articles/9014/Understanding-COM-Event-Handling +// https://blogs.msdn.microsoft.com/ericlippert/2005/02/15/why-does-wscript-connectobject-not-always-work/ + +#include "ole2uno.hxx" + +#include <stdio.h> +#include <list> +#include <sstream> +#include <unordered_map> +#include <vector> + +#if defined _MSC_VER && defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wattributes" +#pragma clang diagnostic ignored "-Wdelete-incomplete" +#pragma clang diagnostic ignored "-Wdynamic-class-memaccess" +#pragma clang diagnostic ignored "-Wextra" +#pragma clang diagnostic ignored "-Wint-to-pointer-cast" +#pragma clang diagnostic ignored "-Winvalid-noreturn" +#pragma clang diagnostic ignored "-Wmicrosoft" +#pragma clang diagnostic ignored "-Wnon-pod-varargs" +#pragma clang diagnostic ignored "-Wnonportable-include-path" +#pragma clang diagnostic ignored "-Wsequence-point" +#pragma clang diagnostic ignored "-Wtypename-missing" +#endif +#include <atlbase.h> +#include <atlcom.h> +#if defined _MSC_VER && defined __clang__ +#pragma clang diagnostic pop +#endif +#include <comdef.h> + +#include <osl/diagnose.h> +#include <salhelper/simplereferenceobject.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/lang/NoSuchMethodException.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/FailReason.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/reflection/ParamInfo.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/script/XInvocation2.hpp> +#include <com/sun/star/script/MemberType.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <ooo/vba/XCollection.hpp> +#include <ooo/vba/XConnectable.hpp> +#include <ooo/vba/XConnectionPoint.hpp> +#include <ooo/vba/XSink.hpp> +#include <ooo/vba/msforms/XCheckBox.hpp> +#include <osl/interlck.h> +#include <com/sun/star/uno/genfunc.h> +#include <comphelper/automationinvokedzone.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> +#include <comphelper/windowsdebugoutput.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/safeint.hxx> + +#include "comifaces.hxx" +#include "jscriptclasses.hxx" +#include "unotypewrapper.hxx" +#include "oleobjw.hxx" +#include "unoobjw.hxx" +#include "servprov.hxx" + +using namespace osl; +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; +using namespace com::sun::star::script; +using namespace com::sun::star::lang; +using namespace com::sun::star::bridge::ModelDependent; +using namespace com::sun::star::reflection; + +std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap; +static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource); +static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource); +static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr); + +/* Does not throw any exceptions. + Param pInfo can be NULL. + */ +static void writeExcepinfo(EXCEPINFO * pInfo, const OUString& message) +{ + if (pInfo != nullptr) + { + pInfo->wCode = UNO_2_OLE_EXCEPTIONCODE; + pInfo->bstrSource = SysAllocString(L"[automation bridge] "); + pInfo->bstrDescription = SysAllocString(o3tl::toW(message.getStr())); + } +} + +InterfaceOleWrapper::InterfaceOleWrapper( Reference<XMultiServiceFactory> const & xFactory, + sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass): + UnoConversionUtilities<InterfaceOleWrapper>( xFactory, unoWrapperClass, comWrapperClass), + m_defaultValueType( 0) +{ +} + +InterfaceOleWrapper::~InterfaceOleWrapper() +{ + MutexGuard guard(getBridgeMutex()); + // remove entries in global map + auto it = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_xOrigin.get())); + if(it != UnoObjToWrapperMap.end()) + UnoObjToWrapperMap.erase(it); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::QueryInterface(REFIID riid, void ** ppv) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::QueryInterface(" << riid << ")"); + + HRESULT ret= S_OK; + + if( !ppv) + return E_POINTER; + + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown*>(static_cast<IDispatch*>(this)); + SAL_INFO("extensions.olebridge", " " << *ppv); + } + else if (IsEqualIID(riid, IID_IDispatch)) + { + AddRef(); + *ppv = static_cast<IDispatch*>(this); + SAL_INFO("extensions.olebridge", " " << *ppv); + } + else if (IsEqualIID(riid, IID_IProvideClassInfo)) + { + Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY); + if (!xConnectable.is()) + return E_NOINTERFACE; + AddRef(); + *ppv = static_cast<IProvideClassInfo*>(this); + SAL_INFO("extensions.olebridge", " " << *ppv); + } + else if (IsEqualIID(riid, IID_IConnectionPointContainer)) + { + Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY); + if (!xConnectable.is()) + return E_NOINTERFACE; + AddRef(); + *ppv = static_cast<IConnectionPointContainer*>(this); + SAL_INFO("extensions.olebridge", " " << *ppv); + } + else if( IsEqualIID( riid, __uuidof( IUnoObjectWrapper))) + { + AddRef(); + *ppv= static_cast<IUnoObjectWrapper*>(this); + SAL_INFO("extensions.olebridge", " " << *ppv); + } + else + ret= E_NOINTERFACE; + return ret; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::AddRef() +{ + acquire(); + // does not need to guard because one should not rely on the return value of + // AddRef anyway + return m_refCount; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::Release() +{ + ULONG n= m_refCount; + release(); + return n - 1; +} + +// IUnoObjectWrapper -------------------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getWrapperXInterface( Reference<XInterface>* pXInt) +{ + pXInt->set( static_cast<XWeak*>( this), UNO_QUERY); + return pXInt->is() ? S_OK : E_FAIL; +} +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getOriginalUnoObject( Reference<XInterface>* pXInt) +{ + *pXInt= m_xOrigin; + return m_xOrigin.is() ? S_OK : E_FAIL; +} +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getOriginalUnoStruct( Any * pStruct) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + HRESULT ret= E_FAIL; + if( !m_xOrigin.is()) + { + Reference<XMaterialHolder> xMatHolder( m_xInvocation, UNO_QUERY); + if( xMatHolder.is()) + { + Any any = xMatHolder->getMaterial(); + if( any.getValueTypeClass() == TypeClass_STRUCT) + { + *pStruct= any; + ret= S_OK; + } + } + } + return ret; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfoCount( UINT *pctinfo ) +{ + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfoCount"); + + if (!pctinfo) + return E_POINTER; + + *pctinfo = 1; + + return S_OK; +} + +namespace { + +class CXTypeInfo : public ITypeInfo, + public CComObjectRoot +{ +public: + enum class Kind { COCLASS, MAIN, OUTGOING }; + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + BEGIN_COM_MAP(CXTypeInfo) +#if defined __clang__ +#pragma clang diagnostic pop +#endif + COM_INTERFACE_ENTRY(ITypeInfo) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + DECLARE_NOT_AGGREGATABLE(CXTypeInfo) + + virtual ~CXTypeInfo() {} + + void InitForCoclass(Reference<XInterface> xOrigin, + const OUString& sImplementationName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF); + void InitForClassItself(Reference<XInterface> xOrigin, + const OUString& sImplementationName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF); + void InitForOutgoing(Reference<XInterface> xOrigin, + const OUString& sInterfaceName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF, + Type aType); + virtual HRESULT STDMETHODCALLTYPE GetTypeAttr(TYPEATTR **ppTypeAttr) override; + virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **ppTComp) override; + virtual HRESULT STDMETHODCALLTYPE GetFuncDesc(UINT index, + FUNCDESC **ppFuncDesc) override; + virtual HRESULT STDMETHODCALLTYPE GetVarDesc(UINT index, + VARDESC **ppVarDesc) override; + virtual HRESULT STDMETHODCALLTYPE GetNames(MEMBERID memid, + BSTR *rgBstrNames, + UINT cMaxNames, + UINT *pcNames) override; + virtual HRESULT STDMETHODCALLTYPE GetRefTypeOfImplType(UINT index, + HREFTYPE *pRefType) override; + virtual HRESULT STDMETHODCALLTYPE GetImplTypeFlags(UINT index, + INT *pImplTypeFlags) override; + virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(LPOLESTR *rgszNames, + UINT cNames, + MEMBERID *pMemId) override; + virtual HRESULT STDMETHODCALLTYPE Invoke(PVOID pvInstance, + MEMBERID memid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) override; + virtual HRESULT STDMETHODCALLTYPE GetDocumentation(MEMBERID memid, + BSTR *pBstrName, + BSTR *pBstrDocString, + DWORD *pdwHelpContext, + BSTR *pBstrHelpFile) override; + virtual HRESULT STDMETHODCALLTYPE GetDllEntry(MEMBERID memid, + INVOKEKIND invKind, + BSTR *pBstrDllName, + BSTR *pBstrName, + WORD *pwOrdinal) override; + virtual HRESULT STDMETHODCALLTYPE GetRefTypeInfo(HREFTYPE hRefType, + ITypeInfo **ppTInfo) override; + virtual HRESULT STDMETHODCALLTYPE AddressOfMember(MEMBERID memid, + INVOKEKIND invKind, + PVOID *ppv) override; + virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, + REFIID riid, + PVOID *ppvObj) override; + virtual HRESULT STDMETHODCALLTYPE GetMops(MEMBERID memid, + BSTR *pBstrMops) override; + virtual HRESULT STDMETHODCALLTYPE GetContainingTypeLib(ITypeLib **ppTLib, + UINT *pIndex) override; + virtual void STDMETHODCALLTYPE ReleaseTypeAttr(TYPEATTR *pTypeAttr) override; + virtual void STDMETHODCALLTYPE ReleaseFuncDesc(FUNCDESC *pFuncDesc) override; + virtual void STDMETHODCALLTYPE ReleaseVarDesc(VARDESC *pVarDesc) override; + +private: + Kind meKind; + Reference<XInterface> mxOrigin; + OUString msImplementationName; + OUString msInterfaceName; + IID maIID; + Reference<XMultiServiceFactory> mxMSF; + Type maType; +}; + +class CXTypeLib : public ITypeLib, + public CComObjectRoot +{ +public: +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + BEGIN_COM_MAP(CXTypeLib) +#if defined __clang__ +#pragma clang diagnostic pop +#endif + COM_INTERFACE_ENTRY(ITypeLib) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + DECLARE_NOT_AGGREGATABLE(CXTypeLib) + + virtual ~CXTypeLib() {} + + void Init(Reference<XInterface> xOrigin, + const OUString& sImplementationName, + Reference<XMultiServiceFactory> xMSF) + { + SAL_INFO("extensions.olebridge", this << "@CXTypeLib::Init for " << sImplementationName); + mxOrigin = xOrigin; + msImplementationName = sImplementationName; + mxMSF = xMSF; + } + + virtual UINT STDMETHODCALLTYPE GetTypeInfoCount() override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoCount"); + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, + ITypeInfo **) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfo: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfoType(UINT, + TYPEKIND *) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoType: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(REFGUID guid, + ITypeInfo **ppTInfo) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoOfGuid(" << guid << ")"); + if (!ppTInfo) + return E_POINTER; + + Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY); + if (!xConnectable.is()) + return TYPE_E_ELEMENTNOTFOUND; + + IID aIID; + if (SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(xConnectable->getIID().pData->buffer), &aIID))) + { + if (IsEqualIID(guid, aIID)) + { + HRESULT ret; + + CComObject<CXTypeInfo>* pTypeInfo; + + ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo); + if (FAILED(ret)) + return ret; + + pTypeInfo->AddRef(); + + pTypeInfo->InitForCoclass(mxOrigin, msImplementationName, aIID, mxMSF); + + *ppTInfo = pTypeInfo; + + return S_OK; + } + } + +#if 0 + ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint(); + + IID aIID; + if (SUCCEEDED(IIDFromString((LPOLESTR)aTypeAndIID.IID.pData->buffer, &aIID))) + { + HRESULT ret; + + CComObject<CXTypeInfo>* pTypeInfo; + + ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo); + if (FAILED(ret)) + return ret; + + pTypeInfo->AddRef(); + + pTypeInfo->InitForOutgoing(mxOrigin, msImplementationName, aIID, mxMSF); + + *ppTInfo = pTypeInfo; + + return S_OK; + } +#else + SAL_WARN("extensions.olebridge", "Not implemented: GetTypeInfoOfGuid(" << guid << ")"); +#endif + + return TYPE_E_ELEMENTNOTFOUND; + + } + + virtual HRESULT STDMETHODCALLTYPE GetLibAttr(TLIBATTR **) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetLibAttr: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeComp: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetDocumentation(INT, + BSTR *, + BSTR *, + DWORD *, + BSTR *) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetDocumentation: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE IsName(LPOLESTR, + ULONG, + BOOL *) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib:IsName: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE FindName(LPOLESTR, + ULONG, + ITypeInfo **, + MEMBERID *, + USHORT *) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::FindName: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual void STDMETHODCALLTYPE ReleaseTLibAttr(TLIBATTR *) override + { + SAL_WARN("extensions.olebridge", this << "@CXTypeLib::ReleaseTLibAttr: E_NOTIMPL"); + } + +private: + Reference<XInterface> mxOrigin; + OUString msImplementationName; + Reference<XMultiServiceFactory> mxMSF; +}; + +} + +void CXTypeInfo::InitForCoclass(Reference<XInterface> xOrigin, + const OUString& sImplementationName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF) +{ + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForCoclass(" << sImplementationName << "," << rIID << ")"); + meKind = Kind::COCLASS; + mxOrigin = xOrigin; + msImplementationName = sImplementationName; + maIID = rIID; + mxMSF = xMSF; +} + +void CXTypeInfo::InitForClassItself(Reference<XInterface> xOrigin, + const OUString& sImplementationName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF) +{ + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForClassItself(" << sImplementationName << "," << rIID << ")"); + meKind = Kind::MAIN; + mxOrigin = xOrigin; + msImplementationName = sImplementationName; + maIID = rIID; + mxMSF = xMSF; +} + +void CXTypeInfo::InitForOutgoing(Reference<XInterface> xOrigin, + const OUString& sInterfaceName, + const IID& rIID, + Reference<XMultiServiceFactory> xMSF, + Type aType) +{ + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForOutgoing(" << sInterfaceName << "," << rIID << ")"); + meKind = Kind::OUTGOING; + mxOrigin = xOrigin; + msInterfaceName = sInterfaceName; + maIID = rIID; + mxMSF = xMSF; + maType = aType; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeAttr(TYPEATTR **ppTypeAttr) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr"); + + if (!ppTypeAttr) + return E_POINTER; + + assert(!IsEqualIID(maIID, IID_NULL)); + + TYPEATTR *pTypeAttr = new TYPEATTR; + memset(pTypeAttr, 0, sizeof(*pTypeAttr)); + + pTypeAttr->guid = maIID; + + if (meKind == Kind::COCLASS) + { + pTypeAttr->typekind = TKIND_COCLASS; + pTypeAttr->cFuncs = 0; + pTypeAttr->cVars = 0; + pTypeAttr->cImplTypes = 3; + pTypeAttr->cbSizeVft = 0; + pTypeAttr->cbAlignment = 8; + pTypeAttr->wTypeFlags = TYPEFLAG_FCANCREATE; + } + else if (meKind == Kind::MAIN) + { + pTypeAttr->typekind = TKIND_DISPATCH; + pTypeAttr->cFuncs = 10; // FIXME, dummy + pTypeAttr->cVars = 0; + pTypeAttr->cImplTypes = 1; + // FIXME: I think this is always supposed to be as if just for the seven methods in + // IDIspatch? + pTypeAttr->cbSizeVft = 7 * sizeof(void*); + pTypeAttr->cbAlignment = 8; + pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FDISPATCHABLE; + } + else if (meKind == Kind::OUTGOING) + { + pTypeAttr->typekind = TKIND_DISPATCH; + + Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF)); + assert(xRefl.is()); + + Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName()); + assert(xClass.is()); + + auto aMethods = xClass->getMethods(); + assert(xClass->getTypeClass() == TypeClass_INTERFACE && + aMethods.getLength() > 0); + + // Drop the three XInterface methods, add the three corresponding IUnknown ones plus the + // four IDispatch ones on top of that. + pTypeAttr->cFuncs = aMethods.getLength() - 3 + 3 + 4; + pTypeAttr->cVars = 0; + pTypeAttr->cImplTypes = 1; + // FIXME: I think this, too, is always supposed to be as if just for the seven methods in + // IDIspatch? + pTypeAttr->cbSizeVft = 7 * sizeof(void*); + pTypeAttr->cbAlignment = 8; + pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FNONEXTENSIBLE|TYPEFLAG_FDISPATCHABLE; + } + else + assert(false); + + pTypeAttr->lcid = LOCALE_USER_DEFAULT; + pTypeAttr->memidConstructor = MEMBERID_NIL; + pTypeAttr->memidDestructor = MEMBERID_NIL; + // FIXME: Is this correct, just the vtable pointer, right? + pTypeAttr->cbSizeInstance = sizeof(void*); + pTypeAttr->wMajorVerNum = 0; + pTypeAttr->wMinorVerNum = 0; + pTypeAttr->idldescType.wIDLFlags = IDLFLAG_NONE; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr: " << pTypeAttr); + + *ppTypeAttr = pTypeAttr; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeComp(ITypeComp **) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetTypeComp: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetFuncDesc(UINT index, + FUNCDESC **ppFuncDesc) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + if (!ppFuncDesc) + return E_POINTER; + + if (meKind != Kind::OUTGOING) + return E_NOTIMPL; + + if (index <= 6) + { + *ppFuncDesc = new FUNCDESC; + (*ppFuncDesc)->memid = 0x60000000 + index; + (*ppFuncDesc)->lprgscode = nullptr; + (*ppFuncDesc)->lprgelemdescParam = nullptr; + (*ppFuncDesc)->funckind = FUNC_DISPATCH; + (*ppFuncDesc)->invkind = INVOKE_FUNC; + (*ppFuncDesc)->callconv = CC_STDCALL; + switch (index) + { + case 0: // QueryInterface + (*ppFuncDesc)->cParams = 2; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; + break; + case 1: // AddRef + (*ppFuncDesc)->cParams = 0; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4; + break; + case 2: // Release + (*ppFuncDesc)->cParams = 1; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4; + break; + case 3: // GetTypeInfoCount + (*ppFuncDesc)->cParams = 1; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; + break; + case 4: // GetTypeInfo + (*ppFuncDesc)->cParams = 3; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; + break; + case 5: // GetIDsOfNames + (*ppFuncDesc)->cParams = 5; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; + break; + case 6: // Invoke + (*ppFuncDesc)->cParams = 8; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; + break; + } + (*ppFuncDesc)->cParamsOpt = 0; + (*ppFuncDesc)->oVft = index * sizeof(void*); + (*ppFuncDesc)->cScodes = 0; + (*ppFuncDesc)->wFuncFlags = FUNCFLAG_FRESTRICTED; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc); + + return S_OK; + } + + Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF)); + assert(xRefl.is()); + + Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName()); + assert(xClass.is()); + + auto aMethods = xClass->getMethods(); + assert(xClass->getTypeClass() == TypeClass_INTERFACE && + aMethods.getLength() > 0); + + if (index > o3tl::make_unsigned(aMethods.getLength() - 3 + 3 + 4)) + return E_INVALIDARG; + + *ppFuncDesc = new FUNCDESC; + + (*ppFuncDesc)->memid = index - 6; + (*ppFuncDesc)->lprgscode = nullptr; + (*ppFuncDesc)->lprgelemdescParam = nullptr; + (*ppFuncDesc)->funckind = FUNC_DISPATCH; + (*ppFuncDesc)->invkind = INVOKE_FUNC; + (*ppFuncDesc)->callconv = CC_STDCALL; + (*ppFuncDesc)->cParams = aMethods[index - 4]->getParameterInfos().getLength(); + (*ppFuncDesc)->cParamsOpt = 0; + (*ppFuncDesc)->oVft = index * sizeof(void*); + (*ppFuncDesc)->cScodes = 0; + (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; // ??? + (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; // ??? + (*ppFuncDesc)->wFuncFlags = 0; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc); + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetVarDesc(UINT, + VARDESC **) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetVarDesc: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetNames(MEMBERID memid, + BSTR *rgBstrNames, + UINT cMaxNames, + UINT *pcNames) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetNames(" << memid << ")"); + assert(meKind != Kind::COCLASS); + + if (!rgBstrNames) + return E_POINTER; + + if (!pcNames) + return E_POINTER; + + if (memid < 1) + return E_INVALIDARG; + + if (cMaxNames < 1) + return E_INVALIDARG; + + if (meKind == Kind::MAIN) + { + SAL_WARN("extensions.olebridge", "GetNames() for MAIN not implemented"); + return E_NOTIMPL; + } + + Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF)); + assert(xRefl.is()); + + Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName()); + assert(xClass.is()); + + auto aMethods = xClass->getMethods(); + assert(xClass->getTypeClass() == TypeClass_INTERFACE && + aMethods.getLength() > 0); + + // Subtract the three XInterface methods. Memid for the first following method is 1. + if (memid > aMethods.getLength() - 3) + return E_INVALIDARG; + + SAL_INFO("extensions.olebridge", "..." << this << "@CXTypeInfo::GetNames(" << memid << "): " << aMethods[memid + 2]->getName()); + rgBstrNames[0] = SysAllocString(reinterpret_cast<LPOLESTR>(aMethods[memid + 2]->getName().pData->buffer)); + *pcNames = 1; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeOfImplType(UINT index, + HREFTYPE *pRefType) +{ + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeOfImplType(" << index << ")"); + + if (!pRefType) + return E_POINTER; + + assert(index == 0 || index == 1); + + *pRefType = 1000+index; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetImplTypeFlags(UINT index, + INT *pImplTypeFlags) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetImplTypeFlags(" << index << ")"); + + if (!pImplTypeFlags) + return E_POINTER; + + assert(meKind == Kind::COCLASS); + assert(index == 0 || index == 1); + + if (index == 0) + *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT; + else if (index == 1) + *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT|IMPLTYPEFLAG_FSOURCE; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetIDsOfNames(LPOLESTR *, + UINT, + MEMBERID *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetIDsOfNames: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::Invoke(PVOID, + MEMBERID, + WORD, + DISPPARAMS *, + VARIANT *, + EXCEPINFO *, + UINT *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::Invoke: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDocumentation(MEMBERID memid, + BSTR *pBstrName, + BSTR *pBstrDocString, + DWORD *pdwHelpContext, + BSTR *pBstrHelpFile) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetDocumentation(" << memid << ")"); + + if (pBstrName) + { + if (memid == MEMBERID_NIL) + { + *pBstrName = SysAllocString(o3tl::toW(msImplementationName.getStr())); + } + else if (memid == DISPID_VALUE) + { + // MEMBERIDs are the same as DISPIDs, apparently? + *pBstrName = SysAllocString(L"Value"); + } + else + { + // FIXME: Shouldn't we be able to know the names of the members of UNO interfaces? + *pBstrName = SysAllocString(o3tl::toW(OUString("UnknownNameOfMember#" + OUString::number(memid)).getStr())); + } + } + if (pBstrDocString) + *pBstrDocString = SysAllocString(L""); + if (pdwHelpContext) + *pdwHelpContext = 0; + if (pBstrHelpFile) + *pBstrHelpFile = nullptr; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDllEntry(MEMBERID, + INVOKEKIND, + BSTR *, + BSTR *, + WORD *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetDllEntry: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeInfo(HREFTYPE hRefType, + ITypeInfo **ppTInfo) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeInfo(" << hRefType << ")"); + + if (!ppTInfo) + return E_POINTER; + + // FIXME: Is it correct to assume that the only interfaces on which GetRefTypeInfo() would be + // called are those that implement ooo::vba::XConnectable? + + Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY); + if (!xConnectable.is()) + return E_NOTIMPL; + + ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint(); + + IID aIID; + if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID))) + return E_NOTIMPL; + + HRESULT ret; + + CComObject<CXTypeInfo>* pTypeInfo; + + ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo); + if (FAILED(ret)) + return ret; + + pTypeInfo->AddRef(); + + pTypeInfo->InitForOutgoing(mxOrigin, aTypeAndIID.Type.getTypeName(), aIID, mxMSF, aTypeAndIID.Type); + + *ppTInfo = pTypeInfo; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::AddressOfMember(MEMBERID, + INVOKEKIND, + PVOID *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::AddressOfMember: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::CreateInstance(IUnknown *, + REFIID, + PVOID *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::CreateInstance: E_NOTIMPL"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetMops(MEMBERID, + BSTR *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetMops: E_NOTIMPL"); + return E_NOTIMPL; +} + +// This is not actually called any more by my vbscript test after I added the IProvideClassInfo +// thing... so all the CXTypeLib stuff is dead code at the moment. + +HRESULT STDMETHODCALLTYPE CXTypeInfo::GetContainingTypeLib(ITypeLib **ppTLib, + UINT *pIndex) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetContainingTypeLib"); + + if (!ppTLib || !pIndex) + return E_POINTER; + + HRESULT ret; + + CComObject<CXTypeLib>* pTypeLib; + + ret = CComObject<CXTypeLib>::CreateInstance(&pTypeLib); + if (FAILED(ret)) + return ret; + + pTypeLib->AddRef(); + + pTypeLib->Init(mxOrigin, msImplementationName, mxMSF); + + *ppTLib = pTypeLib; + + return S_OK; +} + +void STDMETHODCALLTYPE CXTypeInfo::ReleaseTypeAttr(TYPEATTR *pTypeAttr) +{ + SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::ReleaseTypeAttr(" << pTypeAttr << ")"); + + delete pTypeAttr; +} + +void STDMETHODCALLTYPE CXTypeInfo::ReleaseFuncDesc(FUNCDESC *pFuncDesc) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseFuncDesc(" << pFuncDesc << ")"); + + delete pFuncDesc; +} + +void STDMETHODCALLTYPE CXTypeInfo::ReleaseVarDesc(VARDESC *) +{ + SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseVarDesc: E_NOTIMPL"); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(UINT iTInfo, LCID, ITypeInfo ** ppTInfo) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfo(" << iTInfo << ")"); + + if (!ppTInfo) + return E_POINTER; + + if (iTInfo != 0) + return E_NOTIMPL; + + // FIXME: This is surely incorrect. Why is being able to handle GetTypeInfo() here coupled to + // being a source for outgoing events, i.e. implementing XConnectable? What would break if we + // would use XInterfaceWithIID and its getIID instead? + + Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY); + if (!xConnectable.is()) + return E_NOTIMPL; + + OUString sIID = xConnectable->GetIIDForClassItselfNotCoclass(); + IID aIID; + if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID))) + return E_NOTIMPL; + + HRESULT ret; + + CComObject<CXTypeInfo>* pTypeInfo; + + ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo); + if (FAILED(ret)) + return ret; + + pTypeInfo->AddRef(); + + pTypeInfo->InitForClassItself(m_xOrigin, m_sImplementationName, aIID, m_smgr); + + *ppTInfo = pTypeInfo; + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetIDsOfNames(REFIID /*riid*/, + LPOLESTR * rgszNames, + UINT cNames, + LCID /*lcid*/, + DISPID * rgdispid ) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + if( ! rgdispid) + return E_POINTER; + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetIDsOfNames:"); + for (unsigned int i = 0; i < cNames; ++i) + { + // Initialise returned rgdispid values. + rgdispid[i] = DISPID_UNKNOWN; + + SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i]))); + } + + HRESULT ret = DISP_E_UNKNOWNNAME; + try + { + MutexGuard guard( getBridgeMutex()); + + // FIXME: Handle the cNames > 1 case? Note that the rest of the names mean the names of *arguments*. + + if( ! _wcsicmp( *rgszNames, JSCRIPT_VALUE_FUNC) || + ! _wcsicmp( *rgszNames, BRIDGE_VALUE_FUNC)) + { + *rgdispid= DISPID_JSCRIPT_VALUE_FUNC; + return S_OK; + } + else if( ! _wcsicmp( *rgszNames, GET_STRUCT_FUNC) || + ! _wcsicmp( *rgszNames, BRIDGE_GET_STRUCT_FUNC)) + { + *rgdispid= DISPID_GET_STRUCT_FUNC; + return S_OK; + } + else if( ! _wcsicmp( *rgszNames, BRIDGE_CREATE_TYPE_FUNC)) + { + *rgdispid= DISPID_CREATE_TYPE_FUNC; + return S_OK; + } + + if (m_xInvocation.is() && (cNames > 0)) + { + OUString name(o3tl::toU(rgszNames[0])); + NameToIdMap::iterator iter = m_nameToDispIdMap.find(name); + + bool bIsMethod = false; + + OUString exactName = name; + + if (iter == m_nameToDispIdMap.end()) + { + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName(name); + if (exactName.isEmpty()) + exactName = name; + } + + MemberInfo d(0, exactName); + + if (m_xInvocation->hasProperty(exactName)) + { + d.flags |= DISPATCH_PROPERTYGET; + d.flags |= DISPATCH_PROPERTYPUT; + d.flags |= DISPATCH_PROPERTYPUTREF; + } + + if (m_xInvocation->hasMethod(exactName)) + { + d.flags |= DISPATCH_METHOD; + bIsMethod = true; + } + + if (d.flags != 0) + { + m_MemberInfos.push_back(d); + iter = m_nameToDispIdMap.emplace(exactName, static_cast<DISPID>(m_MemberInfos.size())).first; + + if (exactName != name) + { + iter = m_nameToDispIdMap.emplace(name, static_cast<DISPID>(m_MemberInfos.size())).first; + } + } + } + + if (iter == m_nameToDispIdMap.end()) + { + ret = DISP_E_UNKNOWNNAME; + SAL_INFO("extensions.olebridge", " " << name << ": UNKNOWN"); + } + else + { + rgdispid[0] = (*iter).second; + SAL_INFO("extensions.olebridge", " " << name << ": " << rgdispid[0]); + + if (bIsMethod && cNames > 1) + { + Reference<XIdlMethod> xIdlMethod; + Reference<XIntrospectionAccess> xIntrospectionAccess = m_xInvocation->getIntrospection(); + try + { + if (xIntrospectionAccess.is()) + xIdlMethod = xIntrospectionAccess->getMethod(exactName, MethodConcept::ALL); + } + catch (const NoSuchMethodException&) + { + } + if (xIdlMethod.is()) + { + auto aParamInfos = xIdlMethod->getParameterInfos(); + for (unsigned int i = 1; i < cNames; ++i) + { + bool bFound = false; + for (int j = 0; j < aParamInfos.getLength(); ++j) + { + if (aParamInfos[j].aName.equalsIgnoreAsciiCase(o3tl::toU(rgszNames[i]))) + { + rgdispid[i] = j; + bFound = true; + SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": " << rgdispid[i]); + break; + } + } + if (!bFound) + SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": NOT FOUND"); + } + } + } + + // Return value should be S_OK only if *all* the names were found. + unsigned int i; + for (i = 0; i < cNames; ++i) + if (rgdispid[i] == DISPID_UNKNOWN) + break; + if (i == cNames) + ret = S_OK; + } + } + } + catch(const BridgeRuntimeError&) + { + OSL_ASSERT(false); + } + catch(const Exception&) + { + OSL_ASSERT(false); + } + catch(...) + { + OSL_ASSERT(false); + } + + return ret; +} + +// Note: What the comments here say about JScript possibly holds for Automation clients in general, +// like VBScript ones, too. Or not. Hard to say. What is the relevance of JScript nowadays anyway, +// and can LO really be used from JScript code on web pages any longer? + +// "convertDispparamsArgs" converts VARIANTS to their respecting Any counterparts +// The parameters "id", "wFlags" and "pdispparams" equal those as used in +// IDispatch::Invoke. The function handles special JavaScript +// cases where a VARIANT of type VT_DISPATCH is ambiguous and could represent +// an object, array ( JavaScript Array object), out parameter and in/out ( JavaScript Array object) +// parameter (JavaScript Array object) +// Because all those VT_DISPATCH objects need a different conversion +// we have to find out what the object is supposed to be. The function does this +// by either using type information or by help of a specialized ValueObject object. + +// A. Type Information + +// With the help of type information the kind of parameter can be exactly determined +// and an appropriate conversion can be chosen. A problem arises if a method expects +// an Any. Then the type info does not tell what the type of the value, that is kept +// by the any, should be. In this situation the decision whether the param is a +// sequence or an object is made upon the fact if the object has a property "0" +// ( see function "isJScriptArray"). Since this is unsafe it is recommended to use +// the JScript value objects within a JScript script on such an occasion. + +// B. JavaScript Value Object ( class JScriptValue ) + +// A JScriptValue (ValueObject) object is a COM object in that it implements IDispatch and the +// IJScriptValue object interface. Such objects are provided by all UNO wrapper +// objects used within a JScript script. To obtain an instance one has to call +// "_GetValueObject() or Bridge_GetValueObject()" on a UNO wrapper object (class InterfaceOleWrapper). +// A value object is appropriately initialized within the script and passed as +// parameter to a UNO object method or property. The convertDispparamsArgs function +// can easily find out that a param is such an object by querying for the +// IJScriptValue interface. By this interface one the type and kind ( out, in/out) +// can be determined and the right conversion can be applied. +// Using ValueObjects we spare us the effort of acquiring and examining type information +// in order to figure out what the an IDispatch parameter is meant for. + +// Normal JScript object parameter can be mixed with JScriptValue object. If an +// VARIANT contains a VT_DISPATCH that is no JScriptValue than the type information +// is used to find out about the required type. +void InterfaceOleWrapper::convertDispparamsArgs(DISPID id, + unsigned short /*wFlags*/, DISPPARAMS* pdispparams, Sequence<Any>& rSeq) +{ + // Parameters come in in reverse order in pdispparams. There might be less parameters than + // expected. In that case, assume they are "optional" (but can't be marked as such in UNO IDL), + // and fill in the rest with empty Anys. There might also be more than expected. In that case, + // assume the oovbaapi UNO IDL hasn't kept up with added optional parameters in MSO, and just + // ignore the extra ones, as long as they are empty. + + // An example: incoming parameters: <12, 13, "foo/bar.tem"> + // + // Expected parameters: (string filename, int something, int somethingElse, Any whatever, Any + // whateverElse) + // + // Here the existing incoming parameters are placed in reverse order in the first three outgoing + // parameters, and the rest of the outgoing parameters are kept as empty Anys. + // + // Another example: incoming parameters: <EMPTY, TRUE> + // + // Expected parameters: (bool flag) + // + // Here the TRUE is passed as the sole outgoing parameter, and the incoming EMPTY is ignored. + // + // Still an example: incoming parameters: <"foo.doc", TRUE> + // + // Expected parameters: (bool flag) + // + // This throws an error as the incoming string parameter presumably should do something important, + // but there is no corresponding outgoing parameter. + + HRESULT hr = S_OK; + const int countIncomingArgs = pdispparams->cArgs; + + //Get type information for the current call + InvocationInfo info; + if( ! getInvocationInfoForCall( id, info)) + throw BridgeRuntimeError( + "[automation bridge]InterfaceOleWrapper::convertDispparamsArgs \n" + "Could not obtain type information for current call."); + + // Size rSeq according to the number of expected parameters. + const int expectedArgs = info.aParamTypes.getLength() + (info.eMemberType == MemberType_PROPERTY ? 1 : 0); + rSeq.realloc( expectedArgs ); + Any* pParams = rSeq.getArray(); + + Any anyParam; + + int outgoingArgIndex = 0; + + // Go through incoming parameters in reverse order, i.e. in the order as declared in IDL + for (int i = std::max(countIncomingArgs, expectedArgs) - 1; i >= 0; i--) + { + // Ignore too many parameters if they are VT_EMPTY anyway + if ( outgoingArgIndex >= expectedArgs && pdispparams->rgvarg[i].vt == VT_EMPTY ) + continue; + + // But otherwise too many parameters is an error + if ( outgoingArgIndex >= expectedArgs ) + throw BridgeRuntimeError( "[automation bridge] Too many parameters" ); + + if (info.eMemberType == MemberType_METHOD && + info.aParamModes[ outgoingArgIndex ] == ParamMode_OUT) + { + outgoingArgIndex++; + continue; + } + + if (i < countIncomingArgs) + { + // A missing (and hopefully optional) arg (in the middle of the argument list) is passed + // as an empty Any. + if (pdispparams->rgvarg[i].vt == VT_ERROR && pdispparams->rgvarg[i].scode == DISP_E_PARAMNOTFOUND) + { + Any aEmpty; + pParams[ outgoingArgIndex ] = aEmpty; + outgoingArgIndex++; + continue; + } + + if(convertValueObject( & pdispparams->rgvarg[i], anyParam)) + { //a param is a ValueObject and could be converted + pParams[ outgoingArgIndex ] = anyParam; + outgoingArgIndex++; + continue; + } + } + else + { + // A missing arg. Let's hope it is de facto optional (there is no way in UNO IDL to mark + // a parameter as optional). The corresponding slot in pParams is already a void Any. + // Here we don't increase outgoingArgIndex! + continue; + } + + // If the param is an out, in/out parameter in + // JScript (Array object, with value at index 0) then we + // extract Array[0] and put the value into varParam. At the end of the loop varParam + // is converted if it contains a value otherwise the VARIANT from + // DISPPARAMS is converted. + CComVariant varParam; + + // Check for JScript out and in/out paramsobjects (VT_DISPATCH). + // To find them out we use typeinformation of the function being called. + + // No idea how this stuff, originally written for JScript, works for other Automation + // clients. + + if( pdispparams->rgvarg[i].vt == VT_DISPATCH ) + { + if( info.eMemberType == MemberType_METHOD && info.aParamModes[ outgoingArgIndex ] == ParamMode_INOUT) + { + // INOUT-param + // Index ( property) "0" contains the actual IN-param. The object is a JScript + // Array object. + // Get the IN-param at index "0" + IDispatch* pdisp= pdispparams->rgvarg[i].pdispVal; + + OLECHAR const * sindex= L"0"; + DISPID id2; + DISPPARAMS noParams= {nullptr,nullptr,0,0}; + if(SUCCEEDED( hr= pdisp->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, &id2))) + hr= pdisp->Invoke( id2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, + & noParams, & varParam, nullptr, nullptr); + if( FAILED( hr)) + { + throw BridgeRuntimeError( + "[automation bridge] Could not determine " + "if the object has a member \"0\". Error: " + + OUString::number(hr)); + } + } + } + + if( varParam.vt == VT_EMPTY) // then it was no in/out parameter + varParam= pdispparams->rgvarg[i]; + + if(info.eMemberType == MemberType_METHOD) + variantToAny( & varParam, anyParam, + info.aParamTypes[ outgoingArgIndex ]); + else if(info.eMemberType == MemberType_PROPERTY) + variantToAny( & varParam, anyParam, info.aType); + else + OSL_ASSERT(false); + + if (outgoingArgIndex < expectedArgs) + pParams[ outgoingArgIndex ]= anyParam; + outgoingArgIndex++; + }// end for / iterating over all parameters +} + +bool InterfaceOleWrapper::getInvocationInfoForCall( DISPID id, InvocationInfo& info) +{ + bool bTypesAvailable= false; + + if( !m_xInvocation.is() )return false; + Reference<XInvocation2> inv2( m_xInvocation, UNO_QUERY); + if( inv2.is()) + { + // We need the name of the property or method to get its type information. + // The name can be identified through the param "id" + // that is kept as value in the map m_nameToDispIdMap. + // Problem: the Windows JScript engine sometimes changes small letters to capital + // letters as happens in xidlclass_obj.createObject( var) // in JScript. + // IDispatch::GetIdsOfNames is then called with "CreateObject" !!! + // m_nameToDispIdMap can contain several names for one DISPID but only one is + // the exact one. If there's no m_xExactName and therefore no exact name then + // there's only one entry in the map. + OUString sMemberName; + + auto ci1 = std::find_if(m_nameToDispIdMap.cbegin(), m_nameToDispIdMap.cend(), + [&id](const NameToIdMap::value_type& nameToDispId) { return nameToDispId.second == id; }); // item is a pair<OUString, DISPID> + if (ci1 != m_nameToDispIdMap.cend()) + sMemberName= (*ci1).first; + // Get information for the current call ( property or method). + // There could be similar names which only differ in the cases + // of letters. First we assume that the name which was passed into + // GetIDsOfNames is correct. If we won't get information with that + // name then we have the invocation service use the XExactName interface. + bool validInfo= true; + InvocationInfo invInfo; + try{ + invInfo= inv2->getInfoForName( sMemberName, false); + } + catch(const IllegalArgumentException&) + { + validInfo= false; + } + + if( ! validInfo) + { + invInfo= inv2->getInfoForName( sMemberName, true); + } + if( invInfo.aName.pData) + { + bTypesAvailable= true; + info= invInfo; + } + } + return bTypesAvailable; +} + +// XBridgeSupplier2 --------------------------------------------------- +// only bridges itself ( this instance of InterfaceOleWrapper)from UNO to IDispatch +// If sourceModelType is UNO than any UNO interface implemented by InterfaceOleWrapper +// can bridged to IDispatch ( if destModelType == OLE). The IDispatch is +// implemented by this class. +Any SAL_CALL InterfaceOleWrapper::createBridge(const Any& modelDepObject, + const Sequence<sal_Int8>& /*ProcessId*/, + sal_Int16 sourceModelType, + sal_Int16 destModelType) +{ + + Any retAny; + if( sourceModelType == UNO && destModelType == OLE && + modelDepObject.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference<XInterface> xInt; + if( modelDepObject >>= xInt ) + { + if( xInt == Reference<XInterface>( static_cast<XWeak*>( this), UNO_QUERY)) + { + VARIANT *pVar= static_cast<VARIANT*>(CoTaskMemAlloc( sizeof( VARIANT))); + if( pVar) + { + pVar->vt= VT_DISPATCH; + pVar->pdispVal= this; + AddRef(); + + retAny<<= reinterpret_cast< sal_uIntPtr >( pVar); + } + } + } + } + + return retAny; +} + +// XInitialization -------------------------------------------------- +void SAL_CALL InterfaceOleWrapper::initialize( const Sequence< Any >& aArguments ) +{ + switch( aArguments.getLength() ) + { + case 2: // the object wraps a UNO struct + aArguments[0] >>= m_xInvocation; + aArguments[1] >>= m_defaultValueType; + break; + case 3: // the object wraps a UNO interface + aArguments[0] >>= m_xInvocation; + aArguments[1] >>= m_xOrigin; + aArguments[2] >>= m_defaultValueType; + + Reference<XServiceInfo> xServiceInfo(m_xOrigin, UNO_QUERY); + if (xServiceInfo.is()) + m_sImplementationName = xServiceInfo->getImplementationName(); + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::initialize for " + << (m_sImplementationName.isEmpty()?"an unknown implementation":m_sImplementationName)); + break; + } + + m_xExactName.set( m_xInvocation, UNO_QUERY); +} + +Reference< XInterface > InterfaceOleWrapper::createUnoWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + +Reference<XInterface> InterfaceOleWrapper::createComWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + +// "getType" is used in convertValueObject to map the string denoting the type +// to an actual Type object. +bool getType( const BSTR name, Type & type) +{ + bool ret = false; + typelib_TypeDescription * pDesc= nullptr; + OUString str(o3tl::toU(name)); + typelib_typedescription_getByName( &pDesc, str.pData ); + if( pDesc) + { + type = Type( pDesc->pWeakRef ); + typelib_typedescription_release( pDesc); + ret = true; + } + return ret; +} + +static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource) +{ + bool ret = false; + HRESULT hr; + + // Handle JScriptValue objects and JScript out params ( Array object ) + CComVariant varDest( *pDest); + + if( SUCCEEDED( varDest.ChangeType(VT_DISPATCH))) + { + CComPtr<IDispatch> spDispDest(varDest.pdispVal); + + // special Handling for a JScriptValue object + CComQIPtr<IJScriptValueObject> spValueDest(spDispDest); + if (spValueDest) + { + VARIANT_BOOL varBool= VARIANT_FALSE; + if ((SUCCEEDED(hr = spValueDest->IsOutParam(&varBool)) + && varBool == VARIANT_TRUE) + || (SUCCEEDED(hr = spValueDest->IsInOutParam(&varBool)) + && varBool == VARIANT_TRUE)) + { + if( SUCCEEDED( spValueDest->Set( CComVariant(), *pSource))) + ret= true; + } + } + else if (pDest->vt == VT_DISPATCH)// VT_DISPATCH -> JScript out param + { + // We use IDispatchEx because its GetDispID function causes the creation + // of a property if it does not exist already. This is convenient for + // out parameters in JScript. Then the user must not specify property "0" + // explicitly + CComQIPtr<IDispatchEx> spDispEx( spDispDest); + if( spDispEx) + { + CComBSTR nullProp(L"0"); + DISPID dwDispID; + if( SUCCEEDED( spDispEx->GetDispID( nullProp, fdexNameEnsure, &dwDispID))) + { + DISPPARAMS dispparams = {nullptr, nullptr, 1, 1}; + dispparams.rgvarg = pSource; + DISPID dispidPut = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = &dispidPut; + + if (pSource->vt == VT_UNKNOWN || pSource->vt == VT_DISPATCH || + (pSource->vt & VT_ARRAY) || (pSource->vt & VT_BYREF)) + hr = spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, + &dispparams, nullptr, nullptr, nullptr); + else + hr= spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, + &dispparams, nullptr, nullptr, nullptr); + if( SUCCEEDED(hr)) + ret= true; + } + } + } + else + ret= writeBackOutParameter( pDest, pSource); + } + else // The param can't be a JScript out-parameter ( an Array object), it could be a VBScript + { // param. The function checks itself for correct VBScript params + ret= writeBackOutParameter( pDest, pSource); + } + return ret; +} + +// VisualBasic Script passes arguments as VT_VARIANT | VT_BYREF be it in or out parameter. +// Thus we are in charge of freeing an eventual value contained by the inner VARIANT +// Please note: VariantCopy doesn't free a VT_BYREF value +// The out parameters are expected to have always a valid type +static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource) +{ + HRESULT hr; + bool ret = false; + // Out parameter must be VT_BYREF + if ((V_VT(pDest) & VT_BYREF) != 0 ) + { + VARTYPE oleTypeFlags = V_VT(pSource); + + // if caller accept VARIANT as out parameter, any value must be converted + if (V_VT(pDest) == (VT_VARIANT | VT_BYREF)) + { + // When the user provides a VARIANT rather than a concrete type + // we just copy the source to the out, in/out parameter + // VT_DISPATCH, VT_UNKNOWN, VT_ARRAY, VT_BSTR in the VARIANT that + // is contained in pDest are released by VariantCopy + VariantCopy(V_VARIANTREF(pDest), pSource); + ret = true; + } + else + { + // variantarg and variant must have same type + if ((V_VT(pDest) & oleTypeFlags) == oleTypeFlags) + { + if ((oleTypeFlags & VT_ARRAY) != 0) + { + // In / Out Param + if( *V_ARRAYREF(pDest) != nullptr) + hr= SafeArrayCopyData( V_ARRAY(pSource), *V_ARRAYREF(pDest)); + else + // Out Param + hr= SafeArrayCopy(V_ARRAY(pSource), V_ARRAYREF(pDest)); + if( SUCCEEDED( hr)) + ret = true; + } + else + { + // copy base type + switch (V_VT(pSource)) + { + case VT_I2: + { + *V_I2REF(pDest) = V_I2(pSource); + ret = true; + break; + } + case VT_I4: + *V_I4REF(pDest) = V_I4(pSource); + ret = true; + break; + case VT_R4: + *V_R4REF(pDest) = V_R4(pSource); + ret = true; + break; + case VT_R8: + *V_R8REF(pDest) = V_R8(pSource); + ret = true; + break; + case VT_CY: + *V_CYREF(pDest) = V_CY(pSource); + ret = true; + break; + case VT_DATE: + *V_DATEREF(pDest) = V_DATE(pSource); + ret = true; + break; + case VT_BSTR: + SysFreeString( *pDest->pbstrVal); + + *V_BSTRREF(pDest) = SysAllocString(V_BSTR(pSource)); + ret = true; + break; + case VT_DISPATCH: + if (*V_DISPATCHREF(pDest) != nullptr) + (*V_DISPATCHREF(pDest))->Release(); + + *V_DISPATCHREF(pDest) = V_DISPATCH(pSource); + + if (*V_DISPATCHREF(pDest) != nullptr) + (*V_DISPATCHREF(pDest))->AddRef(); + + ret = true; + break; + case VT_ERROR: + *V_ERRORREF(pDest) = V_ERROR(pSource); + ret = true; + break; + case VT_BOOL: + *V_BOOLREF(pDest) = V_BOOL(pSource); + ret = true; + break; + case VT_UNKNOWN: + if (*V_UNKNOWNREF(pDest) != nullptr) + (*V_UNKNOWNREF(pDest))->Release(); + + *V_UNKNOWNREF(pDest) = V_UNKNOWN(pSource); + + if (*V_UNKNOWNREF(pDest) != nullptr) + (*V_UNKNOWNREF(pDest))->AddRef(); + + ret = true; + break; + case VT_I1: + *V_I1REF(pDest) = V_I1(pSource); + ret = true; + break; + case VT_UI1: + *V_UI1REF(pDest) = V_UI1(pSource); + ret = true; + break; + case VT_UI2: + *V_UI2REF(pDest) = V_UI2(pSource); + ret = true; + break; + case VT_UI4: + *V_UI4REF(pDest) = V_UI4(pSource); + ret = true; + break; + case VT_INT: + *V_INTREF(pDest) = V_INT(pSource); + ret = true; + break; + case VT_UINT: + *V_UINTREF(pDest) = V_UINT(pSource); + ret = true; + break; + case VT_DECIMAL: + memcpy(pDest->pdecVal, pSource, sizeof(DECIMAL)); + ret = true; + break; + default: + break; + } + } + } + else + { + // Handling of special cases + // Destination and source types are different + if( pDest->vt == (VT_BSTR | VT_BYREF) + && pSource->vt == VT_I2) + { + // When the user provides a String as out our in/out parameter + // and the type is char (TypeClass_CHAR) then we convert to a BSTR + // instead of VT_I2 as is done otherwise + OLECHAR buff[]= {0,0}; + buff[0]= pSource->iVal; + + SysFreeString( *pDest->pbstrVal); + *pDest->pbstrVal= SysAllocString( buff); + ret = true; + } + } + } + } + return ret; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::Invoke(DISPID dispidMember, + REFIID /*riid*/, + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + UINT * puArgErr ) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + OUString sParams; +#if defined SAL_LOG_INFO + sParams += "["; + for (unsigned int i = 0; i < pdispparams->cArgs; ++i) + { + if (i > 0) + sParams += ","; + std::stringstream aStringStream; + aStringStream << pdispparams->rgvarg[i]; + sParams += OUString::createFromAscii(aStringStream.str()); + } + sParams += "]"; +#endif + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::Invoke(" << dispidMember << "," << sParams << ")"); + + comphelper::ProfileZone aZone("COM Bridge"); + HRESULT ret = S_OK; + + try + { + bool bHandled= false; + ret= InvokeGeneral( dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, + puArgErr, bHandled); + if( bHandled) + return ret; + + if ((dispidMember > 0) && (o3tl::make_unsigned(dispidMember) <= m_MemberInfos.size()) && m_xInvocation.is()) + { + MemberInfo d = m_MemberInfos[dispidMember - 1]; + DWORD flags = wFlags & d.flags; + + if (flags != 0) + { + if ((flags & DISPATCH_METHOD) != 0) + { + std::unique_ptr<DISPPARAMS> pNewDispParams; + std::vector<VARIANTARG> vNewArgs; + + if (pdispparams->cNamedArgs > 0) + { + // Convert named arguments to positional ones. + + // An example: + // + // Function declaration (in pseudo-code): + // int foo(int A, int B, optional int C, optional int D, optional int E, optional int F, optional int G) + // + // Corresponding parameter numbers (DISPIDs): + // 0 1 2 3 4 5 6 + // + // Actual call: + // foo(10, 20, E:=50, D:=40, F:=60) + // + // That is, A and B are passed positionally, D, E, and F as named arguments, + // and the optional C and G parameters are left out. + // + // Incoming DISPPARAMS: + // cArgs=5, cNamedArgs=3 + // rgvarg: [60, 40, 50, 20, 10] + // rgdispidNamedArgs: [5, 3, 4] + // + // We calculate nLowestNamedArgDispid = 3 and nHighestNamedArgDispid = 5. + // + // Result of conversion, no named args: + // cArgs=6, cNamedArgs=0 + // rgvarg: [60, 50, 40, DISP_E_PARAMNOTFOUND, 20, 10] + + // First find the lowest and highest DISPID of the named arguments. + DISPID nLowestNamedArgDispid = 1000000; + DISPID nHighestNamedArgDispid = -1; + for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i) + { + if (pdispparams->rgdispidNamedArgs[i] < nLowestNamedArgDispid) + nLowestNamedArgDispid = pdispparams->rgdispidNamedArgs[i]; + if (pdispparams->rgdispidNamedArgs[i] > nHighestNamedArgDispid) + nHighestNamedArgDispid = pdispparams->rgdispidNamedArgs[i]; + } + + // Make sure named arguments don't overlap with positional ones. The lowest + // DISPID of the named arguments should be >= the number of positional + // arguments. + if (nLowestNamedArgDispid < static_cast<DISPID>(pdispparams->cArgs - pdispparams->cNamedArgs)) + return DISP_E_NONAMEDARGS; + + // Do the actual conversion. + pNewDispParams.reset(new DISPPARAMS); + vNewArgs.resize(nHighestNamedArgDispid + 1); + pNewDispParams->rgvarg = vNewArgs.data(); + pNewDispParams->rgdispidNamedArgs = nullptr; + pNewDispParams->cArgs = nHighestNamedArgDispid + 1; + pNewDispParams->cNamedArgs = 0; + + // Initialise all parameter slots as missing + for (int i = 0; i < nHighestNamedArgDispid; ++i) + { + pNewDispParams->rgvarg[i].vt = VT_ERROR; + pNewDispParams->rgvarg[i].scode = DISP_E_PARAMNOTFOUND; + } + + // Then set the value of those actually present. + for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i) + pNewDispParams->rgvarg[nHighestNamedArgDispid - pdispparams->rgdispidNamedArgs[i]] = pdispparams->rgvarg[i]; + + const int nFirstUnnamedArg = pdispparams->cNamedArgs + (nLowestNamedArgDispid-(pdispparams->cArgs - pdispparams->cNamedArgs)); + + for (unsigned int i = pdispparams->cNamedArgs; i < pdispparams->cArgs; ++i) + pNewDispParams->rgvarg[nFirstUnnamedArg + (i-pdispparams->cNamedArgs)] = pdispparams->rgvarg[i]; + + pdispparams = pNewDispParams.get(); + } + + Sequence<Any> params; + + convertDispparamsArgs(dispidMember, wFlags, pdispparams , params ); + + ret= doInvoke(pdispparams, pvarResult, + pexcepinfo, puArgErr, d.name, params); + } + else if ((flags & DISPATCH_PROPERTYGET) != 0) + { + ret= doGetProperty( pdispparams, pvarResult, + pexcepinfo, d.name); + } + else if ((flags & DISPATCH_PROPERTYPUT) != 0 || (flags & DISPATCH_PROPERTYPUTREF) != 0) + { + if (pdispparams->cArgs != 1) + ret = DISP_E_BADPARAMCOUNT; + else + { + Sequence<Any> params; + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + if(params.getLength() > 0) + ret= doSetProperty( pdispparams, pvarResult, pexcepinfo, puArgErr, d.name, params); + else + ret = DISP_E_BADVARTYPE; + } + } + } + else + ret= DISP_E_MEMBERNOTFOUND; + } + else + ret = DISP_E_MEMBERNOTFOUND; + } + catch(const BridgeRuntimeError& e) + { + writeExcepinfo(pexcepinfo, e.message); + ret = DISP_E_EXCEPTION; + } + catch(const Exception& e) + { + OUString message= "InterfaceOleWrapper::Invoke : \n" + + e.Message; + writeExcepinfo(pexcepinfo, message); + ret = DISP_E_EXCEPTION; + } + catch(...) + { + writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::Invoke : \nUnexpected exception"); + ret = DISP_E_EXCEPTION; + } + + return ret; +} + +HRESULT InterfaceOleWrapper::doInvoke( DISPPARAMS * pdispparams, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any>& params) +{ + + + HRESULT ret= S_OK; + try + { + Sequence<sal_Int16> outIndex; + Sequence<Any> outParams; + Any returnValue; + + if (pdispparams->cNamedArgs > 0) + return DISP_E_NONAMEDARGS; + + // invoke method and take care of exceptions + returnValue = m_xInvocation->invoke(name, + params, + outIndex, + outParams); + + // try to write back out parameter + if (outIndex.getLength() > 0) + { + const sal_Int16* pOutIndex = outIndex.getConstArray(); + const Any* pOutParams = outParams.getConstArray(); + + for (sal_Int32 i = 0; i < outIndex.getLength(); i++) + { + CComVariant variant; + // Currently a Sequence is converted to an SafeArray of VARIANTs. + anyToVariant( &variant, pOutParams[i]); + + // out parameter need special handling if they are VT_DISPATCH + // and used in JScript + int outindex= pOutIndex[i]; + writeBackOutParameter2(&(pdispparams->rgvarg[pdispparams->cArgs - 1 - outindex]), + &variant ); + } + } + + // write back return value + if (pvarResult != nullptr) + anyToVariant(pvarResult, returnValue); + } + catch(const IllegalArgumentException & e) //XInvocation::invoke + { + writeExcepinfo(pexcepinfo, e.Message); + ret = DISP_E_TYPEMISMATCH; + } + catch(const CannotConvertException & e) //XInvocation::invoke + { + writeExcepinfo(pexcepinfo, e.Message); + ret = mapCannotConvertException( e, puArgErr); + } + catch(const InvocationTargetException & e) //XInvocation::invoke + { + const Any& org = e.TargetException; + Exception excTarget; + org >>= excTarget; + OUString message= + org.getValueType().getTypeName() + ": " + excTarget.Message; + writeExcepinfo(pexcepinfo, message); + ret = DISP_E_EXCEPTION; + } + catch(const NoSuchMethodException & e) //XInvocation::invoke + { + writeExcepinfo(pexcepinfo, e.Message); + ret = DISP_E_MEMBERNOTFOUND; + } + catch(const BridgeRuntimeError & e) + { + writeExcepinfo(pexcepinfo, e.message); + ret = DISP_E_EXCEPTION; + } + catch(const Exception & e) + { + OUString message= "InterfaceOleWrapper::doInvoke : \n" + + e.Message; + writeExcepinfo(pexcepinfo, message); + ret = DISP_E_EXCEPTION; + } + catch( ... ) + { + writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception"); + ret = DISP_E_EXCEPTION; + } + return ret; +} + +HRESULT InterfaceOleWrapper::doGetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, OUString& name) +{ + HRESULT ret= S_OK; + + try + { + Any returnValue = m_xInvocation->getValue( name); + // write back return value + if (pvarResult) + anyToVariant(pvarResult, returnValue); + } + catch(const UnknownPropertyException& e) //XInvocation::getValue + { + writeExcepinfo(pexcepinfo, e.Message); + ret = DISP_E_MEMBERNOTFOUND; + } + catch(const BridgeRuntimeError& e) + { + writeExcepinfo(pexcepinfo, e.message); + ret = DISP_E_EXCEPTION; + } + catch(const Exception& e) + { + OUString message= "InterfaceOleWrapper::doGetProperty : \n" + + e.Message; + writeExcepinfo(pexcepinfo, message); + } + catch( ... ) + { + writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception"); + ret = DISP_E_EXCEPTION; + } + return ret; +} + +HRESULT InterfaceOleWrapper::doSetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/, + EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any> const & params) +{ + HRESULT ret= S_OK; + + try + { + m_xInvocation->setValue( name, params.getConstArray()[0]); + } + catch(const UnknownPropertyException &) + { + ret = DISP_E_MEMBERNOTFOUND; + } + catch(const CannotConvertException &e) + { + ret= mapCannotConvertException( e, puArgErr); + } + catch(const InvocationTargetException &e) + { + if (pexcepinfo != nullptr) + { + Any org = e.TargetException; + + pexcepinfo->wCode = UNO_2_OLE_EXCEPTIONCODE; + pexcepinfo->bstrSource = SysAllocString(L"any ONE component"); + pexcepinfo->bstrDescription = SysAllocString( + o3tl::toW(org.getValueType().getTypeName().getStr())); + } + ret = DISP_E_EXCEPTION; + } + catch( ... ) + { + ret= DISP_E_EXCEPTION; + } + return ret; +} + +namespace { + +class CXEnumVariant : public IEnumVARIANT, + public CComObjectRoot +{ +public: + CXEnumVariant() + : mnIndex(1) // ooo::vba::XCollection index starts at one + { + } + + virtual ~CXEnumVariant() + { + } + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + BEGIN_COM_MAP(CXEnumVariant) +#if defined __clang__ +#pragma clang diagnostic pop +#endif + COM_INTERFACE_ENTRY(IEnumVARIANT) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + DECLARE_NOT_AGGREGATABLE(CXEnumVariant) + + // Creates and initializes the enumerator + void Init(InterfaceOleWrapper* pInterfaceOleWrapper, + const Reference<ooo::vba::XCollection > xCollection) + { + mpInterfaceOleWrapper = pInterfaceOleWrapper; + mxCollection = xCollection; + } + + // IEnumVARIANT + virtual HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **) override + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Clone: E_NOTIMPL"); + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE Next(ULONG const celt, + VARIANT *rgVar, + ULONG *pCeltFetched) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + if (pCeltFetched) + *pCeltFetched = 0; + + if (celt == 0) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_INVALIDARG"); + return E_INVALIDARG; + } + + if (rgVar == nullptr || (celt != 1 && pCeltFetched == nullptr)) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_FAIL"); + return E_FAIL; + } + + for (ULONG i = 0; i < celt; i++) + VariantInit(&rgVar[i]); + + ULONG nLeft = celt; + ULONG nReturned = 0; + while (nLeft > 0) + { + if (mnIndex > mxCollection->getCount()) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): got " << nReturned << ": S_FALSE"); + return S_FALSE; + } + Any aIndex; + aIndex <<= mnIndex; + Any aElement = mxCollection->Item(aIndex, Any()); + mpInterfaceOleWrapper->anyToVariant(rgVar, aElement); + // rgVar->pdispVal->AddRef(); ?? + if (pCeltFetched) + (*pCeltFetched)++; + rgVar++; + nReturned++; + mnIndex++; + nLeft--; + } + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): S_OK"); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Reset() override + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Reset: S_OK"); + mnIndex = 1; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE STDMETHODCALLTYPE Skip(ULONG const celt) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + ULONG nLeft = celt; + ULONG nSkipped = 0; + while (nLeft > 0) + { + if (mnIndex > mxCollection->getCount()) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): skipped " << nSkipped << ": S_FALSE"); + return S_FALSE; + } + mnIndex++; + nLeft--; + } + SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): S_OK"); + return S_OK; + } + +private: + InterfaceOleWrapper* mpInterfaceOleWrapper; + Reference<ooo::vba::XCollection> mxCollection; + sal_Int32 mnIndex; +}; + +class Sink : public cppu::WeakImplHelper<ooo::vba::XSink> +{ +public: + Sink(IUnknown* pUnkSink, + Reference<XMultiServiceFactory> xMSF, + ooo::vba::TypeAndIID aTypeAndIID, + InterfaceOleWrapper* pInterfaceOleWrapper); + + // XSink + void SAL_CALL Call( const OUString& Method, Sequence< Any >& Arguments ) override; + +private: + IUnknown* mpUnkSink; + Reference<XMultiServiceFactory> mxMSF; + ooo::vba::TypeAndIID maTypeAndIID; + InterfaceOleWrapper* mpInterfaceOleWrapper; +}; + +} + +Sink::Sink(IUnknown* pUnkSink, + Reference<XMultiServiceFactory> xMSF, + ooo::vba::TypeAndIID aTypeAndIID, + InterfaceOleWrapper* pInterfaceOleWrapper) : + mpUnkSink(pUnkSink), + mxMSF(xMSF), + maTypeAndIID(aTypeAndIID), + mpInterfaceOleWrapper(pInterfaceOleWrapper) +{ + mpUnkSink->AddRef(); +} + +void SAL_CALL +Sink::Call( const OUString& Method, Sequence< Any >& Arguments ) +{ + SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << ", " << Arguments.getLength() << " arguments)"); + + IDispatch* pDispatch; + HRESULT nResult = mpUnkSink->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&pDispatch)); + if (!SUCCEEDED(nResult)) + { + SAL_WARN("extensions.olebridge", "Sink::Call: Not IDispatch: " << WindowsErrorStringFromHRESULT(nResult)); + return; + } + + Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF)); + assert(xRefl.is()); + + Reference<XIdlClass> xClass = xRefl->forName(maTypeAndIID.Type.getTypeName()); + assert(xClass.is()); + + auto aMethods = xClass->getMethods(); + assert(xClass->getTypeClass() == TypeClass_INTERFACE && + aMethods.getLength() > 0); + + int nMemId = 1; + auto ArgumentsRange = asNonConstRange(Arguments); + // Skip the three XInterface methods + for (int i = 3; i < aMethods.getLength(); i++) + { + if (aMethods[i]->getName() == Method) + { + // FIXME: Handle mismatch in type of actual argument and parameter of the method. + + // FIXME: Handle mismatch in number of arguments passed and actual number of parameters + // of the method. + + auto aParamInfos = aMethods[i]->getParameterInfos(); + + assert(Arguments.getLength() == aParamInfos.getLength()); + + DISPPARAMS aDispParams; + aDispParams.rgdispidNamedArgs = nullptr; + aDispParams.cArgs = Arguments.getLength(); + aDispParams.cNamedArgs = 0; + aDispParams.rgvarg = new VARIANT[aDispParams.cArgs]; + for (unsigned j = 0; j < aDispParams.cArgs; j++) + { + VariantInit(aDispParams.rgvarg+j); + // Note: Reverse order of arguments in Arguments and aDispParams.rgvarg! + const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1; + mpInterfaceOleWrapper->anyToVariant(aDispParams.rgvarg+j, Arguments[nIncomingArgIndex]); + + // Handle OUT and INOUT arguments. For instance, the second ('Cancel') parameter to + // DocumentBeforeClose() should be a VT_BYREF|VT_BOOL parameter. Need to handle that + // here. + + if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT || + aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT) + { + switch (aDispParams.rgvarg[j].vt) + { + case VT_I2: + aDispParams.rgvarg[j].byref = new SHORT(aDispParams.rgvarg[j].iVal); + aDispParams.rgvarg[j].vt |= VT_BYREF; + break; + case VT_I4: + aDispParams.rgvarg[j].byref = new LONG(aDispParams.rgvarg[j].lVal); + aDispParams.rgvarg[j].vt |= VT_BYREF; + break; + case VT_BSTR: + aDispParams.rgvarg[j].byref = new BSTR(aDispParams.rgvarg[j].bstrVal); + aDispParams.rgvarg[j].vt |= VT_BYREF; + break; + case VT_BOOL: + aDispParams.rgvarg[j].byref = new VARIANT_BOOL(aDispParams.rgvarg[j].boolVal); + aDispParams.rgvarg[j].vt |= VT_BYREF; + break; + default: + assert(false && "Not handled yet"); + break; + } + } + } + + VARIANT aVarResult; + VariantInit(&aVarResult); + UINT uArgErr; + + // In the case of a VBScript client, which uses "late binding", calling Invoke on the + // sink it provides will cause a callback to our CXTypeInfo::GetNames for the given + // member id, and in that we will tell it the name of the corresponding method, and the + // client will know what event handler to invoke based on that name. + // + // As the outgoing interfaces used (ooo::vba::word::XApplicationOutgoing and others) are + // totally not stable and not published in any way, there can be no client that would + // have done "compile-time binding" and where the sink would actually be an object with + // a vtbl corresponding to the outgoing interface. Late binding clients that work like + // VBScript is all we support. + SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Calling Invoke(" << nMemId << ")"); + + nResult = pDispatch->Invoke(nMemId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &aDispParams, &aVarResult, nullptr, &uArgErr); + SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Invoke() returned"); + + SAL_WARN_IF(!SUCCEEDED(nResult), "extensions.olebridge", "Call to " << Method << " failed: " << WindowsErrorStringFromHRESULT(nResult)); + + // Undo VT_BYREF magic done above. Copy out parameters back to the Anys in Arguments + for (unsigned j = 0; j < aDispParams.cArgs; j++) + { + const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1; + if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT || + aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT) + { + switch (aDispParams.rgvarg[j].vt) + { + case VT_BYREF|VT_I2: + { + SHORT *pI = static_cast<SHORT*>(aDispParams.rgvarg[j].byref); + ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int16>(*pI); + delete pI; + } + break; + case VT_BYREF|VT_I4: + { + LONG *pL = static_cast<LONG*>(aDispParams.rgvarg[j].byref); + ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int32>(*pL); + delete pL; + } + break; + case VT_BYREF|VT_BSTR: + { + BSTR *pBstr = static_cast<BSTR*>(aDispParams.rgvarg[j].byref); + ArgumentsRange[nIncomingArgIndex] <<= OUString(o3tl::toU(*pBstr)); + // Undo SysAllocString() done in anyToVariant() + SysFreeString(*pBstr); + delete pBstr; + } + break; + case VT_BYREF|VT_BOOL: + { + VARIANT_BOOL *pBool = static_cast<VARIANT_BOOL*>(aDispParams.rgvarg[j].byref); + ArgumentsRange[nIncomingArgIndex] <<= (*pBool != VARIANT_FALSE); + delete pBool; + } + break; + default: + assert(false && "Not handled yet"); + break; + } + } + else + { + switch (aDispParams.rgvarg[j].vt) + { + case VT_BSTR: + // Undo SysAllocString() done in anyToVariant() + SysFreeString(aDispParams.rgvarg[j].bstrVal); + break; + } + } + } + + delete[] aDispParams.rgvarg; + return; + } + nMemId++; + } + SAL_WARN("extensions.olebridge", "Sink::Call: Unknown method '" << Method << "'"); +} + +namespace { + +class CXEnumConnections : public IEnumConnections, + public CComObjectRoot +{ +public: + CXEnumConnections() + { + } + + virtual ~CXEnumConnections() + { + } + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + BEGIN_COM_MAP(CXEnumConnections) +#if defined __clang__ +#pragma clang diagnostic pop +#endif + COM_INTERFACE_ENTRY(IEnumConnections) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + DECLARE_NOT_AGGREGATABLE(CXEnumConnections) + + void Init(std::vector<IUnknown*>& rUnknowns, std::vector<DWORD>& rCookies) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Init"); + SAL_WARN_IF(rUnknowns.size() != rCookies.size(), "extensions.olebridge", "Vectors of different size"); + mvUnknowns = rUnknowns; + mvCookies = rCookies; + mnIndex = 0; + } + + virtual HRESULT STDMETHODCALLTYPE Next(ULONG cConnections, + LPCONNECTDATA rgcd, + ULONG *pcFetched) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + if (!rgcd) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_POINTER"); + return E_POINTER; + } + + if (pcFetched && cConnections != 1) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_INVALIDARG"); + return E_INVALIDARG; + } + + ULONG nFetched = 0; + while (nFetched < cConnections && mnIndex < mvUnknowns.size()) + { + rgcd[nFetched].pUnk = mvUnknowns[mnIndex]; + rgcd[nFetched].pUnk->AddRef(); + rgcd[nFetched].dwCookie = mvCookies[mnIndex]; + ++nFetched; + ++mnIndex; + } + if (nFetched != cConnections) + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_FALSE"); + if (pcFetched) + *pcFetched = nFetched; + return S_FALSE; + } + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_OK"); + if (pcFetched) + *pcFetched = nFetched; + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Skip(ULONG cConnections) override + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Skip(" << cConnections << "): E_NOTIMPL"); + + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE Reset() override + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Reset: E_NOTIMPL"); + + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE Clone(IEnumConnections** /* ppEnum */) override + { + SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Clone: E_NOTIMPL"); + + return E_NOTIMPL; + } + +private: + std::vector<IUnknown*> mvUnknowns; + std::vector<DWORD> mvCookies; + ULONG mnIndex; +}; + +class CXConnectionPoint : public IConnectionPoint, + public CComObjectRoot +{ +public: +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + BEGIN_COM_MAP(CXConnectionPoint) +#if defined __clang__ +#pragma clang diagnostic pop +#endif + COM_INTERFACE_ENTRY(IConnectionPoint) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + DECLARE_NOT_AGGREGATABLE(CXConnectionPoint) + + virtual ~CXConnectionPoint() {} + + void Init(InterfaceOleWrapper* pInterfaceOleWrapper, + Reference<ooo::vba::XConnectionPoint>& xCP, + Reference<XMultiServiceFactory>& xMSF, + ooo::vba::TypeAndIID aTypeAndIID) + { + SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Init for " << pInterfaceOleWrapper->getImplementationName()); + + IUnknown *pUnknown; + if (SUCCEEDED(QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnknown)))) + { + // In case QI for IUnknown returns a different pointer, but nah, it doesn't + SAL_INFO("extensions.olebridge", " (IUnknown@" << pUnknown << ")"); + } + + mpInterfaceOleWrapper = pInterfaceOleWrapper; + mxCP = xCP; + mxMSF = xMSF; + maTypeAndIID = aTypeAndIID; + } + + virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(IID *pIID) override + { + SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface(" << *pIID << "): E_NOTIMPL"); + + // FIXME: Needed? + + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(IConnectionPointContainer **) override + { + SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface: E_NOTIMPL"); + + // FIXME: Needed? + + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE Advise(IUnknown *pUnkSink, + DWORD *pdwCookie) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Advise(" << pUnkSink << ")"); + + if (!pdwCookie) + return E_POINTER; + + Reference<ooo::vba::XSink> xSink(new Sink(pUnkSink, mxMSF, maTypeAndIID, mpInterfaceOleWrapper)); + + mvISinks.push_back(pUnkSink); + *pdwCookie = mvISinks.size(); + + mvCookies.push_back(mxCP->Advise(xSink)); + + mvXSinks.push_back(xSink); + + SAL_INFO("extensions.olebridge", " *pdwCookie: " << *pdwCookie); + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Unadvise(DWORD dwCookie) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Unadvise(" << dwCookie << ")"); + + if (dwCookie == 0 || dwCookie > mvISinks.size()) + return E_POINTER; + + mvISinks[dwCookie-1] = nullptr; + + mxCP->Unadvise(mvCookies[dwCookie-1]); + + mvXSinks[dwCookie-1] = Reference<ooo::vba::XSink>(); + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE EnumConnections(IEnumConnections **ppEnum) override + { + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + HRESULT nResult; + + SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::EnumConnections..."); + + if (!ppEnum) + { + SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: E_POINTER"); + return E_POINTER; + } + + CComObject<CXEnumConnections>* pEnumConnections; + + nResult = CComObject<CXEnumConnections>::CreateInstance(&pEnumConnections); + if (FAILED(nResult)) + { + SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: " << WindowsErrorStringFromHRESULT(nResult)); + return nResult; + } + + pEnumConnections->AddRef(); + + pEnumConnections->Init(mvISinks, mvCookies); + *ppEnum = pEnumConnections; + + SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: S_OK"); + + return S_OK; + } + + InterfaceOleWrapper* mpInterfaceOleWrapper; + std::vector<IUnknown*> mvISinks; + std::vector<Reference<ooo::vba::XSink>> mvXSinks; + std::vector<DWORD> mvCookies; + Reference<XMultiServiceFactory> mxMSF; + Reference<ooo::vba::XConnectionPoint> mxCP; + ooo::vba::TypeAndIID maTypeAndIID; +}; + +} + +HRESULT InterfaceOleWrapper::InvokeGeneral( DISPID dispidMember, unsigned short wFlags, + DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, + unsigned int * /*puArgErr*/, bool& bHandled) +{ + HRESULT ret= S_OK; + try + { +// DISPID_VALUE | The DEFAULT Value is required in JScript when the situation +// is that we put an object into an Array object ( out parameter). We have to return +// IDispatch otherwise the object cannot be accessed from the Script. + if( dispidMember == DISPID_VALUE && (wFlags & DISPATCH_PROPERTYGET) != 0 + && m_defaultValueType != VT_EMPTY && pvarResult != nullptr) + { + // Special case hack: If it is a ScVbaCheckBox, return the boolean value + Reference<ooo::vba::msforms::XCheckBox> xCheckBox(m_xOrigin, UNO_QUERY); + if (xCheckBox.is()) + { + bHandled = true; + Any aValue = xCheckBox->getValue(); + anyToVariant(pvarResult, aValue); + return S_OK; + } + + bHandled= true; + if( m_defaultValueType == VT_DISPATCH) + { + pvarResult->vt= VT_DISPATCH; + pvarResult->pdispVal= this; + AddRef(); + ret= S_OK; + } + } + + // function: _GetValueObject + else if( dispidMember == DISPID_JSCRIPT_VALUE_FUNC) + { + bHandled= true; + if( !pvarResult) + return E_POINTER; + CComObject< JScriptValue>* pValue; + if( SUCCEEDED( CComObject<JScriptValue>::CreateInstance( &pValue))) + { + pValue->AddRef(); + pvarResult->vt= VT_DISPATCH; + pvarResult->pdispVal= CComQIPtr<IDispatch>(pValue->GetUnknown()); + ret= S_OK; + } + else + ret= DISP_E_EXCEPTION; + } + else if( dispidMember == DISPID_GET_STRUCT_FUNC) + { + bHandled= true; + bool bStruct= false; + + + Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(m_smgr)); + // the first parameter is in DISPPARAMS rgvargs contains the name of the struct. + CComVariant arg; + if( pdispparams->cArgs == 1 && SUCCEEDED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])) ) + { + Reference<XIdlClass> classStruct= xRefl->forName(OUString(o3tl::toU(arg.bstrVal))); + if( classStruct.is()) + { + Any anyStruct; + classStruct->createObject( anyStruct); + CComVariant var; + anyToVariant( &var, anyStruct ); + + if( var.vt == VT_DISPATCH) + { + VariantCopy( pvarResult, & var); + bStruct= true; + } + } + } + ret= bStruct ? S_OK : DISP_E_EXCEPTION; + } + else if (dispidMember == DISPID_CREATE_TYPE_FUNC) + { + bHandled= true; + if( !pvarResult) + return E_POINTER; + // the first parameter is in DISPPARAMS rgvargs contains the name of the struct. + CComVariant arg; + if( pdispparams->cArgs != 1) + return DISP_E_BADPARAMCOUNT; + if (FAILED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0]))) + return DISP_E_BADVARTYPE; + + //check if the provided name represents a valid type + Type type; + if (!getType(arg.bstrVal, type)) + { + writeExcepinfo(pexcepinfo, OUString::Concat("[automation bridge] A UNO type with the name ") + + o3tl::toU(arg.bstrVal) + " does not exist!"); + return DISP_E_EXCEPTION; + } + + if (!createUnoTypeWrapper(arg.bstrVal, pvarResult)) + { + writeExcepinfo(pexcepinfo, "[automation bridge] InterfaceOleWrapper::InvokeGeneral\n" + "Could not initialize UnoTypeWrapper object!"); + return DISP_E_EXCEPTION; + } + } + else if (dispidMember == DISPID_NEWENUM) + { + bHandled = true; + if( !pvarResult) + return E_POINTER; + + Reference< ooo::vba::XCollection> xCollection(m_xOrigin, UNO_QUERY); + if (!xCollection.is()) + return DISP_E_MEMBERNOTFOUND; + + CComObject<CXEnumVariant>* pEnumVar; + + ret = CComObject<CXEnumVariant>::CreateInstance(&pEnumVar); + if (FAILED(ret)) + return ret; + + pEnumVar->AddRef(); + + pEnumVar->Init(this, xCollection); + + pvarResult->vt = VT_UNKNOWN; + pvarResult->punkVal = nullptr; + + ret = pEnumVar->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&pvarResult->punkVal)); + if (FAILED(ret)) + { + pEnumVar->Release(); + return ret; + } + } + } + catch(const BridgeRuntimeError & e) + { + writeExcepinfo(pexcepinfo, e.message); + ret = DISP_E_EXCEPTION; + } + catch(const Exception & e) + { + OUString message= "InterfaceOleWrapper::InvokeGeneral : \n" + + e.Message; + writeExcepinfo(pexcepinfo, message); + ret = DISP_E_EXCEPTION; + } + catch( ... ) + { + writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::InvokeGeneral : \nUnexpected exception"); + ret = DISP_E_EXCEPTION; + } + return ret; +} + +STDMETHODIMP InterfaceOleWrapper::GetDispID(BSTR /*bstrName*/, DWORD /*grfdex*/, DISPID __RPC_FAR* /*pid*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::InvokeEx( + /* [in] */ DISPID /*id*/, + /* [in] */ LCID /*lcid*/, + /* [in] */ WORD /*wFlags*/, + /* [in] */ DISPPARAMS __RPC_FAR* /*pdp*/, + /* [out] */ VARIANT __RPC_FAR* /*pvarRes*/, + /* [out] */ EXCEPINFO __RPC_FAR* /*pei*/, + /* [unique][in] */ IServiceProvider __RPC_FAR* /*pspCaller*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::DeleteMemberByName( + /* [in] */ BSTR /*bstr*/, + /* [in] */ DWORD /*grfdex*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::DeleteMemberByDispID(DISPID /*id*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::GetMemberProperties( + /* [in] */ DISPID /*id*/, + /* [in] */ DWORD /*grfdexFetch*/, + /* [out] */ DWORD __RPC_FAR* /*pgrfdex*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::GetMemberName( + /* [in] */ DISPID /*id*/, + /* [out] */ BSTR __RPC_FAR* /*pbstrName*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::GetNextDispID( + /* [in] */ DWORD /*grfdex*/, + /* [in] */ DISPID /*id*/, + /* [out] */ DISPID __RPC_FAR* /*pid*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP InterfaceOleWrapper::GetNameSpaceParent( + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR* /*ppunk*/) +{ + return ResultFromScode(E_NOTIMPL); +} + +// IProvideClassInfo +HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::GetClassInfo ( + /* [out] */ ITypeInfo **ppTI) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetClassInfo"); + + if (!ppTI) + return E_POINTER; + + Reference<ooo::vba::XInterfaceWithIID> xIID(m_xOrigin, UNO_QUERY); + if (!xIID.is()) + return E_NOTIMPL; + + OUString sIID = xIID->getIID(); + IID aIID; + if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID))) + return E_NOTIMPL; + + HRESULT ret; + + CComObject<CXTypeInfo>* pTypeInfo; + + ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo); + if (FAILED(ret)) + return ret; + + pTypeInfo->AddRef(); + + pTypeInfo->InitForCoclass(m_xOrigin, m_sImplementationName, aIID, m_smgr); + + *ppTI = pTypeInfo; + + return S_OK; +} + +// IConnectionPointContainer +HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::EnumConnectionPoints( + /* [out] */ IEnumConnectionPoints **) +{ + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::EnumConnectionPoints"); + return ResultFromScode(E_NOTIMPL); +} + +HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::FindConnectionPoint( + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint **ppCP) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::FindConnectionPoint(" << riid << ")"); + + if (!ppCP) + return E_POINTER; + + Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY); + + // We checked already + assert(xConnectable.is()); + if (!xConnectable.is()) + return E_NOTIMPL; + + ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint(); + + IID aIID; + if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID))) + return E_INVALIDARG; + + if (!IsEqualIID(riid, aIID)) + return E_INVALIDARG; + + Reference<ooo::vba::XConnectionPoint> xCP = xConnectable->FindConnectionPoint(); + if (!xCP.is()) + return E_INVALIDARG; + + HRESULT ret; + + CComObject<CXConnectionPoint>* pConnectionPoint; + + ret = CComObject<CXConnectionPoint>::CreateInstance(&pConnectionPoint); + if (FAILED(ret)) + return ret; + + pConnectionPoint->AddRef(); + + pConnectionPoint->Init(this, xCP, m_smgr, aTypeAndIID); + + *ppCP = pConnectionPoint; + + return S_OK; +} + +// UnoObjectWrapperRemoteOpt --------------------------------------------------- + +UnoObjectWrapperRemoteOpt::UnoObjectWrapperRemoteOpt( Reference<XMultiServiceFactory> const & aFactory, + sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass): +InterfaceOleWrapper( aFactory, unoWrapperClass, comWrapperClass), +m_currentId(1) + +{ +} +UnoObjectWrapperRemoteOpt::~UnoObjectWrapperRemoteOpt() +{ +} + +// UnoConversionUtilities +Reference< XInterface > UnoObjectWrapperRemoteOpt::createUnoWrapperInstance() +{ + Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt( + m_smgr, m_nUnoWrapperClass, m_nComWrapperClass)); + return Reference<XInterface>( xWeak, UNO_QUERY); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoObjectWrapperRemoteOpt::GetIDsOfNames ( REFIID /*riid*/, LPOLESTR * rgszNames, UINT cNames, + LCID /*lcid*/, DISPID * rgdispid ) +{ + MutexGuard guard( getBridgeMutex()); + + if( ! rgdispid) + return E_POINTER; + HRESULT ret = E_UNEXPECTED; + + // _GetValueObject + if( ! wcscmp( *rgszNames, JSCRIPT_VALUE_FUNC)) + { + *rgdispid= DISPID_JSCRIPT_VALUE_FUNC; + return S_OK; + } + else if( ! wcscmp( *rgszNames, GET_STRUCT_FUNC)) + { + *rgdispid= DISPID_GET_STRUCT_FUNC; + return S_OK; + } + + if (m_xInvocation.is() && (cNames > 0)) + { + OUString name(o3tl::toU(rgszNames[0])); + // has this name been determined as "bad" + BadNameMap::iterator badIter= m_badNameMap.find( name); + if( badIter == m_badNameMap.end() ) + { + // name has not been bad before( member exists + typedef NameToIdMap::iterator ITnames; + std::pair< ITnames, bool > pair_id= m_nameToDispIdMap.emplace(name, m_currentId++); + // new ID inserted ? + if( pair_id.second ) + {// yes, now create MemberInfo and ad to IdToMemberInfoMap + MemberInfo d(0, name); + m_idToMemberInfoMap.emplace(m_currentId - 1, d); + } + + *rgdispid = pair_id.first->second; + ret = S_OK; + } + else + ret= DISP_E_UNKNOWNNAME; + } + return ret; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoObjectWrapperRemoteOpt::Invoke ( DISPID dispidMember, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags, + DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, + UINT * puArgErr ) +{ + comphelper::Automation::AutomationInvokedZone aAutomationActive; + + HRESULT ret = S_OK; + try + { + bool bHandled= false; + ret= InvokeGeneral( dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, + puArgErr, bHandled); + if( bHandled) + return ret; + + if ( dispidMember > 0 && m_xInvocation.is()) + { + + IdToMemberInfoMap::iterator it_MemberInfo= m_idToMemberInfoMap.find( dispidMember); + if( it_MemberInfo != m_idToMemberInfoMap.end() ) + { + MemberInfo& info= it_MemberInfo->second; + + Sequence<Any> params; // holds converted any s + if( ! info.flags ) + { // DISPID called for the first time + if( wFlags == DISPATCH_METHOD ) + { + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + + if( FAILED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + OUString exactName; + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName( info.name); + // invoke again + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_METHOD; + } + else if( wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_PROPERTYPUTREF) + { + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + if( FAILED( ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + OUString exactName; + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName( info.name); + // invoke again + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET; + } + else if( wFlags == DISPATCH_PROPERTYGET) + { + if( FAILED( ret= doGetProperty( pdispparams, pvarResult, + pexcepinfo, info.name)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + OUString exactName; + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName( info.name); + // invoke again + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doGetProperty( pdispparams, pvarResult, + pexcepinfo, exactName))) + info.name= exactName; + } + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT; + } + else if( wFlags & DISPATCH_METHOD && + (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF)) + { + + OUString exactName; + // convert params for DISPATCH_METHOD or DISPATCH_PROPERTYPUT + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + // try first as method + if( FAILED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName( info.name); + // invoke again + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_METHOD; + + // try as property + if( FAILED( ret) && pdispparams->cArgs == 1) + { + if( FAILED( ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET; + } + } + else if( wFlags & DISPATCH_METHOD && wFlags & DISPATCH_PROPERTYGET) + { + OUString exactName; + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + + if( FAILED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params)) + && ret == DISP_E_MEMBERNOTFOUND) + { + // try to get the exact name + if (m_xExactName.is()) + { + exactName = m_xExactName->getExactName( info.name); + // invoke again + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_METHOD; + + // try as property + if( FAILED( ret) && pdispparams->cArgs == 1) + { + if( FAILED( ret= doGetProperty( pdispparams, pvarResult, + pexcepinfo, info.name)) + && ret == DISP_E_MEMBERNOTFOUND) + { + if( !exactName.isEmpty() ) + { + if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, exactName, params))) + info.name= exactName; + } + } + if( SUCCEEDED( ret ) ) + info.flags= DISPATCH_PROPERTYGET; + } + } + + // update information about this member + if( ret == DISP_E_MEMBERNOTFOUND) + { + // Remember the name as not existing + // and remove the MemberInfo + m_badNameMap[info.name]= false; + m_idToMemberInfoMap.erase( it_MemberInfo); + } + } // if( ! info.flags ) + else // IdToMemberInfoMap contains a MemberInfo + { + if( wFlags & DISPATCH_METHOD && info.flags == DISPATCH_METHOD) + { + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + ret= doInvoke( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params); + } + else if( (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF ) && + info.flags & DISPATCH_PROPERTYPUT) + { + convertDispparamsArgs(dispidMember, wFlags, pdispparams, params ); + ret= doSetProperty( pdispparams, pvarResult, + pexcepinfo, puArgErr, info.name, params); + } + else if( (wFlags & DISPATCH_PROPERTYGET) && ( info.flags & DISPATCH_PROPERTYGET)) + { + ret= doGetProperty( pdispparams, pvarResult, + pexcepinfo, info.name); + } + else + { + ret= DISP_E_MEMBERNOTFOUND; + } + } + }// if( it_MemberInfo != m_idToMemberInfoMap.end() ) + else + ret= DISP_E_MEMBERNOTFOUND; + } + } + catch(const BridgeRuntimeError& e) + { + writeExcepinfo(pexcepinfo, e.message); + ret = DISP_E_EXCEPTION; + } + catch(const Exception& e) + { + OUString message= "UnoObjectWrapperRemoteOpt::Invoke : \n" + + e.Message; + writeExcepinfo(pexcepinfo, message); + ret = DISP_E_EXCEPTION; + } + catch(...) + { + writeExcepinfo(pexcepinfo, "UnoObjectWrapperRemoteOpt::Invoke : \nUnexpected exception"); + ret = DISP_E_EXCEPTION; + } + + return ret; +} + +HRESULT UnoObjectWrapperRemoteOpt::methodInvoke( DISPID /*dispidMember*/, DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/, + EXCEPINFO * /*pexcepinfo*/, unsigned int * /*puArgErr*/, Sequence<Any> const &) +{ + return S_OK; +} + +// The returned HRESULT is only appropriate for IDispatch::Invoke +static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr) +{ + HRESULT ret; + bool bWriteIndex= true; + + switch ( e.Reason) + { + case FailReason::OUT_OF_RANGE: + ret = DISP_E_OVERFLOW; + break; + case FailReason::IS_NOT_NUMBER: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::IS_NOT_ENUM: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::IS_NOT_BOOL: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::NO_SUCH_INTERFACE: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::SOURCE_IS_NO_DERIVED_TYPE: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::TYPE_NOT_SUPPORTED: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::INVALID: + ret = DISP_E_TYPEMISMATCH; + break; + case FailReason::NO_DEFAULT_AVAILABLE: + ret = DISP_E_BADPARAMCOUNT; + break; + case FailReason::UNKNOWN: + ret = E_UNEXPECTED; + break; + default: + ret = E_UNEXPECTED; + bWriteIndex= false; + break; + } + + if( bWriteIndex && puArgErr != nullptr) + *puArgErr = e.ArgumentIndex; + return ret; +} + +// The function maps the TypeClass of the any to VARTYPE: If +// the Any contains STRUCT or INTERFACE then the return value +// is VT_DISPATCH. The function is used from o2u_createUnoObjectWrapper +// and the result is put into the constructor of the uno - wrapper +// object. If a client asks the object for DISPID_VALUE and this +// function returned VT_DISPATCH then the IDispatch of the same +// object is being returned. +// See InterfaceOleWrapper::Invoke, InterfaceOleWrapper::m_defaultValueType +VARTYPE getVarType( const Any& value) +{ + VARTYPE ret= VT_EMPTY; + + switch ( value.getValueTypeClass()) + { + case TypeClass_STRUCT: ret= VT_DISPATCH; break; + case TypeClass_INTERFACE: ret= VT_DISPATCH; break; + default: break; + } + return ret; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/unoobjw.hxx b/extensions/source/ole/unoobjw.hxx new file mode 100644 index 0000000000..845724d819 --- /dev/null +++ b/extensions/source/ole/unoobjw.hxx @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/bridge/XBridgeSupplier2.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/script/InvocationInfo.hpp> +#include <salhelper/simplereferenceobject.hxx> +#include <cppuhelper/implbase.hxx> + +#include "comifaces.hxx" +#include "ole2uno.hxx" +#include "unoconversionutilities.hxx" + +#define JSCRIPT_VALUE_FUNC L"_GetValueObject" +#define GET_STRUCT_FUNC L"_GetStruct" +#define BRIDGE_VALUE_FUNC L"Bridge_GetValueObject" +#define BRIDGE_GET_STRUCT_FUNC L"Bridge_GetStruct" +#define BRIDGE_CREATE_TYPE_FUNC L"Bridge_CreateType" + +#define DISPID_JSCRIPT_VALUE_FUNC -10l +#define DISPID_GET_STRUCT_FUNC -102 +#define DISPID_CREATE_TYPE_FUNC -103 + +using namespace cppu; +using namespace com::sun::star::bridge; +using namespace com::sun::star::script; + +struct MemberInfo +{ + MemberInfo() : flags(0), name() {} + MemberInfo(WORD f, const OUString& n) : flags(f), name(n) {} + + WORD flags; + OUString name; +}; + +typedef std::unordered_map +< + OUString, + DISPID +> NameToIdMap; + +typedef std::unordered_map +< + OUString, + bool +> BadNameMap; + +typedef std::unordered_map +< + DISPID, + MemberInfo +> IdToMemberInfoMap; + +// An InterfaceOleWrapper object can wrap either a UNO struct or a UNO +// interface as a COM IDispatchEx and IUnoObjectWrapper. + +class InterfaceOleWrapper : public WeakImplHelper<XBridgeSupplier2, XInitialization>, + public IDispatchEx, + public IProvideClassInfo, + public IConnectionPointContainer, + public UnoConversionUtilities<InterfaceOleWrapper>, + public IUnoObjectWrapper +{ +public: + InterfaceOleWrapper(Reference<XMultiServiceFactory> const & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass); + ~InterfaceOleWrapper() override; + + // IUnknown + STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + // IDispatch + STDMETHOD( GetTypeInfoCount )( UINT * pctinfo ) override; + STDMETHOD( GetTypeInfo )( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo ) override; + STDMETHOD( GetIDsOfNames )( REFIID riid, LPOLESTR * rgszNames, UINT cNames, + LCID lcid, DISPID * rgdispid ) override; + STDMETHOD( Invoke )( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, + UINT * puArgErr ) override; + + // IDispatchEx + virtual HRESULT STDMETHODCALLTYPE GetDispID( + /* [in] */ BSTR bstrName, + /* [in] */ DWORD grfdex, + /* [out] */ DISPID __RPC_FAR *pid) override; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE InvokeEx( + /* [in] */ DISPID id, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [in] */ DISPPARAMS __RPC_FAR *pdp, + /* [out] */ VARIANT __RPC_FAR *pvarRes, + /* [out] */ EXCEPINFO __RPC_FAR *pei, + /* [unique][in] */ IServiceProvider __RPC_FAR *pspCaller) override; + + virtual HRESULT STDMETHODCALLTYPE DeleteMemberByName( + /* [in] */ BSTR bstr, + /* [in] */ DWORD grfdex) override; + + virtual HRESULT STDMETHODCALLTYPE DeleteMemberByDispID( + /* [in] */ DISPID id) override; + + virtual HRESULT STDMETHODCALLTYPE GetMemberProperties( + /* [in] */ DISPID id, + /* [in] */ DWORD grfdexFetch, + /* [out] */ DWORD __RPC_FAR *pgrfdex) override; + + virtual HRESULT STDMETHODCALLTYPE GetMemberName( + /* [in] */ DISPID id, + /* [out] */ BSTR __RPC_FAR *pbstrName) override; + + virtual HRESULT STDMETHODCALLTYPE GetNextDispID( + /* [in] */ DWORD grfdex, + /* [in] */ DISPID id, + /* [out] */ DISPID __RPC_FAR *pid) override; + + virtual HRESULT STDMETHODCALLTYPE GetNameSpaceParent( + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk) override; + + // IProvideClassInfo + virtual HRESULT STDMETHODCALLTYPE GetClassInfo( + /* [out] */ ITypeInfo **ppTI) override; + + // IConnectionPointContainer + virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints( + /* [out] */ IEnumConnectionPoints **ppEnum) override; + virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint( + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint **ppCP) override; + + // XBridgeSupplier2 + virtual Any SAL_CALL createBridge(const Any& modelDepObject, + const Sequence<sal_Int8>& ProcessId, + sal_Int16 sourceModelType, + sal_Int16 destModelType) override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // IUnoObjectWrapper + STDMETHOD( getWrapperXInterface)( Reference<XInterface>* pXInt) override; + STDMETHOD( getOriginalUnoObject)( Reference<XInterface>* pXInt) override; + STDMETHOD( getOriginalUnoStruct)( Any * pStruct) override; + + // UnoConversionUtility + virtual Reference< XInterface > createUnoWrapperInstance() override; + virtual Reference< XInterface > createComWrapperInstance() override; + + const OUString& getImplementationName() const + { + return m_sImplementationName; + } + +protected: + virtual HRESULT doInvoke( DISPPARAMS * pdispparams, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString & name, Sequence<Any>& params); + + virtual HRESULT doGetProperty( DISPPARAMS * pdispparams, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, OUString & name ); + + virtual HRESULT doSetProperty( DISPPARAMS * pdispparams, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString & name, Sequence<Any> const & params); + + virtual HRESULT InvokeGeneral( DISPID dispidMember, unsigned short wFlags, + DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, + unsigned int * puArgErr, bool& bHandled); + + void convertDispparamsArgs( DISPID id, unsigned short wFlags, DISPPARAMS* pdispparams, + Sequence<Any>& rSeq); + + bool getInvocationInfoForCall(DISPID id, InvocationInfo& info); + + Reference<XInvocation> m_xInvocation; + Reference<XExactName> m_xExactName; + Reference<XInterface> m_xOrigin; + NameToIdMap m_nameToDispIdMap; + std::vector<MemberInfo> m_MemberInfos; + // This member is used to determine the default value + // denoted by DISPID_VALUE (0). For proper results in JavaScript + // we have to return the default value when we write an object + // as out parameter. That is, we get a JScript Array as parameter + // and put a wrapped object on index null. The array object tries + // to detect the default value. The wrapped object must then return + // its own IDispatch* otherwise we cannot access it within the script. + // see InterfaceOleWrapper::Invoke + VARTYPE m_defaultValueType; + + // The name of the implementation. Can be empty if unknown. + OUString m_sImplementationName; +}; + +/***************************************************************************** + + UnoObjectWrapperRemoteOpt = Uno Object Wrapper Remote Optimized + + This is the UNO wrapper used in the service com.sun.star.bridge.OleBridgeSupplierVar1. + Key features: + DISPIDs are passed out blindly. That is in GetIDsOfNames is no name checking carried out. + Only if Invoke fails the name is being checked. Moreover Invoke tries to figure out + if a call is made to a property or method if the flags are DISPATCH_METHOD | DISPATCH_PROPERTYPUT. + If something has been found out about a property or member than it is saved + in a MemberInfo structure hold by an IdToMemberInfoMap stl map. + +*****************************************************************************/ +class UnoObjectWrapperRemoteOpt: public InterfaceOleWrapper +{ +public: + UnoObjectWrapperRemoteOpt( Reference<XMultiServiceFactory> const & aFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass); + ~UnoObjectWrapperRemoteOpt() override; + + STDMETHOD( GetIDsOfNames )( REFIID riid, LPOLESTR * rgszNames, UINT cNames, + LCID lcid, DISPID * rgdispid ) override; + STDMETHOD( Invoke )( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, + UINT * puArgErr ) override; + + // UnoConversionUtility + // If UNO interfaces are converted in methods of this class then + // they are always wrapped with instances of this class + virtual Reference< XInterface > createUnoWrapperInstance() override; + +protected: + + static HRESULT methodInvoke( DISPID dispidMember, DISPPARAMS * pdispparams, VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, unsigned int * puArgErr, Sequence<Any> const & params); + // In GetIDsOfNames are blindly passed out, that is without verifying + // the name. If two names are passed in during different calls to + // GetIDsOfNames and the names differ only in their cases then different + // id's are passed out ( e.g. "doSomethingMethod" or "dosomethingmethod"). + // In Invoke the DISPID is remapped to the name passed to GetIDsOfNames + // and the name is used as parameter for XInvocation::invoke. If invoke + // fails because of a wrong name, then m_xExactName ( XExactName) is used + // to verify the name. The correct name is then inserted to m_MemberInfos + // ( vector<MemberInfo> ). During the next call to Invoke the right name + // is used. . + + + BadNameMap m_badNameMap; + + IdToMemberInfoMap m_idToMemberInfoMap; + + DISPID m_currentId; + + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/unotypewrapper.cxx b/extensions/source/ole/unotypewrapper.cxx new file mode 100644 index 0000000000..2824e8ef7b --- /dev/null +++ b/extensions/source/ole/unotypewrapper.cxx @@ -0,0 +1,160 @@ +/* -*- 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 "unotypewrapper.hxx" +#include <rtl/ustring.hxx> +#include <osl/diagnose.h> +#include <o3tl/char16_t2wchar_t.hxx> + +bool createUnoTypeWrapper(BSTR sTypeName, VARIANT * pVar) +{ + bool ret = false; + OSL_ASSERT(sTypeName && pVar); + CComObject< UnoTypeWrapper>* pObj; + VariantClear(pVar); + if( SUCCEEDED( CComObject<UnoTypeWrapper>::CreateInstance( &pObj))) + { + pObj->AddRef(); + pVar->vt= VT_DISPATCH; + pVar->pdispVal= CComQIPtr<IDispatch>(pObj->GetUnknown()); + //now set the value, e.i. the name of the type + CComQIPtr<IUnoTypeWrapper> spType(pVar->pdispVal); + OSL_ASSERT(spType); + if (SUCCEEDED(spType->put_Name(sTypeName))) + { + ret = true; + } + } + return ret; +} + + +bool createUnoTypeWrapper(const OUString& sTypeName, VARIANT * pVar) +{ + CComBSTR bstr(o3tl::toW(sTypeName.getStr())); + return createUnoTypeWrapper(bstr, pVar); +} + +UnoTypeWrapper::UnoTypeWrapper() +{ +} + +UnoTypeWrapper::~UnoTypeWrapper() +{ +} + + +// UnoTypeWrapper, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetTypeInfoCount(UINT* /*pctinfo*/) +{ + return E_NOTIMPL; +} + +// UnoTypeWrapper, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetTypeInfo( UINT /*iTInfo*/, + LCID /*lcid*/, + ITypeInfo** /*ppTInfo*/) +{ + return E_NOTIMPL; +} + +// UnoTypeWrapper, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetIDsOfNames( REFIID /*riid*/, + LPOLESTR *rgszNames, + UINT /*cNames*/, + LCID /*lcid*/, + DISPID *rgDispId) +{ + if( !rgDispId) + return E_POINTER; + + HRESULT ret= S_OK; + CComBSTR name(*rgszNames); + name.ToLower(); + + if( name == CComBSTR( L"name") ) + *rgDispId= DISPID_VALUE; + else + ret= DISP_E_UNKNOWNNAME; + + return ret; +} + +// UnoTypeWrapper, IDispatch -------------------------------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::Invoke( DISPID dispIdMember, + REFIID /*riid*/, + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO* /*pExcepInfo*/, + UINT* /*puArgErr*/) +{ + if (pDispParams == nullptr) + return DISP_E_EXCEPTION; + + if( pDispParams->cNamedArgs) + return DISP_E_NONAMEDARGS; + + + HRESULT ret= S_OK; + switch( dispIdMember) + { + case DISPID_VALUE: // DISPID_VALUE + if (wFlags & DISPATCH_PROPERTYGET) + { + if (pVarResult == nullptr) + { + ret = E_POINTER; + break; + } + get_Name( & pVarResult->bstrVal); + pVarResult->vt = VT_BSTR; + } + break; + default: + ret= DISP_E_MEMBERNOTFOUND; + break; + } + + return ret; +} + +// IUnoTypeWrapper----------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::put_Name(BSTR val) +{ + Lock(); + m_sName = val; + Unlock(); + return S_OK; +} + +// (UnoTypeWrapper----------------------- +COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::get_Name(BSTR *pVal) +{ + Lock(); + if( !pVal) + return E_POINTER; + *pVal = m_sName.Copy(); + Unlock(); + return S_OK; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/unotypewrapper.hxx b/extensions/source/ole/unotypewrapper.hxx new file mode 100644 index 0000000000..a7515448c9 --- /dev/null +++ b/extensions/source/ole/unotypewrapper.hxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "wincrap.hxx" + +#include "comifaces.hxx" + +/* creates a UnoTypWrapper and sets the Name property to the value + specified by sTypeName. + Returns true if the object could be created and initialized. + */ +bool createUnoTypeWrapper(BSTR sTypeName, VARIANT * pVariant); +bool createUnoTypeWrapper(const OUString& sTypeName, VARIANT * pVar); + +class UnoTypeWrapper: + public CComObjectRootEx<CComMultiThreadModel>, + public IUnoTypeWrapper, + public IDispatch +{ +public: + UnoTypeWrapper(); + virtual ~UnoTypeWrapper(); + + BEGIN_COM_MAP(UnoTypeWrapper) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IUnoTypeWrapper) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif + END_COM_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + + // IDispatch ------------------------------------------- + STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override; + + STDMETHOD( GetTypeInfo)( UINT iTInfo, + LCID lcid, + ITypeInfo **ppTInfo) override; + + STDMETHOD( GetIDsOfNames)( REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId) override; + + STDMETHOD( Invoke)( DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) override; + // IUnoTypeWrapper -------------------------------------- + STDMETHOD(put_Name)(BSTR val) override; + STDMETHOD(get_Name)(BSTR* pVal) override; + + CComBSTR m_sName; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/wincrap.hxx b/extensions/source/ole/wincrap.hxx new file mode 100644 index 0000000000..3c8417cf92 --- /dev/null +++ b/extensions/source/ole/wincrap.hxx @@ -0,0 +1,63 @@ +/* -*- 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 + +/* wrap all includes that need to be wrapped by prewin.h/postwin.h here */ + +#define STRICT + +#define _WIN32_DCOM +#if OSL_DEBUG_LEVEL > 0 +//#define _ATL_DEBUG_INTERFACES +#endif + +#include <dispex.h> + +#include <prewin.h> +#include <list> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wattributes" +#pragma clang diagnostic ignored "-Wdelete-incomplete" +#pragma clang diagnostic ignored "-Wextra" +#pragma clang diagnostic ignored "-Wint-to-pointer-cast" +#pragma clang diagnostic ignored "-Winvalid-noreturn" +#pragma clang diagnostic ignored "-Wmicrosoft" +#pragma clang diagnostic ignored "-Wnon-pod-varargs" +#endif + +// from oleobjw.hxx +#include <atlbase.h> +// from jscriptclasses.hxx +extern CComModule _Module; +#include <atlcom.h> + +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +// from unoobjw.cxx +#include <olectl.h> + +#include <postwin.h> + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/ole/windata.hxx b/extensions/source/ole/windata.hxx new file mode 100644 index 0000000000..d9c4cb3143 --- /dev/null +++ b/extensions/source/ole/windata.hxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <oleidl.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wextra" +#pragma clang diagnostic ignored "-Wignored-attributes" +#pragma clang diagnostic ignored "-Wint-to-pointer-cast" +#pragma clang diagnostic ignored "-Winvalid-noreturn" +#pragma clang diagnostic ignored "-Wmicrosoft" +#pragma clang diagnostic ignored "-Wnon-pod-varargs" +#endif + +#include <atlbase.h> + +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <osl/diagnose.h> + +//Wrapper for VARDESC +class VarDesc +{ + VARDESC* operator = (const VarDesc*); + VarDesc(const VarDesc&); +// Construction +public: + CComPtr< ITypeInfo > m_pTypeInfo; + VARDESC* m_pVarDesc; + + explicit VarDesc(ITypeInfo* pTypeInfo) : + m_pTypeInfo(pTypeInfo), + m_pVarDesc(nullptr) + { + OSL_ASSERT(pTypeInfo); + } + ~VarDesc() + { + if (m_pVarDesc != nullptr) + { + m_pTypeInfo->ReleaseVarDesc(m_pVarDesc); + } + } + + VARDESC* operator->() + { + return m_pVarDesc; + } + + VARDESC** operator&() + { + return &m_pVarDesc; + } + + operator VARDESC* () + { + return m_pVarDesc; + } +}; + +//Wrapper for FUNCDESC structure +class FuncDesc +{ + FUNCDESC* operator = (const FuncDesc &); + FuncDesc(const FuncDesc&); + CComPtr<ITypeInfo> m_pTypeInfo; + FUNCDESC * m_pFuncDesc; + +public: + + explicit FuncDesc(ITypeInfo * pTypeInfo) : + m_pTypeInfo(pTypeInfo), + m_pFuncDesc(nullptr) + { + OSL_ASSERT(pTypeInfo); + } + ~FuncDesc() + { + ReleaseFUNCDESC(); + } + + FUNCDESC* operator -> () + { + return m_pFuncDesc; + } + + FUNCDESC** operator & () + { + return & m_pFuncDesc; + } + + operator FUNCDESC* () + { + return m_pFuncDesc; + } + + FUNCDESC* operator = (FUNCDESC* pDesc) + { + ReleaseFUNCDESC(); + m_pFuncDesc = pDesc; + return m_pFuncDesc; + } + FUNCDESC* Detach() + { + FUNCDESC* pDesc = m_pFuncDesc; + m_pFuncDesc = nullptr; + return pDesc; + } + + void ReleaseFUNCDESC() + { + if (m_pFuncDesc != nullptr) + { + m_pTypeInfo->ReleaseFuncDesc(m_pFuncDesc); + } + m_pFuncDesc = nullptr; + } +}; +//Wrapper for EXCEPINFO structure +class ExcepInfo : public EXCEPINFO +{ + EXCEPINFO* operator = (const ExcepInfo& ); + ExcepInfo(const ExcepInfo &); +public: + ExcepInfo() + : EXCEPINFO{} + { + } + ~ExcepInfo() + { + if (bstrSource != nullptr) + ::SysFreeString(bstrSource); + if (bstrDescription != nullptr) + ::SysFreeString(bstrDescription); + if (bstrHelpFile != nullptr) + ::SysFreeString(bstrHelpFile); + } +}; + +//Wrapper for TYPEATTR +class TypeAttr +{ + TYPEATTR* operator = (const TypeAttr &); + TypeAttr(const TypeAttr &); +public: + CComPtr< ITypeInfo > m_pTypeInfo; + TYPEATTR* m_pTypeAttr; + + explicit TypeAttr(ITypeInfo* pTypeInfo) : + m_pTypeInfo( pTypeInfo ), + m_pTypeAttr( nullptr ) + { + OSL_ASSERT(pTypeInfo); + } + ~TypeAttr() noexcept + { + if (m_pTypeAttr != nullptr) + { + m_pTypeInfo->ReleaseTypeAttr(m_pTypeAttr); + } + } + + TYPEATTR** operator&() noexcept + { + return &m_pTypeAttr; + } + + TYPEATTR* operator->() noexcept + { + return m_pTypeAttr; + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |