Repository.mk                                                                  
   |    5 
 bridges/Library_net_uno.mk                                                     
   |   33 
 bridges/Module_bridges.mk                                                      
   |    1 
 bridges/source/net_uno/net_base.cxx                                            
   |  234 ++
 bridges/source/net_uno/net_base.hxx                                            
   |   58 
 bridges/source/net_uno/net_bridge.cxx                                          
   |  292 +++
 bridges/source/net_uno/net_bridge.hxx                                          
   |   59 
 bridges/source/net_uno/net_data.cxx                                            
   |  855 ++++++++++
 bridges/source/net_uno/net_func.cxx                                            
   |  251 ++
 bridges/source/net_uno/net_proxy.cxx                                           
   |  175 ++
 bridges/source/net_uno/net_proxy.hxx                                           
   |   36 
 codemaker/source/netmaker/netproduce.cxx                                       
   |  104 -
 include/bridges/net_uno/net_context.hxx                                        
   |   38 
 include/bridges/net_uno/net_types.hxx                                          
   |   79 
 include/sal/log-areas.dox                                                      
   |    1 
 include/uno/lbnames.h                                                          
   |    2 
 net_ure/DotnetLibrary_net_bridge.mk                                            
   |   22 
 net_ure/qa/basetypes/AnyTests.cs                                               
   |    1 
 net_ure/source/basetypes/Any.cs                                                
   |   48 
 net_ure/source/basetypes/IQueryInterface.cs                                    
   |   22 
 net_ure/source/bootstrap/bootstrap.cxx                                         
   |   84 
 net_ure/source/bridge/NativeBootstrap.cs                                       
   |   32 
 net_ure/source/bridge/helper/DisposeGuard.cs                                   
   |   25 
 net_ure/source/bridge/helper/StructHelper.cs                                   
   |   56 
 net_ure/source/bridge/helper/TypeHelper.cs                                     
   |  125 +
 net_ure/source/bridge/helper/WeakBase.cs                                       
   |   77 
 net_ure/source/bridge/helper/WeakComponentBase.cs                              
   |   40 
 net_ure/source/bridge/native/InteropMethods.cs                                 
   |   50 
 net_ure/source/bridge/native/InteropTypes.cs                                   
   |   86 +
 net_ure/source/bridge/native/Marshaller.cs                                     
   |  608 +++++++
 net_ure/source/bridge/native/NativeBootstrap.cs                                
   |   49 
 net_ure/source/bridge/native/NativeUnoProxy.cs                                 
   |   58 
 net_ure/source/bridge/native/NetEnvironment.cs                                 
   |  227 ++
 net_ure/source/bridge/native/WeakIndexTable.cs                                 
   |  103 +
 net_ure/source/bridge/native/WeakOidTypeTable.cs                               
   |  179 ++
 odk/Package_examples.mk                                                        
   |    2 
 
odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs
 |   29 
 odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile        
   |   75 
 38 files changed, 4154 insertions(+), 67 deletions(-)

New commits:
commit c3c7b48fa9d4fd5b8f28033ca7453151df9d5f7f
Author:     RMZeroFour <[email protected]>
AuthorDate: Tue Sep 3 01:59:15 2024 +0530
Commit:     Hossein <[email protected]>
CommitDate: Wed Sep 18 08:10:36 2024 +0200

    .NET Bindings: Native bridge for .NET
    
    This patch includes all marshalling and proxy handling code on the
    .NET side as well as the native side needed for a fully functional
    UNO bridge.
    
    It also includes some changes and corrections to net_basetypes and
    netmaker needed for the bridge to work properly.
    
    It also includes the FirstUnoContact example in C# as demonstration.
    
    Change-Id: I406932938a4415d24408fb41ddfa7d8eeb5d1f94
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170916
    Tested-by: Jenkins
    Reviewed-by: Hossein <[email protected]>

diff --git a/Repository.mk b/Repository.mk
index af89d1d50dce..b4f3b36796bd 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -603,7 +603,10 @@ $(eval $(call 
gb_Helper_register_libraries_for_install,PLAINLIBS_URE,ure, \
                $(if $(filter MSC,$(COM)),$(if $(filter-out 
AARCH64_TRUE,$(CPUNAME)_$(CROSS_COMPILING)),cli_uno)) \
        ) \
        i18nlangtag \
-       $(if $(ENABLE_DOTNET),net_bootstrap) \
+       $(if $(ENABLE_DOTNET), \
+               net_bootstrap \
+               net_uno \
+       ) \
        $(if $(ENABLE_JAVA), \
                java_uno \
                jpipe \
diff --git a/bridges/Library_net_uno.mk b/bridges/Library_net_uno.mk
new file mode 100644
index 000000000000..5579c3178492
--- /dev/null
+++ b/bridges/Library_net_uno.mk
@@ -0,0 +1,33 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,net_uno))
+
+$(eval $(call gb_Library_use_udk_api,net_uno))
+
+$(eval $(call gb_Library_set_include,net_uno,\
+       -I$(SRCDIR)/bridges/source/net_uno \
+       $$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_use_libraries,net_uno,\
+       cppu \
+       sal \
+       salhelper \
+))
+
+$(eval $(call gb_Library_add_exception_objects,net_uno,\
+       bridges/source/net_uno/net_base \
+       bridges/source/net_uno/net_bridge \
+       bridges/source/net_uno/net_data \
+       bridges/source/net_uno/net_func \
+       bridges/source/net_uno/net_proxy \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/bridges/Module_bridges.mk b/bridges/Module_bridges.mk
index a943e6537653..a8a07c0e39c6 100644
--- a/bridges/Module_bridges.mk
+++ b/bridges/Module_bridges.mk
@@ -11,6 +11,7 @@ $(eval $(call gb_Module_Module,bridges))
 
 $(eval $(call gb_Module_add_targets,bridges,\
        Library_cpp_uno \
+       $(if $(ENABLE_DOTNET),Library_net_uno) \
        $(if $(ENABLE_JAVA),\
                Jar_java_uno \
                Library_java_uno \
diff --git a/bridges/source/net_uno/net_base.cxx 
b/bridges/source/net_uno/net_base.cxx
new file mode 100644
index 000000000000..1b3b5b2d7223
--- /dev/null
+++ b/bridges/source/net_uno/net_base.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "net_base.hxx"
+
+#include <string_view>
+#include <unordered_map>
+
+#include <o3tl/string_view.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+
+namespace net_uno
+{
+namespace
+{
+const std::unordered_map<std::u16string_view, std::u16string_view> s_typeNames{
+    { u"System.Void", u"void" },
+    { u"System.Boolean", u"boolean" },
+    { u"System.Char", u"char" },
+    { u"System.SByte", u"byte" },
+    { u"System.Int16", u"short" },
+    { u"System.UInt16", u"unsigned short" },
+    { u"System.Int32", u"long" },
+    { u"System.UInt32", u"unsigned long" },
+    { u"System.Int64", u"hyper" },
+    { u"System.UInt64", u"unsigned hyper" },
+    { u"System.Single", u"float" },
+    { u"System.Double", u"double" },
+    { u"System.String", u"string" },
+    { u"System.Type", u"type" },
+    { u"com.sun.star.uno.Any", u"any" },
+    { u"com.sun.star.uno.UnoException", u"com.sun.star.uno.Exception" },
+    { u"com.sun.star.uno.IQueryInterface", u"com.sun.star.uno.XInterface" },
+};
+
+const std::unordered_map<OUString, typelib_TypeClass> s_typeClasses{
+    { u"System.Void"_ustr, typelib_TypeClass_VOID },
+    { u"System.Boolean"_ustr, typelib_TypeClass_BOOLEAN },
+    { u"System.Char"_ustr, typelib_TypeClass_CHAR },
+    { u"System.SByte"_ustr, typelib_TypeClass_BYTE },
+    { u"System.Int16"_ustr, typelib_TypeClass_SHORT },
+    { u"System.UInt16"_ustr, typelib_TypeClass_UNSIGNED_SHORT },
+    { u"System.Int32"_ustr, typelib_TypeClass_LONG },
+    { u"System.UInt32"_ustr, typelib_TypeClass_UNSIGNED_LONG },
+    { u"System.Int64"_ustr, typelib_TypeClass_HYPER },
+    { u"System.UInt64"_ustr, typelib_TypeClass_UNSIGNED_HYPER },
+    { u"System.Single"_ustr, typelib_TypeClass_FLOAT },
+    { u"System.Double"_ustr, typelib_TypeClass_DOUBLE },
+    { u"System.String"_ustr, typelib_TypeClass_STRING },
+    { u"System.Type"_ustr, typelib_TypeClass_TYPE },
+    { u"com.sun.star.uno.Any"_ustr, typelib_TypeClass_ANY },
+};
+
+void map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef, 
OUStringBuffer& buffer)
+{
+    switch (pTDRef->eTypeClass)
+    {
+        case typelib_TypeClass_VOID:
+            buffer.append(u"System.Void");
+            break;
+        case typelib_TypeClass_CHAR:
+            buffer.append(u"System.Char");
+            break;
+        case typelib_TypeClass_BOOLEAN:
+            buffer.append(u"System.Boolean");
+            break;
+        case typelib_TypeClass_BYTE:
+            buffer.append(u"System.SByte");
+            break;
+        case typelib_TypeClass_SHORT:
+            buffer.append(u"System.Int16");
+            break;
+        case typelib_TypeClass_UNSIGNED_SHORT:
+            buffer.append(u"System.UInt16");
+            break;
+        case typelib_TypeClass_LONG:
+            buffer.append(u"System.Int32");
+            break;
+        case typelib_TypeClass_UNSIGNED_LONG:
+            buffer.append(u"System.UInt32");
+            break;
+        case typelib_TypeClass_HYPER:
+            buffer.append(u"System.Int64");
+            break;
+        case typelib_TypeClass_UNSIGNED_HYPER:
+            buffer.append(u"System.UInt64");
+            break;
+        case typelib_TypeClass_FLOAT:
+            buffer.append(u"System.Single");
+            break;
+        case typelib_TypeClass_DOUBLE:
+            buffer.append(u"System.Double");
+            break;
+        case typelib_TypeClass_STRING:
+            buffer.append(u"System.String");
+            break;
+        case typelib_TypeClass_TYPE:
+            buffer.append(u"System.Type");
+            break;
+        case typelib_TypeClass_ANY:
+            buffer.append(u"com.sun.star.uno.Any");
+            break;
+
+        case typelib_TypeClass_ENUM:
+        case typelib_TypeClass_EXCEPTION:
+            // These have the same name on both sides
+            buffer.append(OUString::unacquired(&pTDRef->pTypeName));
+            break;
+
+        case typelib_TypeClass_STRUCT:
+            // These have the same name on both sides
+            // TODO: What about polymorphic structs? Merge this with above 
cases if fine
+            buffer.append(OUString::unacquired(&pTDRef->pTypeName));
+            break;
+
+        case typelib_TypeClass_INTERFACE:
+        {
+            // These have the same name on both sides
+            if (u"com.sun.star.uno.XInterface"_ustr.equals(pTDRef->pTypeName))
+                // Except XInterface, which does not exist on the .NET side
+                buffer.append(u"com.sun.star.uno.IQueryInterface");
+            else
+                buffer.append(OUString::unacquired(&pTDRef->pTypeName));
+            break;
+        }
+
+        case typelib_TypeClass_SEQUENCE:
+        {
+            TypeDescHolder seqType(pTDRef);
+            typelib_TypeDescriptionReference* pElementTDRef
+                = 
reinterpret_cast<typelib_IndirectTypeDescription*>(seqType.get())->pType;
+            map_uno_type_to_net(pElementTDRef, buffer);
+            buffer.append(u"[]");
+            break;
+        }
+
+        default:
+        {
+            throw BridgeRuntimeError(SAL_WHERE, "could not map given type info 
"
+                                                    + 
OUString::unacquired(&pTDRef->pTypeName));
+        }
+    }
+}
+
+void map_net_type_to_uno(std::u16string_view sTypeName, OUStringBuffer& buffer)
+{
+    size_t bracketsStart = sTypeName.find_last_not_of(u"[]");
+    bool isSequence = bracketsStart != std::u16string_view::npos;
+    std::u16string_view sBrackets = isSequence ? 
sTypeName.substr(bracketsStart + 1) : u"";
+    std::u16string_view sFullName = isSequence ? sTypeName.substr(0, 
bracketsStart + 1) : sTypeName;
+
+    size_t genericsStart = sFullName.find_first_of(u'<');
+    size_t genericsEnd = sFullName.find_last_of(u'>');
+    bool hasGenerics = genericsStart != std::u16string_view::npos;
+    std::u16string_view sGenericParams
+        = hasGenerics ? sFullName.substr(genericsStart + 1, genericsEnd - 
genericsStart - 1) : u"";
+    std::u16string_view sBaseName = hasGenerics ? sFullName.substr(0, 
genericsStart) : sFullName;
+
+    // Sequence brackets go at the beginning of UNO name
+    for (size_t i = 0; i < sBrackets.size() / 2; i++)
+        buffer.append(u"[]");
+
+    // Builtin types
+    auto it = s_typeNames.find(sBaseName);
+    if (it != s_typeNames.end())
+    {
+        buffer.append(it->second);
+    }
+    else
+    {
+        buffer.append(sBaseName);
+        if (hasGenerics)
+        {
+            buffer.append(u'<');
+            for (size_t i = 0; i != std::string_view::npos;)
+            {
+                std::u16string_view 
genericParam(o3tl::getToken(sGenericParams, u',', i));
+                map_net_type_to_uno(genericParam, buffer);
+                if (i != std::string_view::npos)
+                    buffer.append(u',');
+            }
+            buffer.append(u'>');
+        }
+    }
+}
+}
+
+OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef)
+{
+    OUStringBuffer buffer;
+    map_uno_type_to_net(pTDRef, buffer);
+    return buffer.makeStringAndClear();
+}
+
+typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& 
sTypeName)
+{
+    typelib_TypeDescriptionReference* retVal = nullptr;
+
+    // Simple types
+    auto it = s_typeClasses.find(sTypeName);
+    if (it != s_typeClasses.end())
+    {
+        retVal = *typelib_static_type_getByTypeClass(it->second);
+        typelib_typedescriptionreference_acquire(retVal);
+        return retVal;
+    }
+
+    // Complex types (structs, interfaces, sequences)
+    OUStringBuffer buffer;
+    map_net_type_to_uno(sTypeName, buffer);
+    OUString convertedName = buffer.makeStringAndClear();
+
+    typelib_TypeDescription* pTD = nullptr;
+    typelib_typedescription_getByName(&pTD, convertedName.pData);
+    if (pTD)
+    {
+        retVal = pTD->pWeakRef;
+        typelib_typedescriptionreference_acquire(retVal);
+        typelib_typedescription_release(pTD);
+        return retVal;
+    }
+
+    throw BridgeRuntimeError(SAL_WHERE, "could not map given type name " + 
sTypeName);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_base.hxx 
b/bridges/source/net_uno/net_base.hxx
new file mode 100644
index 000000000000..a9d8a1c37767
--- /dev/null
+++ b/bridges/source/net_uno/net_base.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <typelib/typedescription.hxx>
+#include <sal/log.hxx>
+
+namespace net_uno
+{
+OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef);
+typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& 
sTypeName);
+
+struct BridgeRuntimeError
+{
+    OUString m_location;
+    OUString m_message;
+
+    explicit BridgeRuntimeError(const char* location, const OUString& message)
+        : m_location(OUString::createFromAscii(location))
+        , m_message(message)
+    {
+    }
+};
+
+class TypeDescHolder
+{
+public:
+    TypeDescHolder(const TypeDescHolder&) = delete;
+    TypeDescHolder& operator=(const TypeDescHolder&) = delete;
+
+    explicit TypeDescHolder(typelib_TypeDescriptionReference* td_ref)
+        : m_td(nullptr)
+    {
+        TYPELIB_DANGER_GET(&m_td, td_ref);
+        if (!m_td)
+        {
+            throw BridgeRuntimeError(SAL_WHERE, "could not get type 
description for "
+                                                    + 
OUString::unacquired(&td_ref->pTypeName));
+        }
+    }
+    ~TypeDescHolder() { TYPELIB_DANGER_RELEASE(m_td); }
+
+    typelib_TypeDescription* get() const { return m_td; }
+
+private:
+    typelib_TypeDescription* m_td;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_bridge.cxx 
b/bridges/source/net_uno/net_bridge.cxx
new file mode 100644
index 000000000000..9a1632fa9744
--- /dev/null
+++ b/bridges/source/net_uno/net_bridge.cxx
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "net_bridge.hxx"
+
+#include <bridges/net_uno/net_context.hxx>
+#include <sal/log.hxx>
+
+namespace net_uno
+{
+namespace
+{
+void SAL_CALL Mapping_acquire(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
+{
+    Mapping* that = static_cast<Mapping*>(mapping);
+    that->m_bridge->acquire();
+}
+
+void SAL_CALL Mapping_release(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
+{
+    Mapping* that = static_cast<Mapping*>(mapping);
+    that->m_bridge->release();
+}
+
+void SAL_CALL Mapping_free(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
+{
+    Mapping* that = static_cast<Mapping*>(mapping);
+    delete that->m_bridge;
+}
+
+void SAL_CALL Mapping_net2uno(uno_Mapping* mapping, void** ppOut, void* pIn,
+                              typelib_InterfaceTypeDescription* pTD) 
SAL_THROW_EXTERN_C()
+{
+    assert(ppOut && pTD && "### null ptr!");
+
+    Mapping* that = static_cast<Mapping*>(mapping);
+    Bridge* bridge = that->m_bridge;
+
+    if (pIn)
+    {
+        Value interfaceValue;
+        interfaceValue.interfaceData = pIn;
+        bridge->map_net_value_to_uno(*ppOut, &interfaceValue, 
pTD->aBase.pWeakRef, true, true);
+    }
+}
+
+void SAL_CALL Mapping_uno2net(uno_Mapping* mapping, void** ppOut, void* pIn,
+                              typelib_InterfaceTypeDescription* pTD) 
SAL_THROW_EXTERN_C()
+{
+    assert(ppOut && pTD && "### null ptr!");
+
+    Mapping* that = static_cast<Mapping*>(mapping);
+    Bridge* bridge = that->m_bridge;
+
+    if (pIn)
+    {
+        Value interfaceValue;
+        bridge->map_uno_to_net_value(&pIn, &interfaceValue, 
pTD->aBase.pWeakRef, false);
+        *ppOut = interfaceValue.interfaceData;
+    }
+}
+}
+
+Bridge::Bridge(uno_Environment* uno_net_env, uno_ExtEnvironment* uno_env, bool 
registered_net2uno)
+    : m_ref(1)
+    , m_uno_env(uno_env)
+    , m_net_env(uno_net_env)
+    , m_registered_net2uno(registered_net2uno)
+{
+    assert(m_net_env && m_uno_env);
+
+    (*m_uno_env->aBase.acquire)(&m_uno_env->aBase);
+    (*m_net_env->acquire)(m_net_env);
+
+    // net2uno
+    m_net2uno.acquire = Mapping_acquire;
+    m_net2uno.release = Mapping_release;
+    m_net2uno.mapInterface = Mapping_net2uno;
+    m_net2uno.m_bridge = this;
+
+    // uno2net
+    m_uno2net.acquire = Mapping_acquire;
+    m_uno2net.release = Mapping_release;
+    m_uno2net.mapInterface = Mapping_uno2net;
+    m_uno2net.m_bridge = this;
+}
+
+Bridge::~Bridge()
+{
+    (*m_net_env->release)(m_net_env);
+    (*m_uno_env->aBase.release)(&m_uno_env->aBase);
+}
+
+void Bridge::acquire()
+{
+    if (osl_atomic_increment(&m_ref) == 1)
+    {
+        if (m_registered_net2uno)
+        {
+            uno_Mapping* mapping = &m_net2uno;
+            uno_registerMapping(&mapping, Mapping_free, m_net_env, 
&m_uno_env->aBase, nullptr);
+        }
+        else
+        {
+            uno_Mapping* mapping = &m_uno2net;
+            uno_registerMapping(&mapping, Mapping_free, &m_uno_env->aBase, 
m_net_env, nullptr);
+        }
+    }
+}
+
+void Bridge::release()
+{
+    if (osl_atomic_decrement(&m_ref) == 0)
+    {
+        uno_revokeMapping(m_registered_net2uno ? &m_net2uno : &m_uno2net);
+    }
+}
+
+extern "C" {
+static void net_env_disposing(uno_Environment* env)
+{
+    // TODO: more rigorous lifetime control
+    // TODO: invoke some dispose routine on .NET side
+    delete static_cast<Context*>(env->pContext);
+}
+
+SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment* net_env) 
SAL_THROW_EXTERN_C()
+{
+    // The code creating the uno_Environment needs to initialize
+    // pContext with a Context object, complete with all callbacks.
+    assert(net_env->pContext);
+
+    net_env->pExtEnv = nullptr;
+    net_env->environmentDisposing = net_env_disposing;
+}
+
+SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(uno_Mapping** ppMapping, 
uno_Environment* pFrom,
+                                             uno_Environment* pTo) 
SAL_THROW_EXTERN_C()
+{
+    assert(ppMapping && pFrom && pTo);
+
+    if (*ppMapping)
+    {
+        (*(*ppMapping)->release)(*ppMapping);
+        *ppMapping = nullptr;
+    }
+
+    const OUString& from_env_typename = 
OUString::unacquired(&pFrom->pTypeName);
+    const OUString& to_env_typename = OUString::unacquired(&pTo->pTypeName);
+
+    uno_Mapping* mapping = nullptr;
+    if (from_env_typename == UNO_LB_NET && to_env_typename == UNO_LB_UNO)
+    {
+        Bridge* bridge = new Bridge(pFrom, pTo->pExtEnv, true);
+        mapping = &bridge->m_net2uno;
+        uno_registerMapping(&mapping, Mapping_free, pFrom, 
&pTo->pExtEnv->aBase, nullptr);
+    }
+    else if (from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_NET)
+    {
+        Bridge* bridge = new Bridge(pTo, pFrom->pExtEnv, false);
+        mapping = &bridge->m_uno2net;
+        uno_registerMapping(&mapping, Mapping_free, &pFrom->pExtEnv->aBase, 
pTo, nullptr);
+    }
+    *ppMapping = mapping;
+}
+
+SAL_DLLPUBLIC_EXPORT IntPtr allocateMemory(int nBytes) { return 
std::malloc(nBytes); }
+
+SAL_DLLPUBLIC_EXPORT void freeMemory(IntPtr pMemory) { std::free(pMemory); }
+
+SAL_DLLPUBLIC_EXPORT void releaseProxy(IntPtr pBridge, IntPtr pUnoInterface, 
IntPtr pTypeDesc)
+{
+    Bridge* bridge = static_cast<Bridge*>(pBridge);
+    uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface);
+    typelib_TypeDescription* pTD = 
static_cast<typelib_TypeDescription*>(pTypeDesc);
+
+    // Revoke from UNO; already revoked from .NET
+    (*bridge->m_uno_env->revokeInterface)(bridge->m_uno_env, pUnoI);
+
+    (*pUnoI->release)(pUnoI);
+    typelib_typedescription_release(pTD);
+    bridge->release();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool dispatchCall(IntPtr pBridge, IntPtr 
pUnoInterface, IntPtr pTypeDesc,
+                                           String sFunctionName, Value* pArgs, 
Value* pRet,
+                                           Value* pExc)
+{
+    Bridge* bridge = static_cast<Bridge*>(pBridge);
+    Context* context = static_cast<Context*>(bridge->m_net_env->pContext);
+
+    OUString sMethodName(sFunctionName);
+
+    try
+    {
+        uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface);
+        typelib_InterfaceTypeDescription* pTD
+            = static_cast<typelib_InterfaceTypeDescription*>(pTypeDesc);
+
+        for (sal_Int32 i = 0; i < pTD->nAllMembers; ++i)
+        {
+            // Try to avoid getting typedescription as long as possible because
+            // of Mutex.acquire() in 
typelib_typedescriptionreference_getDescription()
+            typelib_TypeDescriptionReference* memberType = 
pTD->ppAllMembers[i];
+
+            // Check method_name against fully qualified typeName of memberType
+            // typeName is of the form <name> "::" <method_name> *(":@" <idx> 
"," <idx> ":" <name>)
+            const OUString& typeName = 
OUString::unacquired(&memberType->pTypeName);
+            sal_Int32 offset = typeName.indexOf(':') + 2;
+            sal_Int32 remainder = typeName.getLength() - offset;
+            assert(offset >= 2 && offset < typeName.getLength() && 
typeName[offset - 1] == ':');
+
+            switch (memberType->eTypeClass)
+            {
+                case typelib_TypeClass_INTERFACE_METHOD:
+                    if ((sMethodName.getLength() == remainder
+                         || (sMethodName.getLength() < remainder
+                             && typeName[offset + sMethodName.getLength()] == 
':'))
+                        && typeName.match(sMethodName, offset))
+                    {
+                        TypeDescHolder memberTD(memberType);
+                        typelib_InterfaceMethodTypeDescription* pMethodTD
+                            = 
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(
+                                memberTD.get());
+                        return bridge->call_uno_func(pUnoI, memberTD.get(),
+                                                     
pMethodTD->pReturnTypeRef, pMethodTD->nParams,
+                                                     pMethodTD->pParams, 
pArgs, pRet, pExc);
+                    }
+                    break;
+
+                case typelib_TypeClass_INTERFACE_ATTRIBUTE:
+                    if (sMethodName.getLength() > 4
+                        && (sMethodName.getLength() - 4 == remainder
+                            || (sMethodName.getLength() - 4 < remainder
+                                && typeName[offset + (sMethodName.getLength() 
- 4)] == ':'))
+                        && sMethodName[1] == 'e' && sMethodName[2] == 't'
+                        && rtl_ustr_compare_WithLength(
+                               typeName.getStr() + offset, 
sMethodName.getLength() - 4,
+                               sMethodName.getStr() + 4, 
sMethodName.getLength() - 4)
+                               == 0)
+                    {
+                        TypeDescHolder memberTD(memberType);
+                        typelib_InterfaceAttributeTypeDescription* pAttribTD
+                            = 
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(
+                                memberTD.get());
+                        if (sMethodName[0] == 'g')
+                        {
+                            return bridge->call_uno_func(pUnoI, memberTD.get(),
+                                                         
pAttribTD->pAttributeTypeRef, 0, nullptr,
+                                                         pArgs, pRet, pExc);
+                        }
+                        else if (sMethodName[0] == 's' && 
!pAttribTD->bReadOnly)
+                        {
+                            typelib_MethodParameter param;
+                            param.pTypeRef = pAttribTD->pAttributeTypeRef;
+                            param.bIn = true;
+                            param.bOut = false;
+
+                            return bridge->call_uno_func(pUnoI, memberTD.get(),
+                                                         
pAttribTD->pAttributeTypeRef, 1, &param,
+                                                         pArgs, pRet, pExc);
+                        }
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        // No matching method info found
+        throw BridgeRuntimeError(SAL_WHERE, "could not find function " + 
sMethodName
+                                                + " to call from type "
+                                                + 
OUString::unacquired(&pTD->aBase.pTypeName));
+    }
+    catch (const BridgeRuntimeError& err)
+    {
+        SAL_WARN("bridges", ".NET bridge error: " << err.m_message);
+        context->throwError(err.m_location.getStr(), err.m_message.getStr());
+        return false;
+    }
+}
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_bridge.hxx 
b/bridges/source/net_uno/net_bridge.hxx
new file mode 100644
index 000000000000..19c07243743a
--- /dev/null
+++ b/bridges/source/net_uno/net_bridge.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "net_base.hxx"
+
+#include <bridges/net_uno/net_types.hxx>
+#include <uno/dispatcher.hxx>
+#include <uno/environment.hxx>
+#include <uno/mapping.hxx>
+
+namespace net_uno
+{
+struct Bridge;
+
+struct Mapping : public uno_Mapping
+{
+    Bridge* m_bridge;
+};
+
+struct Bridge
+{
+    mutable oslInterlockedCount m_ref;
+    uno_ExtEnvironment* m_uno_env;
+    uno_Environment* m_net_env;
+
+    Mapping m_net2uno;
+    Mapping m_uno2net;
+    bool m_registered_net2uno;
+
+    Bridge(uno_Environment* net_env, uno_ExtEnvironment* uno_env, bool 
registered_net2uno);
+    ~Bridge();
+
+    void acquire();
+    void release();
+
+    void map_uno_to_net_value(void* pUnoData, Value* pValue,
+                              typelib_TypeDescriptionReference* pTDRef, bool 
destructValue);
+    void map_net_value_to_uno(void* pUnoData, Value* pValue,
+                              typelib_TypeDescriptionReference* pTDRef, bool 
destructObject,
+                              bool assignObject);
+
+    bool call_uno_func(uno_Interface* pUnoI, const typelib_TypeDescription* 
pMethodTD,
+                       typelib_TypeDescriptionReference* pReturnTDRef, int 
nParams,
+                       typelib_MethodParameter* pParams, Value* pArgs, Value* 
pRet, Value* pExc);
+    void call_net_func(IntPtr pNetI, const typelib_TypeDescription* pMethodTD,
+                       typelib_TypeDescriptionReference* pReturnTDRef, int 
nParams,
+                       typelib_MethodParameter* pParams, void** pArgs, void* 
pRet, uno_Any** pExc);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_data.cxx 
b/bridges/source/net_uno/net_data.cxx
new file mode 100644
index 000000000000..b41e9cb960d2
--- /dev/null
+++ b/bridges/source/net_uno/net_data.cxx
@@ -0,0 +1,855 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "net_bridge.hxx"
+#include "net_proxy.hxx"
+
+#include <cstring>
+#include <unordered_map>
+#include <vector>
+
+#include <bridges/net_uno/net_context.hxx>
+
+namespace net_uno
+{
+namespace
+{
+size_t net_sizeof(typelib_TypeClass eTypeClass)
+{
+    static const std::unordered_map<typelib_TypeClass, size_t> s_sizes{
+        { typelib_TypeClass_BOOLEAN, sizeof(sal_Bool) },
+        { typelib_TypeClass_BYTE, sizeof(sal_Int8) },
+        { typelib_TypeClass_CHAR, sizeof(sal_Unicode) },
+        { typelib_TypeClass_SHORT, sizeof(sal_Int16) },
+        { typelib_TypeClass_UNSIGNED_SHORT, sizeof(sal_uInt16) },
+        { typelib_TypeClass_LONG, sizeof(sal_Int32) },
+        { typelib_TypeClass_UNSIGNED_LONG, sizeof(sal_uInt32) },
+        { typelib_TypeClass_HYPER, sizeof(sal_Int64) },
+        { typelib_TypeClass_UNSIGNED_HYPER, sizeof(sal_uInt64) },
+        { typelib_TypeClass_FLOAT, sizeof(float) },
+        { typelib_TypeClass_DOUBLE, sizeof(double) },
+        { typelib_TypeClass_ENUM, sizeof(sal_Int32) },
+        { typelib_TypeClass_STRING, sizeof(IntPtr) },
+        { typelib_TypeClass_TYPE, sizeof(IntPtr) },
+        { typelib_TypeClass_ANY, sizeof(IntPtr) + sizeof(IntPtr) },
+        { typelib_TypeClass_SEQUENCE, sizeof(IntPtr) + sizeof(sal_Int32) },
+        { typelib_TypeClass_INTERFACE, sizeof(IntPtr) },
+        { typelib_TypeClass_EXCEPTION, sizeof(IntPtr) },
+        { typelib_TypeClass_STRUCT, sizeof(IntPtr) },
+    };
+    return s_sizes.at(eTypeClass);
+}
+
+IntPtr alloc_net_string(const OUString& str)
+{
+    const sal_Unicode* src = str.getStr();
+    sal_Int32 len = str.getLength();
+
+    void* dst = std::malloc((len + 1) * sizeof(sal_Unicode));
+    std::memcpy(dst, src, len * sizeof(sal_Unicode));
+    static_cast<sal_Unicode*>(dst)[len] = u'
+
+    return dst;
+}
+
+uno_Sequence* alloc_uno_sequence(sal_Int32 nElements, sal_Int32 nElementSize, 
void* data)
+{
+    void* mem = std::malloc(SAL_SEQUENCE_HEADER_SIZE + nElements * 
nElementSize);
+
+    uno_Sequence* seq = static_cast<uno_Sequence*>(mem);
+    seq->nRefCount = 1;
+    seq->nElements = nElements;
+
+    if (data)
+        std::memcpy(seq->elements, data, nElements * nElementSize);
+
+    return seq;
+}
+
+void marshal_data(void* pUnoData, void* pNetData, 
typelib_TypeDescriptionReference* pTDRef,
+                  bool bDestructValue, Bridge& bridge)
+{
+    switch (pTDRef->eTypeClass)
+    {
+        case typelib_TypeClass_BOOLEAN:
+        case typelib_TypeClass_BYTE:
+        case typelib_TypeClass_CHAR:
+        case typelib_TypeClass_SHORT:
+        case typelib_TypeClass_UNSIGNED_SHORT:
+        case typelib_TypeClass_LONG:
+        case typelib_TypeClass_UNSIGNED_LONG:
+        case typelib_TypeClass_HYPER:
+        case typelib_TypeClass_UNSIGNED_HYPER:
+        case typelib_TypeClass_FLOAT:
+        case typelib_TypeClass_DOUBLE:
+        case typelib_TypeClass_ENUM:
+            std::memcpy(pNetData, pUnoData, net_sizeof(pTDRef->eTypeClass));
+            break;
+        case typelib_TypeClass_STRING:
+        {
+            IntPtr* ppNetStr = static_cast<IntPtr*>(pNetData);
+            rtl_uString* pUnoStr = *static_cast<rtl_uString**>(pUnoData);
+
+            if (bDestructValue && pNetData)
+                std::free(pNetData);
+
+            *ppNetStr = alloc_net_string(OUString::unacquired(&pUnoStr));
+            break;
+        }
+        case typelib_TypeClass_TYPE:
+        {
+            IntPtr* ppNetType = static_cast<IntPtr*>(pNetData);
+            typelib_TypeDescriptionReference* pUnoType
+                = *static_cast<typelib_TypeDescriptionReference**>(pUnoData);
+
+            if (bDestructValue && pNetData)
+                std::free(pNetData);
+
+            *ppNetType = alloc_net_string(map_uno_type_to_net(pUnoType));
+            break;
+        }
+        case typelib_TypeClass_ANY:
+        {
+            Value::Any* ppNetAny = static_cast<Value::Any*>(pNetData);
+            uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData);
+
+            if (bDestructValue && ppNetAny->type)
+                std::free(ppNetAny->type);
+            if (bDestructValue && ppNetAny->data)
+                std::free(ppNetAny->data);
+
+            ppNetAny->type = 
alloc_net_string(map_uno_type_to_net(pUnoAny->pType));
+
+            switch (pUnoAny->pType->eTypeClass)
+            {
+                case typelib_TypeClass_VOID:
+                    ppNetAny->data = nullptr;
+                    break;
+                case typelib_TypeClass_BOOLEAN:
+                case typelib_TypeClass_BYTE:
+                case typelib_TypeClass_CHAR:
+                case typelib_TypeClass_SHORT:
+                case typelib_TypeClass_UNSIGNED_SHORT:
+                case typelib_TypeClass_LONG:
+                case typelib_TypeClass_UNSIGNED_LONG:
+                case typelib_TypeClass_FLOAT:
+                case typelib_TypeClass_ENUM:
+                    std::memcpy(&ppNetAny->data, pUnoAny->pData, 
sizeof(IntPtr));
+                    break;
+                case typelib_TypeClass_HYPER:
+                case typelib_TypeClass_UNSIGNED_HYPER:
+                case typelib_TypeClass_DOUBLE:
+                {
+                    size_t size = net_sizeof(pUnoAny->pType->eTypeClass);
+                    if (size <= sizeof(IntPtr))
+                    {
+                        std::memcpy(&ppNetAny->data, pUnoAny->pData, 
sizeof(IntPtr));
+                    }
+                    else
+                    {
+                        IntPtr mem = std::malloc(size);
+                        std::memcpy(mem, pUnoAny->pData, size);
+                        std::free(ppNetAny->data);
+                        ppNetAny->data = mem;
+                    }
+                    break;
+                }
+                case typelib_TypeClass_ANY:
+                case typelib_TypeClass_SEQUENCE:
+                {
+                    IntPtr mem = 
std::malloc(net_sizeof(pUnoAny->pType->eTypeClass));
+                    marshal_data(pUnoAny->pData, mem, pUnoAny->pType, 
bDestructValue, bridge);
+                    std::free(ppNetAny->data);
+                    ppNetAny->data = mem;
+                    break;
+                }
+                case typelib_TypeClass_STRING:
+                case typelib_TypeClass_TYPE:
+                case typelib_TypeClass_INTERFACE:
+                case typelib_TypeClass_EXCEPTION:
+                case typelib_TypeClass_STRUCT:
+                    marshal_data(pUnoAny->pData, &ppNetAny->data, 
pUnoAny->pType, bDestructValue,
+                                 bridge);
+                    break;
+                default:
+                {
+                    throw BridgeRuntimeError(SAL_WHERE,
+                                             "could not map "
+                                                 + 
OUString::unacquired(&pUnoAny->pType->pTypeName)
+                                                 + " out of an UNO any");
+                }
+            }
+            break;
+        }
+        case typelib_TypeClass_SEQUENCE:
+        {
+            Value::Sequence* ppNetSeq = 
static_cast<Value::Sequence*>(pNetData);
+            uno_Sequence* pUnoSeq = *static_cast<uno_Sequence**>(pUnoData);
+
+            if (bDestructValue && ppNetSeq->data)
+                std::free(ppNetSeq->data);
+
+            ppNetSeq->length = pUnoSeq->nElements;
+
+            TypeDescHolder type(pTDRef);
+            typelib_TypeDescriptionReference* pElemTDRef
+                = 
reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType;
+
+            size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass);
+
+            switch (pElemTDRef->eTypeClass)
+            {
+                case typelib_TypeClass_BOOLEAN:
+                case typelib_TypeClass_BYTE:
+                case typelib_TypeClass_CHAR:
+                case typelib_TypeClass_SHORT:
+                case typelib_TypeClass_UNSIGNED_SHORT:
+                case typelib_TypeClass_LONG:
+                case typelib_TypeClass_UNSIGNED_LONG:
+                case typelib_TypeClass_HYPER:
+                case typelib_TypeClass_UNSIGNED_HYPER:
+                case typelib_TypeClass_FLOAT:
+                case typelib_TypeClass_DOUBLE:
+                case typelib_TypeClass_ENUM:
+                {
+                    ppNetSeq->data = std::malloc(nNetElemSize * 
ppNetSeq->length);
+                    std::memcpy(ppNetSeq->data, pUnoSeq->elements, 
nNetElemSize * ppNetSeq->length);
+                    break;
+                }
+                case typelib_TypeClass_ANY:
+                case typelib_TypeClass_SEQUENCE:
+                case typelib_TypeClass_STRING:
+                case typelib_TypeClass_TYPE:
+                case typelib_TypeClass_INTERFACE:
+                case typelib_TypeClass_EXCEPTION:
+                case typelib_TypeClass_STRUCT:
+                {
+                    TypeDescHolder elemType(pElemTDRef);
+                    sal_Int32 nUnoElemSize = elemType.get()->nSize;
+
+                    ppNetSeq->data = std::malloc(nUnoElemSize * 
ppNetSeq->length);
+
+                    // Convert each element
+                    for (int nPos = 0; nPos < ppNetSeq->length; ++nPos)
+                    {
+                        void* pNetElem = static_cast<char*>(ppNetSeq->data) + 
(nPos * nNetElemSize);
+                        void* pUnoElem = pUnoSeq->elements + (nPos * 
nUnoElemSize);
+                        marshal_data(pUnoElem, pNetElem, pElemTDRef, 
bDestructValue, bridge);
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw BridgeRuntimeError(
+                        SAL_WHERE, "could not map " + 
OUString::unacquired(&pElemTDRef->pTypeName)
+                                       + " out of an UNO sequence");
+                }
+            }
+            break;
+        }
+        case typelib_TypeClass_INTERFACE:
+        {
+            IntPtr* ppNetI = static_cast<IntPtr*>(pNetData);
+            uno_Interface* pUnoI = *static_cast<uno_Interface**>(pUnoData);
+
+            if (pUnoI)
+            {
+                Context* pCtx = 
static_cast<Context*>(bridge.m_net_env->pContext);
+
+                // Get oid and type name
+                rtl_uString* pOid = nullptr;
+                (*bridge.m_uno_env->getObjectIdentifier)(bridge.m_uno_env, 
&pOid, pUnoI);
+                const OUString& sOid = OUString::unacquired(&pOid);
+
+                OUString sTypeName = map_uno_type_to_net(pTDRef);
+
+                // Get the proxy if already created, else create new
+                *ppNetI = pCtx->lookupInterface(sOid.getStr(), 
sTypeName.getStr());
+                if (!*ppNetI)
+                {
+                    TypeDescHolder type(pTDRef);
+                    typelib_InterfaceTypeDescription* pTD
+                        = 
reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get());
+
+                    // TODO: check whether liftime control is correct
+                    bridge.acquire();
+                    (*pUnoI->acquire)(pUnoI);
+                    typelib_typedescription_acquire(&pTD->aBase);
+                    (*bridge.m_uno_env->registerInterface)(
+                        bridge.m_uno_env, reinterpret_cast<void**>(&pUnoI), 
pOid, pTD);
+                    *ppNetI
+                        = pCtx->createProxy(sOid.getStr(), sTypeName.getStr(), 
&bridge, pUnoI, pTD);
+                }
+            }
+            else
+            {
+                *ppNetI = nullptr;
+            }
+            break;
+        }
+        case typelib_TypeClass_EXCEPTION:
+        case typelib_TypeClass_STRUCT:
+        {
+            IntPtr* ppNetStruct = static_cast<IntPtr*>(pNetData);
+            void* pUnoStruct = pUnoData;
+
+            if (bDestructValue && *ppNetStruct)
+                std::free(*ppNetStruct);
+
+            TypeDescHolder type(pTDRef);
+            typelib_CompoundTypeDescription* pCompTD
+                = 
reinterpret_cast<typelib_CompoundTypeDescription*>(type.get());
+            if (!type.get()->bComplete)
+                typelib_typedescription_complete(
+                    reinterpret_cast<typelib_TypeDescription**>(&pCompTD));
+
+            size_t nBytes = 0;
+            std::vector<std::pair<typelib_TypeDescriptionReference*, 
sal_Int32>> vecMembers;
+            for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD;
+                 pHierTD = pHierTD->pBaseTypeDescription)
+            {
+                for (int n = pHierTD->nMembers - 1; n >= 0; n--)
+                {
+                    vecMembers.emplace_back(pHierTD->ppTypeRefs[n], 
pHierTD->pMemberOffsets[n]);
+                    nBytes += net_sizeof(pHierTD->ppTypeRefs[n]->eTypeClass);
+                }
+            }
+
+            *ppNetStruct = std::malloc(nBytes);
+
+            size_t nNetOffset = 0;
+            int nPos = vecMembers.size() - 1;
+            for (; nPos >= 0; nPos--)
+            {
+                auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos];
+
+                void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset;
+                void* pNetField = static_cast<char*>(*ppNetStruct) + 
nNetOffset;
+
+                switch (pMemberTDRef->eTypeClass)
+                {
+                    case typelib_TypeClass_BOOLEAN:
+                    case typelib_TypeClass_BYTE:
+                    case typelib_TypeClass_CHAR:
+                    case typelib_TypeClass_SHORT:
+                    case typelib_TypeClass_UNSIGNED_SHORT:
+                    case typelib_TypeClass_LONG:
+                    case typelib_TypeClass_UNSIGNED_LONG:
+                    case typelib_TypeClass_HYPER:
+                    case typelib_TypeClass_UNSIGNED_HYPER:
+                    case typelib_TypeClass_FLOAT:
+                    case typelib_TypeClass_DOUBLE:
+                    case typelib_TypeClass_ENUM:
+                        std::memcpy(pNetField, pUnoField, 
net_sizeof(pMemberTDRef->eTypeClass));
+                        break;
+                    case typelib_TypeClass_ANY:
+                    case typelib_TypeClass_SEQUENCE:
+                    case typelib_TypeClass_STRING:
+                    case typelib_TypeClass_TYPE:
+                    case typelib_TypeClass_INTERFACE:
+                    case typelib_TypeClass_EXCEPTION:
+                    case typelib_TypeClass_STRUCT:
+                        marshal_data(pUnoField, pNetField, pMemberTDRef, 
bDestructValue, bridge);
+                        break;
+                    default:
+                    {
+                        throw BridgeRuntimeError(
+                            SAL_WHERE,
+                            "could not map " + 
OUString::unacquired(&pMemberTDRef->pTypeName)
+                                + " out of an UNO " + 
OUString::unacquired(&pTDRef->pTypeName));
+                    }
+                }
+
+                nNetOffset += net_sizeof(pMemberTDRef->eTypeClass);
+            }
+            break;
+        }
+        default:
+        {
+            throw BridgeRuntimeError(SAL_WHERE, "could not map "
+                                                    + 
OUString::unacquired(&pTDRef->pTypeName)
+                                                    + " value to .NET");
+        }
+    }
+}
+
+void unmarshal_data(void* pUnoData, void* pNetData, 
typelib_TypeDescriptionReference* pTDRef,
+                    bool bDestructObject, bool bAssignData, Bridge& bridge)
+{
+    switch (pTDRef->eTypeClass)
+    {
+        case typelib_TypeClass_BOOLEAN:
+        case typelib_TypeClass_BYTE:
+        case typelib_TypeClass_CHAR:
+        case typelib_TypeClass_SHORT:
+        case typelib_TypeClass_UNSIGNED_SHORT:
+        case typelib_TypeClass_LONG:
+        case typelib_TypeClass_UNSIGNED_LONG:
+        case typelib_TypeClass_HYPER:
+        case typelib_TypeClass_UNSIGNED_HYPER:
+        case typelib_TypeClass_FLOAT:
+        case typelib_TypeClass_DOUBLE:
+        case typelib_TypeClass_ENUM:
+            if (bAssignData)
+                std::memcpy(pUnoData, pNetData, 
net_sizeof(pTDRef->eTypeClass));
+            break;
+        case typelib_TypeClass_STRING:
+        {
+            rtl_uString** ppUnoStr = static_cast<rtl_uString**>(pUnoData);
+            IntPtr pNetStr = *static_cast<IntPtr*>(pNetData);
+
+            if (bDestructObject && ppUnoStr)
+                rtl_uString_release(*ppUnoStr);
+
+            if (bAssignData)
+            {
+                *ppUnoStr = nullptr;
+                if (pNetStr)
+                    rtl_uString_newFromStr(ppUnoStr, 
static_cast<String>(pNetStr));
+                else
+                    rtl_uString_new(ppUnoStr);
+            }
+
+            std::free(pNetStr);
+            break;
+        }
+        case typelib_TypeClass_TYPE:
+        {
+            typelib_TypeDescriptionReference** ppUnoType
+                = static_cast<typelib_TypeDescriptionReference**>(pUnoData);
+            IntPtr pNetType = *static_cast<IntPtr*>(pNetData);
+
+            if (bDestructObject && ppUnoType)
+                typelib_typedescriptionreference_release(*ppUnoType);
+
+            if (bAssignData)
+                *ppUnoType = 
map_net_type_to_uno(OUString(static_cast<String>(pNetType)));
+
+            std::free(pNetType);
+            break;
+        }
+        case typelib_TypeClass_ANY:
+        {
+            uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData);
+            Value::Any* pNetAny = static_cast<Value::Any*>(pNetData);
+
+            if (bDestructObject && pUnoData)
+                uno_any_destruct(pUnoAny, nullptr);
+
+            typelib_TypeDescriptionReference* pValueTDRef
+                = 
map_net_type_to_uno(OUString(static_cast<String>(pNetAny->type)));
+            std::free(pNetAny->type);
+
+            if (bAssignData)
+            {
+                switch (pValueTDRef->eTypeClass)
+                {
+                    case typelib_TypeClass_VOID:
+                    {
+                        pUnoAny->pType = pValueTDRef;
+                        pUnoAny->pData = &pUnoAny->pReserved;
+                        break;
+                    }
+                    case typelib_TypeClass_BOOLEAN:
+                    case typelib_TypeClass_BYTE:
+                    case typelib_TypeClass_CHAR:
+                    case typelib_TypeClass_SHORT:
+                    case typelib_TypeClass_UNSIGNED_SHORT:
+                    case typelib_TypeClass_LONG:
+                    case typelib_TypeClass_UNSIGNED_LONG:
+                    case typelib_TypeClass_FLOAT:
+                    case typelib_TypeClass_ENUM:
+                    {
+                        pUnoAny->pType = pValueTDRef;
+                        pUnoAny->pData = &pUnoAny->pReserved;
+                        std::memcpy(pUnoAny->pData, &pNetAny->data, 
sizeof(IntPtr));
+                        break;
+                    }
+                    case typelib_TypeClass_HYPER:
+                    case typelib_TypeClass_UNSIGNED_HYPER:
+                    case typelib_TypeClass_DOUBLE:
+                    {
+                        pUnoAny->pType = pValueTDRef;
+                        size_t size = net_sizeof(pValueTDRef->eTypeClass);
+                        if (size <= sizeof(IntPtr))
+                        {
+                            pUnoAny->pData = &pUnoAny->pReserved;
+                            std::memcpy(pUnoAny->pData, &pNetAny->data, 
sizeof(IntPtr));
+                        }
+                        else
+                        {
+                            void* mem = std::malloc(size);
+                            std::memcpy(mem, pNetAny->data, size);
+                            pUnoAny->pData = mem;
+                            std::free(pNetAny->data);
+                        }
+                        break;
+                    }
+                    case typelib_TypeClass_STRING:
+                    case typelib_TypeClass_TYPE:
+                    case typelib_TypeClass_INTERFACE:
+                    {
+                        if (pNetAny->data)
+                        {
+                            pUnoAny->pType = pValueTDRef;
+                            pUnoAny->pData = &pUnoAny->pReserved;
+                            pUnoAny->pReserved = nullptr;
+                            unmarshal_data(pUnoAny->pData, &pNetAny->data, 
pValueTDRef,
+                                           bDestructObject, true, bridge);
+                        }
+                        else
+                        {
+                            uno_any_construct(pUnoAny, nullptr, nullptr, 
nullptr);
+                        }
+                        break;
+                    }
+                    case typelib_TypeClass_ANY:
+                    case typelib_TypeClass_SEQUENCE:
+                    {
+                        if (pNetAny->data)
+                        {
+                            pUnoAny->pType = pValueTDRef;
+                            pUnoAny->pData = &pUnoAny->pReserved;
+                            pUnoAny->pReserved = nullptr;
+                            unmarshal_data(pUnoAny->pData, &pNetAny->data, 
pValueTDRef,
+                                           bDestructObject, true, bridge);
+                        }
+                        else
+                        {
+                            uno_any_construct(pUnoAny, nullptr, nullptr, 
nullptr);
+                        }
+                        break;
+                    }
+                    case typelib_TypeClass_STRUCT:
+                    case typelib_TypeClass_EXCEPTION:
+                    {
+                        if (pNetAny->data)
+                        {
+                            pUnoAny->pType = pValueTDRef;
+                            TypeDescHolder valueType(pValueTDRef);
+                            void* mem = std::malloc(valueType.get()->nSize);
+                            unmarshal_data(mem, &pNetAny->data, pValueTDRef, 
bDestructObject, true,
+                                           bridge);
+                            pUnoAny->pData = mem;
+                        }
+                        else
+                        {
+                            uno_any_construct(pUnoAny, nullptr, nullptr, 
nullptr);
+                        }
+                        break;
+                    }
+                    default:
+                    {
+                        throw BridgeRuntimeError(SAL_WHERE,
+                                                 "could not map "
+                                                     + 
OUString::unacquired(&pValueTDRef->pTypeName)
+                                                     + " into an UNO any");
+                    }
+                }
+            }
+            else
+            {
+                switch (pValueTDRef->eTypeClass)
+                {
+                    case typelib_TypeClass_VOID:
+                    case typelib_TypeClass_BOOLEAN:
+                    case typelib_TypeClass_BYTE:
+                    case typelib_TypeClass_CHAR:
+                    case typelib_TypeClass_SHORT:
+                    case typelib_TypeClass_UNSIGNED_SHORT:
+                    case typelib_TypeClass_LONG:
+                    case typelib_TypeClass_UNSIGNED_LONG:
+                    case typelib_TypeClass_FLOAT:
+                    case typelib_TypeClass_ENUM:
+                        break;
+                    case typelib_TypeClass_HYPER:
+                    case typelib_TypeClass_UNSIGNED_HYPER:
+                    case typelib_TypeClass_DOUBLE:
+                    {
+                        size_t size = net_sizeof(pValueTDRef->eTypeClass);
+                        if (pNetAny->data && size > sizeof(IntPtr))
+                            std::free(pNetAny->data);
+                        break;
+                    }
+                    case typelib_TypeClass_ANY:
+                    case typelib_TypeClass_SEQUENCE:
+                    {
+                        if (pNetAny->data)
+                        {
+                            unmarshal_data(pUnoAny->pData, &pNetAny->data, 
pValueTDRef,
+                                           bDestructObject, false, bridge);
+                            std::free(pNetAny->data);
+                        }
+                        break;
+                    }
+                    case typelib_TypeClass_STRING:
+                    case typelib_TypeClass_TYPE:
+                    case typelib_TypeClass_INTERFACE:
+                    case typelib_TypeClass_STRUCT:
+                    case typelib_TypeClass_EXCEPTION:
+                    {
+                        if (pNetAny->data)
+                        {
+                            unmarshal_data(pUnoAny->pData, &pNetAny->data, 
pValueTDRef,
+                                           bDestructObject, false, bridge);
+                        }
+                        break;
+                    }
+                    default:
+                    {
+                        throw BridgeRuntimeError(SAL_WHERE,
+                                                 "could not map "
+                                                     + 
OUString::unacquired(&pValueTDRef->pTypeName)
+                                                     + " into an UNO any");
+                    }
+                }
+            }
+            break;
+        }
+        case typelib_TypeClass_SEQUENCE:
+        {
+            uno_Sequence** ppUnoSeq = static_cast<uno_Sequence**>(pUnoData);
+            Value::Sequence* pNetSeq = static_cast<Value::Sequence*>(pNetData);
+
+            TypeDescHolder type(pTDRef);
+            if (bDestructObject && ppUnoSeq)
+                uno_destructData(ppUnoSeq, type.get(), nullptr);
+
+            typelib_TypeDescriptionReference* pElemTDRef
+                = 
reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType;
+            size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass);
+
+            switch (pElemTDRef->eTypeClass)
+            {
+                case typelib_TypeClass_BOOLEAN:
+                case typelib_TypeClass_BYTE:
+                case typelib_TypeClass_CHAR:
+                case typelib_TypeClass_SHORT:
+                case typelib_TypeClass_UNSIGNED_SHORT:
+                case typelib_TypeClass_LONG:
+                case typelib_TypeClass_UNSIGNED_LONG:
+                case typelib_TypeClass_HYPER:
+                case typelib_TypeClass_UNSIGNED_HYPER:
+                case typelib_TypeClass_FLOAT:
+                case typelib_TypeClass_DOUBLE:
+                case typelib_TypeClass_ENUM:
+                    if (bAssignData)
+                        *ppUnoSeq
+                            = alloc_uno_sequence(pNetSeq->length, 
nNetElemSize, pNetSeq->data);
+                    break;
+                case typelib_TypeClass_ANY:
+                case typelib_TypeClass_SEQUENCE:
+                case typelib_TypeClass_STRING:
+                case typelib_TypeClass_TYPE:
+                case typelib_TypeClass_INTERFACE:
+                case typelib_TypeClass_EXCEPTION:
+                case typelib_TypeClass_STRUCT:
+                {
+                    TypeDescHolder elemType(pElemTDRef);
+                    sal_Int32 nUnoElemSize = elemType.get()->nSize;
+
+                    *ppUnoSeq = alloc_uno_sequence(pNetSeq->length, 
nUnoElemSize, nullptr);
+                    int nPos = 0;
+                    try
+                    {
+                        // Convert each element
+                        for (; nPos < pNetSeq->length; ++nPos)
+                        {
+                            void* pNetElem
+                                = static_cast<char*>(pNetSeq->data) + (nPos * 
nNetElemSize);
+                            void* pUnoElem = (*ppUnoSeq)->elements + (nPos * 
nUnoElemSize);
+                            unmarshal_data(pUnoElem, pNetElem, pElemTDRef, 
bDestructObject,
+                                           bAssignData, bridge);
+                        }
+                    }
+                    catch (...)
+                    {
+                        if (bAssignData)
+                        {
+                            // Clean up already converted elements
+                            for (int nClean = 0; nClean < nPos; ++nClean)
+                            {
+                                void* pUnoElem = (*ppUnoSeq)->elements + 
(nClean * nUnoElemSize);
+                                uno_destructData(pUnoElem, elemType.get(), 
nullptr);
+                            }
+                        }
+                        throw;
+                    }
+
+                    break;
+                }
+                default:
+                {
+                    throw BridgeRuntimeError(
+                        SAL_WHERE, "could not map " + 
OUString::unacquired(&pElemTDRef->pTypeName)
+                                       + " into an UNO sequence");
+                }
+            }
+
+            std::free(pNetSeq->data);
+            break;
+        }
+        case typelib_TypeClass_INTERFACE:
+        {
+            uno_Interface** ppUnoI = static_cast<uno_Interface**>(pUnoData);
+            IntPtr pNetI = *static_cast<IntPtr*>(pNetData);
+
+            TypeDescHolder type(pTDRef);
+            if (bDestructObject && ppUnoI)
+                uno_destructData(ppUnoI, type.get(), nullptr);
+
+            if (bAssignData)
+            {
+                if (pNetI)
+                {
+                    Context* pCtx = 
static_cast<Context*>(bridge.m_net_env->pContext);
+
+                    // Get oid and type description
+                    OUString sOid(pCtx->lookupObjectId(pNetI));
+
+                    typelib_InterfaceTypeDescription* pInterfaceTD
+                        = 
reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get());
+
+                    // Get the proxy if already created, else create new
+                    *ppUnoI = nullptr;
+                    
(*bridge.m_uno_env->getRegisteredInterface)(bridge.m_uno_env,
+                                                                
reinterpret_cast<void**>(ppUnoI),
+                                                                sOid.pData, 
pInterfaceTD);
+
+                    if (!*ppUnoI)
+                        *ppUnoI = new NetProxy(bridge, pNetI, pInterfaceTD, 
sOid);
+                }
+                else
+                {
+                    *ppUnoI = nullptr;
+                }
+            }
+
+            break;
+        }
+        case typelib_TypeClass_EXCEPTION:
+        case typelib_TypeClass_STRUCT:
+        {
+            void* pUnoStruct = pUnoData;
+            IntPtr pNetStruct = *static_cast<IntPtr*>(pNetData);
+
+            TypeDescHolder type(pTDRef);
+            if (bDestructObject && pNetStruct)
+                uno_destructData(pNetStruct, type.get(), nullptr);
+
+            typelib_CompoundTypeDescription* pCompTD
+                = 
reinterpret_cast<typelib_CompoundTypeDescription*>(type.get());
+            if (!type.get()->bComplete)
+                typelib_typedescription_complete(
+                    reinterpret_cast<typelib_TypeDescription**>(&pCompTD));
+
+            std::vector<std::pair<typelib_TypeDescriptionReference*, 
sal_Int32>> vecMembers;
+            for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD;
+                 pHierTD = pHierTD->pBaseTypeDescription)
+            {
+                for (int n = pHierTD->nMembers - 1; n >= 0; n--)
+                {
+                    vecMembers.emplace_back(pHierTD->ppTypeRefs[n], 
pHierTD->pMemberOffsets[n]);
+                }
+            }
+
+            size_t nNetOffset = 0;
+            int nPos = vecMembers.size() - 1;
+            try
+            {
+                for (; nPos >= 0; nPos--)
+                {
+                    auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos];
+
+                    void* pUnoField = static_cast<char*>(pUnoStruct) + 
nUnoOffset;
+                    void* pNetField = static_cast<char*>(pNetStruct) + 
nNetOffset;
+
+                    switch (pMemberTDRef->eTypeClass)
+                    {
+                        case typelib_TypeClass_BOOLEAN:
+                        case typelib_TypeClass_BYTE:
+                        case typelib_TypeClass_CHAR:
+                        case typelib_TypeClass_SHORT:
+                        case typelib_TypeClass_UNSIGNED_SHORT:
+                        case typelib_TypeClass_LONG:
+                        case typelib_TypeClass_UNSIGNED_LONG:
+                        case typelib_TypeClass_HYPER:
+                        case typelib_TypeClass_UNSIGNED_HYPER:
+                        case typelib_TypeClass_FLOAT:
+                        case typelib_TypeClass_DOUBLE:
+                        case typelib_TypeClass_ENUM:
+                            if (bAssignData)
+                                std::memcpy(pUnoField, pNetField,
+                                            
net_sizeof(pMemberTDRef->eTypeClass));
+                            break;
+                        case typelib_TypeClass_ANY:
+                        case typelib_TypeClass_SEQUENCE:
+                        case typelib_TypeClass_STRING:
+                        case typelib_TypeClass_TYPE:
+                        case typelib_TypeClass_INTERFACE:
+                        case typelib_TypeClass_EXCEPTION:
+                        case typelib_TypeClass_STRUCT:
+                            unmarshal_data(pUnoField, pNetField, pMemberTDRef, 
bDestructObject,
+                                           bAssignData, bridge);
+                            break;
+                        default:
+                        {
+                            throw BridgeRuntimeError(
+                                SAL_WHERE,
+                                "could not map " + 
OUString::unacquired(&pMemberTDRef->pTypeName)
+                                    + " into an UNO " + 
OUString::unacquired(&pTDRef->pTypeName));
+                        }
+                    }
+
+                    nNetOffset += net_sizeof(pMemberTDRef->eTypeClass);
+                }
+            }
+            catch (...)
+            {
+                if (bAssignData)
+                {
+                    // Clean up already converted fields
+                    for (int nClean = vecMembers.size() - 1; nClean > nPos; 
nClean--)
+                    {
+                        auto[pMemberTDRef, nUnoOffset] = vecMembers[nClean];
+                        void* pUnoField = static_cast<char*>(pUnoStruct) + 
nUnoOffset;
+                        uno_type_destructData(pUnoField, pMemberTDRef, 
nullptr);
+                    }
+                }
+                throw;
+            }
+
+            std::free(pNetStruct);
+            break;
+        }
+        default:
+        {
+            throw BridgeRuntimeError(SAL_WHERE, "could not map "
+                                                    + 
OUString::unacquired(&pTDRef->pTypeName)
+                                                    + " value to UNO");
+        }
+    }
+}
+}
+
+void Bridge::map_uno_to_net_value(void* pUnoData, Value* pValue,
+                                  typelib_TypeDescriptionReference* pTDRef, 
bool bDestructValue)
+{
+    marshal_data(pUnoData, pValue, pTDRef, bDestructValue, *this);
+}
+
+void Bridge::map_net_value_to_uno(void* pUnoData, Value* pValue,
+                                  typelib_TypeDescriptionReference* pTDRef, 
bool bDestructObject,
+                                  bool bAssignObject)
+{
+    unmarshal_data(pUnoData, pValue, pTDRef, bDestructObject, bAssignObject, 
*this);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_func.cxx 
b/bridges/source/net_uno/net_func.cxx
new file mode 100644
index 000000000000..788bfe1a76c4
--- /dev/null
+++ b/bridges/source/net_uno/net_func.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "net_bridge.hxx"
+
+#include <bridges/net_uno/net_context.hxx>
+
+namespace net_uno
+{
+void Bridge::call_net_func(IntPtr pNetI, const typelib_TypeDescription* 
pMethodTD,
+                           typelib_TypeDescriptionReference* pReturnTDRef, int 
nParams,
+                           typelib_MethodParameter* pParams, void** pArgs, 
void* pRet,
+                           uno_Any** pExc)
+{
+    Value* pArgsRetExc = static_cast<Value*>(alloca((nParams + 2) * 
sizeof(Value)));
+
+    // Convert in and inout arguments
+    for (int i = 0; i < nParams; ++i)
+    {
+        const typelib_MethodParameter& param = pParams[i];
+        if (param.bIn)
+        {
+            map_uno_to_net_value(pArgs[i], &pArgsRetExc[i], param.pTypeRef, 
false);
+        }
+    }
+
+    OUString sMethodName = OUString::unacquired(&pMethodTD->pTypeName);
+    Context* pCtx = static_cast<Context*>(m_net_env->pContext);
+    bool error = !pCtx->dispatchCall(pNetI, sMethodName.getStr(), pArgsRetExc,
+                                     &pArgsRetExc[nParams], 
&pArgsRetExc[nParams + 1]);
+
+    // Convert out and inout arguments
+    for (int i = 0; i < nParams; ++i)
+    {
+        const typelib_MethodParameter& param = pParams[i];
+        try
+        {
+            if (param.bOut)
+                map_net_value_to_uno(pArgs[i], &pArgsRetExc[i], param.pTypeRef,
+                                     param.bIn && param.bOut, true);
+        }
+        catch (...)
+        {
+            // Clean up uno out args
+            for (int n = 0; n < i; ++n)
+            {
+                const typelib_MethodParameter& param2 = pParams[n];
+                if (param2.bOut)
+                {
+                    uno_type_destructData(pArgs[n], param2.pTypeRef, nullptr);
+                }
+            }
+            throw;
+        }
+    }
+
+    if (error)
+    {
+        map_net_value_to_uno(*pExc, &pArgsRetExc[nParams + 1],
+                             
cppu::UnoType<css::uno::Any>::get().getTypeLibType(), false, true);
+    }
+    else
+    {
+        if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID)
+        {
+            map_net_value_to_uno(pRet, &pArgsRetExc[nParams], pReturnTDRef, 
false, true);
+        }
+
+        *pExc = nullptr;
+    }
+}
+
+bool Bridge::call_uno_func(uno_Interface* pUnoI, const 
typelib_TypeDescription* pMethodTD,
+                           typelib_TypeDescriptionReference* pReturnTDRef, int 
nParams,
+                           typelib_MethodParameter* pParams, Value* pArgs, 
Value* pRet, Value* pExc)
+{
+    union largest {
+        sal_Int64 n;
+        double d;
+        void* p;
+        uno_Any a;
+    };
+
+    // Calculate size of memory required for return value
+    sal_Int32 nReturnSize = sizeof(largest);
+    if (pReturnTDRef)
+    {
+        if (pReturnTDRef->eTypeClass == typelib_TypeClass_VOID)
+        {
+            nReturnSize = 0;
+        }
+        else if (pReturnTDRef->eTypeClass == typelib_TypeClass_STRUCT
+                 || pReturnTDRef->eTypeClass == typelib_TypeClass_EXCEPTION)
+        {
+            TypeDescHolder returnTD(pReturnTDRef);
+            if (o3tl::make_unsigned(returnTD.get()->nSize) > sizeof(largest))
+                nReturnSize = returnTD.get()->nSize;
+        }
+    }
+
+    // Prepare a memory block to contain all the converted arguments and 
return value
+    //
+    // The memory block contains pointers to small arguments stored in the 
same block.
+    // If an argument is larger then `largest` union, such as a struct, then 
the pointer
+    // points to an extra block of memory.
+    //
+    // The argument pointers are followed by the return value. If the return 
value is
+    // larger than the `largest` union, this is a pointer to an extra block 
containing
+    // the return value instead.
+    //
+    // For example: 2 arguments and return value
+    // | Pointer 1    (void*)
+    // | Pointer 2    (void*)
+    // | Return Value (void*)
+    // | Argument 1   (largest*)
+    // | Argument 2   (largest*)
+
+    // Complete memory block
+    char* mem = static_cast<char*>(
+        alloca(nParams * sizeof(void*) + nReturnSize + nParams * 
sizeof(largest)));
+    // Array of argument pointers; at the start of the memory block
+    void** uno_args = reinterpret_cast<void**>(mem);
+    // Pointer to return value memory; after all argument pointers
+    void* uno_ret = nReturnSize == 0 ? nullptr : mem + nParams * sizeof(void*);
+    // Space for actual arguments; after return value
+    largest* uno_args_mem = reinterpret_cast<largest*>(mem + nParams * 
sizeof(void*) + nReturnSize);
+
+    for (sal_Int32 i = 0; i < nParams; ++i)
+    {
+        const typelib_MethodParameter& param = pParams[i];
+        typelib_TypeDescriptionReference* type = param.pTypeRef;
+
+        uno_args[i] = &uno_args_mem[i];
+        if (type->eTypeClass == typelib_TypeClass_STRUCT
+            || type->eTypeClass == typelib_TypeClass_EXCEPTION)
+        {
+            TypeDescHolder td(type);
+            if (o3tl::make_unsigned(td.get()->nSize) > sizeof(largest))
+                uno_args[i] = alloca(td.get()->nSize);
+        }
+
+        if (param.bIn)
+        {
+            try
+            {
+                // in, in/out params
+                map_net_value_to_uno(uno_args[i], &pArgs[i], type, false, 
true);
+            }
+            catch (...)
+            {
+                // cleanup uno in args
+                for (sal_Int32 n = 0; n < i; ++n)
+                {
+                    const typelib_MethodParameter& param2 = pParams[n];
+                    if (param2.bIn)
+                    {
+                        uno_type_destructData(uno_args[n], param2.pTypeRef, 
nullptr);
+                    }
+                }
+                throw;
+            }
+        }
+    }
+
+    uno_Any uno_exc_holder;
+    uno_Any* uno_exc = &uno_exc_holder;
+
+    // Propagate function call to binary uno
+    (*pUnoI->pDispatcher)(pUnoI, pMethodTD, uno_ret, uno_args, &uno_exc);
+
+    if (!uno_exc)
+    {
+        // Convert out arguments and destruct previously converted uno args
+        for (sal_Int32 i = 0; i < nParams; ++i)
+        {
+            const typelib_MethodParameter& param = pParams[i];
+            typelib_TypeDescriptionReference* type = param.pTypeRef;
+
+            if (param.bOut)
+            {
+                try
+                {
+                    map_uno_to_net_value(uno_args[i], &pArgs[i], 
param.pTypeRef, false);
+                }
+                catch (...)
+                {
+                    // Cleanup rest of uno args
+                    for (sal_Int32 n = i; n < nParams; ++n)
+                    {
+                        uno_type_destructData(uno_args[n], 
pParams[n].pTypeRef, nullptr);
+                    }
+
+                    // Cleanup uno return value
+                    uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
+
+                    throw;
+                }
+            }
+
+            // Cleanup args
+            if (type->eTypeClass > typelib_TypeClass_DOUBLE
+                && type->eTypeClass != typelib_TypeClass_ENUM)
+            {
+                uno_type_destructData(uno_args[i], type, nullptr);
+            }
+        }
+
+        if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID)
+        {
+            // Convert uno return value
+            try
+            {
+                map_uno_to_net_value(uno_ret, pRet, pReturnTDRef, false);
+                uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
+            }
+            catch (...)
+            {
+                uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
+                throw;
+            }
+        }
+
+        return true;
+    }
+    else // An exception occurred
+    {
+        // Destruct uno in arguments
+        for (sal_Int32 i = 0; i < nParams; ++i)
+        {
+            const typelib_MethodParameter& param = pParams[i];
+            if (param.bIn)
+            {
+                uno_type_destructData(uno_args[i], param.pTypeRef, nullptr);
+            }
+        }
+
+        map_uno_to_net_value(uno_exc, pExc, 
cppu::UnoType<css::uno::Any>::get().getTypeLibType(),
+                             false);
+
+        return false;
+    }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_proxy.cxx 
b/bridges/source/net_uno/net_proxy.cxx
new file mode 100644
index 000000000000..d4c9c1869012
--- /dev/null
+++ b/bridges/source/net_uno/net_proxy.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "net_proxy.hxx"
+#include "net_base.hxx"
+
+#include <bridges/net_uno/net_context.hxx>
+
+namespace net_uno
+{
+namespace
+{
+void SAL_CALL NetProxy_acquire(uno_Interface* pUnoI) SAL_THROW_EXTERN_C()
+{
+    NetProxy* that = static_cast<NetProxy*>(pUnoI);
+    that->acquire();
+}
+
+void SAL_CALL NetProxy_release(uno_Interface* pUnoI) SAL_THROW_EXTERN_C()
+{
+    NetProxy* that = static_cast<NetProxy*>(pUnoI);
+    that->release();
+}
+
+void SAL_CALL NetProxy_free([[maybe_unused]] uno_ExtEnvironment* pEnv, void* 
pUnoI)
+    SAL_THROW_EXTERN_C()
+{
+    NetProxy* that = static_cast<NetProxy*>(pUnoI);
+    delete that;
+}
+
+void SAL_CALL NetProxy_dispatch(uno_Interface* pUnoI, const 
typelib_TypeDescription* pMemberTD,
+                                void* pUnoRet, void** pUnoArgs, uno_Any** 
pUnoExc)
+    SAL_THROW_EXTERN_C()
+{
+    NetProxy* proxy = static_cast<NetProxy*>(pUnoI);
+
+    try
+    {
+        switch (pMemberTD->eTypeClass)
+        {
+            case typelib_TypeClass_INTERFACE_ATTRIBUTE:
+            {
+                const typelib_InterfaceAttributeTypeDescription* pAttribTD
+                    = reinterpret_cast<const 
typelib_InterfaceAttributeTypeDescription*>(pMemberTD);
+
+                if (pUnoRet) // getter
+                {
+                    proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD,
+                                                  
pAttribTD->pAttributeTypeRef, 0, nullptr, nullptr,
+                                                  pUnoRet, pUnoExc);
+                }
+                else // setter
+                {
+                    typelib_MethodParameter param;
+                    param.pTypeRef = pAttribTD->pAttributeTypeRef;
+                    param.bIn = true;
+                    param.bOut = false;
+
+                    proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD, 
nullptr, 1, &param,
+                                                  pUnoArgs, nullptr, pUnoExc);
+                }
+                break;
+            }
+            case typelib_TypeClass_INTERFACE_METHOD:
+            {
+                const typelib_InterfaceMethodTypeDescription* pMethodTD
+                    = reinterpret_cast<const 
typelib_InterfaceMethodTypeDescription*>(pMemberTD);
+
+                sal_Int32 nMemberPos = pMethodTD->aBase.nPosition;
+                assert(nMemberPos < proxy->m_TD->nAllMembers);
+
+                sal_Int32 nFunctionPos = 
proxy->m_TD->pMapMemberIndexToFunctionIndex[nMemberPos];
+                assert(nFunctionPos < 
proxy->m_TD->nMapFunctionIndexToMemberIndex);
+
+                switch (nFunctionPos)
+                {
+                    case 1: // acquire()
+                        NetProxy_acquire(proxy);
+                        *pUnoExc = nullptr;
+                        break;
+                    case 2: // release()
+                        NetProxy_release(proxy);
+                        *pUnoExc = nullptr;
+                        break;
+                    default: // arbitrary method call
+                        proxy->m_bridge.call_net_func(
+                            proxy->m_netI, pMemberTD, 
pMethodTD->pReturnTypeRef, pMethodTD->nParams,
+                            pMethodTD->pParams, pUnoArgs, pUnoRet, pUnoExc);
+                        break;
+                }
+                break;
+            }
+            default:
+            {
+                throw BridgeRuntimeError(SAL_WHERE,
+                                         "could not find function "
+                                             + 
OUString::unacquired(&pMemberTD->pTypeName));
+            }
+        }
+    }
+    catch (const BridgeRuntimeError& err)
+    {
+        SAL_WARN("bridges", ".NET bridge error: " << err.m_message);
+
+        css::uno::RuntimeException exc("[net_uno bridge error] " + 
err.m_message,
+                                       
css::uno::Reference<css::uno::XInterface>());
+        uno_type_any_construct(*pUnoExc, &exc,
+                               
cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(),
+                               nullptr);
+    }
+}
+}
+
+NetProxy::NetProxy(Bridge& rBridge, IntPtr pNetI, 
typelib_InterfaceTypeDescription* pTD,
+                   const OUString& sOid)
+    : m_ref(1)
+    , m_bridge(rBridge)
+    , m_netI(nullptr)
+    , m_oid(sOid)
+    , m_TD(pTD)
+{
+    void* that = this;
+    (*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, &that, 
NetProxy_free,
+                                                  m_oid.pData, m_TD);
+
+    OUString sInterfaceName = map_uno_type_to_net(pTD->aBase.pWeakRef);
+
+    Context* pCtx = static_cast<Context*>(m_bridge.m_net_env->pContext);
+    m_netI = pCtx->registerInterface(pNetI, m_oid.getStr(), 
sInterfaceName.getStr());
+
+    m_bridge.acquire();
+
+    uno_Interface::acquire = NetProxy_acquire;
+    uno_Interface::release = NetProxy_release;
+    uno_Interface::pDispatcher = NetProxy_dispatch;
+}
+
+NetProxy::~NetProxy()
+{
+    OUString sInterfaceName = map_uno_type_to_net(m_TD->aBase.pWeakRef);
+    static_cast<Context*>(m_bridge.m_net_env->pContext)
+        ->revokeInterface(m_oid.getStr(), sInterfaceName.getStr());
+
+    m_bridge.release();
+}
+
+void NetProxy::acquire()
+{
+    // rebirth of proxy zombie
+    if (osl_atomic_increment(&m_ref) == 1)
+    {
+        void* that = this;
+        (*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, 
&that, NetProxy_free,
+                                                      m_oid.pData, m_TD);
+    }
+}
+
+void NetProxy::release()
+{
+    // revoke from uno env on last release
+    if (osl_atomic_decrement(&m_ref) == 0)
+    {
+        (*m_bridge.m_uno_env->revokeInterface)(m_bridge.m_uno_env, this);
+    }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/net_uno/net_proxy.hxx 
b/bridges/source/net_uno/net_proxy.hxx
new file mode 100644
index 000000000000..428073b06e84
--- /dev/null
+++ b/bridges/source/net_uno/net_proxy.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "net_bridge.hxx"
+
+#include <bridges/net_uno/net_types.hxx>
+#include <rtl/ustring.hxx>
+
+namespace net_uno
+{
+struct NetProxy : public uno_Interface
+{
+    mutable oslInterlockedCount m_ref;
+    Bridge& m_bridge;
+    IntPtr m_netI;
+    OUString m_oid;
+    typelib_InterfaceTypeDescription* m_TD;
+
+    NetProxy(Bridge& rBridge, IntPtr pNetI, typelib_InterfaceTypeDescription* 
pTD,
+             const OUString& sOid);
+    ~NetProxy();
+
+    void acquire();
+    void release();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/codemaker/source/netmaker/netproduce.cxx 
b/codemaker/source/netmaker/netproduce.cxx
index 8f9ad8e007c5..d1fa788f28de 100644
--- a/codemaker/source/netmaker/netproduce.cxx
+++ b/codemaker/source/netmaker/netproduce.cxx
@@ -377,6 +377,30 @@ void NetProducer::producePlainStruct(std::string_view name,
     file.endBlock();
     file.endLine(); // extra blank line
 
+    // generate deconstructor
+    file.beginLine().append("public void Deconstruct(");
+    separatedForeach(allFields, [&file]() { file.append(", "); },
+                     [this, &file](const auto& member) {
+                         file.append("out ")
+                             .append(getNetName(member.type))
+                             .append(" ")
+                             .append(getSafeIdentifier(member.name));
+                     });
+    file.append(")").endLine();
+    file.beginBlock();
+    for (const auto& member : allFields)
+    {
+        file.beginLine()
+            .append(getSafeIdentifier(member.name))
+            .append(" = ")
+            .append("this.")
+            .append(getSafeIdentifier(member.name))
+            .append(";")
+            .endLine();
+    }
+    file.endBlock();
+    file.endLine(); // extra blank line
+
     // generate struct fields
     for (const auto& member : entity->getDirectMembers())
     {
@@ -586,6 +610,30 @@ void NetProducer::produceException(std::string_view name,
     file.endBlock();
     file.endLine(); // extra blank line
 
+    // generate deconstructor
+    file.beginLine().append("public void Deconstruct(");
+    separatedForeach(allFields, [&file]() { file.append(", "); },
+                     [this, &file](const auto& member) {
+                         file.append("out ")
+                             .append(getNetName(member.type))
+                             .append(" ")
+                             .append(getSafeIdentifier(member.name));
+                     });
+    file.append(")").endLine();
+    file.beginBlock();
+    for (const auto& member : allFields)
+    {
+        file.beginLine()
+            .append(getSafeIdentifier(member.name))
+            .append(" = ")
+            .append("this.")
+            .append(getSafeIdentifier(member.name))
+            .append(";")
+            .endLine();
+    }
+    file.endBlock();
+    file.endLine(); // extra blank line
+
     // generate exception fields
     for (const auto& member : entity->getDirectMembers())
     {
@@ -918,15 +966,16 @@ void NetProducer::produceService(
                 .append("ctx.getServiceManager();")
                 .endLine()
                 .beginLine()
-                .append(returnType)
-                .append(" srv = (")
-                .append(returnType)
-                .append(")mcf.createInstanceWithContext(\"")
+                .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(")
+                .append("mcf.createInstanceWithContext(\"")
                 .append(name)
-                .append("\", ctx);")
+                .append("\", ctx));")
                 .endLine()
                 .beginLine()
-                .append("return srv;")
+                .append("return srv.cast<")
+                .append(returnType)
+                .append(">();")
+                .beginLine()
                 .endLine()
                 .endBlock();
 
@@ -949,8 +998,8 @@ void NetProducer::produceService(
                 .endLine()
                 .beginBlock()
                 .beginLine()
-                .append(
-                    "throw new com.sun.star.uno.DeploymentException(\"Could 
not create service ")
+                .append("throw new com.sun.star.uno.DeploymentException(")
+                .append("\"Could not create service ")
                 .append(name)
                 .append(" from given XComponentContext\", ctx);")
                 .endLine()
@@ -989,10 +1038,8 @@ void NetProducer::produceService(
                 .append("ctx.getServiceManager();")
                 .endLine()
                 .beginLine()
-                .append(returnType)
-                .append(" srv = (")
-                .append(returnType)
-                .append(")mcf.createInstanceWithArgumentsAndContext(\"")
+                .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(")
+                .append("mcf.createInstanceWithArgumentsAndContext(\"")
                 .append(name)
                 .append("\", ");
             if (restParam)
@@ -1014,7 +1061,14 @@ void NetProducer::produceService(
                                  });
                 file.append(" }");
             }
-            file.append(", ctx);").endLine().beginLine().append("return 
srv;").endLine().endBlock();
+            file.append(", ctx));")
+                .endLine()
+                .beginLine()
+                .append("return srv.cast<")
+                .append(returnType)
+                .append(">();")
+                .endLine()
+                .endBlock();
 
             for (const auto& e : ctor.exceptions)
             {
@@ -1035,8 +1089,8 @@ void NetProducer::produceService(
                 .endLine()
                 .beginBlock()
                 .beginLine()
-                .append(
-                    "throw new com.sun.star.uno.DeploymentException(\"Could 
not create service ")
+                .append("throw new com.sun.star.uno.DeploymentException(")
+                .append("\"Could not create service ")
                 .append(name)
                 .append(" from given XComponentContext\", ctx);")
                 .endLine()
@@ -1089,12 +1143,23 @@ void NetProducer::produceSingleton(
         .beginBlock();
 
     file.beginLine()
+        .append("try")
+        .endLine()
+        .beginBlock()
+        .beginLine()
         .append("com.sun.star.uno.Any sgtn = 
ctx.getValueByName(\"/singletons/")
         .append(name)
         .append("\");")
-        .endLine();
+        .endLine()
+        .beginLine()
+        .append("return sgtn.cast<")
+        .append(getNetName(entity->getBase()))
+        .append(">();")
+        .endLine()
+        .endBlock();
+
     file.beginLine()
-        .append("if (!sgtn.hasValue())")
+        .append("catch")
         .endLine()
         .beginBlock()
         .beginLine()
@@ -1103,11 +1168,6 @@ void NetProducer::produceSingleton(
         .append(" from given XComponentContext\", ctx);")
         .endLine()
         .endBlock();
-    file.beginLine()
-        .append("return (")
-        .append(getNetName(entity->getBase()))
-        .append(")sgtn.Value;")
-        .endLine();
 
     file.endBlock().endBlock();
 
diff --git a/include/bridges/net_uno/net_context.hxx 
b/include/bridges/net_uno/net_context.hxx
new file mode 100644
index 000000000000..33dc2594629f
--- /dev/null
+++ b/include/bridges/net_uno/net_context.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <bridges/net_uno/net_types.hxx>
+
+namespace net_uno
+{
+typedef IntPtr (*CreateProxyFunc)(String pOid, String pInterfaceName, IntPtr 
pBridge,
+                                  IntPtr pUnoInterface, IntPtr pTD);
+typedef String (*LookupObjectIdFunc)(IntPtr pNetInterface);
+typedef IntPtr (*RegisterInterfaceFunc)(IntPtr pNetInterface, String pOid, 
String pInterfaceName);
+typedef IntPtr (*LookupInterfaceFunc)(String pOid, String pInterfaceName);
+typedef void (*RevokeInterfaceFunc)(String pOid, String pInterfaceName);
+typedef sal_Int8 (*DispatchCallFunc)(IntPtr pNetInterface, String pMethodName, 
Value* pArgs,
+                                     Value* pRet, Value* pExc);
+typedef void (*ThrowErrorFunc)(String pWhere, String pMessage);
+
+struct Context
+{
+    CreateProxyFunc createProxy;
+    LookupObjectIdFunc lookupObjectId;
+    RegisterInterfaceFunc registerInterface;
+    LookupInterfaceFunc lookupInterface;
+    RevokeInterfaceFunc revokeInterface;
+    DispatchCallFunc dispatchCall;
+    ThrowErrorFunc throwError;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/bridges/net_uno/net_types.hxx 
b/include/bridges/net_uno/net_types.hxx
new file mode 100644
index 000000000000..9bb6f50f4d11
--- /dev/null
+++ b/include/bridges/net_uno/net_types.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace net_uno
+{
+using IntPtr = void*;
+using String = const sal_Unicode*;
+
+union Value {
+    // bool
+    sal_Bool boolData;
+
+    // byte
+    sal_Int8 byteData;
+
+    // char
+    sal_Unicode charData;
+
+    // short
+    sal_Int16 shortData;
+    sal_uInt16 unsigShortData;
+
+    // long
+    sal_Int32 longData;
+    sal_uInt32 unsigLongData;
+
+    // hyper
+    sal_Int64 hyperData;
+    sal_uInt64 unsigHyperData;
+
+    // float/double
+    float floatData;
+    double doubleData;
+
+    // string
+    IntPtr stringData;
+
+    // type
+    IntPtr typeData;
+
+    // any
+    struct Any
+    {
+        IntPtr data;
+        IntPtr type;
+    } anyData;
+
+    // enum
+    sal_Int32 enumData;
+
+    // struct
+    IntPtr structData;
+
+    // exception
+    IntPtr exceptionData;
+
+    // sequence
+    struct Sequence
+    {
+        IntPtr data;
+        sal_Int32 length;
+    } sequenceData;
+
+    // interface
+    IntPtr interfaceData;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 5fb0751cafd3..a079f64589a7 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -641,6 +641,7 @@ certain functionality.
 @li @c lingucomponent
 @li @c linguistic
 @li @c lwp - lotuswordpro
+@li @c net
 @li @c opencl
 @li @c opencl.device
 @li @c opencl.file
diff --git a/include/uno/lbnames.h b/include/uno/lbnames.h
index 56f9d9942142..2c59cbc44354 100644
--- a/include/uno/lbnames.h
+++ b/include/uno/lbnames.h
@@ -51,6 +51,8 @@ provoking error here, because PP ignores #error
 #define UNO_LB_JAVA "java"
 /** Environment type name for CLI (Common Language Infrastructure). */
 #define UNO_LB_CLI "cli"
+/** Environment type name for new .NET Bindings. */
+#define UNO_LB_NET "net"
 
 #endif
 
diff --git a/net_ure/DotnetLibrary_net_bridge.mk 
b/net_ure/DotnetLibrary_net_bridge.mk
index 30b320b05b3f..d6a47c947e7e 100644
--- a/net_ure/DotnetLibrary_net_bridge.mk
+++ b/net_ure/DotnetLibrary_net_bridge.mk
@@ -9,13 +9,33 @@
 $(eval $(call 
gb_DotnetLibrary_DotnetLibrary,net_bridge,$(gb_DotnetLibrary_CS)))
 
 $(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\
-       net_ure/source/bridge/NativeBootstrap \
+       net_ure/source/bridge/helper/DisposeGuard \
+       net_ure/source/bridge/helper/StructHelper \
+       net_ure/source/bridge/helper/TypeHelper \
+       net_ure/source/bridge/helper/WeakBase \
+       net_ure/source/bridge/helper/WeakComponentBase \
+))
+
+$(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\
+       net_ure/source/bridge/native/InteropMethods \
+       net_ure/source/bridge/native/InteropTypes \
+       net_ure/source/bridge/native/Marshaller \
+       net_ure/source/bridge/native/NativeBootstrap \
+       net_ure/source/bridge/native/NativeUnoProxy \
+       net_ure/source/bridge/native/NetEnvironment \
+       net_ure/source/bridge/native/WeakIndexTable \
+       net_ure/source/bridge/native/WeakOidTypeTable \
 ))
 
 $(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\
+       <AllowUnsafeBlocks>true</AllowUnsafeBlocks> \
        
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> \
 ))
 
+$(eval $(call gb_DotnetLibrary_add_items,net_bridge,\
+       <PackageReference Include="System.Reflection.DispatchProxy" 
Version="4.7.1" /> \
+))
+
 $(eval $(call gb_DotnetLibrary_link_library,net_bridge,net_uretypes))
 
 $(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\
diff --git a/net_ure/qa/basetypes/AnyTests.cs b/net_ure/qa/basetypes/AnyTests.cs
index a66fc1639d56..bedaa7ac478e 100644
--- a/net_ure/qa/basetypes/AnyTests.cs
+++ b/net_ure/qa/basetypes/AnyTests.cs
@@ -28,7 +28,6 @@ public class AnyTests
     public void with_RejectsInvalidParams()
     {
         Assert.That(() => Any.with<Any>(Any.VOID), Throws.ArgumentException);
-        Assert.That(() => Any.with<int>(null), Throws.ArgumentException);
     }
 
     [Test]
diff --git a/net_ure/source/basetypes/Any.cs b/net_ure/source/basetypes/Any.cs
index 03222a9f1f4f..b5d46660af8a 100644
--- a/net_ure/source/basetypes/Any.cs
+++ b/net_ure/source/basetypes/Any.cs
@@ -17,9 +17,51 @@ namespace com.sun.star.uno
         public Type Type { get; private set; }
         public Object Value { get; private set; }
 
+        public Any(object value) => setValue(value?.GetType() ?? typeof(void), 
value);
         public Any(Type type, object value) => setValue(type, value);
-        public Any(object value) : this(value?.GetType() ?? typeof(void), 
value) { }
-        public static Any with<T>(object value) => new Any(typeof(T), value);
+        public static Any with<T>(T value) => new Any(typeof(T), value);
+
+        public bool contains<T>()
+        {
+            if (typeof(IQueryInterface).IsAssignableFrom(Type))
+            {
+                if (typeof(T).IsAssignableFrom(Type))
+                    // Special case for already implemented interface
+                    return true;
+                else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
+                    // Special case for contained IQueryInterface
+                    return 
((IQueryInterface)Value).queryInterface(typeof(T)).hasValue();
+            }
+            return typeof(T).IsAssignableFrom(Type);
+        }
+
+        public T cast<T>()
+        {
+            if (typeof(IQueryInterface).IsAssignableFrom(Type))
+            {
+                if (typeof(T).IsAssignableFrom(Type))
+                    // Special case for already implemented interface
+                    return (T)Value;
+                else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
+                    // Special case for contained IQueryInterface
+                    return 
(T)((IQueryInterface)Value).queryInterface(typeof(T)).Value;
+            }
+            return (T)Value;
+        }
+
+        public T castOrDefault<T>(T fallback = default)
+        {
+            if (typeof(IQueryInterface).IsAssignableFrom(Type))
+            {
+                if (typeof(T).IsAssignableFrom(Type))
+                    // Special case for already implemented interface
+                    return (T)Value;
+                else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
+                    // Special case for contained IQueryInterface
+                    return 
(T)((IQueryInterface)Value).queryInterface(typeof(T)).Value;
+            }
+            return typeof(T).IsAssignableFrom(Type) ? (T)Value : fallback;
+        }
 
         public bool hasValue() => Type != typeof(void);
 
@@ -53,6 +95,6 @@ namespace com.sun.star.uno
         public override bool Equals(object obj) => obj is Any other && 
equals(other);
         public override int GetHashCode() => (Type, Value).GetHashCode();
 
-        public override string ToString() => $"uno.Any {{ Type = {Type}, Value 
= {Value ?? "Null"} }}";
+        public override string ToString() => $"com.sun.star.uno.Any {{ Type = 
{Type}, Value = {Value ?? "Null"} }}";
     }
 }
diff --git a/net_ure/source/basetypes/IQueryInterface.cs 
b/net_ure/source/basetypes/IQueryInterface.cs
index 714beb54f333..8517c79baa48 100644
--- a/net_ure/source/basetypes/IQueryInterface.cs
+++ b/net_ure/source/basetypes/IQueryInterface.cs
@@ -12,6 +12,26 @@ namespace com.sun.star.uno
 {
     public interface IQueryInterface
     {
-        // Kept empty for now, until the .NET bridge is in place
+        Any queryInterface(Type type);
+    }
+
+    public static class IQueryInterfaceExtensions
+    {
+        public static bool implements<T>(this IQueryInterface iqi)
+            where T : IQueryInterface
+            => iqi is T ? true : iqi.queryInterface(typeof(T)).hasValue();
+
+        public static T query<T>(this IQueryInterface iqi)
+            where T : IQueryInterface
+            => iqi is T t ? t : (T)iqi.queryInterface(typeof(T)).Value;
+
+        public static T queryOrDefault<T>(this IQueryInterface iqi, T fallback 
= default)
+            where T : IQueryInterface
+        {
+            if (iqi is T t)
+                return t;
+            Any result = iqi.queryInterface(typeof(T));
+            return result.hasValue() ? (T)result.Value : fallback;
+        }
     }
 }
diff --git a/net_ure/source/bootstrap/bootstrap.cxx 
b/net_ure/source/bootstrap/bootstrap.cxx
index 97bff2ab6506..4f4f45355a8a 100644
--- a/net_ure/source/bootstrap/bootstrap.cxx
+++ b/net_ure/source/bootstrap/bootstrap.cxx
@@ -9,41 +9,113 @@
 
 #include <string_view>
 
+#include <bridges/net_uno/net_context.hxx>
 #include <cppuhelper/bootstrap.hxx>
 #include <o3tl/string_view.hxx>
 #include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <uno/mapping.hxx>
 
+#include <com/sun/star/lang/XComponent.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::uno;
 
+namespace net_uno
+{
 extern "C" {
+SAL_DLLPUBLIC_EXPORT IntPtr bootstrap(const Context aContext)
+{
+    try
+    {
+        // Bootstrap UNO and start a LibreOffice process if needed
+        Reference<XComponentContext> xContext(::cppu::bootstrap());
+
+        // Get a mapping between the C++ and .NET environments
+        Environment cpp_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME);
+        Environment net_env(u"" UNO_LB_NET ""_ustr, new Context(aContext));
+        Mapping mapping(cpp_env.get(), net_env.get());
+        if (!mapping.is())
+        {
+            Reference<lang::XComponent> xComp(xContext, UNO_QUERY);
+            if (xComp.is())
+            {
+                xComp->dispose();
+            }
+
+            throw RuntimeException(u"could not get mapping between C++ and 
.NET"_ustr);
+        }
+
+        // Map the XComponentContext to a .NET proxy
+        return mapping.mapInterface(xContext.get(), 
cppu::UnoType<decltype(xContext)>::get());
+    }
+    catch (const Exception& exc)
+    {
+        SAL_WARN("net", ".NET bootstrap error: " << exc.Message);
+        aContext.throwError((u"" SAL_WHERE ""_ustr).getStr(), 
exc.Message.getStr());
+        return nullptr;
+    }
+}
 
-SAL_DLLPUBLIC_EXPORT void* bootstrap(const sal_Unicode* sParams)
+SAL_DLLPUBLIC_EXPORT IntPtr defaultBootstrap_InitialComponentContext(const 
sal_Unicode* sIniFile,
+                                                                     const 
sal_Unicode* sParams,
-e 
... etc. - the rest is truncated

Reply via email to