diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /cli_ure/source/uno_bridge | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'cli_ure/source/uno_bridge')
-rw-r--r-- | cli_ure/source/uno_bridge/README.txt | 20 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_base.h | 171 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_bridge.cxx | 321 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_bridge.h | 113 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_data.cxx | 1929 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_environment.cxx | 168 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_environment.h | 105 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_proxy.cxx | 1105 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_proxy.h | 294 | ||||
-rw-r--r-- | cli_ure/source/uno_bridge/cli_uno.cxx | 277 |
10 files changed, 4503 insertions, 0 deletions
diff --git a/cli_ure/source/uno_bridge/README.txt b/cli_ure/source/uno_bridge/README.txt new file mode 100644 index 000000000..39b3ce364 --- /dev/null +++ b/cli_ure/source/uno_bridge/README.txt @@ -0,0 +1,20 @@ +Because of the LoaderLock bug in .NET Framework 1.0 and 1.1 the cli_uno.dll is linked +with the /NOENTRY switch, which prevent that the C-runtime is initialized when loading +the dll. + +Also I removed all static c++ objects which need construction by the CRT, +exception handling seems to need an initialised CRT. Therefore +I added CRT initialization code in uno_initEnvironment (cli_bridge.cxx) +However there is no deinitialization done because bridge libraries remain +in memory until the process dies. There is actually no good place where +this had to be called. If we would do that we would have to implement that +the bridge can be disposed. + + +Sell also: + +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vcconmixeddllloadingproblem.asp +http://support.microsoft.com/?id=814472 +http://www.ddj.com/dept/windows/184416689 +http://blogs.msdn.com/cbrumme/archive/2003/08/20/51504.aspx +http://msdn2.microsoft.com/en-US/library/ms172219.aspx
\ No newline at end of file diff --git a/cli_ure/source/uno_bridge/cli_base.h b/cli_ure/source/uno_bridge/cli_base.h new file mode 100644 index 000000000..bca780f7c --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_base.h @@ -0,0 +1,171 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BASE_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BASE_H + +#pragma unmanaged +// Workaround: osl/mutex.h contains only a forward declaration of _oslMutexImpls. +// When using the inline class in Mutex in osl/mutex.hxx, the loader needs to find +// a declaration for the struct. If not found a TypeLoadException is being thrown. +struct _oslMutexImpl +{ +}; +#pragma managed +#include <memory> +#include "rtl/ustring.hxx" +#include "typelib/typedescription.hxx" + +#using <system.dll> + +namespace cli_uno +{ +System::Type^ loadCliType(System::String ^ typeName); +System::Type^ mapUnoType(typelib_TypeDescription const * pTD); +System::Type^ mapUnoType(typelib_TypeDescriptionReference const * pTD); +typelib_TypeDescriptionReference* mapCliType(System::Type^ cliType); +OUString mapCliString(System::String ^ data); +System::String^ mapUnoString(rtl_uString const * data); +System::String^ mapUnoTypeName(rtl_uString const * typeName); + +ref struct Constants +{ + static System::String^ sXInterfaceName= gcnew System::String( + "unoidl.com.sun.star.uno.XInterface"); + static System::String^ sObject= gcnew System::String("System.Object"); + static System::String^ sType= gcnew System::String("System.Type"); + static System::String^ sUnoidl= gcnew System::String("unoidl."); + static System::String^ sVoid= gcnew System::String("System.Void"); + static System::String^ sAny= gcnew System::String("uno.Any"); + static System::String^ sArArray= gcnew System::String("System.Array[]"); + static System::String^ sBoolean= gcnew System::String("System.Boolean"); + static System::String^ sChar= gcnew System::String("System.Char"); + static System::String^ sByte= gcnew System::String("System.Byte"); + static System::String^ sInt16= gcnew System::String("System.Int16"); + static System::String^ sUInt16= gcnew System::String("System.UInt16"); + static System::String^ sInt32= gcnew System::String("System.Int32"); + static System::String^ sUInt32= gcnew System::String("System.UInt32"); + static System::String^ sInt64= gcnew System::String("System.Int64"); + static System::String^ sUInt64= gcnew System::String("System.UInt64"); + static System::String^ sString= gcnew System::String("System.String"); + static System::String^ sSingle= gcnew System::String("System.Single"); + static System::String^ sDouble= gcnew System::String("System.Double"); + static System::String^ sArBoolean= gcnew System::String("System.Boolean[]"); + static System::String^ sArChar= gcnew System::String("System.Char[]"); + static System::String^ sArByte= gcnew System::String("System.Byte[]"); + static System::String^ sArInt16= gcnew System::String("System.Int16[]"); + static System::String^ sArUInt16= gcnew System::String("System.UInt16[]"); + static System::String^ sArInt32= gcnew System::String("System.Int32[]"); + static System::String^ sArUInt32= gcnew System::String("System.UInt32[]"); + static System::String^ sArInt64= gcnew System::String("System.Int64[]"); + static System::String^ sArUInt64= gcnew System::String("System.UInt64[]"); + static System::String^ sArString= gcnew System::String("System.String[]"); + static System::String^ sArSingle= gcnew System::String("System.Single[]"); + static System::String^ sArDouble= gcnew System::String("System.Double[]"); + static System::String^ sArType= gcnew System::String("System.Type[]"); + static System::String^ sArObject= gcnew System::String("System.Object[]"); + static System::String^ sBrackets= gcnew System::String("[]"); + static System::String^ sAttributeSet= gcnew System::String("set_"); + static System::String^ sAttributeGet= gcnew System::String("get_"); + + static System::String^ usXInterface = "com.sun.star.uno.XInterface"; + static System::String^ usVoid = "void"; + static System::String^ usType = "type"; + static System::String^ usAny = "any"; + static System::String^ usBrackets = "[]"; + static System::String^ usBool = "boolean"; + static System::String^ usByte = "byte"; + static System::String^ usChar = "char"; + static System::String^ usShort = "short"; + static System::String^ usUShort = "unsigned short"; + static System::String^ usLong = "long"; + static System::String^ usULong = "unsigned long"; + static System::String^ usHyper = "hyper"; + static System::String^ usUHyper = "unsigned hyper"; + static System::String^ usString = "string"; + static System::String^ usFloat = "float"; + static System::String^ usDouble = "double"; +}; + +struct BridgeRuntimeError +{ + OUString m_message; + + inline BridgeRuntimeError( OUString const & message ) + : m_message( message ) + {} +}; + + +struct rtl_mem +{ + inline static void * operator new ( size_t nSize ) + { return std::malloc( nSize ); } + inline static void operator delete ( void * mem ) + { std::free( mem ); } + inline static void * operator new ( size_t, void * mem ) + { return mem; } + inline static void operator delete ( void *, void * ) + {} + + static inline std::unique_ptr< rtl_mem > allocate( std::size_t bytes ); +}; + +inline std::unique_ptr< rtl_mem > rtl_mem::allocate( std::size_t bytes ) +{ + void * p = std::malloc( bytes ); + if (0 == p) + throw BridgeRuntimeError("out of memory!" ); + return std::unique_ptr< rtl_mem >( (rtl_mem *)p ); +} + + +class TypeDescr +{ + typelib_TypeDescription * m_td; + + TypeDescr( TypeDescr const & ) = delete; + TypeDescr& operator = ( TypeDescr const & ) = delete; + +public: + inline explicit TypeDescr( typelib_TypeDescriptionReference * td_ref ); + inline ~TypeDescr() + { TYPELIB_DANGER_RELEASE( m_td ); } + + inline typelib_TypeDescription * get() const + { return m_td; } +}; + +inline TypeDescr::TypeDescr( typelib_TypeDescriptionReference * td_ref ) + : m_td( 0 ) +{ + TYPELIB_DANGER_GET( &m_td, td_ref ); + if (0 == m_td) + { + throw BridgeRuntimeError( + "cannot get comprehensive type description for " + + OUString::unacquired(&td_ref->pTypeName) ); + } +} + + +} //end namespace cli_uno + #endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_bridge.cxx b/cli_ure/source/uno_bridge/cli_bridge.cxx new file mode 100644 index 000000000..d9b4cdd0b --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_bridge.cxx @@ -0,0 +1,321 @@ +/* -*- 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 <vcclr.h> +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include "uno/environment.hxx" +#include "uno/lbnames.h" +#include "uno/mapping.hxx" +#include "typelib/typedescription.hxx" +#include "rtl/ustring.hxx" +#include <sal/log.hxx> + +#include "cli_bridge.h" +#include "cli_proxy.h" + +namespace sri= System::Runtime::InteropServices; + +namespace cli_uno +{ + +extern "C" +{ +void SAL_CALL Mapping_acquire( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping const * that = static_cast< Mapping const * >( mapping ); + that->m_bridge->acquire(); +} + +void SAL_CALL Mapping_release( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping const * that = static_cast< Mapping const * >( mapping ); + that->m_bridge->release(); +} + + +void SAL_CALL Mapping_cli2uno( + uno_Mapping * mapping, void ** ppOut, + void * pIn, typelib_InterfaceTypeDescription * td ) + SAL_THROW_EXTERN_C() +{ + uno_Interface ** ppUnoI = (uno_Interface **)ppOut; + intptr_t cliI = (intptr_t)pIn; + + OSL_ENSURE( ppUnoI && td, "### null ptr!" ); + + if (0 != *ppUnoI) + { + uno_Interface * pUnoI = *(uno_Interface **)ppUnoI; + (*pUnoI->release)( pUnoI ); + *ppUnoI = 0; + } + try + { + Mapping const * that = static_cast< Mapping const * >( mapping ); + Bridge * bridge = that->m_bridge; + + if (0 != cliI) + { + System::Object^ cliObj= sri::GCHandle::FromIntPtr(IntPtr(cliI)).Target; + (*ppOut)= bridge->map_cli2uno(cliObj, (typelib_TypeDescription*) td); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } +} + +void SAL_CALL Mapping_uno2cli( + uno_Mapping * mapping, void ** ppOut, + void * pIn, typelib_InterfaceTypeDescription * td ) + SAL_THROW_EXTERN_C() +{ + try + { + OSL_ENSURE( td && ppOut, "### null ptr!" ); + OSL_ENSURE( (sizeof(System::Char) == sizeof(sal_Unicode)) + && (sizeof(System::Boolean) == sizeof(sal_Bool)) + && (sizeof(System::SByte) == sizeof(sal_Int8)) + && (sizeof(System::Int16) == sizeof(sal_Int16)) + && (sizeof(System::UInt16) == sizeof(sal_uInt16)) + && (sizeof(System::Int32) == sizeof(sal_Int32)) + && (sizeof(System::UInt32) == sizeof(sal_uInt32)) + && (sizeof(System::Int64) == sizeof(sal_Int64)) + && (sizeof(System::UInt64) == sizeof(sal_uInt64)) + && (sizeof(System::Single) == sizeof(float)) + && (sizeof(System::Double) == sizeof(double)), + "[cli_uno bridge] incompatible .NET data types"); + intptr_t * ppDNetI = (intptr_t *)ppOut; + uno_Interface * pUnoI = (uno_Interface *)pIn; + + Mapping const * that = static_cast< Mapping const * >( mapping ); + Bridge * bridge = that->m_bridge; + + if (0 != *ppDNetI) + { + sri::GCHandle::FromIntPtr(IntPtr(ppDNetI)).Free(); + } + + if (0 != pUnoI) + { + System::Object^ cliI= bridge->map_uno2cli(pUnoI, td); + intptr_t ptr= NULL; + if(cliI) + { + ptr= sri::GCHandle::ToIntPtr(sri::GCHandle::Alloc(cliI)) +#ifdef _WIN64 + .ToInt64(); +#else /* defined(_WIN32) */ + .ToInt32(); +#endif + } + (*ppOut)= reinterpret_cast<void*>(ptr); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } +} + + +void SAL_CALL Bridge_free( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping * that = static_cast< Mapping * >( mapping ); + delete that->m_bridge; +} + +} //extern C +} //namespace + +namespace cli_uno +{ + + +/** ToDo + I doubt that the case that the ref count raises from 0 to 1 + can occur. uno_ext_getMapping returns an acquired mapping. Every time + that function is called then a new mapping is created. Following the + rules of ref counted objects, then if the ref count is null no one has + a reference to the object anymore. Hence no one can call acquire. If someone + calls acquire then they must have kept an unacquired pointer which is + illegal. + */ +void Bridge::acquire() const +{ + if (1 == osl_atomic_increment( &m_ref )) + { + if (m_registered_cli2uno) + { + uno_Mapping * mapping = const_cast<Mapping*>(&m_cli2uno); + uno_registerMapping( + const_cast<uno_Mapping**>(&mapping), Bridge_free, m_uno_cli_env, (uno_Environment *)m_uno_env, 0 ); + } + else + { + uno_Mapping * mapping = const_cast<Mapping*>(&m_uno2cli); + uno_registerMapping( + &mapping, Bridge_free, (uno_Environment *)m_uno_env, m_uno_cli_env, 0 ); + } + } +} + +void Bridge::release() const +{ + if (! osl_atomic_decrement( &m_ref )) + { + uno_revokeMapping( + m_registered_cli2uno + ? const_cast<Mapping*>(&m_cli2uno) + : const_cast<Mapping*>(&m_uno2cli) ); + } +} + +Bridge::Bridge( + uno_Environment * uno_cli_env, uno_ExtEnvironment * uno_env, + bool registered_cli2uno ) + : m_ref( 1 ), + m_uno_env( uno_env ), + m_uno_cli_env( uno_cli_env ), + m_registered_cli2uno( registered_cli2uno ) +{ + OSL_ASSERT( 0 != m_uno_cli_env && 0 != m_uno_env ); + (*((uno_Environment *)m_uno_env)->acquire)( (uno_Environment *)m_uno_env ); + (*m_uno_cli_env->acquire)( m_uno_cli_env ); + + // cli2uno + m_cli2uno.acquire = Mapping_acquire; + m_cli2uno.release = Mapping_release; + m_cli2uno.mapInterface = Mapping_cli2uno; + m_cli2uno.m_bridge = this; + // uno2cli + m_uno2cli.acquire = Mapping_acquire; + m_uno2cli.release = Mapping_release; + m_uno2cli.mapInterface = Mapping_uno2cli; + m_uno2cli.m_bridge = this; + +} + + +Bridge::~Bridge() +{ + //System::GC::Collect(); + (*m_uno_cli_env->release)( m_uno_cli_env ); + (*((uno_Environment *)m_uno_env)->release)( (uno_Environment *)m_uno_env ); +} + + +} //namespace cli_uno + +extern "C" +{ + +namespace cli_uno +{ + +void SAL_CALL cli_env_disposing( uno_Environment * uno_cli_env ) + SAL_THROW_EXTERN_C() +{ + uno_cli_env->pContext = 0; +} + + +SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_initEnvironment( uno_Environment * uno_cli_env ) + SAL_THROW_EXTERN_C() +{ + //ToDo: remove when compiled with .NET 2 + + // Unclear whether the above comment refers to this whole function + // or a call to __crt_dll_initialize() that used to be here for + // _MSC_VER < 1400 + + uno_cli_env->environmentDisposing= cli_env_disposing; + uno_cli_env->pExtEnv = 0; + //Set the console to print Trace messages +#if OSL_DEBUG_LEVEL >= 1 + System::Diagnostics::Trace::Listeners-> + Add( gcnew System::Diagnostics::TextWriterTraceListener(System::Console::Out)); +#endif + OSL_ASSERT( 0 == uno_cli_env->pContext ); + + // We let the Cli_environment leak, since there is no good point where we could destruct it. + //dispose is not used because we would have then also synchronize the calls to proxies. If the + //Cli_environment is disposed, we must prevent all calls, otherwise we may crash at points + //where g_cli_env is accessed. + //When we compile the bridge with .NET 2 then we can again hold g_cli_env as a static gcroot + //member in a unmanaged class, such as Bridge. + CliEnvHolder::g_cli_env = gcnew Cli_environment(); +} + +SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_ext_getMapping( + uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo ) + SAL_THROW_EXTERN_C() +{ + OSL_ASSERT( 0 != ppMapping && 0 != pFrom && 0 != pTo ); + if (*ppMapping) + { + (*(*ppMapping)->release)( *ppMapping ); + *ppMapping = 0; + } + + + OUString const & from_env_typename = OUString::unacquired( + &pFrom->pTypeName ); + OUString const & to_env_typename = OUString::unacquired( &pTo->pTypeName ); + + uno_Mapping * mapping = 0; + + try + { + if ( from_env_typename == UNO_LB_CLI && to_env_typename == UNO_LB_UNO ) + { + Bridge * bridge = new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1 + mapping = &bridge->m_cli2uno; + uno_registerMapping( + &mapping, Bridge_free, pFrom, (uno_Environment *)pTo->pExtEnv, 0 ); + } + else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_CLI ) + { + Bridge * bridge = new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1 + mapping = &bridge->m_uno2cli; + uno_registerMapping( + &mapping, Bridge_free, (uno_Environment *)pFrom->pExtEnv, pTo, 0 ); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } + *ppMapping = mapping; +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_bridge.h b/cli_ure/source/uno_bridge/cli_bridge.h new file mode 100644 index 000000000..95ddb0394 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_bridge.h @@ -0,0 +1,113 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BRIDGE_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BRIDGE_H +#include <vcclr.h> +#include "osl/interlck.h" +#include "uno/mapping.h" +#include "uno/environment.h" +#include "uno/dispatcher.h" +#include "cli_base.h" +#include "cli_environment.h" +//#using <cli_uretypes.dll> +#using <cli_basetypes.dll> +#using <system.dll> + +namespace sr = System::Reflection; + +namespace cli_uno +{ + + +// holds environments and mappings +struct Bridge; +struct Mapping : public uno_Mapping +{ + Bridge* m_bridge; +}; + +// The environment will be created in uno_initEnvironment. See also the remarks there +//Managed cli environment for cli objects a UNO proxies (which are cli +//objects. The uno_Environment is not used for cli objects. +ref struct CliEnvHolder { +static Cli_environment ^ g_cli_env = nullptr; +}; + + +/** An instance of Bridge represents exactly one mapping therefore either + m_cli2uno or m_uno2cli is valid. +*/ +struct Bridge +{ + mutable oslInterlockedCount m_ref; + uno_ExtEnvironment * m_uno_env; + uno_Environment * m_uno_cli_env; + + Mapping m_cli2uno; + Mapping m_uno2cli; + bool m_registered_cli2uno; + + ~Bridge(); + Bridge( uno_Environment * java_env, uno_ExtEnvironment * uno_env, bool registered_java2uno ); + + void acquire() const; + void release() const; + + void map_to_uno( + void * uno_data, System::Object^ cli_data, + typelib_TypeDescriptionReference * type, + bool assign) const; + + /** + @param info + the type of the converted data. It may be a byref type. + */ + void map_to_cli( + System::Object^ *cli_data, void const * uno_data, + typelib_TypeDescriptionReference * type, System::Type^ info /* maybe 0 */, + bool bDontCreateObj) const; + + System::Object^ map_uno2cli(uno_Interface * pUnoI, typelib_InterfaceTypeDescription* pTD) const; + + System::Object^ call_uno(uno_Interface * pUnoI, + typelib_TypeDescription* member_td, + typelib_TypeDescriptionReference * return_type, + sal_Int32 nParams, typelib_MethodParameter const * pParams, + array<System::Object^>^ args, array<System::Type^>^ argTypes, + System::Object^* pException) const; + + + void call_cli( + System::Object^ cliI, sr::MethodInfo^ method, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, int nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const; + + uno_Interface * map_cli2uno( + System::Object^ cliI, typelib_TypeDescription* pTD) const; + +}; + +} //namespace cli_uno + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_data.cxx b/cli_ure/source/uno_bridge/cli_data.cxx new file mode 100644 index 000000000..72b39df48 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_data.cxx @@ -0,0 +1,1929 @@ +/* -*- 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 . + */ + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include "windows.h" +#include <ole2.h> + +#include <memory> + +#include "rtl/ustring.hxx" +#include "rtl/ustrbuf.hxx" +#include "uno/sequence2.h" +#include "typelib/typedescription.hxx" +#include "cli_proxy.h" +#include "cli_base.h" +#include "cli_bridge.h" + +#using <cli_uretypes.dll> + +#undef VOID + +namespace sri = System::Runtime::InteropServices; +namespace sr = System::Reflection; +namespace st = System::Text; +namespace ucss = unoidl::com::sun::star; + + +namespace cli_uno +{ +System::String^ mapUnoPolymorphicName(System::String^ unoName); +OUString mapCliTypeName(System::String^ typeName); +System::String^ mapCliPolymorphicName(System::String^ unoName); +System::String^ mapPolymorphicName(System::String^ unoName, bool bCliToUno); + +inline std::unique_ptr< rtl_mem > seq_allocate( sal_Int32 nElements, sal_Int32 nSize ) +{ + std::unique_ptr< rtl_mem > seq( + rtl_mem::allocate( SAL_SEQUENCE_HEADER_SIZE + (nElements * nSize) ) ); + uno_Sequence * p = (uno_Sequence *)seq.get(); + p->nRefCount = 1; + p->nElements = nElements; + return seq; +} + +System::Object^ Bridge::map_uno2cli(uno_Interface * pUnoI, typelib_InterfaceTypeDescription *pTD) const +{ + System::Object^ retVal= nullptr; + // get oid + rtl_uString * pOid = 0; + (*m_uno_env->getObjectIdentifier)( m_uno_env, &pOid, pUnoI ); + OSL_ASSERT( 0 != pOid ); + OUString oid(pOid, SAL_NO_ACQUIRE); + + // see if the interface was already mapped + System::Type^ ifaceType= mapUnoType(reinterpret_cast<typelib_TypeDescription*>(pTD)); + System::String^ sOid= mapUnoString(oid.pData); + + System::Threading::Monitor::Enter( CliEnvHolder::g_cli_env ); + try + { + retVal = CliEnvHolder::g_cli_env->getRegisteredInterface(sOid, ifaceType); + if (retVal) + { + // There is already a registered object. It can either be a proxy + // for the UNO object or a real cli object. In the first case we + // tell the proxy that it shall also represent the current UNO + // interface. If it already does that, then it does nothing + if (srr::RemotingServices::IsTransparentProxy(retVal)) + { + UnoInterfaceProxy^ p = static_cast<UnoInterfaceProxy^>( + srr::RemotingServices::GetRealProxy(retVal)); + p->addUnoInterface(pUnoI, pTD); + } + } + else + { + retVal = UnoInterfaceProxy::create( + (Bridge *) this, pUnoI, pTD, oid ); + } + } + __finally + { + System::Threading::Monitor::Exit( CliEnvHolder::g_cli_env ); + } + + return retVal; +} + +uno_Interface* Bridge::map_cli2uno(System::Object^ cliObj, typelib_TypeDescription *pTD) const +{ + uno_Interface* retIface = NULL; + // get oid from dot net environment + System::String^ ds_oid = CliEnvHolder::g_cli_env->getObjectIdentifier( cliObj); + OUString ousOid = mapCliString(ds_oid); + // look if interface is already mapped + m_uno_env->getRegisteredInterface(m_uno_env, (void**) &retIface, ousOid.pData, + (typelib_InterfaceTypeDescription*) pTD); + if ( ! retIface) + { + System::Threading::Monitor::Enter(Cli_environment::typeid); + try + { + m_uno_env->getRegisteredInterface(m_uno_env, (void**) &retIface, ousOid.pData, + (typelib_InterfaceTypeDescription*) pTD); + if ( ! retIface) + { + retIface = CliProxy::create((Bridge*)this, cliObj, pTD, ousOid); + } + } + __finally + { + System::Threading::Monitor::Exit(Cli_environment::typeid); + } + } + return retIface; +} + +inline System::Type^ loadCliType(rtl_uString * unoName) +{ + return loadCliType(mapUnoTypeName(unoName)); +} + +System::Type^ loadCliType(System::String ^ unoName) +{ + System::Type^ retVal= nullptr; + try + { + //If unoName denotes a polymorphic type, e.g com.sun.star.beans.Defaulted<System.Char> + //then we remove the type list, otherwise the type could not be loaded. + bool bIsPolymorphic = false; + + System::String ^ loadName = unoName; + int index = unoName->IndexOf('<'); + if (index != -1) + { + loadName = unoName->Substring(0, index); + bIsPolymorphic = true; + } + System::AppDomain^ currentDomain = System::AppDomain::CurrentDomain; + cli::array<sr::Assembly^>^ assems = currentDomain->GetAssemblies(); + for (int i = 0; i < assems->Length; i++) + { + retVal = assems[i]->GetType(loadName, false); + if (retVal) + break; + } + + if (retVal == nullptr) + { + System::String ^ msg = gcnew System::String("A type could not be loaded: "); + msg = System::String::Concat(msg, loadName); + throw BridgeRuntimeError(mapCliString(msg)); + } + + if (bIsPolymorphic) + { + retVal = uno::PolymorphicType::GetType(retVal, unoName); + } + } + catch( System::Exception ^ e) + { + OUString ouMessage(mapCliString(e->Message)); + throw BridgeRuntimeError(ouMessage); + } + return retVal; +} + +System::Type^ mapUnoType(typelib_TypeDescription const * pTD) +{ + return mapUnoType(pTD->pWeakRef); +} + +System::Type^ mapUnoType(typelib_TypeDescriptionReference const * pTD) +{ + System::Type ^ retVal = nullptr; + switch (pTD->eTypeClass) + { + case typelib_TypeClass_VOID: + retVal= void::typeid; break; + case typelib_TypeClass_CHAR: + retVal= System::Char::typeid; break; + case typelib_TypeClass_BOOLEAN: + retVal= System::Boolean::typeid; break; + case typelib_TypeClass_BYTE: + retVal= System::Byte::typeid; break; + case typelib_TypeClass_SHORT: + retVal= System::Int16::typeid; break; + case typelib_TypeClass_UNSIGNED_SHORT: + retVal= System::UInt16::typeid; break; + case typelib_TypeClass_LONG: + retVal= System::Int32::typeid; break; + case typelib_TypeClass_UNSIGNED_LONG: + retVal= System::UInt32::typeid; break; + case typelib_TypeClass_HYPER: + retVal= System::Int64::typeid; break; + case typelib_TypeClass_UNSIGNED_HYPER: + retVal= System::UInt64::typeid; break; + case typelib_TypeClass_FLOAT: + retVal= System::Single::typeid; break; + case typelib_TypeClass_DOUBLE: + retVal= System::Double::typeid; break; + case typelib_TypeClass_STRING: + retVal= System::String::typeid; break; + case typelib_TypeClass_TYPE: + retVal= System::Type::typeid; break; + case typelib_TypeClass_ANY: + retVal= uno::Any::typeid; break; + case typelib_TypeClass_ENUM: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + retVal= loadCliType(pTD->pTypeName); break; + case typelib_TypeClass_INTERFACE: + { + //special handling for XInterface, since it does not exist in cli. + OUString usXInterface("com.sun.star.uno.XInterface"); + if (usXInterface.equals(pTD->pTypeName)) + retVal= System::Object::typeid; + else + retVal= loadCliType(pTD->pTypeName); + break; + } + case typelib_TypeClass_SEQUENCE: + { + css::uno::TypeDescription seqType( + const_cast<typelib_TypeDescriptionReference*>(pTD)); + typelib_TypeDescriptionReference* pElementTDRef= + reinterpret_cast<typelib_IndirectTypeDescription*>(seqType.get())->pType; + switch (pElementTDRef->eTypeClass) + { + case typelib_TypeClass_CHAR: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArChar)); break; + case typelib_TypeClass_BOOLEAN: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArBoolean)); + break; + case typelib_TypeClass_BYTE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArByte)); + break; + case typelib_TypeClass_SHORT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt16)); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt16)); + break; + case typelib_TypeClass_LONG: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt32)); + break; + case typelib_TypeClass_UNSIGNED_LONG: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt32)); + break; + case typelib_TypeClass_HYPER: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt64)); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt64)); + break; + case typelib_TypeClass_FLOAT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArSingle)); + break; + case typelib_TypeClass_DOUBLE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArDouble)); + break; + case typelib_TypeClass_STRING: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArString)); + break; + case typelib_TypeClass_TYPE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArType)); + break; + case typelib_TypeClass_ANY: + case typelib_TypeClass_ENUM: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_SEQUENCE: + { + retVal= loadCliType(pTD->pTypeName); + break; + } + default: + //All cases should be handled by the case statements above + OSL_ASSERT(0); + break; + } + break; + } + default: + OSL_ASSERT(false); + break; + } + return retVal; +} + +/** Returns an acquired td. + */ +typelib_TypeDescriptionReference* mapCliType(System::Type^ cliType) +{ + typelib_TypeDescriptionReference* retVal= NULL; + if (cliType == nullptr) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_VOID ); + typelib_typedescriptionreference_acquire( retVal ); + return retVal; + } + //check for Enum first, + //because otherwise case System::TypeCode::Int32 applies + if (cliType->IsEnum) + { + OUString usTypeName= mapCliTypeName(cliType->FullName); + css::uno::Type unoType(css::uno::TypeClass_ENUM, usTypeName); + retVal= unoType.getTypeLibType(); + typelib_typedescriptionreference_acquire(retVal); + } + else + { + switch (System::Type::GetTypeCode(cliType)) + { + case System::TypeCode::Boolean: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_BOOLEAN ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Char: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_CHAR ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Byte: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_BYTE ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int16: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_SHORT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int32: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_LONG ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int64: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_HYPER ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt16: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_SHORT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt32: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_LONG ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt64: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_HYPER ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Single: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_FLOAT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Double: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_DOUBLE ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::String: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_STRING ); + typelib_typedescriptionreference_acquire( retVal ); + break; + default: + break; + } + } + if (retVal == NULL) + { + System::String^ cliTypeName= cliType->FullName; + // Void + if (const_cast<System::String^>(Constants::sVoid)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_VOID ); + typelib_typedescriptionreference_acquire( retVal ); + } + // Type + else if (const_cast<System::String^>(Constants::sType)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_TYPE ); + typelib_typedescriptionreference_acquire( retVal ); + } + // Any + else if (const_cast<System::String^>(Constants::sAny)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_ANY ); + typelib_typedescriptionreference_acquire( retVal ); + } + //struct, interfaces, sequences + else + { + OUString usTypeName; + uno::PolymorphicType ^ poly = dynamic_cast<uno::PolymorphicType^>(cliType); + if (poly != nullptr) + usTypeName = mapCliTypeName( poly->PolymorphicName); + else + usTypeName = mapCliTypeName(cliTypeName); + typelib_TypeDescription* td = NULL; + typelib_typedescription_getByName(&td, usTypeName.pData); + if (td) + { + retVal = td->pWeakRef; + typelib_typedescriptionreference_acquire(retVal); + typelib_typedescription_release(td); + } + } + } + if (retVal == NULL) + { + throw BridgeRuntimeError("[cli_uno bridge] mapCliType():could not map type: " + mapCliString(cliType->FullName)); + } + return retVal; +} + +/** + Otherwise a leading "unoidl." is removed. + */ +System::String^ mapUnoTypeName(rtl_uString const * typeName) +{ + OUString usUnoName( const_cast< rtl_uString * >( typeName ) ); + st::StringBuilder^ buf= gcnew st::StringBuilder(); + //determine if the type is a sequence and its dimensions + int dims= 0; + if (usUnoName[0] == '[') + { + sal_Int32 index= 1; + while (true) + { + if (usUnoName[index++] == ']') + dims++; + if (usUnoName[index++] != '[') + break; + } + usUnoName = usUnoName.copy(index - 1); + } + System::String ^ sUnoName = mapUnoString(usUnoName.pData); + if (sUnoName->Equals(const_cast<System::String^>(Constants::usBool))) + buf->Append(const_cast<System::String^>(Constants::sBoolean)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usChar))) + buf->Append(const_cast<System::String^>(Constants::sChar)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usByte))) + buf->Append(const_cast<System::String^>(Constants::sByte)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usShort))) + buf->Append(const_cast<System::String^>(Constants::sInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usUShort))) + buf->Append(const_cast<System::String^>(Constants::sUInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usLong))) + buf->Append(const_cast<System::String^>(Constants::sInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usULong))) + buf->Append(const_cast<System::String^>(Constants::sUInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usHyper))) + buf->Append(const_cast<System::String^>(Constants::sInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usUHyper))) + buf->Append(const_cast<System::String^>(Constants::sUInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usFloat))) + buf->Append(const_cast<System::String^>(Constants::sSingle)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usDouble))) + buf->Append(const_cast<System::String^>(Constants::sDouble)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usString))) + buf->Append(const_cast<System::String^>(Constants::sString)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usVoid))) + buf->Append(const_cast<System::String^>(Constants::sVoid)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usType))) + buf->Append(const_cast<System::String^>(Constants::sType)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usXInterface))) + buf->Append(const_cast<System::String^>(Constants::sObject)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usAny))) + { + buf->Append(const_cast<System::String^>(Constants::sAny)); + } + else + { + //put "unoidl." at the beginning + buf->Append(const_cast<System::String^>(Constants::sUnoidl)); + //for polymorphic struct types remove the brackets, e.g mystruct<bool> -> mystruct + System::String ^ sName = mapUnoPolymorphicName(sUnoName); + buf->Append(sName); + } + // append [] + for (;dims--;) + buf->Append(const_cast<System::String^>(Constants::sBrackets)); + + return buf->ToString(); +} + +/** For example, there is a uno type + com.sun.star.Foo<char, long>. + The values in the type list + are uno types and are replaced by cli types, such as System.Char, + System.Int32, etc. + The prefix unoidl is not added. + */ +inline System::String^ mapUnoPolymorphicName(System::String^ unoName) +{ + return mapPolymorphicName(unoName, false); +} + +/** For example, there is a type name such as + com.sun.star.Foo<System.Char, System.Int32>. + The values in the type list + are CLI types and are replaced by uno types, such as char, + long, etc. + The prefix unoidl remains. + */ +inline System::String^ mapCliPolymorphicName(System::String^ unoName) +{ + return mapPolymorphicName(unoName, true); +} + +System::String^ mapPolymorphicName(System::String^ unoName, bool bCliToUno) +{ + int index = unoName->IndexOf('<'); + if (index == -1) + return unoName; + + System::Text::StringBuilder ^ builder = gcnew System::Text::StringBuilder(256); + builder->Append(unoName->Substring(0, index +1 )); + + //Find the first occurrence of ',' + //If the parameter is a polymorphic struct then we need to ignore everything + //between the brackets because it can also contain commas + //get the type list within < and > + int endIndex = unoName->Length - 1; + index++; + int cur = index; + int countParams = 0; + while (cur <= endIndex) + { + System::Char c = unoName[cur]; + if (c == ',' || c == '>') + { + //insert a comma if needed + if (countParams != 0) + builder->Append(","); + countParams++; + System::String ^ sParam = unoName->Substring(index, cur - index); + //skip the comma + cur++; + //the index to the beginning of the next param + index = cur; + if (bCliToUno) + { + builder->Append(mapCliTypeName(sParam).getStr()); + } + else + { + OUString s = mapCliString(sParam); + builder->Append(mapUnoTypeName(s.pData)); + } + } + else if (c == '<') + { + cur++; + //continue until the matching '>' + int numNested = 0; + for (;;cur++) + { + System::Char curChar = unoName[cur]; + if (curChar == '<') + { + numNested ++; + } + else if (curChar == '>') + { + if (numNested > 0) + numNested--; + else + break; + } + } + } + cur++; + } + + builder->Append((System::Char) '>'); + return builder->ToString(); +} + +OUString mapCliTypeName(System::String^ typeName) +{ + int dims= 0; + // Array? determine the "rank" (number of "[]") + // move from the rightmost end to the left, for example + // unoidl.PolymorphicStruct<System.Char[]>[] + // has only a "dimension" of 1 + int cur = typeName->Length - 1; + bool bRightBracket = false; + while (cur >= 0) + { + System::Char c = typeName[cur]; + if (c == ']') + { + bRightBracket = true; + } + else if (c == '[') + { + if (!bRightBracket) + throw BridgeRuntimeError( + "Typename is wrong. No matching brackets for sequence. Name is: " + + mapCliString(typeName)); + bRightBracket = false; + dims ++; + } + else + { + if (bRightBracket) + throw BridgeRuntimeError( + "Typename is wrong. No matching brackets for sequence. Name is: " + + mapCliString(typeName)); + break; + } + cur--; + } + + if (bRightBracket || cur < 0) + throw BridgeRuntimeError( + "Typename is wrong. " + + mapCliString(typeName)); + + typeName = typeName->Substring(0, cur + 1); + + System::Text::StringBuilder ^ buf = gcnew System::Text::StringBuilder(512); + + //Put the "[]" at the beginning of the uno type name + for (;dims--;) + buf->Append(const_cast<System::String^>(Constants::usBrackets)); + + if (typeName->Equals(const_cast<System::String^>(Constants::sBoolean))) + buf->Append(const_cast<System::String^>(Constants::usBool)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sChar))) + buf->Append(const_cast<System::String^>(Constants::usChar)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sByte))) + buf->Append(const_cast<System::String^>(Constants::usByte)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt16))) + buf->Append(const_cast<System::String^>(Constants::usShort)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt16))) + buf->Append(const_cast<System::String^>(Constants::usUShort)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt32))) + buf->Append(const_cast<System::String^>(Constants::usLong)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt32))) + buf->Append(const_cast<System::String^>(Constants::usULong)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt64))) + buf->Append(const_cast<System::String^>(Constants::usHyper)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt64))) + buf->Append(const_cast<System::String^>(Constants::usUHyper)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sSingle))) + buf->Append(const_cast<System::String^>(Constants::usFloat)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sDouble))) + buf->Append(const_cast<System::String^>(Constants::usDouble)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sString))) + buf->Append(const_cast<System::String^>(Constants::usString)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sVoid))) + buf->Append(const_cast<System::String^>(Constants::usVoid)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sType))) + buf->Append(const_cast<System::String^>(Constants::usType)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sObject))) + buf->Append(const_cast<System::String^>(Constants::usXInterface)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sAny))) + buf->Append(const_cast<System::String^>(Constants::usAny)); + else + { + System::String ^ sName = mapCliPolymorphicName(typeName); + int i= sName->IndexOf(L'.'); + buf->Append(sName->Substring(i + 1)); + } + return mapCliString(buf->ToString()); +} + +/** Maps uno types to dot net types. + * If uno_data is null then the type description is converted to System::Type + */ +inline System::String^ mapUnoString( rtl_uString const * data) +{ + OSL_ASSERT(data); + return gcnew System::String((__wchar_t*) data->buffer, 0, data->length); +} + +OUString mapCliString(System::String ^ data) +{ + + if (data != nullptr) + { + static_assert(sizeof(wchar_t) == sizeof(sal_Unicode), "char mismatch"); + pin_ptr<wchar_t const> pdata= PtrToStringChars(data); + return OUString( + reinterpret_cast<sal_Unicode const *>(pdata), + const_cast<System::String^>(data)->Length); + } + else + { + return OUString(); + } +} + +// ToDo convert cli types to expected types, e.g a long to a short where the uno type +// is a sal_Int16. This could be necessary if a scripting language (typeless) is used +// @param assign the uno_data has to be destructed (in/out args) +void Bridge::map_to_uno(void * uno_data, System::Object^ cli_data, + typelib_TypeDescriptionReference * type, + bool assign) const +{ + try{ + switch (type->eTypeClass) + { + case typelib_TypeClass_VOID: + break; + case typelib_TypeClass_CHAR: + { + System::Char aChar= *safe_cast<System::Char^>(cli_data); + *(sal_Unicode*) uno_data= aChar; + break; + } + case typelib_TypeClass_BOOLEAN: + { + System::Boolean aBool= *safe_cast<System::Boolean^>(cli_data); + *(sal_Bool*)uno_data= aBool == true ? sal_True : sal_False; + break; + } + case typelib_TypeClass_BYTE: + { + System::Byte aByte= *safe_cast<System::Byte^>(cli_data); + *(sal_Int8*) uno_data= aByte; + break; + } + case typelib_TypeClass_SHORT: + { + System::Int16 aShort= *safe_cast<System::Int16^>(cli_data); + *(sal_Int16*) uno_data= aShort; + break; + } + case typelib_TypeClass_UNSIGNED_SHORT: + { + System::UInt16 aUShort= *safe_cast<System::UInt16^>(cli_data); + *(sal_uInt16*) uno_data= aUShort; + break; + } + case typelib_TypeClass_LONG: + { + System::Int32 aLong= *safe_cast<System::Int32^>(cli_data); + *(sal_Int32*) uno_data= aLong; + break; + } + case typelib_TypeClass_UNSIGNED_LONG: + { + System::UInt32 aULong= *safe_cast<System::UInt32^>(cli_data); + *(sal_uInt32*) uno_data= aULong; + break; + } + case typelib_TypeClass_HYPER: + { + System::Int64 aHyper= *safe_cast<System::Int64^>(cli_data); + *(sal_Int64*) uno_data= aHyper; + break; + } + case typelib_TypeClass_UNSIGNED_HYPER: + { + System::UInt64 aLong= *safe_cast<System::UInt64^>(cli_data); + *(sal_uInt64*) uno_data= aLong; + break; + } + case typelib_TypeClass_FLOAT: + { + System::Single aFloat= *safe_cast<System::Single^>(cli_data); + *(float*) uno_data= aFloat; + break; + } + case typelib_TypeClass_DOUBLE: + { + System::Double aDouble= *safe_cast<System::Double^>(cli_data); + *(double*) uno_data= aDouble; + break; + } + case typelib_TypeClass_STRING: + { + if (assign && *(rtl_uString**) uno_data) + rtl_uString_release(*(rtl_uString**) uno_data); + + *(rtl_uString **)uno_data = 0; + if (cli_data == nullptr) + { + rtl_uString_new((rtl_uString**) uno_data); + } + else + { + System::String ^s= safe_cast<System::String^>(cli_data); + pin_ptr<const wchar_t> pdata= PtrToStringChars(s); + rtl_uString_newFromStr_WithLength( + reinterpret_cast<rtl_uString **>(uno_data), + reinterpret_cast<sal_Unicode const *>(pdata), s->Length); + } + break; + } + case typelib_TypeClass_TYPE: + { + typelib_TypeDescriptionReference* td= mapCliType(safe_cast<System::Type^>( + cli_data)); + if (assign) + { + typelib_typedescriptionreference_release( + *(typelib_TypeDescriptionReference **)uno_data ); + } + *(typelib_TypeDescriptionReference **)uno_data = td; + break; + } + case typelib_TypeClass_ANY: + { + uno_Any * pAny = (uno_Any *)uno_data; + if (cli_data == nullptr) // null-ref or uninitialized any maps to empty any + { + if (assign) + uno_any_destruct( pAny, 0 ); + uno_any_construct( pAny, 0, 0, 0 ); + break; + } + uno::Any aAny= *safe_cast<uno::Any^>(cli_data); + css::uno::Type value_td( mapCliType(aAny.Type), SAL_NO_ACQUIRE); + + if (assign) + uno_any_destruct( pAny, 0 ); + + try + { + switch (value_td.getTypeClass()) + { + case css::uno::TypeClass_VOID: + pAny->pData = &pAny->pReserved; + break; + case css::uno::TypeClass_CHAR: + pAny->pData = &pAny->pReserved; + *(sal_Unicode*) &pAny->pReserved = *safe_cast<System::Char^>(aAny.Value); + break; + case css::uno::TypeClass_BOOLEAN: + pAny->pData = &pAny->pReserved; + *(sal_Bool *) &pAny->pReserved = *safe_cast<System::Boolean^>(aAny.Value); + break; + case css::uno::TypeClass_BYTE: + pAny->pData = &pAny->pReserved; + *(sal_Int8*) &pAny->pReserved = *safe_cast<System::Byte^>(aAny.Value); + break; + case css::uno::TypeClass_SHORT: + pAny->pData = &pAny->pReserved; + *(sal_Int16*) &pAny->pReserved = *safe_cast<System::Int16^>(aAny.Value); + break; + case css::uno::TypeClass_UNSIGNED_SHORT: + pAny->pData = &pAny->pReserved; + *(sal_uInt16*) &pAny->pReserved = *safe_cast<System::UInt16^>(aAny.Value); + break; + case css::uno::TypeClass_LONG: + pAny->pData = &pAny->pReserved; + *(sal_Int32*) &pAny->pReserved = *safe_cast<System::Int32^>(aAny.Value); + break; + case css::uno::TypeClass_UNSIGNED_LONG: + pAny->pData = &pAny->pReserved; + *(sal_uInt32*) &pAny->pReserved = *safe_cast<System::UInt32^>(aAny.Value); + break; + case css::uno::TypeClass_HYPER: + if (sizeof (sal_Int64) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(sal_Int64*) &pAny->pReserved = *safe_cast<System::Int64^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (sal_Int64) ) ); + *(sal_Int64 *) mem.get()= *safe_cast<System::Int64^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_UNSIGNED_HYPER: + if (sizeof (sal_uInt64) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(sal_uInt64*) &pAny->pReserved = *safe_cast<System::UInt64^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (sal_uInt64) ) ); + *(sal_uInt64 *) mem.get()= *safe_cast<System::UInt64^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_FLOAT: + if (sizeof (float) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(float*) &pAny->pReserved = *safe_cast<System::Single^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (float) ) ); + *(float*) mem.get() = *safe_cast<System::Single^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_DOUBLE: + if (sizeof (double) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(double*) &pAny->pReserved= *safe_cast<System::Double^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (double) ) ); + *(double*) mem.get()= *safe_cast<System::Double^>(aAny.Value); + pAny->pData= mem.release(); + } + break; + case css::uno::TypeClass_STRING: // anies often contain strings; copy string directly + { + pAny->pData= &pAny->pReserved; + OUString _s = mapCliString(static_cast<System::String^>(aAny.Value)); + pAny->pReserved= _s.pData; + rtl_uString_acquire(_s.pData); + break; + } + case css::uno::TypeClass_TYPE: + case css::uno::TypeClass_ENUM: //ToDo copy enum direct + case css::uno::TypeClass_SEQUENCE: + case css::uno::TypeClass_INTERFACE: + pAny->pData = &pAny->pReserved; + pAny->pReserved = 0; + map_to_uno( + &pAny->pReserved, aAny.Value, value_td.getTypeLibType(), + false /* no assign */); + break; + case css::uno::TypeClass_STRUCT: + case css::uno::TypeClass_EXCEPTION: + { + css::uno::Type anyType(value_td); + typelib_TypeDescription* td= NULL; + anyType.getDescription(&td); + std::unique_ptr< rtl_mem > mem(rtl_mem::allocate(td->nSize)); + typelib_typedescription_release(td); + map_to_uno( + mem.get(), aAny.Value, value_td.getTypeLibType(), + false /* no assign */); + pAny->pData = mem.release(); + break; + } + default: + { + throw BridgeRuntimeError("[map_to_uno():" + value_td.getTypeName() + "] unsupported value type of any!"); + } + } + } + catch(System::InvalidCastException^ ) + { +// ToDo check this + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + OUString str = "[map_to_uno():Any" + value_td.getTypeName() + "]The Any type " + value_td.getTypeName() + " does not correspond to its value type: "; + if(aAny.Value != nullptr) + { + css::uno::Type td(mapCliType(aAny.Value->GetType()), SAL_NO_ACQUIRE); + str += td.getTypeName(); + } + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw BridgeRuntimeError(str); + } + catch (BridgeRuntimeError& ) + { + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw; + } + catch (...) + { + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw; + } + + pAny->pType = value_td.getTypeLibType(); + typelib_typedescriptionreference_acquire(pAny->pType); + break; + } + case typelib_TypeClass_ENUM: + { + // InvalidCastException is caught at the end of this method + System::Int32 aEnum= System::Convert::ToInt32((cli_data)); + *(sal_Int32*) uno_data = aEnum; + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + css::uno::TypeDescription td(type); + typelib_CompoundTypeDescription * comp_td = + (typelib_CompoundTypeDescription*) td.get(); + + typelib_StructTypeDescription * struct_td = NULL; + if (type->eTypeClass == typelib_TypeClass_STRUCT) + struct_td = (typelib_StructTypeDescription*) td.get(); + + if ( ! ((typelib_TypeDescription*) comp_td)->bComplete) + ::typelib_typedescription_complete( + (typelib_TypeDescription**) & comp_td ); + + sal_Int32 nMembers = comp_td->nMembers; + boolean bException= false; + System::Type^ cliType = nullptr; + if (cli_data) + cliType = cli_data->GetType(); + + if (0 != comp_td->pBaseTypeDescription) + { + map_to_uno( + uno_data, cli_data, + ((typelib_TypeDescription *)comp_td->pBaseTypeDescription)->pWeakRef, + assign); + } + sal_Int32 nPos = 0; + try + { + OUString usUnoException("com.sun.star.uno.Exception"); + for (; nPos < nMembers; ++nPos) + { + typelib_TypeDescriptionReference * member_type= comp_td->ppTypeRefs[nPos]; + System::Object^ val= nullptr; + if (cli_data != nullptr) + { + sr::FieldInfo^ aField= cliType->GetField( + mapUnoString(comp_td->ppMemberNames[nPos])); + // special case for Exception.Message property + // The com.sun.star.uno.Exception.Message field is mapped to the + // System.Exception property. Type.GetField("Message") returns null + if ( ! aField && usUnoException.equals(td.get()->pTypeName)) + {// get Exception.Message property + OUString usMessageMember("Message"); + if (usMessageMember.equals(comp_td->ppMemberNames[nPos])) + { + sr::PropertyInfo^ pi= cliType->GetProperty( + mapUnoString(comp_td->ppMemberNames[nPos])); + val= pi->GetValue(cli_data, nullptr); + } + else + { + throw BridgeRuntimeError("[map_to_uno(): Member: " + OUString::unacquired(&comp_td->ppMemberNames[nPos])); + } + } + else + { + val= aField->GetValue(cli_data); + } + } + void * p = (char *) uno_data + comp_td->pMemberOffsets[ nPos ]; + //When using polymorphic structs then the parameterized members can be null. + //Then we set a default value. + bool bDefault = (struct_td != NULL + && struct_td->pParameterizedTypes != NULL + && struct_td->pParameterizedTypes[nPos] == sal_True + && val == nullptr) + || cli_data == nullptr; + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (bDefault) + *(sal_Unicode*) p = 0; + else + *(sal_Unicode*) p = *safe_cast<System::Char^>(val); + break; + case typelib_TypeClass_BOOLEAN: + if (bDefault) + *(sal_Bool*) p = sal_False; + else + *(sal_Bool*) p = *safe_cast<System::Boolean^>(val); + break; + case typelib_TypeClass_BYTE: + if (bDefault) + *(sal_Int8*) p = 0; + else + *(sal_Int8*) p = *safe_cast<System::Byte^>(val); + break; + case typelib_TypeClass_SHORT: + if (bDefault) + *(sal_Int16*) p = 0; + else + *(sal_Int16*) p = *safe_cast<System::Int16^>(val); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + if (bDefault) + *(sal_uInt16*) p = 0; + else + *(sal_uInt16*) p = *safe_cast<System::UInt16^>(val); + break; + case typelib_TypeClass_LONG: + if (bDefault) + *(sal_Int32*) p = 0; + else + *(sal_Int32*) p = *safe_cast<System::Int32^>(val); + break; + case typelib_TypeClass_UNSIGNED_LONG: + if (bDefault) + *(sal_uInt32*) p = 0; + else + *(sal_uInt32*) p = *safe_cast<System::UInt32^>(val); + break; + case typelib_TypeClass_HYPER: + if (bDefault) + *(sal_Int64*) p = 0; + else + *(sal_Int64*) p = *safe_cast<System::Int64^>(val); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + if (bDefault) + *(sal_uInt64*) p = 0; + else + *(sal_uInt64*) p= *safe_cast<System::UInt64^>(val); + break; + case typelib_TypeClass_FLOAT: + if (bDefault) + *(float*) p = 0.; + else + *(float*) p = *safe_cast<System::Single^>(val); + break; + case typelib_TypeClass_DOUBLE: + if (bDefault) + *(double*) p = 0.; + else + *(double*) p = *safe_cast<System::Double^>(val); + break; + default: + { // ToDo enum, should be converted here + map_to_uno(p, val, member_type, assign); + break; + } + } + } + } + catch (BridgeRuntimeError& e) + { + bException= true; + OUString str = "[map_to_uno():"; + if (cliType) + { + str += mapCliString(cliType->FullName) + "." + OUString::unacquired(&comp_td->ppMemberNames[nPos]) + " "; + } + str += e.m_message; + throw BridgeRuntimeError(str); + } + catch (System::InvalidCastException^ ) + { + bException= true; + OUString str = "[map_to_uno():"; + if (cliType) + { + str += mapCliString(cliType->FullName) + "." + OUString::unacquired(&comp_td->ppMemberNames[nPos]); + } + str += "] Value has not the required type."; + throw BridgeRuntimeError(str); + } + catch (...) + { + OSL_ASSERT(0); + bException= true; + throw; + } + __finally + { + if (bException && !assign) // if assign then caller cleans up + { + // cleanup the members which we have converted so far + for ( sal_Int32 nCleanup = 0; nCleanup < nPos; ++nCleanup ) + { + uno_type_destructData( + uno_data, comp_td->ppTypeRefs[ nCleanup ], 0 ); + } + if (0 != comp_td->pBaseTypeDescription) + { + uno_destructData( + uno_data, (typelib_TypeDescription *)comp_td->pBaseTypeDescription, 0 ); + } + } + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + ((typelib_IndirectTypeDescription *)td.get())->pType; + + std::unique_ptr< rtl_mem > seq; + + System::Array^ ar = nullptr; + if (cli_data != nullptr) + { + ar = safe_cast<System::Array^>(cli_data); + sal_Int32 nElements = ar->GetLength(0); + + try + { + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + seq = seq_allocate(nElements, sizeof (sal_Unicode)); + sri::Marshal::Copy(safe_cast<cli::array<System::Char>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_BOOLEAN: + seq = seq_allocate(nElements, sizeof (sal_Bool)); + sri::Marshal::Copy(safe_cast<cli::array<System::Char>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_BYTE: + seq = seq_allocate( nElements, sizeof (sal_Int8) ); + sri::Marshal::Copy(safe_cast<cli::array<System::Byte>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_SHORT: + seq = seq_allocate(nElements, sizeof (sal_Int16)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int16>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + seq = seq_allocate( nElements, sizeof (sal_uInt16) ); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int16>^>( + safe_cast<cli::array<System::UInt16>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_LONG: + seq = seq_allocate(nElements, sizeof (sal_Int32)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int32>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_LONG: + seq = seq_allocate( nElements, sizeof (sal_uInt32) ); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int32>^>( + safe_cast<cli::array<System::UInt32>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_HYPER: + seq = seq_allocate(nElements, sizeof (sal_Int64)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int64>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + seq = seq_allocate(nElements, sizeof (sal_uInt64)); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int64>^>( + safe_cast<cli::array<System::UInt64>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_FLOAT: + seq = seq_allocate(nElements, sizeof (float)); + sri::Marshal::Copy(safe_cast<cli::array<System::Single>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_DOUBLE: + seq = seq_allocate(nElements, sizeof (double)); + sri::Marshal::Copy(safe_cast<cli::array<System::Double>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_STRING: + { + seq = seq_allocate(nElements, sizeof (rtl_uString*)); + cli::array<System::String^>^ arStr= safe_cast<cli::array<System::String^>^>(cli_data); + for (int i= 0; i < nElements; i++) + { + pin_ptr<const wchar_t> pdata= PtrToStringChars(arStr[i]); + rtl_uString** pStr= & ((rtl_uString**) & + ((uno_Sequence*) seq.get())->elements)[i]; + *pStr= NULL; + rtl_uString_newFromStr_WithLength( + pStr, + reinterpret_cast<sal_Unicode const *>(pdata), + arStr[i]->Length); + } + break; + } + case typelib_TypeClass_ENUM: + seq = seq_allocate(nElements, sizeof (sal_Int32)); + for (int i= 0; i < nElements; i++) + { + ((sal_Int32*) &((uno_Sequence*) seq.get())->elements)[i]= + System::Convert::ToInt32(ar->GetValue(i)); + } + break; + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + { + TypeDescr element_td( element_type ); + seq = seq_allocate( nElements, element_td.get()->nSize ); + + for (sal_Int32 nPos = 0; nPos < nElements; ++nPos) + { + try + { + void * p= ((uno_Sequence *) seq.get())->elements + + (nPos * element_td.get()->nSize); + System::Object^ elemData= safe_cast<System::Array^>(cli_data)->GetValue(System::Int32(nPos)); + map_to_uno( + p, elemData, element_td.get()->pWeakRef, + false /* no assign */); + } + catch (...) + { + // cleanup + for ( sal_Int32 nCleanPos = 0; nCleanPos < nPos; ++nCleanPos ) + { + void * p = + ((uno_Sequence *)seq.get())->elements + + (nCleanPos * element_td.get()->nSize); + uno_destructData( p, element_td.get(), 0 ); + } + throw; + } + } + break; + } + default: + { + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported sequence element type: " + OUString::unacquired( &element_type->pTypeName )); + } + } + } + catch (BridgeRuntimeError& e) + { + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] conversion failed\n " + e.m_message); + } + catch (System::InvalidCastException^ ) + { + // Ok, checked + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName) + + "] could not convert sequence element type: " + OUString::unacquired( &element_type->pTypeName )); + } + catch (...) + { + OSL_ASSERT(0); + throw; + } + __finally + { + if (assign) + uno_destructData( uno_data, td.get(), 0 ); + } + } + else + { + seq = seq_allocate(0, sizeof (sal_Int32)); + } + *(uno_Sequence **)uno_data = (uno_Sequence *)seq.release(); + break; + } + case typelib_TypeClass_INTERFACE: + { + if (assign) + { + uno_Interface * p = *(uno_Interface **)uno_data; + if (0 != p) + (*p->release)( p ); + } + if (nullptr == cli_data) // null-ref + { + *(uno_Interface **)uno_data = 0; + } + else + { + TypeDescr td( type ); + uno_Interface * pUnoI = map_cli2uno(cli_data, td.get()); + *(uno_Interface **)uno_data = pUnoI; + } + break; + } + default: + { + //ToDo check + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] unsupported type!"); + } + } + } + // BridgeRuntimeError are allowed to be thrown + catch (System::InvalidCastException^ ) + { + //ToDo check + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] could not convert type!"); + } + catch (System::NullReferenceException ^ e) + { + throw BridgeRuntimeError("[map_to_uno()] Illegal null reference passed!\n" + mapCliString(e->StackTrace)); + } + catch (BridgeRuntimeError& ) + { + throw; + } + catch (...) + { + OSL_ASSERT(0); + throw; + } +} + +/** + @param info + The expected target type. Currently info is provided when this method is called + to convert the in/out and out parameters of a call from cli to uno. Then info + is always a byref type, e.g. "System.String&". info is used for Any and Enum conversion. + @param bDontCreateObj + false - a new object is created which holds the mapped uno value and is assigned to + cli_data. + true - cli_data already contains the newly constructed object. This is the case if + a struct is converted then on the first call to map_to_cli the new object is created. + If the struct inherits another struct then this function is called recursively while the + newly created object is passed in cli_data. + */ +void Bridge::map_to_cli( + System::Object^ *cli_data, void const * uno_data, + typelib_TypeDescriptionReference * type, System::Type^ info, + bool bDontCreateObj) const +{ + switch (type->eTypeClass) + { + case typelib_TypeClass_CHAR: + *cli_data= *(__wchar_t const*)uno_data; + break; + case typelib_TypeClass_BOOLEAN: + *cli_data = (*(bool const*)uno_data) == sal_True; + break; + case typelib_TypeClass_BYTE: + *cli_data = *(unsigned char const*) uno_data; + break; + case typelib_TypeClass_SHORT: + *cli_data= *(short const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_SHORT: + *cli_data= *(unsigned short const*) uno_data; + break; + case typelib_TypeClass_LONG: + *cli_data= *(int const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_LONG: + *cli_data= *(unsigned int const*) uno_data; + break; + case typelib_TypeClass_HYPER: + *cli_data= *(__int64 const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_HYPER: + *cli_data= *(unsigned __int64 const*) uno_data; + break; + case typelib_TypeClass_FLOAT: + *cli_data= *(float const*) uno_data; + break; + case typelib_TypeClass_DOUBLE: + *cli_data= *(double const*) uno_data; + break; + case typelib_TypeClass_STRING: + { + rtl_uString const* sVal= NULL; + sVal= *(rtl_uString* const*) uno_data; + *cli_data= gcnew System::String((__wchar_t*) sVal->buffer,0, sVal->length); + break; + } + case typelib_TypeClass_TYPE: + { + *cli_data= mapUnoType( *(typelib_TypeDescriptionReference * const *)uno_data ); + break; + } + case typelib_TypeClass_ANY: + { + uno_Any const * pAny = (uno_Any const *)uno_data; + if (typelib_TypeClass_VOID != pAny->pType->eTypeClass) + { + System::Object^ objCli= nullptr; + map_to_cli( + &objCli, pAny->pData, pAny->pType, nullptr, + false); + + uno::Any anyVal(mapUnoType(pAny->pType), objCli); + *cli_data= anyVal; + } + else + { // void any + *cli_data= uno::Any::VOID; + } + break; + } + case typelib_TypeClass_ENUM: + { + if (info != nullptr) + { + OSL_ASSERT(info->IsByRef); + info= info->GetElementType(); + *cli_data= System::Enum::ToObject(info, *(System::Int32*) uno_data); + } + else + *cli_data= System::Enum::ToObject( + mapUnoType(type), *(System::Int32*) uno_data); + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + TypeDescr td( type ); + typelib_CompoundTypeDescription * comp_td = + (typelib_CompoundTypeDescription *) td.get(); + if ( ! ((typelib_TypeDescription*) comp_td)->bComplete) + ::typelib_typedescription_complete( + (typelib_TypeDescription**) & comp_td ); + + + //create the type + System::Type^ cliType= loadCliType(td.get()->pTypeName); + //detect if we recursively convert inherited structures + //If this point is reached because of a recursive call during covering a + //struct then we must not create a new object rather we use the one in + // cli_data argument. + System::Object^ cliObj; + if (bDontCreateObj) + cliObj = *cli_data; // recursive call + else + { + //Special handling for Exception conversion. We must call constructor System::Exception + //to pass the message string + if (ucss::uno::Exception::typeid->IsAssignableFrom(cliType)) + { + //We need to get the Message field. Therefore we must obtain the offset from + //the typedescription. The base interface of all exceptions is + //com::sun::star::uno::Exception which contains the message + typelib_CompoundTypeDescription* pCTD = comp_td; + while (pCTD->pBaseTypeDescription) + pCTD = pCTD->pBaseTypeDescription; + int nPos = -1; + + OUString usMessageMember("Message"); + for (int i = 0; i < pCTD->nMembers; i ++) + { + if (usMessageMember.equals(pCTD->ppMemberNames[i])) + { + nPos = i; + break; + } + } + OSL_ASSERT (nPos != -1); + int offset = pCTD->pMemberOffsets[nPos]; + //With the offset within the exception we can get the message string + System::String^ sMessage = mapUnoString(*(rtl_uString**) + ((char*) uno_data + offset)); + //We need to find a constructor for the exception that takes the message string + //We assume that the first argument is the message string + cli::array<sr::ConstructorInfo^>^ arCtorInfo = cliType->GetConstructors(); + sr::ConstructorInfo^ ctorInfo = nullptr; + int numCtors = arCtorInfo->Length; + //Constructor must at least have 2 params for the base + //unoidl.com.sun.star.uno.Exception (String, Object); + cli::array<sr::ParameterInfo^>^ arParamInfo; + for (int i = 0; i < numCtors; i++) + { + arParamInfo = arCtorInfo[i]->GetParameters(); + if (arParamInfo->Length < 2) + continue; + ctorInfo = arCtorInfo[i]; + break; + } + OSL_ASSERT(arParamInfo[0]->ParameterType->Equals(System::String::typeid) + && arParamInfo[1]->ParameterType->Equals(System::Object::typeid) + && arParamInfo[0]->Position == 0 + && arParamInfo[1]->Position == 1); + //Prepare parameters for constructor + int numArgs = arParamInfo->Length; + cli::array<System::Object^>^ args = gcnew cli::array<System::Object^>(numArgs); + //only initialize the first argument with the message + args[0] = sMessage; + cliObj = ctorInfo->Invoke(args); + } + else + cliObj = System::Activator::CreateInstance(cliType); + } + sal_Int32 * pMemberOffsets = comp_td->pMemberOffsets; + + if (comp_td->pBaseTypeDescription) + { + //convert inherited struct + //cliObj is passed inout (args in_param, out_param are true), hence the passed + // cliObj is used by the callee instead of a newly created struct + map_to_cli( + &cliObj, uno_data, + ((typelib_TypeDescription *)comp_td->pBaseTypeDescription)->pWeakRef, nullptr, + true); + } + OUString usUnoException("com.sun.star.uno.Exception"); + for (sal_Int32 nPos = comp_td->nMembers; nPos--; ) + { + typelib_TypeDescriptionReference * member_type = comp_td->ppTypeRefs[ nPos ]; + System::String^ sMemberName= mapUnoString(comp_td->ppMemberNames[nPos]); + sr::FieldInfo^ aField= cliType->GetField(sMemberName); + // special case for Exception.Message. The field has already been + // set while constructing cli object + if ( ! aField && usUnoException.equals(td.get()->pTypeName)) + { + continue; + } + void const * p = (char const *)uno_data + pMemberOffsets[ nPos ]; + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + aField->SetValue(cliObj, *(System::Char*) p); + break; + case typelib_TypeClass_BOOLEAN: + aField->SetValue(cliObj, *(System::Boolean*) p); + break; + case typelib_TypeClass_BYTE: + aField->SetValue(cliObj, *(System::Byte*) p); + break; + case typelib_TypeClass_SHORT: + aField->SetValue(cliObj, *(System::Int16*) p); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + aField->SetValue(cliObj, *(System::UInt16*) p); + break; + case typelib_TypeClass_LONG: + aField->SetValue(cliObj, *(System::Int32*) p); + break; + case typelib_TypeClass_UNSIGNED_LONG: + aField->SetValue(cliObj, *(System::UInt32*) p); + break; + case typelib_TypeClass_HYPER: + aField->SetValue(cliObj, *(System::Int64*) p); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + aField->SetValue(cliObj, *(System::UInt64*) p); + break; + case typelib_TypeClass_FLOAT: + aField->SetValue(cliObj, *(System::Single*) p); + break; + case typelib_TypeClass_DOUBLE: + aField->SetValue(cliObj, *(System::Double*) p); + break; + default: + { + System::Object^ cli_val; + map_to_cli( + &cli_val, p, member_type, nullptr, + false); + aField->SetValue(cliObj, cli_val); + break; + } + } + } + *cli_data= cliObj; + break; + } + case typelib_TypeClass_SEQUENCE: + { + sal_Int32 nElements; + uno_Sequence const * seq = 0; + seq = *(uno_Sequence * const *)uno_data; + nElements = seq->nElements; + + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + ((typelib_IndirectTypeDescription *)td.get())->pType; + + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + { + cli::array<System::Char>^ arChar= gcnew cli::array<System::Char>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arChar, 0, nElements); + *cli_data= arChar; + break; + } + case typelib_TypeClass_BOOLEAN: + { + cli::array<System::Byte>^ arBool= gcnew cli::array<System::Byte>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arBool, 0, nElements); + *cli_data= dynamic_cast<cli::array<System::Boolean>^>(arBool); + break; + } + case typelib_TypeClass_BYTE: + { + cli::array<System::Byte>^ arByte= gcnew cli::array<System::Byte>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arByte, 0, nElements); + *cli_data= arByte; + break; + } + case typelib_TypeClass_SHORT: + { + cli::array<System::Int16>^ arShort= gcnew cli::array<System::Int16>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arShort, 0, nElements); + *cli_data= arShort; + break; + } + case typelib_TypeClass_UNSIGNED_SHORT: + { + cli::array<System::UInt16>^ arUInt16= gcnew cli::array<System::UInt16>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), dynamic_cast<cli::array<System::Int16>^>(arUInt16), + 0, nElements); + *cli_data= arUInt16; + break; + } + case typelib_TypeClass_LONG: + { + cli::array<System::Int32>^ arInt32= gcnew cli::array<System::Int32>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arInt32, 0, nElements); + *cli_data= arInt32; + break; + } + case typelib_TypeClass_UNSIGNED_LONG: + { + cli::array<System::UInt32>^ arUInt32= gcnew cli::array<System::UInt32>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), dynamic_cast<cli::array<System::Int32>^>(arUInt32), + 0, nElements); + *cli_data= arUInt32; + break; + } + case typelib_TypeClass_HYPER: + { + cli::array<System::Int64>^ arInt64= gcnew cli::array<System::Int64>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arInt64, 0, nElements); + *cli_data= arInt64; + break; + } + //FIXME: Marshal::Copy of UInt64? + case typelib_TypeClass_UNSIGNED_HYPER: + { + cli::array<System::IntPtr>^ arUInt64= gcnew cli::array<System::IntPtr>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arUInt64, 0, nElements); + *cli_data= dynamic_cast<cli::array<System::UInt64>^>(arUInt64); + break; + } + case typelib_TypeClass_FLOAT: + { + cli::array<System::Single>^ arSingle= gcnew cli::array<System::Single>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arSingle, 0, nElements); + *cli_data= arSingle; + break; + } + case typelib_TypeClass_DOUBLE: + { + cli::array<System::Double>^ arDouble= gcnew cli::array<System::Double>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arDouble, 0, nElements); + *cli_data= arDouble; + break; + } + case typelib_TypeClass_STRING: + { + cli::array<System::String^>^ arString= gcnew cli::array<System::String^>(nElements); + for (int i= 0; i < nElements; i++) + { + rtl_uString *aStr= ((rtl_uString**)(&seq->elements))[i]; + arString[i]= gcnew System::String( (__wchar_t *) &aStr->buffer, 0, aStr->length); + } + *cli_data= arString; + break; + } + case typelib_TypeClass_TYPE: + { + cli::array<System::Type^>^ arType= gcnew cli::array<System::Type^>(nElements); + for (int i= 0; i < nElements; i++) + { + arType[i]= + mapUnoType( ((typelib_TypeDescriptionReference**) seq->elements)[i]); + } + *cli_data= arType; + break; + } + case typelib_TypeClass_ANY: + { + cli::array<uno::Any>^ arCli= gcnew cli::array<uno::Any>(nElements); + uno_Any const * p = (uno_Any const *)seq->elements; + for (sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ cli_obj = nullptr; + map_to_cli( + &cli_obj, &p[ nPos ], element_type, nullptr, false); + arCli[nPos]= *safe_cast<uno::Any^>(cli_obj); + } + *cli_data= arCli; + break; + } + case typelib_TypeClass_ENUM: + { + //get the Enum type + System::Type^ enumType= nullptr; + if (info != nullptr) + { + //info is EnumType[]&, remove & + OSL_ASSERT(info->IsByRef); + enumType = info->GetElementType(); + //enumType is EnumType[], remove [] + enumType = enumType->GetElementType(); + } + else + enumType= mapUnoType(element_type); + + System::Array^ arEnum = System::Array::CreateInstance( + enumType, nElements); + for (int i= 0; i < nElements; i++) + { + arEnum->SetValue(System::Enum::ToObject(enumType, + System::Int32(((sal_Int32*) seq->elements)[i])), i); + } + *cli_data = arEnum; + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + TypeDescr element_td( element_type ); + System::Array^ ar= System::Array::CreateInstance( + mapUnoType(element_type),nElements); + if (0 < nElements) + { + // ToDo check this + char * p = (char *) &seq->elements; + sal_Int32 nSize = element_td.get()->nSize; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, p + (nSize * nPos), element_type, nullptr, false); + ar->SetValue(val, System::Int32(nPos)); + } + } + *cli_data = ar; + break; + } +// ToDo, verify + case typelib_TypeClass_SEQUENCE: + { + System::Array ^ar= System::Array::CreateInstance( + mapUnoType(element_type), nElements); + if (0 < nElements) + { + TypeDescr element_td( element_type ); + uno_Sequence ** elements = (uno_Sequence**) seq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, &elements[nPos], element_type, nullptr, false); + ar->SetValue(val, System::Int32(nPos)); + } + } + *cli_data = ar; + break; + } + case typelib_TypeClass_INTERFACE: + { + TypeDescr element_td( element_type ); + System::Type ^ ifaceType= mapUnoType(element_type); + System::Array^ ar= System::Array::CreateInstance(ifaceType, nElements); + + char * p = (char *)seq->elements; + sal_Int32 nSize = element_td.get()->nSize; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, p + (nSize * nPos), element_type, nullptr, false); + + ar->SetValue(val, System::Int32(nPos)); + } + *cli_data= ar; + break; + } + default: + { + throw BridgeRuntimeError("[map_to_cli():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported element type: " + OUString::unacquired( &element_type->pTypeName )); + } + } + break; + } + case typelib_TypeClass_INTERFACE: + { + uno_Interface * pUnoI = *(uno_Interface * const *)uno_data; + if (0 != pUnoI) + { + TypeDescr td( type ); + *cli_data= map_uno2cli( pUnoI, reinterpret_cast< + typelib_InterfaceTypeDescription*>(td.get())) ; + } + else + *cli_data= nullptr; + break; + } + default: + { + //ToDo check this exception. The String is probably crippled + throw BridgeRuntimeError("[map_to_cli():" + OUString::unacquired( &type->pTypeName ) + "] unsupported type!"); + } + } //switch +} // method +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_environment.cxx b/cli_ure/source/uno_bridge/cli_environment.cxx new file mode 100644 index 000000000..3c054b5be --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_environment.cxx @@ -0,0 +1,168 @@ +/* -*- 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 "Cli_environment.h" + +#using <cli_ure.dll> +#using <system.dll> +#include "osl/diagnose.h" +#include "cli_proxy.h" + +using namespace System::Runtime::Remoting; +using namespace System::Runtime::Remoting::Proxies; +using namespace System::Collections; +using namespace System::Text; +using namespace System::Diagnostics; +using namespace System::Threading; + +namespace cli_uno +{ + +inline System::String^ Cli_environment::createKey(System::String^ oid, System::Type^ t) +{ + return System::String::Concat(oid, t->FullName); +} + + +inline Cli_environment::Cli_environment() +{ +#if OSL_DEBUG_LEVEL >= 2 + _numRegisteredObjects = 0; +#endif +} + +Cli_environment::~Cli_environment() ///< IDisposable Cli_environment::Dispose() +{ + this->!Cli_environment(); // call finalizer +} + +Cli_environment::!Cli_environment() ///< Cli_environment::Finalize() +{ +#if OSL_DEBUG_LEVEL >= 2 + OSL_ENSURE(_numRegisteredObjects == 0, + "cli uno bridge: CLI environment contains unrevoked objects"); +#endif +} + +System::Object^ Cli_environment::registerInterface( + System::Object^ obj, System::String^ oid) +{ +#if OSL_DEBUG_LEVEL >= 1 + //obj must be a transparent proxy + OSL_ASSERT(RemotingServices::IsTransparentProxy(obj)); + _numRegisteredObjects ++; +#endif + OSL_ASSERT( ! m_objects->ContainsKey(oid)); + m_objects->Add(oid, gcnew WeakReference(obj)); + return obj; +} +System::Object^ Cli_environment::registerInterface ( + System::Object^ obj, System::String^ oid, System::Type^ type) +{ +#if OSL_DEBUG_LEVEL >= 1 + //obj must be a real cli object + OSL_ASSERT( ! RemotingServices::IsTransparentProxy(obj)); + _numRegisteredObjects ++; +#endif + System::String^ key = createKey(oid, type); + //see synchronization in map_uno2cli in cli_data.cxx + OSL_ASSERT( ! m_objects->ContainsKey(key)); + m_objects->Add(key, gcnew WeakReference(obj)); + return obj; +} + +void Cli_environment::revokeInterface(System::String^ oid, System::Type^ type) +{ + System::String^ key = type != nullptr ? createKey(oid, type) : oid; +#if OSL_DEBUG_LEVEL >= 1 + _numRegisteredObjects --; +#endif +#if OSL_DEBUG_LEVEL >= 2 + int i = 1; + if (m_objects->ContainsKey(key) == false) + { + Trace::WriteLine("cli uno bridge: try to revoke unregistered interface"); + Trace::WriteLine(oid); + i = 0; + } + Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: {0} remaining registered interfaces"), + m_objects->Count - 1)); +#endif + m_objects->Remove(key); +} + +inline void Cli_environment::revokeInterface(System::String^ oid) +{ + return revokeInterface(oid, nullptr); +} + +System::Object^ Cli_environment::getRegisteredInterface(System::String^ oid, + System::Type^ type) +{ + //try if it is a UNO interface + System::Object^ ret = m_objects[oid]; + if (! ret) + { + //try if it is a proxy for a cli object + oid = createKey(oid, type); + ret = m_objects[ oid ]; + } + if (ret != nullptr) + { + System::WeakReference^ weakIface = + static_cast< System::WeakReference ^ >( ret ); + ret = weakIface->Target; + } + if (ret == nullptr) + m_objects->Remove( oid ); + return ret; +} + +System::String^ Cli_environment::getObjectIdentifier(System::Object^ obj) +{ + System::String^ oId= nullptr; + RealProxy^ aProxy= RemotingServices::GetRealProxy(obj); + if (aProxy) + { + UnoInterfaceProxy^ proxyImpl= dynamic_cast<UnoInterfaceProxy^>(aProxy); + if (proxyImpl) + oId= proxyImpl->getOid(); + } + + if (oId == nullptr) + { + StringBuilder ^ buf= gcnew StringBuilder(256); + bool bFirst = new bool(false); + System::Threading::Monitor::Enter(Cli_environment::typeid); + try { + buf->Append(m_IDGen->GetId(obj, bFirst)); + } finally + { + System::Threading::Monitor::Exit(Cli_environment::typeid); + } + + buf->Append(sOidPart); + oId= buf->ToString(); + } + return oId; +} +} //namespace cli_uno + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_environment.h b/cli_ure/source/uno_bridge/cli_environment.h new file mode 100644 index 000000000..3828d971c --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_environment.h @@ -0,0 +1,105 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_ENVIRONMENT_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_ENVIRONMENT_H + +#include "cli_base.h" + +using namespace System; +using namespace System::Collections; +using namespace System::Runtime::Serialization; + +namespace cli_uno +{ +public +ref class Cli_environment +{ + static System::String ^ sOidPart; + static Hashtable ^ m_objects; + static System::Runtime::Serialization::ObjectIDGenerator ^ m_IDGen; + inline static System::String ^ createKey(System::String ^ oid, System::Type ^ t); + +#if OSL_DEBUG_LEVEL >= 1 + int _numRegisteredObjects; +#endif + + static Cli_environment() + { + m_objects = Hashtable::Synchronized(gcnew Hashtable()); + m_IDGen = gcnew System::Runtime::Serialization::ObjectIDGenerator(); + System::Text::StringBuilder ^ buffer = gcnew System::Text::StringBuilder(256); + Guid gd = Guid::NewGuid(); + buffer->Append(";cli[0];"); + buffer->Append(gd.ToString()); + sOidPart = buffer->ToString(); + } + +public: + inline Cli_environment(); + + ~Cli_environment(); + !Cli_environment(); + + /** + Registers a UNO object as being mapped by this bridge. The resulting + cli object is represents all interfaces of the UNO object. Therefore the + object can be registered only with its OID; a type is not necessary. + */ + Object ^ registerInterface(Object ^ obj, System::String ^ oid); + /** + Registers a CLI object as being mapped by this bridge. The resulting + object represents exactly one UNO interface. + */ + Object ^ registerInterface(Object ^ obj, System::String ^ oid, System::Type ^ type); + + /** + By revoking an interface it is declared that the respective interface has + not been mapped. The proxy implementations call revoke interface in their + destructors. + */ + inline void revokeInterface(System::String ^ oid); + + void revokeInterface(System::String ^ oid, System::Type ^ type); + /** + * Retrieves an interface identified by its object id and type from this + * environment. + * + * @param oid object id of interface to be retrieved + * @param type the type description of the interface to be retrieved + * @see com.sun.star.uno.IEnvironment#getRegisteredInterface + */ + Object ^ getRegisteredInterface(System::String ^ oid, System::Type ^ type); + + /** + * Generates a worldwide unique object identifier (oid) for the given object. It is + * guaranteed, that subsequent calls to the method with the same object + * will give the same id. + * <p> + * @return the generated oid. + * @param object the object for which an Oid should be generated. + */ + static System::String ^ getObjectIdentifier(Object ^ obj); +}; + +} //namespace cli_uno + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_proxy.cxx b/cli_ure/source/uno_bridge/cli_proxy.cxx new file mode 100644 index 000000000..22c478177 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_proxy.cxx @@ -0,0 +1,1105 @@ +/* -*- 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 "typelib/typedescription.h" +#include "rtl/ustrbuf.hxx" +#include <sal/log.hxx> +#include "com/sun/star/uno/RuntimeException.hpp" +#include "cli_proxy.h" +#include "cli_base.h" +#include "cli_bridge.h" + +#using <cli_ure.dll> +#using <cli_uretypes.dll> + +namespace sr = System::Reflection; +namespace st = System::Text; +namespace sc = System::Collections; +namespace srrm = System::Runtime::Remoting::Messaging; +namespace srr = System::Runtime::Remoting; +namespace srrp = System::Runtime::Remoting::Proxies; +namespace sd = System::Diagnostics; +namespace ucss = unoidl::com::sun::star; + +using namespace cli_uno; + +extern "C" +{ + +void SAL_CALL cli_proxy_free( uno_ExtEnvironment * env, void * proxy ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args[], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C(); + + +} + +namespace cli_uno +{ + +UnoInterfaceInfo::UnoInterfaceInfo(Bridge const * bridge, uno_Interface* unoI, + typelib_InterfaceTypeDescription* td): + + m_unoI(unoI), + m_typeDesc(td), + m_bridge(bridge) +{ + m_bridge->acquire(); + m_type = mapUnoType(reinterpret_cast<typelib_TypeDescription*>(td)); + m_unoI->acquire(m_unoI); + typelib_typedescription_acquire(&m_typeDesc->aBase); + if ( ! m_typeDesc->aBase.bComplete) + { + typelib_TypeDescription* _pt = &m_typeDesc->aBase; + sal_Bool bComplete = ::typelib_typedescription_complete( & _pt); + if( ! bComplete) + { + throw BridgeRuntimeError("cannot make type complete: " + OUString::unacquired(& m_typeDesc->aBase.pTypeName)); + } + } +} + +UnoInterfaceInfo::~UnoInterfaceInfo() ///< IDisposable UnoInterfaceInfo::Dispose() +{ + this->!UnoInterfaceInfo(); // call finalizer +} + +UnoInterfaceInfo::!UnoInterfaceInfo() ///< UnoInterfaceInfo::Finalize() +{ + //accessing unmanaged objects is ok. + m_bridge->m_uno_env->revokeInterface( + m_bridge->m_uno_env, m_unoI ); + m_bridge->release(); + + m_unoI->release(m_unoI); + typelib_typedescription_release( + reinterpret_cast<typelib_TypeDescription*>(m_typeDesc)); +} + +UnoInterfaceProxy::UnoInterfaceProxy( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid ) + :RealProxy(MarshalByRefObject::typeid), + m_bridge(bridge), + m_oid(mapUnoString(oid.pData)), + m_sTypeName(m_system_Object_String) +{ + m_bridge->acquire(); + // create the list that holds all UnoInterfaceInfos + m_listIfaces = gcnew ArrayList(10); + m_numUnoIfaces = 0; + m_listAdditionalProxies = gcnew ArrayList(); + m_nlistAdditionalProxies = 0; + //put the information of the first UNO interface into the arraylist +#if OSL_DEBUG_LEVEL >= 2 + _numInterfaces = 0; + _sInterfaces = NULL; +#endif + addUnoInterface(pUnoI, pTD); + +} + +UnoInterfaceProxy::~UnoInterfaceProxy() ///< IDisposable UnoInterfaceProxy::Dispose() +{ + this->!UnoInterfaceProxy(); // call finalizer +} + +UnoInterfaceProxy::!UnoInterfaceProxy() ///< UnoInterfaceProxy::Finalize() +{ +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Destroying proxy " + "for UNO object, OID: \n\t{0} \n\twith uno interfaces: "), + m_oid)); + + sd::Trace::WriteLine( mapUnoString(_sInterfaces)); + rtl_uString_release(_sInterfaces); +#endif + //m_bridge is unmanaged, therefore we can access it in this finalizer + CliEnvHolder::g_cli_env->revokeInterface(m_oid); + m_bridge->release(); +} + +System::Object^ UnoInterfaceProxy::create( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid) +{ + UnoInterfaceProxy^ proxyHandler= + gcnew UnoInterfaceProxy(bridge, pUnoI, pTD, oid); + System::Object^ proxy= proxyHandler->GetTransparentProxy(); + CliEnvHolder::g_cli_env->registerInterface(proxy, mapUnoString(oid.pData)); + return proxy; +} + + +void UnoInterfaceProxy::addUnoInterface(uno_Interface* pUnoI, + typelib_InterfaceTypeDescription* pTd) +{ + sc::IEnumerator^ enumInfos = m_listIfaces->GetEnumerator(); + System::Threading::Monitor::Enter(this); + try + { + while (enumInfos->MoveNext()) + { + UnoInterfaceInfo^ info = static_cast<UnoInterfaceInfo^>( + enumInfos->Current); + if (typelib_typedescription_equals( + reinterpret_cast<typelib_TypeDescription*>(info->m_typeDesc), + reinterpret_cast<typelib_TypeDescription*>(pTd))) + { + return; + } + } + OUString oid(mapCliString(m_oid)); + (*m_bridge->m_uno_env->registerInterface)( + m_bridge->m_uno_env, reinterpret_cast< void ** >( &pUnoI ), + oid.pData, pTd); + //This proxy does not contain the uno_Interface. Add it. + m_listIfaces->Add(gcnew UnoInterfaceInfo(m_bridge, pUnoI, pTd)); + m_numUnoIfaces = m_listIfaces->Count; +#if OSL_DEBUG_LEVEL >= 2 + System::String^ sInterfaceName = static_cast<UnoInterfaceInfo^>( + m_listIfaces[m_numUnoIfaces - 1])->m_type->FullName; + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Creating proxy for uno object, " + "id:\n\t{0}\n\t{1}"), m_oid, sInterfaceName)); + // add to the string that contains all interface names + _numInterfaces++; + OUString _sNewInterface = "\t" + OUString::number(_numInterfaces) + ". " + mapCliString(sInterfaceName) + "\n"; + pin_ptr<rtl_uString *> pp_sInterfaces = &_sInterfaces; + rtl_uString_newConcat( pp_sInterfaces, * pp_sInterfaces, + _sNewInterface.pData); +#endif + } + __finally { + System::Threading::Monitor::Exit(this); + } +} + + +// IRemotingTypeInfo +bool UnoInterfaceProxy::CanCastTo(System::Type^ fromType, + System::Object^) +{ + if (fromType == System::Object::typeid) // trivial case + return true; + + System::Threading::Monitor::Enter(this); + try + { + if (nullptr != findInfo( fromType )) // proxy supports demanded interface + return true; + + //query a uno interface for the required type + + // we use the first interface in the list (m_listIfaces) to make + // the queryInterface call + UnoInterfaceInfo^ info = + static_cast<UnoInterfaceInfo^>(m_listIfaces[0]); + css::uno::TypeDescription membertd( + reinterpret_cast<typelib_InterfaceTypeDescription*>( + info->m_typeDesc)->ppAllMembers[0]); + array<System::Object^>^ args = gcnew array<System::Object^>(1); + + args[0] = fromType; + uno::Any ^ pAny; + System::Object^ pException = nullptr; + + pAny= static_cast<uno::Any ^>( + m_bridge->call_uno( + info->m_unoI, + membertd.get(), + ((typelib_InterfaceMethodTypeDescription*) + membertd.get())->pReturnTypeRef, + 1, + ((typelib_InterfaceMethodTypeDescription*) + membertd.get())->pParams, + args, nullptr, &pException) ); + + // handle regular exception from target + OSL_ENSURE( + nullptr == pException, + OUStringToOString( + mapCliString( pException->ToString()), + RTL_TEXTENCODING_UTF8 ).getStr() ); + + if (pAny->Type != void::typeid) // has value? + { + if (nullptr != findInfo( fromType )) + { + // proxy now supports demanded interface + return true; + } + + // via aggregation: it is possible that queryInterface() returns + // and interface with a different oid. + // That way, this type is supported for the CLI + // interpreter (CanCastTo() returns true) + ::System::Object ^ obj = pAny->Value; + OSL_ASSERT( srr::RemotingServices::IsTransparentProxy( obj ) ); + if (srr::RemotingServices::IsTransparentProxy( obj )) + { + UnoInterfaceProxy ^ proxy = + static_cast< UnoInterfaceProxy ^ >( + srr::RemotingServices::GetRealProxy( obj ) ); + OSL_ASSERT( nullptr != proxy->findInfo( fromType ) ); + m_listAdditionalProxies->Add( proxy ); + m_nlistAdditionalProxies = m_listAdditionalProxies->Count; + OSL_ASSERT( nullptr != findInfo( fromType ) ); + return true; + } + } + } + catch (BridgeRuntimeError& e) + { + (void) e; // avoid warning + OSL_FAIL( + OUStringToOString( + e.m_message, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + catch (System::Exception^ e) + { + System::String^ msg= gcnew System::String( + "An unexpected CLI exception occurred in " + "UnoInterfaceProxy::CanCastTo(). Original" + "message: \n"); + msg= System::String::Concat(msg, e->Message); + OSL_FAIL( + OUStringToOString( + mapCliString(msg), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + catch (...) + { + OSL_FAIL( + "An unexpected native C++ exception occurred in " + "UnoInterfaceProxy::CanCastTo()" ); + } + __finally + { + System::Threading::Monitor::Exit(this); + } + return false; +} + +srrm::IMessage^ UnoInterfaceProxy::invokeObject( + sc::IDictionary^ props, + srrm::LogicalCallContext^ context, + srrm::IMethodCallMessage^ mcm) +{ + System::Object^ retMethod = nullptr; + System::String^ sMethod = static_cast<System::String^> + (props[m_methodNameString]); + array<System::Object^>^ args = static_cast<array<System::Object^>^>( + props[m_ArgsString]); + if (m_Equals_String->Equals(sMethod)) + { + // Object.Equals + OSL_ASSERT(args->Length == 1); + srrp::RealProxy^ rProxy = srr::RemotingServices::GetRealProxy(args[0]); + bool bDone = false; + if (rProxy) + { + UnoInterfaceProxy^ unoProxy = + dynamic_cast<UnoInterfaceProxy^>(rProxy); + if (unoProxy) + { + bool b = m_oid->Equals(unoProxy->getOid()); + retMethod = b; + bDone = true; + } + } + if (bDone == false) + { + //no proxy or not our proxy, therefore Equals must be false + retMethod = false; + } + } + else if (m_GetHashCode_String->Equals(sMethod)) + { + // Object.GetHashCode + int nHash = m_oid->GetHashCode(); + retMethod = nHash; + } + else if (m_GetType_String->Equals(sMethod)) + { + // Object.GetType + retMethod = System::Object::typeid; + } + else if (m_ToString_String->Equals(sMethod)) + { + // Object.ToString + st::StringBuilder^ sb = gcnew st::StringBuilder(256); +// sb->AppendFormat("Uno object proxy. Implemented interface: {0}" +// ". OID: {1}", m_type->ToString(), m_oid); + sb->AppendFormat("Uno object proxy. OID: {0}", m_oid); + retMethod = sb->ToString(); + } + else + { + //Either Object has new functions or a protected method was called + //which should not be possible + OSL_ASSERT(0); + } + srrm::IMessage^ retVal= gcnew srrm::ReturnMessage( + retMethod, gcnew array<System::Object^>(0), 0, context, mcm); + return retVal; +} + +UnoInterfaceInfo ^ UnoInterfaceProxy::findInfo( ::System::Type ^ type ) +{ + for (int i = 0; i < m_numUnoIfaces; i++) + { + UnoInterfaceInfo^ tmpInfo = static_cast<UnoInterfaceInfo^>( + m_listIfaces[i]); + if (type->IsAssignableFrom(tmpInfo->m_type)) + return tmpInfo; + } + for ( int i = 0; i < m_nlistAdditionalProxies; ++i ) + { + UnoInterfaceProxy ^ proxy = + static_cast< UnoInterfaceProxy ^ >( + m_listAdditionalProxies[ i ] ); + UnoInterfaceInfo ^ info = proxy->findInfo( type ); + if (nullptr != info) + return info; + } + return nullptr; +} + +srrm::IMessage^ UnoInterfaceProxy::Invoke(srrm::IMessage^ callmsg) +{ + try + { + sc::IDictionary^ props= callmsg->Properties; + srrm::LogicalCallContext^ context= + static_cast<srrm::LogicalCallContext^>( + props[m_CallContextString]); + srrm::IMethodCallMessage^ mcm= + static_cast<srrm::IMethodCallMessage^>(callmsg); + + //Find out which UNO interface is being called + System::String^ sTypeName = static_cast<System::String^>( + props[m_typeNameString]); + sTypeName = sTypeName->Substring(0, sTypeName->IndexOf(',')); + + // Special Handling for System.Object methods + if(sTypeName->IndexOf(m_system_Object_String) != -1) + { + return invokeObject(props, context, mcm); + } + + System::Type^ typeBeingCalled = loadCliType(sTypeName); + UnoInterfaceInfo^ info = findInfo( typeBeingCalled ); + OSL_ASSERT( nullptr != info ); + + // ToDo do without string conversion, an OUString is not needed here + // get the type description of the call + OUString usMethodName(mapCliString(static_cast<System::String^>( + props[m_methodNameString]))); + typelib_TypeDescriptionReference ** ppAllMembers = + info->m_typeDesc->ppAllMembers; + sal_Int32 numberMembers = info->m_typeDesc->nAllMembers; + for ( sal_Int32 nPos = numberMembers; nPos--; ) + { + typelib_TypeDescriptionReference * member_type = ppAllMembers[nPos]; + + // check usMethodName against fully qualified usTypeName + // of member_type; usTypeName is of the form + // <name> "::" <usMethodName> *(":@" <idx> "," <idx> ":" <name>) + OUString const & usTypeName = + OUString::unacquired( & member_type->pTypeName ); + +#if OSL_DEBUG_LEVEL >= 2 + System::String^ pTypeName; + pTypeName = mapUnoString(usTypeName.pData); +#endif + sal_Int32 offset = usTypeName.indexOf( ':' ) + 2; + OSL_ASSERT( + offset >= 2 && offset < usTypeName.getLength() + && usTypeName[offset - 1] == ':' ); + sal_Int32 remainder = usTypeName.getLength() - offset; + + if (typelib_TypeClass_INTERFACE_METHOD == member_type->eTypeClass) + { + if ((usMethodName.getLength() == remainder + || (usMethodName.getLength() < remainder + && usTypeName[offset + usMethodName.getLength()] == ':')) + && usTypeName.match(usMethodName, offset)) + { + TypeDescr member_td( member_type ); + typelib_InterfaceMethodTypeDescription * method_td = + (typelib_InterfaceMethodTypeDescription *) + member_td.get(); + + array<System::Object^>^ args = static_cast<array<System::Object^>^>( + props[m_ArgsString]); + array<System::Type^>^ argTypes = static_cast<array<System::Type^>^>( + props[m_methodSignatureString]); + System::Object^ pExc = nullptr; + System::Object ^ cli_ret = m_bridge->call_uno( + info->m_unoI, member_td.get(), + method_td->pReturnTypeRef, method_td->nParams, + method_td->pParams, args, argTypes, &pExc); + return constructReturnMessage(cli_ret, args, method_td, + callmsg, pExc); + } + } + else + { + OSL_ASSERT( typelib_TypeClass_INTERFACE_ATTRIBUTE == + member_type->eTypeClass ); + if (usMethodName.getLength() > 4 + && (usMethodName.getLength() - 4 == remainder + || (usMethodName.getLength() - 4 < remainder + && usTypeName[ + offset + (usMethodName.getLength() - 4)] == ':')) + && usMethodName[1] == 'e' && usMethodName[2] == 't' + && rtl_ustr_compare_WithLength( + usTypeName.getStr() + offset, + usMethodName.getLength() - 4, + usMethodName.getStr() + 4, + usMethodName.getLength() - 4) == 0) + { + if ('g' == usMethodName[0]) + { + TypeDescr member_td( member_type ); + typelib_InterfaceAttributeTypeDescription * attribute_td = + (typelib_InterfaceAttributeTypeDescription*) + member_td.get(); + + System::Object^ pExc = nullptr; + System::Object^ cli_ret= m_bridge->call_uno( + info->m_unoI, member_td.get(), + attribute_td->pAttributeTypeRef, + 0, 0, + nullptr, nullptr, &pExc); + return constructReturnMessage(cli_ret, nullptr, NULL, + callmsg, pExc); + } + else if ('s' == usMethodName[0]) + { + TypeDescr member_td( member_type ); + typelib_InterfaceAttributeTypeDescription * attribute_td = + (typelib_InterfaceAttributeTypeDescription *) + member_td.get(); + if (! attribute_td->bReadOnly) + { + typelib_MethodParameter param; + param.pTypeRef = attribute_td->pAttributeTypeRef; + param.bIn = sal_True; + param.bOut = sal_False; + + array<System::Object^>^ args = + static_cast<array<System::Object^>^>( + props[m_ArgsString]); + System::Object^ pExc = nullptr; + m_bridge->call_uno( + info->m_unoI, member_td.get(), + cppu::UnoType<void>::get().getTypeLibType(), + 1, ¶m, args, nullptr, &pExc); + return constructReturnMessage(nullptr, nullptr, NULL, + callmsg, pExc); + } + else + { + return constructReturnMessage(nullptr, nullptr, NULL, + callmsg, nullptr); + } + } + break; + } + } + } + // ToDo check if the message of the exception is not crippled + // the thing that should not be... no method info found! + throw BridgeRuntimeError("[cli_uno bridge]calling undeclared function on interface " + + OUString::unacquired(& ((typelib_TypeDescription *)info->m_typeDesc)->pTypeName) + + ": " + usMethodName); + } + catch (BridgeRuntimeError & err) + { + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + mapUnoString(err.m_message.pData), nullptr), mcm); + } + catch (System::Exception^ e) + { + st::StringBuilder ^ sb = gcnew st::StringBuilder(512); + sb->Append(gcnew System::String( + "An unexpected CLI exception occurred in " + "UnoInterfaceProxy::Invoke. Original" + "message: \n")); + sb->Append(e->Message); + sb->Append((__wchar_t) '\n'); + sb->Append(e->StackTrace); + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + sb->ToString(), nullptr), mcm); + } + catch (...) + { + System::String^ msg = gcnew System::String( + "An unexpected native C++ exception occurred in " + "UnoInterfaceProxy::Invoke."); + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + msg, nullptr), mcm); + } + return nullptr; +} +/** If the argument args is NULL then this function is called for an attribute + method (either setXXX or getXXX). + For attributes the argument mtd is also NULL. +*/ +srrm::IMessage^ UnoInterfaceProxy::constructReturnMessage( + System::Object^ cliReturn, + array<System::Object^>^ args, + typelib_InterfaceMethodTypeDescription* mtd, + srrm::IMessage^ msg, System::Object^ exc) +{ + srrm::IMessage ^ retVal= nullptr; + srrm::IMethodCallMessage^ mcm = static_cast<srrm::IMethodCallMessage^>(msg); + if (exc) + { + retVal = gcnew srrm::ReturnMessage( + dynamic_cast<System::Exception^>(exc), mcm); + } + else + { + sc::IDictionary^ props= msg->Properties; + srrm::LogicalCallContext^ context= + static_cast<srrm::LogicalCallContext^>( + props[m_CallContextString]); + if (args != nullptr) + { + // Method + //build the array of out parameters, allocate max length + array<System::Object^>^ arOut= gcnew array<System::Object^>(mtd->nParams); + int nOut = 0; + for (int i= 0; i < mtd->nParams; i++) + { + if (mtd->pParams[i].bOut) + { + arOut[i]= args[i]; + nOut++; + } + } + retVal= gcnew srrm::ReturnMessage(cliReturn, arOut, nOut, + context, mcm); + } + else + { + // Attribute (getXXX) + retVal= gcnew srrm::ReturnMessage(cliReturn, nullptr, 0, + context, mcm); + } + } + return retVal; +} + + +CliProxy::CliProxy(Bridge const* bridge, System::Object^ cliI, + typelib_TypeDescription const* td, + const OUString& usOid): + m_ref(1), + m_bridge(bridge), + m_cliI(cliI), + m_unoType(const_cast<typelib_TypeDescription*>(td)), + m_usOid(usOid), + m_oid(mapUnoString(usOid.pData)), + m_nInheritedInterfaces(0) +{ + m_bridge->acquire(); + uno_Interface::acquire = cli_proxy_acquire; + uno_Interface::release = cli_proxy_release; + uno_Interface::pDispatcher = cli_proxy_dispatch; + + m_unoType.makeComplete(); + m_type= mapUnoType(m_unoType.get()); + + makeMethodInfos(); +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Creating proxy for cli object, " + "id:\n\t{0}\n\t{1}"), m_oid, m_type)); +#endif + +} + +void CliProxy::makeMethodInfos() +{ +#if OSL_DEBUG_LEVEL >= 2 + System::Object^ cliI; + System::Type^ type; + cliI = m_cliI; + type = m_type; +#endif + + if (m_type->IsInterface == false) + return; + array<sr::MethodInfo^>^ arThisMethods = m_type->GetMethods(); + //get the inherited interfaces + array<System::Type^>^ arInheritedIfaces = m_type->GetInterfaces(); + m_nInheritedInterfaces = arInheritedIfaces->Length; + //array containing the number of methods for the interface and its + //inherited interfaces + m_arInterfaceMethodCount = gcnew array<int^>(m_nInheritedInterfaces + 1); + //determine the number of all interface methods, including the inherited + //interfaces + int numMethods = arThisMethods->Length; + for (int i= 0; i < m_nInheritedInterfaces; i++) + { + numMethods += arInheritedIfaces[i]->GetMethods()->Length; + } + //array containing MethodInfos of the cli object + m_arMethodInfos = gcnew array<sr::MethodInfo^>(numMethods); + //array containing MethodInfos of the interface + m_arInterfaceMethodInfos = gcnew array<sr::MethodInfo^>(numMethods); + //array containing the mapping of Uno interface pos to pos in + //m_arMethodInfos + m_arUnoPosToCliPos = gcnew array<System::Int32>(numMethods); + // initialize with -1 + for (int i = 0; i < numMethods; i++) + m_arUnoPosToCliPos[i] = -1; + + //fill m_arMethodInfos with the mappings + // !!! InterfaceMapping.TargetMethods should be MethodInfo*[] according + // to documentation + // but it is Type*[] instead. Bug in the framework? + System::Type^ objType = m_cliI->GetType(); + try + { + int index = 0; + // now get the methods from the inherited interface + //arInheritedIfaces[0] is the direct base interface + //arInheritedIfaces[n] is the furthest inherited interface + //Start with the base interface + int nArLength = arInheritedIfaces->Length; + for (;nArLength > 0; nArLength--) + { + sr::InterfaceMapping mapInherited = objType->GetInterfaceMap( + arInheritedIfaces[nArLength - 1]); + int numMethods = mapInherited.TargetMethods->Length; + m_arInterfaceMethodCount[nArLength - 1] = numMethods; + for (int i = 0; i < numMethods; i++, index++) + { + m_arMethodInfos[index] = safe_cast<sr::MethodInfo^>( + mapInherited.TargetMethods[i]); + + m_arInterfaceMethodInfos[index] = safe_cast<sr::MethodInfo^>( + mapInherited.InterfaceMethods[i]); + } + } + //At last come the methods of the furthest derived interface + sr::InterfaceMapping map = objType->GetInterfaceMap(m_type); + nArLength = map.TargetMethods->Length; + m_arInterfaceMethodCount[m_nInheritedInterfaces] = nArLength; + for (int i = 0; i < nArLength; i++,index++) + { + m_arMethodInfos[index]= safe_cast<sr::MethodInfo^>( + map.TargetMethods[i]); + m_arInterfaceMethodInfos[index]= safe_cast<sr::MethodInfo^>( + map.InterfaceMethods[i]); + } + } + catch (System::InvalidCastException^ ) + { + throw BridgeRuntimeError("[cli_uno bridge] preparing proxy for cli interface: " + mapCliString(m_type->ToString()) + " \nfailed!"); + } +} + +sr::MethodInfo^ CliProxy::getMethodInfo(int nUnoFunctionPos, + const OUString& usMethodName, MethodKind methodKind) +{ + sr::MethodInfo^ ret = nullptr; + //deduct 3 for XInterface methods + nUnoFunctionPos -= 3; + System::Threading::Monitor::Enter(m_arUnoPosToCliPos); + try + { + int cliPos = m_arUnoPosToCliPos[nUnoFunctionPos]; + if (cliPos != -1) + return m_arMethodInfos[cliPos]; + + //create the method function name + System::String^ sMethodName = mapUnoString(usMethodName.pData); + switch (methodKind) + { + case MK_METHOD: + break; + case MK_SET: + sMethodName = System::String::Concat( + const_cast<System::String^>(Constants::sAttributeSet), + sMethodName); + break; + case MK_GET: + sMethodName = System::String::Concat( + const_cast<System::String^>(Constants::sAttributeGet), + sMethodName); + break; + default: + OSL_ASSERT(0); + } + //Find the cli interface method that corresponds to the Uno method +// System::String* sMethodName= mapUnoString(usMethodName.pData); + int indexCliMethod = -1; + //If the cli interfaces and their methods are in the same order + //as they were declared (inheritance chain and within the interface) + //then nUnoFunctionPos should lead to the correct method. However, + //the documentation does not say that this ordering is given. + if (sMethodName->Equals(m_arInterfaceMethodInfos[nUnoFunctionPos]->Name)) + indexCliMethod = nUnoFunctionPos; + else + { + int cMethods = m_arInterfaceMethodInfos->Length; + for (int i = 0; i < cMethods; i++) + { + System::String^ cliMethod = m_arInterfaceMethodInfos[i]->Name; + if (cliMethod->Equals(sMethodName)) + { + indexCliMethod = i; + break; + } + } + } + if (indexCliMethod == -1) + { + throw BridgeRuntimeError("[cli_uno bridge] CliProxy::getMethodInfo():cli object does not implement interface method: " + usMethodName); + } + m_arUnoPosToCliPos[nUnoFunctionPos] = indexCliMethod; + ret = m_arMethodInfos[indexCliMethod]; + } + __finally + { + System::Threading::Monitor::Exit(m_arUnoPosToCliPos); + } + + return ret; +} + +CliProxy::~CliProxy() +{ +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String( + "cli uno bridge: Destroying proxy for cli object, " + "id:\n\t{0}\n\t{1}\n"), + m_oid, m_type)); +#endif + CliEnvHolder::g_cli_env->revokeInterface(m_oid, mapUnoType(m_unoType.get())); + m_bridge->release(); +} + +uno_Interface* CliProxy::create(Bridge const * bridge, + System::Object^ cliI, + typelib_TypeDescription const* pTD, + const OUString& ousOid) +{ + uno_Interface* proxy= static_cast<uno_Interface*>( + new CliProxy(bridge, cliI, pTD, ousOid)); + + //register proxy with target environment (uno) + (*bridge->m_uno_env->registerProxyInterface)( + bridge->m_uno_env, + reinterpret_cast<void**>(&proxy), + cli_proxy_free, + ousOid.pData, (typelib_InterfaceTypeDescription*) pTD); + //register original interface + CliEnvHolder::g_cli_env->registerInterface(cliI, mapUnoString(ousOid.pData), + mapUnoType((pTD))); + + return proxy; +} + + +void SAL_CALL CliProxy::uno_DispatchMethod( + struct _uno_Interface *, + const struct _typelib_TypeDescription *, + void *, + void **, + uno_Any ** ) +{ +} +inline void CliProxy::acquire() const +{ + if (1 == osl_atomic_increment( &m_ref )) + { + // rebirth of proxy zombie + void * that = const_cast< CliProxy * >( this ); + // register at uno env + (*m_bridge->m_uno_env->registerProxyInterface)( + m_bridge->m_uno_env, &that, + cli_proxy_free, m_usOid.pData, + (typelib_InterfaceTypeDescription *)m_unoType.get() ); +#if OSL_DEBUG_LEVEL >= 2 + OSL_ASSERT( this == (void const * const)that ); +#endif + } +} + +inline void CliProxy::release() const +{ + if (0 == osl_atomic_decrement( &m_ref )) + { + // revoke from uno env on last release, + // The proxy can be resurrected if acquire is called before the uno + // environment calls cli_proxy_free. cli_proxy_free will + //delete the proxy. The environment does not acquire a registered + //proxy. + (*m_bridge->m_uno_env->revokeInterface)( + m_bridge->m_uno_env, const_cast< CliProxy * >( this ) ); + } +} +} + + +extern "C" +void SAL_CALL cli_proxy_free( uno_ExtEnvironment *, void * proxy ) + SAL_THROW_EXTERN_C() +{ + cli_uno::CliProxy * cliProxy = reinterpret_cast< + cli_uno::CliProxy * >( proxy ); + + delete cliProxy; +} + +extern "C" +void SAL_CALL cli_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + CliProxy const * cliProxy = static_cast< CliProxy const * >( pUnoI ); + cliProxy->acquire(); +} + +extern "C" +void SAL_CALL cli_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + CliProxy * cliProxy = static_cast< CliProxy * >( pUnoI ); + cliProxy->release(); +} + + +extern "C" + +void SAL_CALL cli_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C() +{ + CliProxy * proxy = static_cast< CliProxy* >( pUnoI ); + try + { + Bridge const* bridge = proxy->m_bridge; + + switch (member_td->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + + sal_Int32 member_pos = ((typelib_InterfaceMemberTypeDescription *) + member_td)->nPosition; + typelib_InterfaceTypeDescription * iface_td = + (typelib_InterfaceTypeDescription *)proxy->m_unoType.get(); + OSL_ENSURE( + member_pos < iface_td->nAllMembers, + "### member pos out of range!" ); + sal_Int32 function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ member_pos ]; + OSL_ENSURE( + function_pos < iface_td->nMapFunctionIndexToMemberIndex, + "### illegal function index!" ); + + if (uno_ret) // is getter method + { + OUString const& usAttrName= *(rtl_uString**)& + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos, + usAttrName, CliProxy::MK_GET); + bridge->call_cli( + proxy->m_cliI, + info, + ((typelib_InterfaceAttributeTypeDescription *)member_td) + ->pAttributeTypeRef, + 0, 0, // no params + uno_ret, 0, uno_exc ); + } + else // is setter method + { + OUString const& usAttrName= *(rtl_uString**) & + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos + 1, + usAttrName, CliProxy::MK_SET); + typelib_MethodParameter param; + param.pTypeRef = + ((typelib_InterfaceAttributeTypeDescription *)member_td) + ->pAttributeTypeRef; + param.bIn = sal_True; + param.bOut = sal_False; + + bridge->call_cli( + proxy->m_cliI, + // set follows get method + info, + 0 /* indicates void return */, ¶m, 1, + 0, uno_args, uno_exc ); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + sal_Int32 member_pos = ((typelib_InterfaceMemberTypeDescription *) + member_td)->nPosition; + typelib_InterfaceTypeDescription * iface_td = + (typelib_InterfaceTypeDescription *)proxy->m_unoType.get(); + OSL_ENSURE( + member_pos < iface_td->nAllMembers, + "### member pos out of range!" ); + sal_Int32 function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ member_pos ]; + OSL_ENSURE( + function_pos < iface_td->nMapFunctionIndexToMemberIndex, + "### illegal function index!" ); + + switch (function_pos) + { + case 0: // queryInterface() + { + TypeDescr demanded_td( + *reinterpret_cast<typelib_TypeDescriptionReference **>( + uno_args[0])); + if (typelib_TypeClass_INTERFACE + != demanded_td.get()->eTypeClass) + { + throw BridgeRuntimeError( + "queryInterface() call demands an INTERFACE type!"); + } + + uno_Interface * pInterface = 0; + (*bridge->m_uno_env->getRegisteredInterface)( + bridge->m_uno_env, + (void **)&pInterface, proxy->m_usOid.pData, + (typelib_InterfaceTypeDescription *)demanded_td.get() ); + + if (0 == pInterface) + { + System::Type^ mgdDemandedType = + mapUnoType(demanded_td.get()); + if (mgdDemandedType->IsInstanceOfType( proxy->m_cliI )) + { +#if OSL_DEBUG_LEVEL > 0 + OUString usOid( + mapCliString( + CliEnvHolder::g_cli_env->getObjectIdentifier( + proxy->m_cliI ))); + OSL_ENSURE(usOid.equals( proxy->m_usOid ), + "### different oids!"); +#endif + uno_Interface* pUnoI = bridge->map_cli2uno( + proxy->m_cliI, demanded_td.get() ); + uno_any_construct( + (uno_Any *)uno_ret, &pUnoI, demanded_td.get(), 0 ); + (*pUnoI->release)( pUnoI ); + } + else // object does not support demanded interface + { + uno_any_construct( (uno_Any *)uno_ret, 0, 0, 0 ); + } + // no exception occurred + *uno_exc = 0; + } + else + { + uno_any_construct( + reinterpret_cast< uno_Any * >( uno_ret ), + &pInterface, demanded_td.get(), 0 ); + (*pInterface->release)( pInterface ); + *uno_exc = 0; + } + break; + } + case 1: // acquire this proxy + cli_proxy_acquire(proxy); + *uno_exc = 0; + break; + case 2: // release this proxy + cli_proxy_release(proxy); + *uno_exc = 0; + break; + default: // arbitrary method call + { + typelib_InterfaceMethodTypeDescription * method_td = + (typelib_InterfaceMethodTypeDescription *)member_td; + OUString const& usMethodName= *(rtl_uString**) & + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos, + usMethodName, CliProxy::MK_METHOD); + bridge->call_cli( + proxy->m_cliI, + info, + method_td->pReturnTypeRef, method_td->pParams, + method_td->nParams, + uno_ret, uno_args, uno_exc); + return; + } + } + break; + } + default: + { + throw BridgeRuntimeError( + "illegal member type description!" ); + } + } + } + catch (BridgeRuntimeError & err) + { + // binary identical struct + css::uno::RuntimeException exc( + "[cli_uno bridge error] " + err.m_message, + css::uno::Reference< + css::uno::XInterface >() ); + css::uno::Type const & exc_type = cppu::UnoType<decltype(exc)>::get(); + uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), 0); + SAL_WARN( "cli", exc); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_proxy.h b/cli_ure/source/uno_bridge/cli_proxy.h new file mode 100644 index 000000000..25f7d2801 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_proxy.h @@ -0,0 +1,294 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_PROXY_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_PROXY_H + +#include "uno/environment.hxx" +#include "uno/mapping.hxx" +#include "uno/dispatcher.h" +#include "cli_bridge.h" +#include "cli_environment.h" + +#using <cli_ure.dll> + +namespace srrp = System::Runtime::Remoting::Proxies; +namespace srrm = System::Runtime::Remoting::Messaging; +namespace srr = System::Runtime::Remoting; +namespace sr = System::Reflection; +namespace sc = System::Collections; +using namespace uno; + +namespace cli_uno +{ + +public ref class UnoInterfaceInfo +{ +public: + UnoInterfaceInfo(Bridge const * bridge, uno_Interface* unoI, + typelib_InterfaceTypeDescription* td); + ~UnoInterfaceInfo(); + !UnoInterfaceInfo(); + + uno_Interface * m_unoI; // wrapped interface + System::Type ^ m_type; + typelib_InterfaceTypeDescription* m_typeDesc; + + Bridge const* m_bridge; +}; + +public ref class UnoInterfaceProxy: public srrp::RealProxy, + public srr::IRemotingTypeInfo +{ + /** used for IRemotingTypeInfo.TypeName + */ + System::String^ m_sTypeName; + /** The list is filled with UnoInterfaceInfo objects. The list can only + grow and elements are never changed. If an element was added it + must not be changed! + */ + sc::ArrayList^ m_listIfaces; + /** The number of UNO interfaces this proxy represents. It corresponds + to the number of elements in m_listIfaces. + */ + int m_numUnoIfaces; + /** The list is filled with additional UnoInterfaceProxy object due + to aggregation via bridges. Though the latter is strongly + discouraged, this has to be supported. + */ + sc::ArrayList^ m_listAdditionalProxies; + int m_nlistAdditionalProxies; + + UnoInterfaceInfo ^ findInfo( ::System::Type ^ type ); + + Bridge const* m_bridge; + System::String^ m_oid; + +#if OSL_DEBUG_LEVEL >= 2 + /** The string contains all names of UNO interfaces which are + represented by this proxy. It is used to print out the interfaces + when this proxy dies. In the destructor it is not allowed to + access m_listIfaces or any other managed object. + */ + rtl_uString * _sInterfaces; +// /** Count of interfaces. Used in conjunction with _sInterfaces. +// */ + int _numInterfaces; +#endif + +public: + + /** Creates a proxy and registers it on the dot NET side. + */ + static System::Object^ create(Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTd, + const OUString& oid); + + /** RealProxy::Invoke */ + virtual srrm::IMessage^ Invoke(srrm::IMessage^ msg) override; + + /** Must be called from within a synchronized section. + Add only the interface if it is not already contained. + This method is called from the constructor and as a result + of IRemotingTypeInfo::CanCastTo + */ + void addUnoInterface(uno_Interface* pUnoI, + typelib_InterfaceTypeDescription* pTd); + ~UnoInterfaceProxy(); + !UnoInterfaceProxy(); + + inline System::String ^ getOid() + { return m_oid; } + + //IRemotingTypeInfo ---------------------------------------------- + virtual bool CanCastTo(System::Type^ fromType, System::Object^ o); + + virtual property System::String^ TypeName + { + System::String^ get() + { + return m_sTypeName; + }; + void set(System::String^ name) + { + m_sTypeName = name; + }; + } + + +private: + UnoInterfaceProxy( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid ); + + static srrm::IMessage^ constructReturnMessage(System::Object^ retVal, + array<System::Object^>^ outArgs, + typelib_InterfaceMethodTypeDescription* mtd, + srrm::IMessage^ msg, System::Object^ exc); + + static System::String^ m_methodNameString = + gcnew System::String("__MethodName"); + static System::String^ m_typeNameString = gcnew System::String("__TypeName"); + static System::String^ m_ArgsString = gcnew System::String("__Args"); + static System::String^ m_CallContextString = + gcnew System::String("__CallContext"); + static System::String^ m_system_Object_String = + gcnew System::String("System.Object"); + static System::String^ m_methodSignatureString = + gcnew System::String("__MethodSignature"); + static System::String^ m_Equals_String = gcnew System::String("Equals"); + static System::String^ m_GetHashCode_String = + gcnew System::String("GetHashCode"); + static System::String^ m_GetType_String = gcnew System::String("GetType"); + static System::String^ m_ToString_String = gcnew System::String("ToString"); + +protected: + srrm::IMessage^ invokeObject(sc::IDictionary^ properties, + srrm::LogicalCallContext^ context, + srrm::IMethodCallMessage^ mcm); +}; + + +//Cannot make this __gc because a managed type cannot derive from unmanaged type +struct CliProxy: public uno_Interface +{ + mutable oslInterlockedCount m_ref; + const Bridge* m_bridge; + const gcroot<System::Object^> m_cliI; + gcroot<System::Type^> m_type; + const css::uno::TypeDescription m_unoType; + const gcroot<System::String^> m_oid; + const OUString m_usOid; + + enum MethodKind {MK_METHOD = 0, MK_SET, MK_GET}; + /** The array contains MethodInfos of the cli object. Each one reflects an + implemented interface method of the interface for which this proxy was + created. The MethodInfos are from the object's method and not from the + interface type. That is, they can be used to invoke the methods. The + order of the MethodInfo objects corresponds to the order of the + interface methods (see member m_type). Position 0 contains the + MethodInfo of the first method of the interface which represents the + root of the inheritance chain. The last MethodInfo represents the last + method of the furthest derived interface. + + The array is completely initialized in the constructor of this object. + + When the uno_DispatchMethod is called for this proxy then it receives + a typelib_TypeDescription of the member which is either an attribute + (setter or getter) or method. After determining the position of the + method within the UNO interface one can use the position to obtain the + MethodInfo of the corresponding cli method. To obtain the index for the + m_arMethodInfos array the function position has to be decreased by 3. + This is because, the cli interface does not contain the XInterface + methods. + */ + gcroot<array<sr::MethodInfo^>^> m_arMethodInfos; + + /** This array is similar to m_arMethodInfos but it contains the MethodInfo + objects of the interface (not the object). When a call is made from uno + to cli then the uno method name is compared to the cli method name. The + cli method name can be obtained from the MethodInfo object in this + array. The name of the actual implemented method may not be the same as + the interface method. + */ + gcroot<array<sr::MethodInfo^>^> m_arInterfaceMethodInfos; + + /** Maps the position of the method in the UNO interface to the position of + the corresponding MethodInfo in m_arMethodInfos. The Uno position must + not include the XInterface methods. For example, + pos 0 = XInterface::queryInterface + pos 1 = XInterface::acquire + pos 2 = XInterface::release + + That is the real Uno position has to be deducted by 3. Then + arUnoPosToCliPos[pos] contains the index for m_arMethodInfos. + + */ + gcroot<array<System::Int32>^> m_arUnoPosToCliPos; + + /** Count of inherited interfaces of the cli interface. + */ + int m_nInheritedInterfaces; + /** Contains the number of methods of each interface. + */ + gcroot<array<System::Int32^>^> m_arInterfaceMethodCount; + + CliProxy( Bridge const* bridge, System::Object^ cliI, + typelib_TypeDescription const* pTD, + const OUString& usOid); + ~CliProxy(); + + static uno_Interface* create(Bridge const * bridge, + System::Object^ cliI, + typelib_TypeDescription const * TD, + OUString const & usOid ); + + /** Prepares an array (m_arMethoInfos) containing MethodInfo object of the + interface and all inherited interfaces. At index null is the first + method of the base interface and at the last position is the last method + of the furthest derived interface. + If a UNO call is received then one can determine the position of the + method (or getter or setter for an attribute) from the passed type + information. The position minus 3 (there is no XInterface in the cli + mapping) corresponds to the index of the cli interface method in the + array. + */ + void makeMethodInfos(); + + /**Obtains a MethodInfo which can be used to invoke the cli object. + Internally it maps nUnoFunctionPos to an index that is used to get the + corresponding MethodInfo object from m_arMethoInfos. The mapping table + is dynamically initialized. If the cli interface has no base interface + or exactly one then the mapping table is initialized in one go at the + first call. In all ensuing calls the MethodInfo object is immediately + retrieved through the mapping table. + + If the interface has more than one interface in its inheritance chain, + that is Type.GetInterfaces return more than one Type, then the mapping + table is partially initialized. On the first call the mappings for the + methods of the belonging interface are created. + + The implementation assumes that the order of interface methods as + provided by InterfaceMapping.InterfaceMethods corresponds to the order + of methods in the interface declaration. + + @param nUnoFunctionPos + Position of the method in the uno interface. + */ + sr::MethodInfo^ getMethodInfo(int nUnoFunctionPos, + const OUString & usMethodName, + MethodKind mk); + + void SAL_CALL uno_DispatchMethod( + struct _uno_Interface * pUnoI, + const struct _typelib_TypeDescription * pMemberType, + void * pReturn, + void * pArgs[], + uno_Any ** ppException ); + + inline void acquire() const; + inline void release() const; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_uno.cxx b/cli_ure/source/uno_bridge/cli_uno.cxx new file mode 100644 index 000000000..6af17e065 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_uno.cxx @@ -0,0 +1,277 @@ +/* -*- 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 <memory> +#include <sal/alloca.h> +#include <osl/diagnose.h> +#include "rtl/ustrbuf.hxx" +#include "cli_base.h" +#include "cli_bridge.h" + +namespace sr=System::Reflection; + + +namespace cli_uno +{ + +union largest +{ + sal_Int64 n; + double d; + void * p; + uno_Any a; +}; + +System::Object^ Bridge::call_uno(uno_Interface * pUnoI, + typelib_TypeDescription* member_td, + typelib_TypeDescriptionReference * return_type, + sal_Int32 nParams, typelib_MethodParameter const * pParams, + array<System::Object^>^ args, array<System::Type^>^ argTypes, + System::Object^* ppExc) const +{ + // return mem + sal_Int32 return_size = sizeof (largest); + if ((0 != return_type) && + (typelib_TypeClass_STRUCT == return_type->eTypeClass || + typelib_TypeClass_EXCEPTION == return_type->eTypeClass)) + { + TypeDescr return_td( return_type ); + if (return_td.get()->nSize > sizeof (largest)) + return_size = return_td.get()->nSize; + } + //Prepare memory that contains all converted arguments and return value + //The memory block contains first pointers to the arguments which are in the same block + // For example, 2 arguments, 1 ret. + // + // | Pointer + // | Pointer + // | Return value + // | Arg 1 + // | Arg 2 + // + // If an argument is larger then union largest, such as some structures, then the pointer + // points to an extra block of memory. The same goes for a big return value. + + char * mem = (char *)alloca( + (nParams * sizeof (void *)) + return_size + (nParams * sizeof (largest)) ); + //array of pointers to args + void ** uno_args = (void **)mem; + //If an attribute is set, then uno_ret must be null, e.g void setAttribute(int ) + void * uno_ret= NULL; + if ( !(member_td->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE && nParams == 1)) + uno_ret = (mem + (nParams * sizeof (void *))); + largest * uno_args_mem = (largest *)(mem + (nParams * sizeof (void *)) + return_size); + + OSL_ASSERT( (0 == nParams) || (nParams == args->Length) ); + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + typelib_TypeDescriptionReference * type = param.pTypeRef; + + uno_args[ nPos ] = &uno_args_mem[ nPos ]; + if (typelib_TypeClass_STRUCT == type->eTypeClass || + typelib_TypeClass_EXCEPTION == type->eTypeClass) + { + TypeDescr td( type ); + if (td.get()->nSize > sizeof (largest)) + uno_args[ nPos ] = alloca( td.get()->nSize ); + } + + if (param.bIn) + { + try + { + // in, in/out params + map_to_uno( + uno_args[ nPos ],args[nPos] , type, false /* no assign */); + } + catch (...) + { + // cleanup uno in args + for (sal_Int32 n = 0; n < nPos; ++n) + { + typelib_MethodParameter const & param = pParams[n]; + if (param.bIn) + { + uno_type_destructData(uno_args[n], param.pTypeRef, 0); + } + } + throw; + } + } + } + uno_Any uno_exc_holder; + uno_Any * uno_exc = &uno_exc_holder; + // call binary uno + + (*pUnoI->pDispatcher)( pUnoI, member_td, uno_ret, uno_args, &uno_exc ); + + if (0 == uno_exc) + { + // convert out args; destruct uno args + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + typelib_TypeDescriptionReference * type = param.pTypeRef; + if (param.bOut) + { + try + { + pin_ptr<System::Object^> ptr = &args[nPos]; + map_to_cli( + ptr, uno_args[nPos], param.pTypeRef, + argTypes != nullptr ? argTypes[nPos] : nullptr, false ); + } + catch (...) + { + // cleanup further uno args + for ( sal_Int32 n = nPos; n < nParams; ++n ) + { + uno_type_destructData( uno_args[n], pParams[n].pTypeRef, 0 ); + } + // cleanup uno return value + uno_type_destructData( uno_ret, return_type, 0 ); + throw; + } + } + //cleanup args + if (typelib_TypeClass_DOUBLE < type->eTypeClass && + typelib_TypeClass_ENUM != type->eTypeClass) // opt + { + uno_type_destructData(uno_args[nPos], type, 0); + } + } + + if ((0 != return_type) && + (typelib_TypeClass_VOID != return_type->eTypeClass)) + { + // convert uno return value + try + { + System::Object^ cli_ret; + map_to_cli( + &cli_ret, uno_ret, return_type, nullptr, false); + uno_type_destructData(uno_ret, return_type, 0); + return cli_ret; + } + catch (...) + { + uno_type_destructData(uno_ret, return_type, 0); + throw; + } + } + return nullptr; // void return + } + else // exception occurred + { + // destruct uno in args + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + if (param.bIn) + { + uno_type_destructData( uno_args[ nPos ], param.pTypeRef, 0 ); + } + } + map_to_cli(ppExc, uno_exc_holder.pData, + uno_exc_holder.pType, nullptr, false); + return nullptr; + } +} + +void Bridge::call_cli( + System::Object^ cliI, + sr::MethodInfo^ method, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, int nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const +{ + array<System::Object^>^ args= gcnew array<System::Object^>(nParams); + for (int nPos= 0; nPos < nParams; nPos++) + { + typelib_MethodParameter const & param= params[nPos]; + if (param.bIn) + { + pin_ptr<System::Object^> ptr = &args[nPos]; + map_to_cli( ptr, + uno_args[nPos], param.pTypeRef, nullptr, false); + } + } + System::Object^ retInvoke= nullptr; + try + { + retInvoke= method->Invoke(cliI, args); + } + catch (sr::TargetInvocationException^ e) + { + System::Exception^ exc= e->InnerException; + css::uno::TypeDescription td(mapCliType(exc->GetType())); + // memory for exception + std::unique_ptr< rtl_mem > memExc(rtl_mem::allocate(td.get()->nSize)); + map_to_uno(memExc.get(), exc, td.get()->pWeakRef, false); + (*uno_exc)->pType= td.get()->pWeakRef; + (*uno_exc)->pData= memExc.release(); + return; + } + catch (System::Exception^ e) + { + throw BridgeRuntimeError("Unexpected exception during invocation of cli object. Original message is: \n" + mapCliString(e->Message)); + } + + //convert out, in/out params + for (int nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = params[ nPos ]; + + if (param.bOut) + { + try + { + map_to_uno( + uno_args[ nPos ], args[ nPos ], param.pTypeRef, + sal_True == param.bIn /* assign if inout */); + // out array + } + catch (...) + { + // cleanup uno pure out + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + typelib_MethodParameter const & param = params[ n ]; + if (! param.bIn) + uno_type_destructData( uno_args[ n ], param.pTypeRef, 0 ); + } + throw; + } + } + } + // return value + if (0 != return_type) + { + map_to_uno( + uno_ret, retInvoke, return_type, false /* no assign */); + } + // no exception occurred + *uno_exc = 0; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |