/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

// Documentation pointers for recent work:
//
// https://www.codeproject.com/Articles/9014/Understanding-COM-Event-Handling
// https://blogs.msdn.microsoft.com/ericlippert/2005/02/15/why-does-wscript-connectobject-not-always-work/

#include "ole2uno.hxx"

#include <stdio.h>
#include <list>
#include <sstream>
#include <unordered_map>
#include <vector>

#if defined _MSC_VER && defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wall"
#pragma clang diagnostic ignored "-Wattributes"
#pragma clang diagnostic ignored "-Wdelete-incomplete"
#pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
#pragma clang diagnostic ignored "-Wextra"
#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
#pragma clang diagnostic ignored "-Winvalid-noreturn"
#pragma clang diagnostic ignored "-Wmicrosoft"
#pragma clang diagnostic ignored "-Wnon-pod-varargs"
#pragma clang diagnostic ignored "-Wnonportable-include-path"
#pragma clang diagnostic ignored "-Wsequence-point"
#pragma clang diagnostic ignored "-Wtypename-missing"
#endif
#include <atlbase.h>
#include <atlcom.h>
#if defined _MSC_VER && defined __clang__
#pragma clang diagnostic pop
#endif
#include <comdef.h>

#include <osl/diagnose.h>
#include <salhelper/simplereferenceobject.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <com/sun/star/beans/MethodConcept.hpp>
#include <com/sun/star/beans/PropertyConcept.hpp>
#include <com/sun/star/lang/NoSuchMethodException.hpp>
#include <com/sun/star/script/CannotConvertException.hpp>
#include <com/sun/star/script/FailReason.hpp>
#include <com/sun/star/reflection/theCoreReflection.hpp>
#include <com/sun/star/reflection/ParamInfo.hpp>
#include <com/sun/star/beans/XExactName.hpp>
#include <com/sun/star/container/NoSuchElementException.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>

#include <com/sun/star/beans/XMaterialHolder.hpp>
#include <com/sun/star/script/XInvocation2.hpp>
#include <com/sun/star/script/MemberType.hpp>
#include <com/sun/star/reflection/XIdlReflection.hpp>
#include <ooo/vba/XCollection.hpp>
#include <ooo/vba/XConnectable.hpp>
#include <ooo/vba/XConnectionPoint.hpp>
#include <ooo/vba/XSink.hpp>
#include <ooo/vba/msforms/XCheckBox.hpp>
#include <osl/interlck.h>
#include <com/sun/star/uno/genfunc.h>
#include <comphelper/automationinvokedzone.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/profilezone.hxx>
#include <comphelper/windowsdebugoutput.hxx>
#include <comphelper/windowserrorstring.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
#include <o3tl/safeint.hxx>
#include <systools/win32/oleauto.hxx>

#include "comifaces.hxx"
#include "jscriptclasses.hxx"
#include "unotypewrapper.hxx"
#include "oleobjw.hxx"
#include "unoobjw.hxx"
#include "servprov.hxx"

using namespace osl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::beans;
using namespace com::sun::star::container;
using namespace com::sun::star::script;
using namespace com::sun::star::lang;
using namespace com::sun::star::bridge::ModelDependent;
using namespace com::sun::star::reflection;

std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap;
static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource);
static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource);
static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr);

/* Does not throw any exceptions.
   Param pInfo can be NULL.
 */
static void writeExcepinfo(EXCEPINFO * pInfo, const OUString& message)
{
    if (pInfo != nullptr)
    {
        pInfo->wCode = UNO_2_OLE_EXCEPTIONCODE;
        pInfo->bstrSource = SysAllocString(L"[automation bridge] ");
        pInfo->bstrDescription = sal::systools::BStr::newBSTR(message);
    }
}

InterfaceOleWrapper::InterfaceOleWrapper( Reference<XMultiServiceFactory> const & xFactory,
                                          sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass):
        UnoConversionUtilities<InterfaceOleWrapper>( xFactory, unoWrapperClass, comWrapperClass),
        m_defaultValueType( 0)
{
}

InterfaceOleWrapper::~InterfaceOleWrapper()
{
    MutexGuard guard(getBridgeMutex());
    // remove entries in global map
    auto it = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_xOrigin.get()));
    if(it != UnoObjToWrapperMap.end())
        UnoObjToWrapperMap.erase(it);
}

COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::QueryInterface(REFIID riid, void ** ppv)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::QueryInterface(" << riid << ")");

    HRESULT ret= S_OK;

    if( !ppv)
        return E_POINTER;

    if(IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppv = static_cast<IUnknown*>(static_cast<IDispatch*>(this));
        SAL_INFO("extensions.olebridge", "  " << *ppv);
    }
    else if (IsEqualIID(riid, IID_IDispatch))
    {
        AddRef();
        *ppv = static_cast<IDispatch*>(this);
        SAL_INFO("extensions.olebridge", "  " << *ppv);
    }
    else if (IsEqualIID(riid, IID_IProvideClassInfo))
    {
        Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
        if (!xConnectable.is())
            return E_NOINTERFACE;
        AddRef();
        *ppv = static_cast<IProvideClassInfo*>(this);
        SAL_INFO("extensions.olebridge", "  " << *ppv);
    }
    else if (IsEqualIID(riid, IID_IConnectionPointContainer))
    {
        Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
        if (!xConnectable.is())
            return E_NOINTERFACE;
        AddRef();
        *ppv = static_cast<IConnectionPointContainer*>(this);
        SAL_INFO("extensions.olebridge", "  " << *ppv);
    }
    else if( IsEqualIID( riid, __uuidof( IUnoObjectWrapper)))
    {
        AddRef();
        *ppv= static_cast<IUnoObjectWrapper*>(this);
        SAL_INFO("extensions.olebridge", "  " << *ppv);
    }
    else
        ret= E_NOINTERFACE;
    return ret;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::AddRef()
{
    acquire();
    // does not need to guard because one should not rely on the return value of
    // AddRef anyway
    return m_refCount;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::Release()
{
    ULONG n= m_refCount;
    release();
    return n - 1;
}

// IUnoObjectWrapper --------------------------------------------------------
COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getWrapperXInterface( Reference<XInterface>* pXInt)
{
    pXInt->set( static_cast<XWeak*>( this), UNO_QUERY);
    return pXInt->is() ? S_OK : E_FAIL;
}
COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getOriginalUnoObject( Reference<XInterface>* pXInt)
{
    *pXInt= m_xOrigin;
    return m_xOrigin.is() ? S_OK : E_FAIL;
}
COM_DECLSPEC_NOTHROW STDMETHODIMP  InterfaceOleWrapper::getOriginalUnoStruct( Any * pStruct)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    HRESULT ret= E_FAIL;
    if( !m_xOrigin.is())
    {
        Reference<XMaterialHolder> xMatHolder( m_xInvocation, UNO_QUERY);
        if( xMatHolder.is())
        {
            Any any = xMatHolder->getMaterial();
            if( any.getValueTypeClass() == TypeClass_STRUCT)
            {
                *pStruct= any;
                ret= S_OK;
            }
        }
    }
    return ret;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfoCount( UINT *pctinfo )
{
    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfoCount");

    if (!pctinfo)
        return E_POINTER;

    *pctinfo = 1;

    return S_OK;
}

namespace {

class CXTypeInfo : public ITypeInfo,
                   public CComObjectRoot
{
public:
    enum class Kind { COCLASS, MAIN, OUTGOING };

#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    BEGIN_COM_MAP(CXTypeInfo)
#if defined __clang__
#pragma clang diagnostic pop
#endif
        COM_INTERFACE_ENTRY(ITypeInfo)
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    END_COM_MAP()
#if defined __clang__
#pragma clang diagnostic pop
#endif

    DECLARE_NOT_AGGREGATABLE(CXTypeInfo)

    virtual ~CXTypeInfo() {}

    void InitForCoclass(Reference<XInterface> xOrigin,
                        const OUString& sImplementationName,
                        const IID& rIID,
                        Reference<XMultiServiceFactory> xMSF);
    void InitForClassItself(Reference<XInterface> xOrigin,
                            const OUString& sImplementationName,
                            const IID& rIID,
                            Reference<XMultiServiceFactory> xMSF);
    void InitForOutgoing(Reference<XInterface> xOrigin,
                         const OUString& sInterfaceName,
                         const IID& rIID,
                         Reference<XMultiServiceFactory> xMSF,
                         Type aType);
    virtual HRESULT STDMETHODCALLTYPE GetTypeAttr(TYPEATTR **ppTypeAttr) override;
    virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **ppTComp) override;
    virtual HRESULT STDMETHODCALLTYPE GetFuncDesc(UINT index,
                                                  FUNCDESC **ppFuncDesc) override;
    virtual HRESULT STDMETHODCALLTYPE GetVarDesc(UINT index,
                                                 VARDESC **ppVarDesc) override;
    virtual HRESULT STDMETHODCALLTYPE GetNames(MEMBERID memid,
                                               BSTR *rgBstrNames,
                                               UINT cMaxNames,
                                               UINT *pcNames) override;
    virtual HRESULT STDMETHODCALLTYPE GetRefTypeOfImplType(UINT index,
                                                           HREFTYPE *pRefType) override;
    virtual HRESULT STDMETHODCALLTYPE GetImplTypeFlags(UINT index,
                                                       INT *pImplTypeFlags) override;
    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(LPOLESTR *rgszNames,
                                                    UINT cNames,
                                                    MEMBERID *pMemId) override;
    virtual HRESULT STDMETHODCALLTYPE Invoke(PVOID pvInstance,
                                             MEMBERID memid,
                                             WORD wFlags,
                                             DISPPARAMS *pDispParams,
                                             VARIANT *pVarResult,
                                             EXCEPINFO *pExcepInfo,
                                             UINT *puArgErr) override;
    virtual HRESULT STDMETHODCALLTYPE GetDocumentation(MEMBERID memid,
                                                       BSTR *pBstrName,
                                                       BSTR *pBstrDocString,
                                                       DWORD *pdwHelpContext,
                                                       BSTR *pBstrHelpFile) override;
    virtual HRESULT STDMETHODCALLTYPE GetDllEntry(MEMBERID memid,
                                                  INVOKEKIND invKind,
                                                  BSTR *pBstrDllName,
                                                  BSTR *pBstrName,
                                                  WORD *pwOrdinal) override;
    virtual HRESULT STDMETHODCALLTYPE GetRefTypeInfo(HREFTYPE hRefType,
                                                     ITypeInfo **ppTInfo) override;
    virtual HRESULT STDMETHODCALLTYPE AddressOfMember(MEMBERID memid,
                                                      INVOKEKIND invKind,
                                                      PVOID *ppv) override;
    virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter,
                                                     REFIID riid,
                                                     PVOID *ppvObj) override;
    virtual HRESULT STDMETHODCALLTYPE GetMops(MEMBERID memid,
                                              BSTR *pBstrMops) override;
    virtual HRESULT STDMETHODCALLTYPE GetContainingTypeLib(ITypeLib **ppTLib,
                                                           UINT *pIndex) override;
    virtual void STDMETHODCALLTYPE ReleaseTypeAttr(TYPEATTR *pTypeAttr) override;
    virtual void STDMETHODCALLTYPE ReleaseFuncDesc(FUNCDESC *pFuncDesc) override;
    virtual void STDMETHODCALLTYPE ReleaseVarDesc(VARDESC *pVarDesc) override;

private:
    Kind meKind;
    Reference<XInterface> mxOrigin;
    OUString msImplementationName;
    OUString msInterfaceName;
    IID maIID;
    Reference<XMultiServiceFactory> mxMSF;
    Type maType;
};

class CXTypeLib : public ITypeLib,
                  public CComObjectRoot
{
public:
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    BEGIN_COM_MAP(CXTypeLib)
#if defined __clang__
#pragma clang diagnostic pop
#endif
        COM_INTERFACE_ENTRY(ITypeLib)
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    END_COM_MAP()
#if defined __clang__
#pragma clang diagnostic pop
#endif

    DECLARE_NOT_AGGREGATABLE(CXTypeLib)

    virtual ~CXTypeLib() {}

    void Init(Reference<XInterface> xOrigin,
              const OUString& sImplementationName,
              Reference<XMultiServiceFactory> xMSF)
    {
        SAL_INFO("extensions.olebridge", this << "@CXTypeLib::Init for " << sImplementationName);
        mxOrigin = xOrigin;
        msImplementationName = sImplementationName;
        mxMSF = xMSF;
    }

    virtual UINT STDMETHODCALLTYPE GetTypeInfoCount() override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoCount");
        return 1;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT,
                                                  ITypeInfo **) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfo: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoType(UINT,
                                                      TYPEKIND *) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoType: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(REFGUID guid,
                                                        ITypeInfo **ppTInfo) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        SAL_INFO("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoOfGuid(" << guid << ")");
        if (!ppTInfo)
            return E_POINTER;

        Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY);
        if (!xConnectable.is())
            return TYPE_E_ELEMENTNOTFOUND;

        IID aIID;
        if (SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(xConnectable->getIID().pData->buffer), &aIID)))
        {
            if (IsEqualIID(guid, aIID))
            {
                HRESULT ret;

                CComObject<CXTypeInfo>* pTypeInfo;

                ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
                if (FAILED(ret))
                    return ret;

                pTypeInfo->AddRef();

                pTypeInfo->InitForCoclass(mxOrigin, msImplementationName, aIID, mxMSF);

                *ppTInfo = pTypeInfo;

                return S_OK;
            }
        }

#if 0
        ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();

        IID aIID;
        if (SUCCEEDED(IIDFromString((LPOLESTR)aTypeAndIID.IID.pData->buffer, &aIID)))
        {
            HRESULT ret;

            CComObject<CXTypeInfo>* pTypeInfo;

            ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
            if (FAILED(ret))
                return ret;

            pTypeInfo->AddRef();

            pTypeInfo->InitForOutgoing(mxOrigin, msImplementationName, aIID, mxMSF);

            *ppTInfo = pTypeInfo;

            return S_OK;
        }
#else
        SAL_WARN("extensions.olebridge", "Not implemented: GetTypeInfoOfGuid(" << guid << ")");
#endif

        return TYPE_E_ELEMENTNOTFOUND;

    }

    virtual HRESULT STDMETHODCALLTYPE GetLibAttr(TLIBATTR **) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetLibAttr: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeComp: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetDocumentation(INT,
                                                       BSTR *,
                                                       BSTR *,
                                                       DWORD *,
                                                       BSTR *) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetDocumentation: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE IsName(LPOLESTR,
                                             ULONG,
                                             BOOL *) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib:IsName: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE FindName(LPOLESTR,
                                               ULONG,
                                               ITypeInfo **,
                                               MEMBERID *,
                                               USHORT *) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::FindName: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual void STDMETHODCALLTYPE ReleaseTLibAttr(TLIBATTR *) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXTypeLib::ReleaseTLibAttr: E_NOTIMPL");
    }

private:
    Reference<XInterface> mxOrigin;
    OUString msImplementationName;
    Reference<XMultiServiceFactory> mxMSF;
};

}

void CXTypeInfo::InitForCoclass(Reference<XInterface> xOrigin,
                                const OUString& sImplementationName,
                                const IID& rIID,
                                Reference<XMultiServiceFactory> xMSF)
{
    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForCoclass(" << sImplementationName << "," << rIID << ")");
    meKind = Kind::COCLASS;
    mxOrigin = xOrigin;
    msImplementationName = sImplementationName;
    maIID = rIID;
    mxMSF = xMSF;
}

void CXTypeInfo::InitForClassItself(Reference<XInterface> xOrigin,
                                    const OUString& sImplementationName,
                                    const IID& rIID,
                                    Reference<XMultiServiceFactory> xMSF)
{
    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForClassItself(" << sImplementationName << "," << rIID << ")");
    meKind = Kind::MAIN;
    mxOrigin = xOrigin;
    msImplementationName = sImplementationName;
    maIID = rIID;
    mxMSF = xMSF;
}

void CXTypeInfo::InitForOutgoing(Reference<XInterface> xOrigin,
                                 const OUString& sInterfaceName,
                                 const IID& rIID,
                                 Reference<XMultiServiceFactory> xMSF,
                                 Type aType)
{
    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForOutgoing(" << sInterfaceName << "," << rIID << ")");
    meKind = Kind::OUTGOING;
    mxOrigin = xOrigin;
    msInterfaceName = sInterfaceName;
    maIID = rIID;
    mxMSF = xMSF;
    maType = aType;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeAttr(TYPEATTR **ppTypeAttr)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr");

    if (!ppTypeAttr)
        return E_POINTER;

    assert(!IsEqualIID(maIID, IID_NULL));

    TYPEATTR *pTypeAttr = new TYPEATTR;
    memset(pTypeAttr, 0, sizeof(*pTypeAttr));

    pTypeAttr->guid = maIID;

    if (meKind == Kind::COCLASS)
    {
        pTypeAttr->typekind = TKIND_COCLASS;
        pTypeAttr->cFuncs = 0;
        pTypeAttr->cVars = 0;
        pTypeAttr->cImplTypes = 3;
        pTypeAttr->cbSizeVft = 0;
        pTypeAttr->cbAlignment = 8;
        pTypeAttr->wTypeFlags = TYPEFLAG_FCANCREATE;
    }
    else if (meKind == Kind::MAIN)
    {
        pTypeAttr->typekind = TKIND_DISPATCH;
        pTypeAttr->cFuncs = 10; // FIXME, dummy
        pTypeAttr->cVars = 0;
        pTypeAttr->cImplTypes = 1;
        // FIXME: I think this is always supposed to be as if just for the seven methods in
        // IDIspatch?
        pTypeAttr->cbSizeVft = 7 * sizeof(void*);
        pTypeAttr->cbAlignment = 8;
        pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FDISPATCHABLE;
    }
    else if (meKind == Kind::OUTGOING)
    {
        pTypeAttr->typekind = TKIND_DISPATCH;

        Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
        assert(xRefl.is());

        Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
        assert(xClass.is());

        auto aMethods = xClass->getMethods();
        assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
               aMethods.getLength() > 0);

        // Drop the three XInterface methods, add the three corresponding IUnknown ones plus the
        // four IDispatch ones on top of that.
        pTypeAttr->cFuncs = aMethods.getLength() - 3 + 3 + 4;
        pTypeAttr->cVars = 0;
        pTypeAttr->cImplTypes = 1;
        // FIXME: I think this, too, is always supposed to be as if just for the seven methods in
        // IDIspatch?
        pTypeAttr->cbSizeVft = 7 * sizeof(void*);
        pTypeAttr->cbAlignment = 8;
        pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FNONEXTENSIBLE|TYPEFLAG_FDISPATCHABLE;
    }
    else
        assert(false);

    pTypeAttr->lcid = LOCALE_USER_DEFAULT;
    pTypeAttr->memidConstructor = MEMBERID_NIL;
    pTypeAttr->memidDestructor = MEMBERID_NIL;
    // FIXME: Is this correct, just the vtable pointer, right?
    pTypeAttr->cbSizeInstance = sizeof(void*);
    pTypeAttr->wMajorVerNum = 0;
    pTypeAttr->wMinorVerNum = 0;
    pTypeAttr->idldescType.wIDLFlags = IDLFLAG_NONE;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr: " << pTypeAttr);

    *ppTypeAttr = pTypeAttr;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeComp(ITypeComp **)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetTypeComp: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetFuncDesc(UINT index,
                                                  FUNCDESC **ppFuncDesc)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    if (!ppFuncDesc)
        return E_POINTER;

    if (meKind != Kind::OUTGOING)
        return E_NOTIMPL;

    if (index <= 6)
    {
        *ppFuncDesc = new FUNCDESC;
        (*ppFuncDesc)->memid = 0x60000000 + index;
        (*ppFuncDesc)->lprgscode = nullptr;
        (*ppFuncDesc)->lprgelemdescParam = nullptr;
        (*ppFuncDesc)->funckind = FUNC_DISPATCH;
        (*ppFuncDesc)->invkind = INVOKE_FUNC;
        (*ppFuncDesc)->callconv = CC_STDCALL;
        switch (index)
        {
        case 0: // QueryInterface
            (*ppFuncDesc)->cParams = 2;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
            break;
        case 1: // AddRef
            (*ppFuncDesc)->cParams = 0;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4;
            break;
        case 2: // Release
            (*ppFuncDesc)->cParams = 1;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4;
            break;
        case 3: // GetTypeInfoCount
            (*ppFuncDesc)->cParams = 1;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
            break;
        case 4: // GetTypeInfo
            (*ppFuncDesc)->cParams = 3;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
            break;
        case 5: // GetIDsOfNames
            (*ppFuncDesc)->cParams = 5;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
            break;
        case 6: // Invoke
            (*ppFuncDesc)->cParams = 8;
            (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
            (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
            break;
        }
        (*ppFuncDesc)->cParamsOpt = 0;
        (*ppFuncDesc)->oVft = index * sizeof(void*);
        (*ppFuncDesc)->cScodes = 0;
        (*ppFuncDesc)->wFuncFlags = FUNCFLAG_FRESTRICTED;

        SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc);

        return S_OK;
    }

    Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
    assert(xRefl.is());

    Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
    assert(xClass.is());

    auto aMethods = xClass->getMethods();
    assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
           aMethods.getLength() > 0);

    if (index > o3tl::make_unsigned(aMethods.getLength() - 3 + 3 + 4))
        return E_INVALIDARG;

    *ppFuncDesc = new FUNCDESC;

    (*ppFuncDesc)->memid = index - 6;
    (*ppFuncDesc)->lprgscode = nullptr;
    (*ppFuncDesc)->lprgelemdescParam = nullptr;
    (*ppFuncDesc)->funckind = FUNC_DISPATCH;
    (*ppFuncDesc)->invkind = INVOKE_FUNC;
    (*ppFuncDesc)->callconv = CC_STDCALL;
    (*ppFuncDesc)->cParams = aMethods[index - 4]->getParameterInfos().getLength();
    (*ppFuncDesc)->cParamsOpt = 0;
    (*ppFuncDesc)->oVft = index * sizeof(void*);
    (*ppFuncDesc)->cScodes = 0;
    (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; // ???
    (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; // ???
    (*ppFuncDesc)->wFuncFlags = 0;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc);

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetVarDesc(UINT,
                                                 VARDESC **)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetVarDesc: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetNames(MEMBERID memid,
                                               BSTR *rgBstrNames,
                                               UINT cMaxNames,
                                               UINT *pcNames)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetNames(" << memid << ")");
    assert(meKind != Kind::COCLASS);

    if (!rgBstrNames)
        return E_POINTER;

    if (!pcNames)
        return E_POINTER;

    if (memid < 1)
        return E_INVALIDARG;

    if (cMaxNames < 1)
        return E_INVALIDARG;

    if (meKind == Kind::MAIN)
    {
        SAL_WARN("extensions.olebridge", "GetNames() for MAIN not implemented");
        return E_NOTIMPL;
    }

    Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
    assert(xRefl.is());

    Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
    assert(xClass.is());

    auto aMethods = xClass->getMethods();
    assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
           aMethods.getLength() > 0);

    // Subtract the three XInterface methods. Memid for the first following method is 1.
    if (memid > aMethods.getLength() - 3)
        return E_INVALIDARG;

    SAL_INFO("extensions.olebridge", "..." << this << "@CXTypeInfo::GetNames(" << memid << "): " << aMethods[memid + 2]->getName());
    rgBstrNames[0] = sal::systools::BStr::newBSTR(aMethods[memid + 2]->getName());
    *pcNames = 1;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeOfImplType(UINT index,
                                                           HREFTYPE *pRefType)
{
    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeOfImplType(" << index << ")");

    if (!pRefType)
        return E_POINTER;

    assert(index == 0 || index == 1);

    *pRefType = 1000+index;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetImplTypeFlags(UINT index,
                                                       INT *pImplTypeFlags)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetImplTypeFlags(" << index << ")");

    if (!pImplTypeFlags)
        return E_POINTER;

    assert(meKind == Kind::COCLASS);
    assert(index == 0 || index == 1);

    if (index == 0)
        *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT;
    else if (index == 1)
        *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT|IMPLTYPEFLAG_FSOURCE;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetIDsOfNames(LPOLESTR *,
                                                    UINT,
                                                    MEMBERID *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetIDsOfNames: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::Invoke(PVOID,
                                             MEMBERID,
                                             WORD,
                                             DISPPARAMS *,
                                             VARIANT *,
                                             EXCEPINFO *,
                                             UINT *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::Invoke: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDocumentation(MEMBERID memid,
                                                       BSTR *pBstrName,
                                                       BSTR *pBstrDocString,
                                                       DWORD *pdwHelpContext,
                                                       BSTR *pBstrHelpFile)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetDocumentation(" << memid << ")");

    if (pBstrName)
    {
        if (memid == MEMBERID_NIL)
        {
            *pBstrName = sal::systools::BStr::newBSTR(msImplementationName);
        }
        else if (memid == DISPID_VALUE)
        {
            // MEMBERIDs are the same as DISPIDs, apparently?
            *pBstrName = SysAllocString(L"Value");
        }
        else
        {
            // FIXME: Shouldn't we be able to know the names of the members of UNO interfaces?
            *pBstrName = sal::systools::BStr::newBSTR(Concat2View("UnknownNameOfMember#" + OUString::number(memid)));
        }
    }
    if (pBstrDocString)
        *pBstrDocString = SysAllocString(L"");
    if (pdwHelpContext)
        *pdwHelpContext = 0;
    if (pBstrHelpFile)
        *pBstrHelpFile = nullptr;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDllEntry(MEMBERID,
                                                  INVOKEKIND,
                                                  BSTR *,
                                                  BSTR *,
                                                  WORD *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetDllEntry: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeInfo(HREFTYPE hRefType,
                                                     ITypeInfo **ppTInfo)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeInfo(" << hRefType << ")");

    if (!ppTInfo)
        return E_POINTER;

    // FIXME: Is it correct to assume that the only interfaces on which GetRefTypeInfo() would be
    // called are those that implement ooo::vba::XConnectable?

    Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY);
    if (!xConnectable.is())
        return E_NOTIMPL;

    ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();

    IID aIID;
    if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID)))
        return E_NOTIMPL;

    HRESULT ret;

    CComObject<CXTypeInfo>* pTypeInfo;

    ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
    if (FAILED(ret))
        return ret;

    pTypeInfo->AddRef();

    pTypeInfo->InitForOutgoing(mxOrigin, aTypeAndIID.Type.getTypeName(), aIID, mxMSF, aTypeAndIID.Type);

    *ppTInfo = pTypeInfo;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::AddressOfMember(MEMBERID,
                                                      INVOKEKIND,
                                                      PVOID *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::AddressOfMember: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::CreateInstance(IUnknown *,
                                                     REFIID,
                                                     PVOID *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::CreateInstance: E_NOTIMPL");
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetMops(MEMBERID,
                                              BSTR *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetMops: E_NOTIMPL");
    return E_NOTIMPL;
}

// This is not actually called any more by my vbscript test after I added the IProvideClassInfo
// thing... so all the CXTypeLib stuff is dead code at the moment.

HRESULT STDMETHODCALLTYPE CXTypeInfo::GetContainingTypeLib(ITypeLib **ppTLib,
                                                           UINT *pIndex)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetContainingTypeLib");

    if (!ppTLib || !pIndex)
        return E_POINTER;

    HRESULT ret;

    CComObject<CXTypeLib>* pTypeLib;

    ret = CComObject<CXTypeLib>::CreateInstance(&pTypeLib);
    if (FAILED(ret))
        return ret;

    pTypeLib->AddRef();

    pTypeLib->Init(mxOrigin, msImplementationName, mxMSF);

    *ppTLib = pTypeLib;

    return S_OK;
}

void STDMETHODCALLTYPE CXTypeInfo::ReleaseTypeAttr(TYPEATTR *pTypeAttr)
{
    SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::ReleaseTypeAttr(" << pTypeAttr << ")");

    delete pTypeAttr;
}

void STDMETHODCALLTYPE CXTypeInfo::ReleaseFuncDesc(FUNCDESC *pFuncDesc)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseFuncDesc(" << pFuncDesc << ")");

    delete pFuncDesc;
}

void STDMETHODCALLTYPE CXTypeInfo::ReleaseVarDesc(VARDESC *)
{
    SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseVarDesc: E_NOTIMPL");
}

COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(UINT iTInfo, LCID, ITypeInfo ** ppTInfo)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfo(" << iTInfo << ")");

    if (!ppTInfo)
        return E_POINTER;

    if (iTInfo != 0)
        return E_NOTIMPL;

    // FIXME: This is surely incorrect. Why is being able to handle GetTypeInfo() here coupled to
    // being a source for outgoing events, i.e. implementing XConnectable? What would break if we
    // would use XInterfaceWithIID and its getIID instead?

    Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
    if (!xConnectable.is())
        return E_NOTIMPL;

    OUString sIID = xConnectable->GetIIDForClassItselfNotCoclass();
    IID aIID;
    if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID)))
        return E_NOTIMPL;

    HRESULT ret;

    CComObject<CXTypeInfo>* pTypeInfo;

    ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
    if (FAILED(ret))
        return ret;

    pTypeInfo->AddRef();

    pTypeInfo->InitForClassItself(m_xOrigin, m_sImplementationName, aIID, m_smgr);

    *ppTInfo = pTypeInfo;

    return S_OK;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetIDsOfNames(REFIID /*riid*/,
                                                LPOLESTR * rgszNames,
                                                UINT cNames,
                                                LCID /*lcid*/,
                                                DISPID * rgdispid )
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    if( ! rgdispid)
        return E_POINTER;

    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetIDsOfNames:");
    for (unsigned int i = 0; i < cNames; ++i)
    {
        // Initialise returned rgdispid values.
        rgdispid[i] = DISPID_UNKNOWN;

        SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])));
    }

    HRESULT ret = DISP_E_UNKNOWNNAME;
    try
    {
        MutexGuard guard( getBridgeMutex());

        // FIXME: Handle the cNames > 1 case? Note that the rest of the names mean the names of *arguments*.

        if( ! _wcsicmp( *rgszNames, JSCRIPT_VALUE_FUNC) ||
            ! _wcsicmp( *rgszNames, BRIDGE_VALUE_FUNC))
        {
            *rgdispid= DISPID_JSCRIPT_VALUE_FUNC;
            return S_OK;
        }
        else if( ! _wcsicmp( *rgszNames, GET_STRUCT_FUNC) ||
                 ! _wcsicmp( *rgszNames, BRIDGE_GET_STRUCT_FUNC))
        {
            *rgdispid= DISPID_GET_STRUCT_FUNC;
            return S_OK;
        }
        else if( ! _wcsicmp( *rgszNames, BRIDGE_CREATE_TYPE_FUNC))
        {
            *rgdispid= DISPID_CREATE_TYPE_FUNC;
            return S_OK;
        }

        if (m_xInvocation.is() && (cNames > 0))
        {
            OUString name(o3tl::toU(rgszNames[0]));
            NameToIdMap::iterator iter = m_nameToDispIdMap.find(name);

            bool bIsMethod = false;

            OUString exactName = name;

            if (iter == m_nameToDispIdMap.end())
            {
                if (m_xExactName.is())
                {
                    exactName = m_xExactName->getExactName(name);
                    if (exactName.isEmpty())
                        exactName = name;
                }

                MemberInfo d(0, exactName);

                if (m_xInvocation->hasProperty(exactName))
                {
                    d.flags |= DISPATCH_PROPERTYGET;
                    d.flags |= DISPATCH_PROPERTYPUT;
                    d.flags |= DISPATCH_PROPERTYPUTREF;
                }

                if (m_xInvocation->hasMethod(exactName))
                {
                    d.flags |= DISPATCH_METHOD;
                    bIsMethod = true;
                }

                if (d.flags != 0)
                {
                    m_MemberInfos.push_back(d);
                    iter = m_nameToDispIdMap.emplace(exactName, static_cast<DISPID>(m_MemberInfos.size())).first;

                    if (exactName != name)
                    {
                        iter = m_nameToDispIdMap.emplace(name, static_cast<DISPID>(m_MemberInfos.size())).first;
                    }
                }
            }

            if (iter == m_nameToDispIdMap.end())
            {
                ret = DISP_E_UNKNOWNNAME;
                SAL_INFO("extensions.olebridge", "  " << name << ": UNKNOWN");
            }
            else
            {
                rgdispid[0] = (*iter).second;
                SAL_INFO("extensions.olebridge", "  " << name << ": " << rgdispid[0]);

                if (bIsMethod && cNames > 1)
                {
                    Reference<XIdlMethod> xIdlMethod;
                    Reference<XIntrospectionAccess> xIntrospectionAccess = m_xInvocation->getIntrospection();
                    try
                    {
                        if (xIntrospectionAccess.is())
                            xIdlMethod = xIntrospectionAccess->getMethod(exactName, MethodConcept::ALL);
                    }
                    catch (const NoSuchMethodException&)
                    {
                    }
                    if (xIdlMethod.is())
                    {
                        auto aParamInfos = xIdlMethod->getParameterInfos();
                        for (unsigned int i = 1; i < cNames; ++i)
                        {
                            bool bFound = false;
                            for (int j = 0; j < aParamInfos.getLength(); ++j)
                            {
                                if (aParamInfos[j].aName.equalsIgnoreAsciiCase(o3tl::toU(rgszNames[i])))
                                {
                                    rgdispid[i] = j;
                                    bFound = true;
                                    SAL_INFO("extensions.olebridge", "  " << OUString(o3tl::toU(rgszNames[i])) << ": " << rgdispid[i]);
                                    break;
                                }
                            }
                            if (!bFound)
                                SAL_INFO("extensions.olebridge", "  " << OUString(o3tl::toU(rgszNames[i])) << ": NOT FOUND");
                        }
                    }
                }

                // Return value should be S_OK only if *all* the names were found.
                unsigned int i;
                for (i = 0; i < cNames; ++i)
                    if (rgdispid[i] == DISPID_UNKNOWN)
                        break;
                if (i == cNames)
                    ret = S_OK;
            }
        }
    }
    catch(const BridgeRuntimeError&)
    {
        OSL_ASSERT(false);
    }
    catch(const Exception&)
    {
        OSL_ASSERT(false);
    }
    catch(...)
    {
        OSL_ASSERT(false);
    }

    return ret;
}

// Note: What the comments here say about JScript possibly holds for Automation clients in general,
// like VBScript ones, too. Or not. Hard to say. What is the relevance of JScript nowadays anyway,
// and can LO really be used from JScript code on web pages any longer?

// "convertDispparamsArgs" converts VARIANTS to their respecting Any counterparts
// The parameters "id", "wFlags" and "pdispparams" equal those as used in
// IDispatch::Invoke. The function handles special JavaScript
// cases where a VARIANT of type VT_DISPATCH is ambiguous and could represent
// an object, array ( JavaScript Array object), out parameter and in/out ( JavaScript Array object)
//  parameter (JavaScript Array object)
// Because all those VT_DISPATCH objects need a different conversion
// we have to find out what the object is supposed to be. The function does this
// by either using type information or by help of a specialized ValueObject object.

// A. Type Information

// With the help of type information the kind of parameter can be exactly determined
// and an appropriate conversion can be chosen. A problem arises if a method expects
// an Any. Then the type info does not tell what the type of the value, that is kept
// by the any, should be. In this situation the decision whether the param is a
// sequence or an object is made upon the fact if the object has a property "0"
// ( see function "isJScriptArray"). Since this is unsafe it is recommended to use
// the JScript value objects within a JScript script on such an occasion.

// B. JavaScript Value Object ( class JScriptValue )

// A JScriptValue (ValueObject) object is a COM object in that it implements IDispatch and the
// IJScriptValue object interface. Such objects are provided by all UNO wrapper
// objects used within a JScript script. To obtain an instance one has to call
// "_GetValueObject() or Bridge_GetValueObject()" on a UNO wrapper object (class InterfaceOleWrapper).
// A value object is appropriately initialized within the script and passed as
// parameter to a UNO object method or property. The convertDispparamsArgs function
// can easily find out that a param is such an object by querying for the
// IJScriptValue interface. By this interface one the type and kind ( out, in/out)
// can be determined and the right conversion can be applied.
// Using ValueObjects we spare us the effort of acquiring and examining type information
// in order to figure out what the an IDispatch parameter is meant for.

// Normal JScript object parameter can be mixed with JScriptValue object. If an
// VARIANT contains a VT_DISPATCH that is no JScriptValue than the type information
// is used to find out about the required type.
void InterfaceOleWrapper::convertDispparamsArgs(DISPID id,
    unsigned short /*wFlags*/, DISPPARAMS* pdispparams, Sequence<Any>& rSeq)
{
    // Parameters come in reverse order in pdispparams. There might be less parameters than
    // expected. In that case, assume they are "optional" (but can't be marked as such in UNO IDL),
    // and fill in the rest with empty Anys. There might also be more than expected. In that case,
    // assume the oovbaapi UNO IDL hasn't kept up with added optional parameters in MSO, and just
    // ignore the extra ones, as long as they are empty.

    // An example: incoming parameters: <12, 13, "foo/bar.tem">
    //
    // Expected parameters: (string filename, int something, int somethingElse, Any whatever, Any
    // whateverElse)
    //
    // Here the existing incoming parameters are placed in reverse order in the first three outgoing
    // parameters, and the rest of the outgoing parameters are kept as empty Anys.
    //
    // Another example: incoming parameters: <EMPTY, TRUE>
    //
    // Expected parameters: (bool flag)
    //
    // Here the TRUE is passed as the sole outgoing parameter, and the incoming EMPTY is ignored.
    //
    // Still an example: incoming parameters: <"foo.doc", TRUE>
    //
    // Expected parameters: (bool flag)
    //
    // This throws an error as the incoming string parameter presumably should do something important,
    // but there is no corresponding outgoing parameter.

    HRESULT hr = S_OK;
    const int countIncomingArgs = pdispparams->cArgs;

    //Get type information for the current call
    InvocationInfo info;
    if( ! getInvocationInfoForCall( id, info))
        throw BridgeRuntimeError(
                  "[automation bridge]InterfaceOleWrapper::convertDispparamsArgs \n"
                  "Could not obtain type information for current call.");

    // Size rSeq according to the number of expected parameters.
    const int expectedArgs = info.aParamTypes.getLength() + (info.eMemberType == MemberType_PROPERTY ? 1 : 0);
    rSeq.realloc( expectedArgs );
    Any* pParams = rSeq.getArray();

    Any anyParam;

    int outgoingArgIndex = 0;

    // Go through incoming parameters in reverse order, i.e. in the order as declared in IDL
    for (int i = std::max(countIncomingArgs, expectedArgs) - 1; i >= 0; i--)
    {
        // Ignore too many parameters if they are VT_EMPTY anyway
        if ( outgoingArgIndex >= expectedArgs && pdispparams->rgvarg[i].vt == VT_EMPTY )
            continue;

        // But otherwise too many parameters is an error
        if ( outgoingArgIndex >= expectedArgs )
            throw BridgeRuntimeError( "[automation bridge] Too many parameters" );

        if (info.eMemberType == MemberType_METHOD &&
            info.aParamModes[ outgoingArgIndex ] == ParamMode_OUT)
        {
            outgoingArgIndex++;
            continue;
        }

        if (i < countIncomingArgs)
        {
            // A missing (and hopefully optional) arg (in the middle of the argument list) is passed
            // as an empty Any.
            if (pdispparams->rgvarg[i].vt == VT_ERROR && pdispparams->rgvarg[i].scode == DISP_E_PARAMNOTFOUND)
            {
                Any aEmpty;
                pParams[ outgoingArgIndex ] = aEmpty;
                outgoingArgIndex++;
                continue;
            }

            if(convertValueObject( & pdispparams->rgvarg[i], anyParam))
            { //a param is a ValueObject and could be converted
                pParams[ outgoingArgIndex ] = anyParam;
                outgoingArgIndex++;
                continue;
            }
        }
        else
        {
            // A missing arg. Let's hope it is de facto optional (there is no way in UNO IDL to mark
            // a parameter as optional). The corresponding slot in pParams is already a void Any.
            // Here we don't increase outgoingArgIndex!
            continue;
        }

        // If the param is an out, in/out parameter in
        // JScript (Array object, with value at index 0) then we
        // extract Array[0] and put the value into varParam. At the end of the loop varParam
        // is converted if it contains a value otherwise the VARIANT from
        // DISPPARAMS is converted.
        CComVariant varParam;

        // Check for JScript out and in/out paramsobjects (VT_DISPATCH).
        // To find them out we use typeinformation of the function being called.

        // No idea how this stuff, originally written for JScript, works for other Automation
        // clients.

        if( pdispparams->rgvarg[i].vt == VT_DISPATCH )
        {
            if( info.eMemberType == MemberType_METHOD && info.aParamModes[ outgoingArgIndex ] == ParamMode_INOUT)
            {
                // INOUT-param
                // Index ( property) "0" contains the actual IN-param. The object is a JScript
                // Array object.
                // Get the IN-param at index "0"
                IDispatch* pdisp= pdispparams->rgvarg[i].pdispVal;

                OLECHAR const * sindex= L"0";
                DISPID id2;
                DISPPARAMS noParams= {nullptr,nullptr,0,0};
                if(SUCCEEDED( hr= pdisp->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, &id2)))
                    hr= pdisp->Invoke( id2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
                                       & noParams, & varParam, nullptr, nullptr);
                if( FAILED( hr))
                {
                    throw BridgeRuntimeError(
                        "[automation bridge] Could not determine "
                        "if the object has a member \"0\". Error: " +
                        OUString::number(hr));
                }
            }
        }

        if( varParam.vt == VT_EMPTY) // then it was no in/out parameter
            varParam= pdispparams->rgvarg[i];

        if(info.eMemberType == MemberType_METHOD)
            variantToAny( & varParam, anyParam,
                           info.aParamTypes[ outgoingArgIndex ]);
        else if(info.eMemberType == MemberType_PROPERTY)
            variantToAny( & varParam, anyParam, info.aType);
        else
            OSL_ASSERT(false);

        if (outgoingArgIndex < expectedArgs)
            pParams[ outgoingArgIndex ]= anyParam;
        outgoingArgIndex++;
    }// end for / iterating over all parameters
}

bool  InterfaceOleWrapper::getInvocationInfoForCall( DISPID id, InvocationInfo& info)
{
    bool bTypesAvailable= false;

    if( !m_xInvocation.is() )return false;
    Reference<XInvocation2> inv2( m_xInvocation, UNO_QUERY);
    if( inv2.is())
    {
        // We need the name of the property or method to get its type information.
        // The name can be identified through the param "id"
        // that is kept as value in the map m_nameToDispIdMap.
        // Problem: the Windows JScript engine sometimes changes small letters to capital
        // letters as happens in xidlclass_obj.createObject( var) // in JScript.
        // IDispatch::GetIdsOfNames is then called with "CreateObject" !!!
        // m_nameToDispIdMap can contain several names for one DISPID but only one is
        // the exact one. If there's no m_xExactName and therefore no exact name then
        // there's only one entry in the map.
        OUString sMemberName;

        auto ci1 = std::find_if(m_nameToDispIdMap.cbegin(), m_nameToDispIdMap.cend(),
            [&id](const NameToIdMap::value_type& nameToDispId) { return nameToDispId.second == id; }); // item is a pair<OUString, DISPID>
        if (ci1 != m_nameToDispIdMap.cend())
            sMemberName= (*ci1).first;
        // Get information for the current call ( property or method).
        // There could be similar names which only differ in the cases
        // of letters. First we assume that the name which was passed into
        // GetIDsOfNames is correct. If we won't get information with that
        // name then we have the invocation service use the XExactName interface.
        bool validInfo= true;
        InvocationInfo invInfo;
        try{
            invInfo= inv2->getInfoForName( sMemberName, false);
        }
        catch(const IllegalArgumentException&)
        {
            validInfo= false;
        }

        if( ! validInfo)
        {
            invInfo= inv2->getInfoForName( sMemberName, true);
        }
        if( invInfo.aName.pData)
        {
            bTypesAvailable= true;
            info= invInfo;
        }
    }
    return bTypesAvailable;
}

// XBridgeSupplier2 ---------------------------------------------------
// only bridges itself ( this instance of InterfaceOleWrapper)from UNO to IDispatch
// If sourceModelType is UNO than any UNO interface implemented by InterfaceOleWrapper
// can bridged to IDispatch ( if destModelType == OLE). The IDispatch is
// implemented by this class.
Any SAL_CALL InterfaceOleWrapper::createBridge(const Any& modelDepObject,
                                const Sequence<sal_Int8>& /*ProcessId*/,
                                sal_Int16 sourceModelType,
                                sal_Int16 destModelType)
{

    Any retAny;
    if( sourceModelType == UNO && destModelType == OLE &&
        modelDepObject.getValueTypeClass() == TypeClass_INTERFACE )
    {
        Reference<XInterface> xInt;
        if( modelDepObject >>= xInt )
        {
            if( xInt == Reference<XInterface>( static_cast<XWeak*>( this), UNO_QUERY))
            {
                VARIANT *pVar= static_cast<VARIANT*>(CoTaskMemAlloc( sizeof( VARIANT)));
                if( pVar)
                {
                    pVar->vt= VT_DISPATCH;
                    pVar->pdispVal= this;
                    AddRef();

                    retAny<<= reinterpret_cast< sal_uIntPtr >( pVar);
                }
            }
        }
    }

    return retAny;
}

// XInitialization --------------------------------------------------
void SAL_CALL InterfaceOleWrapper::initialize( const Sequence< Any >& aArguments )
{
    switch( aArguments.getLength() )
    {
    case 2: // the object wraps a UNO struct
        aArguments[0] >>= m_xInvocation;
        aArguments[1] >>= m_defaultValueType;
        break;
    case 3: // the object wraps a UNO interface
        aArguments[0] >>= m_xInvocation;
        aArguments[1] >>= m_xOrigin;
        aArguments[2] >>= m_defaultValueType;

        Reference<XServiceInfo> xServiceInfo(m_xOrigin, UNO_QUERY);
        if (xServiceInfo.is())
            m_sImplementationName = xServiceInfo->getImplementationName();

        SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::initialize for "
                 << (m_sImplementationName.isEmpty()?"an unknown implementation":m_sImplementationName));
        break;
    }

    m_xExactName.set( m_xInvocation, UNO_QUERY);
}

Reference< XInterface > InterfaceOleWrapper::createUnoWrapperInstance()
{
    Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper(
                            m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
    return Reference<XInterface>( xWeak, UNO_QUERY);
}

Reference<XInterface> InterfaceOleWrapper::createComWrapperInstance()
{
    Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper(
                            m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
    return Reference<XInterface>( xWeak, UNO_QUERY);
}

// "getType" is used in convertValueObject to map the string denoting the type
// to an actual Type object.
bool getType( const BSTR name, Type & type)
{
    bool ret = false;
    typelib_TypeDescription * pDesc= nullptr;
    OUString str(o3tl::toU(name));
    typelib_typedescription_getByName( &pDesc, str.pData );
    if( pDesc)
    {
        type = Type( pDesc->pWeakRef );
        typelib_typedescription_release( pDesc);
        ret = true;
    }
    return ret;
}

static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource)
{
    bool ret = false;
    HRESULT hr;

    // Handle JScriptValue objects and JScript out params ( Array object )
    CComVariant varDest( *pDest);

    if( SUCCEEDED( varDest.ChangeType(VT_DISPATCH)))
    {
        CComPtr<IDispatch> spDispDest(varDest.pdispVal);

        // special Handling for a JScriptValue object
        CComQIPtr<IJScriptValueObject> spValueDest(spDispDest);
        if (spValueDest)
        {
            VARIANT_BOOL varBool= VARIANT_FALSE;
            if ((SUCCEEDED(hr = spValueDest->IsOutParam(&varBool))
                 && varBool == VARIANT_TRUE)
                || (SUCCEEDED(hr = spValueDest->IsInOutParam(&varBool))
                    && varBool == VARIANT_TRUE))
            {
                if( SUCCEEDED( spValueDest->Set( CComVariant(), *pSource)))
                    ret= true;
            }
        }
        else if (pDest->vt == VT_DISPATCH)// VT_DISPATCH -> JScript out param
        {
            // We use IDispatchEx because its GetDispID function causes the creation
            // of a property if it does not exist already. This is convenient for
            // out parameters in JScript. Then the user must not specify property "0"
            // explicitly
            CComQIPtr<IDispatchEx> spDispEx( spDispDest);
            if( spDispEx)
            {
                CComBSTR nullProp(L"0");
                DISPID dwDispID;
                if( SUCCEEDED( spDispEx->GetDispID( nullProp, fdexNameEnsure, &dwDispID)))
                {
                    DISPPARAMS dispparams = {nullptr, nullptr, 1, 1};
                    dispparams.rgvarg = pSource;
                    DISPID dispidPut = DISPID_PROPERTYPUT;
                    dispparams.rgdispidNamedArgs = &dispidPut;

                    if (pSource->vt == VT_UNKNOWN || pSource->vt == VT_DISPATCH ||
                        (pSource->vt & VT_ARRAY) || (pSource->vt & VT_BYREF))
                        hr = spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
                                                &dispparams, nullptr, nullptr, nullptr);
                    else
                        hr= spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
                                               &dispparams, nullptr, nullptr, nullptr);
                    if( SUCCEEDED(hr))
                        ret= true;
                }
            }
        }
        else
            ret= writeBackOutParameter( pDest, pSource);
    }
    else // The param can't be a JScript out-parameter ( an Array object), it could be a VBScript
    {   // param. The function checks itself for correct VBScript params
        ret= writeBackOutParameter( pDest, pSource);
    }
    return ret;
}

// VisualBasic Script passes arguments as VT_VARIANT | VT_BYREF be it in or out parameter.
// Thus we are in charge of freeing an eventual value contained by the inner VARIANT
// Please note: VariantCopy doesn't free a VT_BYREF value
// The out parameters are expected to have always a valid type
static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource)
{
    HRESULT hr;
    bool ret = false;
    // Out parameter must be VT_BYREF
    if ((V_VT(pDest) & VT_BYREF) != 0 )
    {
        VARTYPE oleTypeFlags = V_VT(pSource);

        // if caller accept VARIANT as out parameter, any value must be converted
        if (V_VT(pDest) == (VT_VARIANT | VT_BYREF))
        {
            // When the user provides a VARIANT rather than a concrete type
            // we just copy the source to the out, in/out parameter
            // VT_DISPATCH, VT_UNKNOWN, VT_ARRAY, VT_BSTR in the VARIANT that
            // is contained in pDest are released by VariantCopy
            VariantCopy(V_VARIANTREF(pDest), pSource);
            ret = true;
        }
        else
        {
            // variantarg and variant must have same type
            if ((V_VT(pDest) & oleTypeFlags) == oleTypeFlags)
            {
                if ((oleTypeFlags & VT_ARRAY) != 0)
                {
                    // In / Out Param
                    if( *V_ARRAYREF(pDest) != nullptr)
                        hr= SafeArrayCopyData( V_ARRAY(pSource), *V_ARRAYREF(pDest));
                    else
                        // Out Param
                        hr= SafeArrayCopy(V_ARRAY(pSource), V_ARRAYREF(pDest));
                    if( SUCCEEDED( hr))
                        ret = true;
                }
                else
                {
                    // copy base type
                    switch (V_VT(pSource))
                    {
                    case VT_I2:
                    {
                        *V_I2REF(pDest) = V_I2(pSource);
                        ret = true;
                        break;
                    }
                    case VT_I4:
                        *V_I4REF(pDest) = V_I4(pSource);
                        ret = true;
                        break;
                    case VT_R4:
                        *V_R4REF(pDest) = V_R4(pSource);
                        ret = true;
                        break;
                    case VT_R8:
                        *V_R8REF(pDest) = V_R8(pSource);
                        ret = true;
                        break;
                    case VT_CY:
                        *V_CYREF(pDest) = V_CY(pSource);
                        ret = true;
                        break;
                    case VT_DATE:
                        *V_DATEREF(pDest) = V_DATE(pSource);
                        ret = true;
                        break;
                    case VT_BSTR:
                        SysFreeString( *pDest->pbstrVal);

                        *V_BSTRREF(pDest) = SysAllocString(V_BSTR(pSource));
                        ret = true;
                        break;
                    case VT_DISPATCH:
                        if (*V_DISPATCHREF(pDest) != nullptr)
                            (*V_DISPATCHREF(pDest))->Release();

                        *V_DISPATCHREF(pDest) = V_DISPATCH(pSource);

                        if (*V_DISPATCHREF(pDest) != nullptr)
                            (*V_DISPATCHREF(pDest))->AddRef();

                        ret = true;
                        break;
                    case VT_ERROR:
                        *V_ERRORREF(pDest) = V_ERROR(pSource);
                        ret = true;
                        break;
                    case VT_BOOL:
                        *V_BOOLREF(pDest) = V_BOOL(pSource);
                        ret = true;
                        break;
                    case VT_UNKNOWN:
                        if (*V_UNKNOWNREF(pDest) != nullptr)
                            (*V_UNKNOWNREF(pDest))->Release();

                        *V_UNKNOWNREF(pDest) = V_UNKNOWN(pSource);

                        if (*V_UNKNOWNREF(pDest) != nullptr)
                            (*V_UNKNOWNREF(pDest))->AddRef();

                        ret = true;
                        break;
                    case VT_I1:
                        *V_I1REF(pDest) = V_I1(pSource);
                        ret = true;
                        break;
                    case VT_UI1:
                        *V_UI1REF(pDest) = V_UI1(pSource);
                        ret = true;
                        break;
                    case VT_UI2:
                        *V_UI2REF(pDest) = V_UI2(pSource);
                        ret = true;
                        break;
                    case VT_UI4:
                        *V_UI4REF(pDest) = V_UI4(pSource);
                        ret = true;
                        break;
                    case VT_INT:
                        *V_INTREF(pDest) = V_INT(pSource);
                        ret = true;
                        break;
                    case VT_UINT:
                        *V_UINTREF(pDest) = V_UINT(pSource);
                        ret = true;
                        break;
                    case VT_DECIMAL:
                        memcpy(pDest->pdecVal, pSource, sizeof(DECIMAL));
                        ret = true;
                        break;
                    default:
                        break;
                    }
                }
            }
            else
            {
                // Handling of special cases
                // Destination and source types are different
                if( pDest->vt == (VT_BSTR | VT_BYREF)
                    && pSource->vt == VT_I2)
                {
                    // When the user provides a String as out our in/out parameter
                    // and the type is char (TypeClass_CHAR) then we convert to a BSTR
                    // instead of VT_I2 as is done otherwise
                    OLECHAR buff[]= {0,0};
                    buff[0]= pSource->iVal;

                    SysFreeString( *pDest->pbstrVal);
                    *pDest->pbstrVal= SysAllocString( buff);
                    ret = true;
                }
            }
        }
    }
    return ret;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::Invoke(DISPID dispidMember,
                                         REFIID /*riid*/,
                                         LCID /*lcid*/,
                                         WORD wFlags,
                                         DISPPARAMS * pdispparams,
                                         VARIANT * pvarResult,
                                         EXCEPINFO * pexcepinfo,
                                         UINT * puArgErr )
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    OUString sParams;
#if defined SAL_LOG_INFO
    sParams += "[";
    for (unsigned int i = 0; i < pdispparams->cArgs; ++i)
    {
        if (i > 0)
            sParams += ",";
        std::stringstream aStringStream;
        aStringStream << pdispparams->rgvarg[i];
        sParams += OUString::createFromAscii(aStringStream.str());
    }
    sParams += "]";
#endif
    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::Invoke(" << dispidMember << "," << sParams << ")");

    comphelper::ProfileZone aZone("COM Bridge");
    HRESULT ret = S_OK;

    try
    {
        bool bHandled= false;
        ret= InvokeGeneral( dispidMember,  wFlags, pdispparams, pvarResult,  pexcepinfo,
                            puArgErr, bHandled);
        if( bHandled)
            return ret;

        if ((dispidMember > 0) && (o3tl::make_unsigned(dispidMember) <= m_MemberInfos.size()) && m_xInvocation.is())
        {
            MemberInfo d = m_MemberInfos[dispidMember - 1];
            DWORD flags = wFlags & d.flags;

            if (flags != 0)
            {
                if ((flags & DISPATCH_METHOD) != 0)
                {
                    std::unique_ptr<DISPPARAMS> pNewDispParams;
                    std::vector<VARIANTARG> vNewArgs;

                    if (pdispparams->cNamedArgs > 0)
                    {
                        // Convert named arguments to positional ones.

                        // An example:
                        //
                        // Function declaration (in pseudo-code):
                        // int foo(int A, int B, optional int C, optional int D, optional int E, optional int F, optional int G)
                        //
                        // Corresponding parameter numbers (DISPIDs):
                        //             0      1               2               3               4               5               6
                        //
                        // Actual call:
                        // foo(10, 20, E:=50, D:=40, F:=60)
                        //
                        // That is, A and B are passed positionally, D, E, and F as named arguments,
                        // and the optional C and G parameters are left out.
                        //
                        // Incoming DISPPARAMS:
                        //     cArgs=5, cNamedArgs=3
                        //     rgvarg: [60, 40, 50, 20, 10]
                        //     rgdispidNamedArgs: [5, 3, 4]
                        //
                        // We calculate nLowestNamedArgDispid = 3 and nHighestNamedArgDispid = 5.
                        //
                        // Result of conversion, no named args:
                        //     cArgs=6, cNamedArgs=0
                        //     rgvarg: [60, 50, 40, DISP_E_PARAMNOTFOUND, 20, 10]

                        // First find the lowest and highest DISPID of the named arguments.
                        DISPID nLowestNamedArgDispid = 1000000;
                        DISPID nHighestNamedArgDispid = -1;
                        for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i)
                        {
                            if (pdispparams->rgdispidNamedArgs[i] < nLowestNamedArgDispid)
                                nLowestNamedArgDispid = pdispparams->rgdispidNamedArgs[i];
                            if (pdispparams->rgdispidNamedArgs[i] > nHighestNamedArgDispid)
                                nHighestNamedArgDispid = pdispparams->rgdispidNamedArgs[i];
                        }

                        // Make sure named arguments don't overlap with positional ones. The lowest
                        // DISPID of the named arguments should be >= the number of positional
                        // arguments.
                        if (nLowestNamedArgDispid < static_cast<DISPID>(pdispparams->cArgs - pdispparams->cNamedArgs))
                            return DISP_E_NONAMEDARGS;

                        // Do the actual conversion.
                        pNewDispParams.reset(new DISPPARAMS);
                        vNewArgs.resize(nHighestNamedArgDispid + 1);
                        pNewDispParams->rgvarg = vNewArgs.data();
                        pNewDispParams->rgdispidNamedArgs = nullptr;
                        pNewDispParams->cArgs = nHighestNamedArgDispid + 1;
                        pNewDispParams->cNamedArgs = 0;

                        // Initialise all parameter slots as missing
                        for (int i = 0; i < nHighestNamedArgDispid; ++i)
                        {
                            pNewDispParams->rgvarg[i].vt = VT_ERROR;
                            pNewDispParams->rgvarg[i].scode = DISP_E_PARAMNOTFOUND;
                        }

                        // Then set the value of those actually present.
                        for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i)
                            pNewDispParams->rgvarg[nHighestNamedArgDispid - pdispparams->rgdispidNamedArgs[i]] = pdispparams->rgvarg[i];

                        const int nFirstUnnamedArg = pdispparams->cNamedArgs + (nLowestNamedArgDispid-(pdispparams->cArgs - pdispparams->cNamedArgs));

                        for (unsigned int i = pdispparams->cNamedArgs; i < pdispparams->cArgs; ++i)
                            pNewDispParams->rgvarg[nFirstUnnamedArg + (i-pdispparams->cNamedArgs)] = pdispparams->rgvarg[i];

                        pdispparams = pNewDispParams.get();
                    }

                    Sequence<Any> params;

                    convertDispparamsArgs(dispidMember, wFlags, pdispparams , params );

                    ret= doInvoke(pdispparams, pvarResult,
                                  pexcepinfo, puArgErr, d.name, params);
                }
                else if ((flags & DISPATCH_PROPERTYGET) != 0)
                {
                    ret=  doGetProperty( pdispparams, pvarResult,
                                         pexcepinfo, d.name);
                }
                else if ((flags & DISPATCH_PROPERTYPUT) != 0 || (flags & DISPATCH_PROPERTYPUTREF) != 0)
                {
                    if (pdispparams->cArgs != 1)
                        ret = DISP_E_BADPARAMCOUNT;
                    else
                    {
                        Sequence<Any> params;
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
                        if(params.getLength() > 0)
                            ret= doSetProperty( pdispparams, pvarResult, pexcepinfo, puArgErr, d.name, params);
                        else
                            ret = DISP_E_BADVARTYPE;
                    }
                }
            }
            else
                ret= DISP_E_MEMBERNOTFOUND;
        }
        else
            ret = DISP_E_MEMBERNOTFOUND;
    }
    catch(const BridgeRuntimeError& e)
    {
        writeExcepinfo(pexcepinfo, e.message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const Exception& e)
    {
        OUString message= "InterfaceOleWrapper::Invoke : \n" +
                                e.Message;
        writeExcepinfo(pexcepinfo, message);
        ret = DISP_E_EXCEPTION;
    }
    catch(...)
    {
        writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::Invoke : \nUnexpected exception");
        ret = DISP_E_EXCEPTION;
    }

    return ret;
}

HRESULT InterfaceOleWrapper::doInvoke( DISPPARAMS * pdispparams, VARIANT * pvarResult,
                              EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any>& params)
{


    HRESULT ret= S_OK;
    try
    {
        Sequence<sal_Int16>     outIndex;
        Sequence<Any>   outParams;
        Any                 returnValue;

        if (pdispparams->cNamedArgs > 0)
            return DISP_E_NONAMEDARGS;

        // invoke method and take care of exceptions
        returnValue = m_xInvocation->invoke(name,
                                            params,
                                            outIndex,
                                            outParams);

        // try to write back out parameter
        if (outIndex.getLength() > 0)
        {
            const sal_Int16* pOutIndex = outIndex.getConstArray();
            const Any* pOutParams = outParams.getConstArray();

            for (sal_Int32 i = 0; i < outIndex.getLength(); i++)
            {
                CComVariant variant;
                // Currently a Sequence is converted to an SafeArray of VARIANTs.
                anyToVariant( &variant, pOutParams[i]);

                // out parameter need special handling if they are VT_DISPATCH
                // and used in JScript
                int outindex= pOutIndex[i];
                writeBackOutParameter2(&(pdispparams->rgvarg[pdispparams->cArgs - 1 - outindex]),
                                       &variant );
            }
        }

        // write back return value
        if (pvarResult != nullptr)
            anyToVariant(pvarResult, returnValue);
    }
    catch(const IllegalArgumentException & e) //XInvocation::invoke
    {
        writeExcepinfo(pexcepinfo, e.Message);
        ret = DISP_E_TYPEMISMATCH;
    }
    catch(const CannotConvertException & e) //XInvocation::invoke
    {
        writeExcepinfo(pexcepinfo, e.Message);
        ret = mapCannotConvertException( e, puArgErr);
    }
    catch(const InvocationTargetException &  e) //XInvocation::invoke
    {
        const Any& org = e.TargetException;
        Exception excTarget;
        org >>= excTarget;
        OUString message=
            org.getValueTypeName() + ": " + excTarget.Message;
        writeExcepinfo(pexcepinfo, message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const NoSuchMethodException & e) //XInvocation::invoke
    {
        writeExcepinfo(pexcepinfo, e.Message);
        ret = DISP_E_MEMBERNOTFOUND;
    }
    catch(const BridgeRuntimeError & e)
    {
        writeExcepinfo(pexcepinfo, e.message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const Exception & e)
    {
        OUString message= "InterfaceOleWrapper::doInvoke : \n" +
                                e.Message;
        writeExcepinfo(pexcepinfo, message);
        ret = DISP_E_EXCEPTION;
    }
    catch( ... )
     {
        writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception");
        ret = DISP_E_EXCEPTION;
     }
    return ret;
}

HRESULT InterfaceOleWrapper::doGetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * pvarResult,
                                            EXCEPINFO * pexcepinfo, OUString& name)
{
    HRESULT ret= S_OK;

    try
    {
        Any returnValue = m_xInvocation->getValue( name);
        // write back return value
        if (pvarResult)
            anyToVariant(pvarResult, returnValue);
    }
    catch(const UnknownPropertyException& e) //XInvocation::getValue
    {
        writeExcepinfo(pexcepinfo, e.Message);
        ret = DISP_E_MEMBERNOTFOUND;
    }
    catch(const BridgeRuntimeError& e)
    {
        writeExcepinfo(pexcepinfo, e.message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const Exception& e)
    {
        OUString message= "InterfaceOleWrapper::doGetProperty : \n" +
                                e.Message;
        writeExcepinfo(pexcepinfo, message);
    }
    catch( ... )
    {
        writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception");
        ret = DISP_E_EXCEPTION;
    }
    return  ret;
}

HRESULT InterfaceOleWrapper::doSetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/,
                                        EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any> const & params)
{
    HRESULT ret= S_OK;

    try
    {
        m_xInvocation->setValue( name, params.getConstArray()[0]);
    }
    catch(const UnknownPropertyException &)
    {
        ret = DISP_E_MEMBERNOTFOUND;
    }
    catch(const CannotConvertException &e)
    {
        ret= mapCannotConvertException( e, puArgErr);
    }
    catch(const InvocationTargetException &e)
    {
        if (pexcepinfo != nullptr)
        {
            Any org = e.TargetException;

            pexcepinfo->wCode = UNO_2_OLE_EXCEPTIONCODE;
            pexcepinfo->bstrSource = SysAllocString(L"any ONE component");
            pexcepinfo->bstrDescription = SysAllocString(
                o3tl::toW(org.getValueTypeName().getStr()));
        }
        ret = DISP_E_EXCEPTION;
    }
    catch( ... )
    {
        ret= DISP_E_EXCEPTION;
    }
    return ret;
}

namespace {

class CXEnumVariant : public IEnumVARIANT,
                      public CComObjectRoot
{
public:
    CXEnumVariant()
        : mnIndex(1)            // ooo::vba::XCollection index starts at one
    {
    }

    virtual ~CXEnumVariant()
    {
    }

#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    BEGIN_COM_MAP(CXEnumVariant)
#if defined __clang__
#pragma clang diagnostic pop
#endif
        COM_INTERFACE_ENTRY(IEnumVARIANT)
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    END_COM_MAP()
#if defined __clang__
#pragma clang diagnostic pop
#endif

    DECLARE_NOT_AGGREGATABLE(CXEnumVariant)

    // Creates and initializes the enumerator
    void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
              const Reference<ooo::vba::XCollection > xCollection)
    {
        mpInterfaceOleWrapper = pInterfaceOleWrapper;
        mxCollection = xCollection;
    }

    // IEnumVARIANT
    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **) override
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Clone: E_NOTIMPL");
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Next(ULONG const celt,
                                           VARIANT *rgVar,
                                           ULONG *pCeltFetched) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        if (pCeltFetched)
            *pCeltFetched = 0;

        if (celt == 0)
        {
            SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_INVALIDARG");
            return E_INVALIDARG;
        }

        if (rgVar == nullptr || (celt != 1 && pCeltFetched == nullptr))
        {
            SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_FAIL");
            return E_FAIL;
        }

        for (ULONG i = 0; i < celt; i++)
            VariantInit(&rgVar[i]);

        ULONG nLeft = celt;
        ULONG nReturned = 0;
        while (nLeft > 0)
        {
            if (mnIndex > mxCollection->getCount())
            {
                SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): got " << nReturned << ": S_FALSE");
                return S_FALSE;
            }
            Any aIndex;
            aIndex <<= mnIndex;
            Any aElement = mxCollection->Item(aIndex, Any());
            mpInterfaceOleWrapper->anyToVariant(rgVar, aElement);
            // rgVar->pdispVal->AddRef(); ??
            if (pCeltFetched)
                (*pCeltFetched)++;
            rgVar++;
            nReturned++;
            mnIndex++;
            nLeft--;
        }
        SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): S_OK");
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Reset() override
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Reset: S_OK");
        mnIndex = 1;
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE STDMETHODCALLTYPE Skip(ULONG const celt) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        ULONG nLeft = celt;
        ULONG nSkipped = 0;
        while (nLeft > 0)
        {
            if (mnIndex > mxCollection->getCount())
            {
                SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): skipped " << nSkipped << ": S_FALSE");
                return S_FALSE;
            }
            mnIndex++;
            nLeft--;
        }
        SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): S_OK");
        return S_OK;
    }

private:
    InterfaceOleWrapper* mpInterfaceOleWrapper;
    Reference<ooo::vba::XCollection> mxCollection;
    sal_Int32 mnIndex;
};

class Sink : public cppu::WeakImplHelper<ooo::vba::XSink>
{
public:
    Sink(IUnknown* pUnkSink,
         Reference<XMultiServiceFactory> xMSF,
         ooo::vba::TypeAndIID aTypeAndIID,
         InterfaceOleWrapper* pInterfaceOleWrapper);

    // XSink
    void SAL_CALL Call( const OUString& Method, Sequence< Any >& Arguments ) override;

private:
    IUnknown* mpUnkSink;
    Reference<XMultiServiceFactory> mxMSF;
    ooo::vba::TypeAndIID maTypeAndIID;
    InterfaceOleWrapper* mpInterfaceOleWrapper;
};

}

Sink::Sink(IUnknown* pUnkSink,
           Reference<XMultiServiceFactory> xMSF,
           ooo::vba::TypeAndIID aTypeAndIID,
           InterfaceOleWrapper* pInterfaceOleWrapper) :
    mpUnkSink(pUnkSink),
    mxMSF(xMSF),
    maTypeAndIID(aTypeAndIID),
    mpInterfaceOleWrapper(pInterfaceOleWrapper)
{
    mpUnkSink->AddRef();
}

void SAL_CALL
Sink::Call( const OUString& Method, Sequence< Any >& Arguments )
{
    SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << ", " << Arguments.getLength() << " arguments)");

    IDispatch* pDispatch;
    HRESULT nResult = mpUnkSink->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&pDispatch));
    if (!SUCCEEDED(nResult))
    {
        SAL_WARN("extensions.olebridge", "Sink::Call: Not IDispatch: " << WindowsErrorStringFromHRESULT(nResult));
        return;
    }

    Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
    assert(xRefl.is());

    Reference<XIdlClass> xClass = xRefl->forName(maTypeAndIID.Type.getTypeName());
    assert(xClass.is());

    auto aMethods = xClass->getMethods();
    assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
           aMethods.getLength() > 0);

    int nMemId = 1;
    auto ArgumentsRange = asNonConstRange(Arguments);
    // Skip the three XInterface methods
    for (int i = 3; i < aMethods.getLength(); i++)
    {
        if (aMethods[i]->getName() == Method)
        {
            // FIXME: Handle mismatch in type of actual argument and parameter of the method.

            // FIXME: Handle mismatch in number of arguments passed and actual number of parameters
            // of the method.

            auto aParamInfos = aMethods[i]->getParameterInfos();

            assert(Arguments.getLength() == aParamInfos.getLength());

            DISPPARAMS aDispParams;
            aDispParams.rgdispidNamedArgs = nullptr;
            aDispParams.cArgs = Arguments.getLength();
            aDispParams.cNamedArgs = 0;
            aDispParams.rgvarg = new VARIANT[aDispParams.cArgs];
            for (unsigned j = 0; j < aDispParams.cArgs; j++)
            {
                VariantInit(aDispParams.rgvarg+j);
                // Note: Reverse order of arguments in Arguments and aDispParams.rgvarg!
                const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1;
                mpInterfaceOleWrapper->anyToVariant(aDispParams.rgvarg+j, Arguments[nIncomingArgIndex]);

                // Handle OUT and INOUT arguments. For instance, the second ('Cancel') parameter to
                // DocumentBeforeClose() should be a VT_BYREF|VT_BOOL parameter. Need to handle that
                // here.

                if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT ||
                    aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT)
                {
                    switch (aDispParams.rgvarg[j].vt)
                    {
                    case VT_I2:
                        aDispParams.rgvarg[j].byref = new SHORT(aDispParams.rgvarg[j].iVal);
                        aDispParams.rgvarg[j].vt |= VT_BYREF;
                        break;
                    case VT_I4:
                        aDispParams.rgvarg[j].byref = new LONG(aDispParams.rgvarg[j].lVal);
                        aDispParams.rgvarg[j].vt |= VT_BYREF;
                        break;
                    case VT_BSTR:
                        aDispParams.rgvarg[j].byref = new BSTR(aDispParams.rgvarg[j].bstrVal);
                        aDispParams.rgvarg[j].vt |= VT_BYREF;
                        break;
                    case VT_BOOL:
                        aDispParams.rgvarg[j].byref = new VARIANT_BOOL(aDispParams.rgvarg[j].boolVal);
                        aDispParams.rgvarg[j].vt |= VT_BYREF;
                        break;
                    default:
                        assert(false && "Not handled yet");
                        break;
                    }
                }
            }

            VARIANT aVarResult;
            VariantInit(&aVarResult);
            UINT uArgErr;

            // In the case of a VBScript client, which uses "late binding", calling Invoke on the
            // sink it provides will cause a callback to our CXTypeInfo::GetNames for the given
            // member id, and in that we will tell it the name of the corresponding method, and the
            // client will know what event handler to invoke based on that name.
            //
            // As the outgoing interfaces used (ooo::vba::word::XApplicationOutgoing and others) are
            // totally not stable and not published in any way, there can be no client that would
            // have done "compile-time binding" and where the sink would actually be an object with
            // a vtbl corresponding to the outgoing interface. Late binding clients that work like
            // VBScript is all we support.
            SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Calling Invoke(" << nMemId << ")");

            nResult = pDispatch->Invoke(nMemId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &aDispParams, &aVarResult, nullptr, &uArgErr);
            SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Invoke() returned");

            SAL_WARN_IF(!SUCCEEDED(nResult), "extensions.olebridge", "Call to " << Method << " failed: " << WindowsErrorStringFromHRESULT(nResult));

            // Undo VT_BYREF magic done above. Copy out parameters back to the Anys in Arguments
            for (unsigned j = 0; j < aDispParams.cArgs; j++)
            {
                const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1;
                if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT ||
                    aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT)
                {
                    switch (aDispParams.rgvarg[j].vt)
                    {
                    case VT_BYREF|VT_I2:
                        {
                            SHORT *pI = static_cast<SHORT*>(aDispParams.rgvarg[j].byref);
                            ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int16>(*pI);
                            delete pI;
                        }
                        break;
                    case VT_BYREF|VT_I4:
                        {
                            LONG *pL = static_cast<LONG*>(aDispParams.rgvarg[j].byref);
                            ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int32>(*pL);
                            delete pL;
                        }
                        break;
                    case VT_BYREF|VT_BSTR:
                        {
                            BSTR *pBstr = static_cast<BSTR*>(aDispParams.rgvarg[j].byref);
                            ArgumentsRange[nIncomingArgIndex] <<= OUString(o3tl::toU(*pBstr));
                            // Undo SysAllocString() done in anyToVariant()
                            SysFreeString(*pBstr);
                            delete pBstr;
                        }
                        break;
                    case VT_BYREF|VT_BOOL:
                        {
                            VARIANT_BOOL *pBool = static_cast<VARIANT_BOOL*>(aDispParams.rgvarg[j].byref);
                            ArgumentsRange[nIncomingArgIndex] <<= (*pBool != VARIANT_FALSE);
                            delete pBool;
                        }
                        break;
                    default:
                        assert(false && "Not handled yet");
                        break;
                    }
                }
                else
                {
                    switch (aDispParams.rgvarg[j].vt)
                    {
                    case VT_BSTR:
                        // Undo SysAllocString() done in anyToVariant()
                        SysFreeString(aDispParams.rgvarg[j].bstrVal);
                        break;
                    }
                }
            }

            delete[] aDispParams.rgvarg;
            return;
        }
        nMemId++;
    }
    SAL_WARN("extensions.olebridge", "Sink::Call: Unknown method '" << Method << "'");
}

namespace {

class CXEnumConnections : public IEnumConnections,
                          public CComObjectRoot
{
public:
    CXEnumConnections()
    {
    }

    virtual ~CXEnumConnections()
    {
    }

#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    BEGIN_COM_MAP(CXEnumConnections)
#if defined __clang__
#pragma clang diagnostic pop
#endif
        COM_INTERFACE_ENTRY(IEnumConnections)
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    END_COM_MAP()
#if defined __clang__
#pragma clang diagnostic pop
#endif

    DECLARE_NOT_AGGREGATABLE(CXEnumConnections)

    void Init(std::vector<IUnknown*>& rUnknowns, std::vector<DWORD>& rCookies)
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Init");
        SAL_WARN_IF(rUnknowns.size() != rCookies.size(), "extensions.olebridge", "Vectors of different size");
        mvUnknowns = rUnknowns;
        mvCookies = rCookies;
        mnIndex = 0;
    }

    virtual HRESULT STDMETHODCALLTYPE Next(ULONG cConnections,
                                           LPCONNECTDATA rgcd,
                                           ULONG *pcFetched) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        if (!rgcd)
        {
            SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_POINTER");
            return E_POINTER;
        }

        if (pcFetched && cConnections != 1)
        {
            SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_INVALIDARG");
            return E_INVALIDARG;
        }

        ULONG nFetched = 0;
        while (nFetched < cConnections && mnIndex < mvUnknowns.size())
        {
            rgcd[nFetched].pUnk = mvUnknowns[mnIndex];
            rgcd[nFetched].pUnk->AddRef();
            rgcd[nFetched].dwCookie = mvCookies[mnIndex];
            ++nFetched;
            ++mnIndex;
        }
        if (nFetched != cConnections)
        {
            SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_FALSE");
            if (pcFetched)
                *pcFetched = nFetched;
            return S_FALSE;
        }
        SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_OK");
        if (pcFetched)
            *pcFetched = nFetched;

        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Skip(ULONG cConnections) override
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Skip(" << cConnections << "): E_NOTIMPL");

        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Reset() override
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Reset: E_NOTIMPL");

        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumConnections** /* ppEnum */) override
    {
        SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Clone: E_NOTIMPL");

        return E_NOTIMPL;
    }

private:
    std::vector<IUnknown*> mvUnknowns;
    std::vector<DWORD> mvCookies;
    ULONG mnIndex;
};

class CXConnectionPoint : public IConnectionPoint,
                          public CComObjectRoot
{
public:
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    BEGIN_COM_MAP(CXConnectionPoint)
#if defined __clang__
#pragma clang diagnostic pop
#endif
        COM_INTERFACE_ENTRY(IConnectionPoint)
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
    END_COM_MAP()
#if defined __clang__
#pragma clang diagnostic pop
#endif

    DECLARE_NOT_AGGREGATABLE(CXConnectionPoint)

    virtual ~CXConnectionPoint() {}

    void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
              Reference<ooo::vba::XConnectionPoint>& xCP,
              Reference<XMultiServiceFactory>& xMSF,
              ooo::vba::TypeAndIID aTypeAndIID)
    {
        SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Init for " << pInterfaceOleWrapper->getImplementationName());

        IUnknown *pUnknown;
        if (SUCCEEDED(QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnknown))))
        {
            // In case QI for IUnknown returns a different pointer, but nah, it doesn't
            SAL_INFO("extensions.olebridge", "  (IUnknown@" << pUnknown << ")");
        }

        mpInterfaceOleWrapper = pInterfaceOleWrapper;
        mxCP = xCP;
        mxMSF = xMSF;
        maTypeAndIID = aTypeAndIID;
    }

    virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(IID *pIID) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface(" << *pIID << "): E_NOTIMPL");

        // FIXME: Needed?

        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(IConnectionPointContainer **) override
    {
        SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface: E_NOTIMPL");

        // FIXME: Needed?

        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Advise(IUnknown *pUnkSink,
                                             DWORD *pdwCookie) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Advise(" << pUnkSink << ")");

        if (!pdwCookie)
            return E_POINTER;

        Reference<ooo::vba::XSink> xSink(new Sink(pUnkSink, mxMSF, maTypeAndIID, mpInterfaceOleWrapper));

        mvISinks.push_back(pUnkSink);
        *pdwCookie = mvISinks.size();

        mvCookies.push_back(mxCP->Advise(xSink));

        mvXSinks.push_back(xSink);

        SAL_INFO("extensions.olebridge", "  *pdwCookie: " << *pdwCookie);

        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Unadvise(DWORD dwCookie) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Unadvise(" << dwCookie << ")");

        if (dwCookie == 0 || dwCookie > mvISinks.size())
            return E_POINTER;

        mvISinks[dwCookie-1] = nullptr;

        mxCP->Unadvise(mvCookies[dwCookie-1]);

        mvXSinks[dwCookie-1] = Reference<ooo::vba::XSink>();

        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE EnumConnections(IEnumConnections **ppEnum) override
    {
        comphelper::Automation::AutomationInvokedZone aAutomationActive;

        HRESULT nResult;

        SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::EnumConnections...");

        if (!ppEnum)
        {
            SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: E_POINTER");
            return E_POINTER;
        }

        CComObject<CXEnumConnections>* pEnumConnections;

        nResult = CComObject<CXEnumConnections>::CreateInstance(&pEnumConnections);
        if (FAILED(nResult))
        {
            SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: " << WindowsErrorStringFromHRESULT(nResult));
            return nResult;
        }

        pEnumConnections->AddRef();

        pEnumConnections->Init(mvISinks, mvCookies);
        *ppEnum = pEnumConnections;

        SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: S_OK");

        return S_OK;
    }

    InterfaceOleWrapper* mpInterfaceOleWrapper;
    std::vector<IUnknown*> mvISinks;
    std::vector<Reference<ooo::vba::XSink>> mvXSinks;
    std::vector<DWORD> mvCookies;
    Reference<XMultiServiceFactory> mxMSF;
    Reference<ooo::vba::XConnectionPoint> mxCP;
    ooo::vba::TypeAndIID maTypeAndIID;
};

}

HRESULT InterfaceOleWrapper::InvokeGeneral( DISPID dispidMember, unsigned short wFlags,
                         DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
                         unsigned int * /*puArgErr*/, bool& bHandled)
{
    HRESULT ret= S_OK;
    try
    {
// DISPID_VALUE | The DEFAULT Value is required in JScript when the situation
// is that we put an object into an Array object ( out parameter). We have to return
// IDispatch otherwise the object cannot be accessed from the Script.
        if( dispidMember == DISPID_VALUE && (wFlags & DISPATCH_PROPERTYGET) != 0
            && m_defaultValueType != VT_EMPTY && pvarResult != nullptr)
        {
            // Special case hack: If it is a ScVbaCheckBox, return the boolean value
            Reference<ooo::vba::msforms::XCheckBox> xCheckBox(m_xOrigin, UNO_QUERY);
            if (xCheckBox.is())
            {
                bHandled = true;
                Any aValue = xCheckBox->getValue();
                anyToVariant(pvarResult, aValue);
                return S_OK;
            }

            bHandled= true;
            if( m_defaultValueType == VT_DISPATCH)
            {
                pvarResult->vt= VT_DISPATCH;
                pvarResult->pdispVal= this;
                AddRef();
                ret= S_OK;
            }
        }

        // function: _GetValueObject
        else if( dispidMember == DISPID_JSCRIPT_VALUE_FUNC)
        {
            bHandled= true;
            if( !pvarResult)
                return E_POINTER;
            CComObject< JScriptValue>* pValue;
            if( SUCCEEDED( CComObject<JScriptValue>::CreateInstance( &pValue)))
            {
                pValue->AddRef();
                pvarResult->vt= VT_DISPATCH;
                pvarResult->pdispVal= CComQIPtr<IDispatch>(pValue->GetUnknown());
                ret= S_OK;
            }
            else
                ret= DISP_E_EXCEPTION;
        }
        else if( dispidMember == DISPID_GET_STRUCT_FUNC)
        {
            bHandled= true;
            bool bStruct= false;


            Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(m_smgr));
            // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
            CComVariant arg;
            if( pdispparams->cArgs == 1 && SUCCEEDED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])) )
            {
                Reference<XIdlClass> classStruct= xRefl->forName(OUString(o3tl::toU(arg.bstrVal)));
                if( classStruct.is())
                {
                    Any anyStruct;
                    classStruct->createObject( anyStruct);
                    CComVariant var;
                    anyToVariant( &var, anyStruct );

                    if( var.vt == VT_DISPATCH)
                    {
                        VariantCopy( pvarResult, & var);
                        bStruct= true;
                    }
                }
            }
            ret= bStruct ? S_OK : DISP_E_EXCEPTION;
        }
        else if (dispidMember == DISPID_CREATE_TYPE_FUNC)
        {
            bHandled= true;
            if( !pvarResult)
                return E_POINTER;
            // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
            CComVariant arg;
            if( pdispparams->cArgs != 1)
                return DISP_E_BADPARAMCOUNT;
            if (FAILED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])))
                return DISP_E_BADVARTYPE;

            //check if the provided name represents a valid type
            Type type;
            if (!getType(arg.bstrVal, type))
            {
                writeExcepinfo(pexcepinfo, OUString::Concat("[automation bridge] A UNO type with the name ") +
                                           o3tl::toU(arg.bstrVal) + " does not exist!");
                return DISP_E_EXCEPTION;
            }

            if (!createUnoTypeWrapper(arg.bstrVal, pvarResult))
            {
                writeExcepinfo(pexcepinfo, "[automation bridge] InterfaceOleWrapper::InvokeGeneral\n"
                                           "Could not initialize UnoTypeWrapper object!");
                return DISP_E_EXCEPTION;
            }
        }
        else if (dispidMember == DISPID_NEWENUM)
        {
            bHandled = true;
            if( !pvarResult)
                return E_POINTER;

            Reference< ooo::vba::XCollection> xCollection(m_xOrigin, UNO_QUERY);
            if (!xCollection.is())
                return DISP_E_MEMBERNOTFOUND;

            CComObject<CXEnumVariant>* pEnumVar;

            ret = CComObject<CXEnumVariant>::CreateInstance(&pEnumVar);
            if (FAILED(ret))
                return ret;

            pEnumVar->AddRef();

            pEnumVar->Init(this, xCollection);

            pvarResult->vt = VT_UNKNOWN;
            pvarResult->punkVal = nullptr;

            ret = pEnumVar->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&pvarResult->punkVal));
            if (FAILED(ret))
            {
                pEnumVar->Release();
                return ret;
            }
        }
    }
    catch(const BridgeRuntimeError & e)
    {
        writeExcepinfo(pexcepinfo, e.message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const Exception & e)
    {
        OUString message= "InterfaceOleWrapper::InvokeGeneral : \n" +
                                e.Message;
        writeExcepinfo(pexcepinfo, message);
        ret = DISP_E_EXCEPTION;
    }
    catch( ... )
     {
        writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::InvokeGeneral : \nUnexpected exception");
        ret = DISP_E_EXCEPTION;
     }
    return ret;
}

STDMETHODIMP InterfaceOleWrapper::GetDispID(BSTR /*bstrName*/, DWORD /*grfdex*/, DISPID __RPC_FAR* /*pid*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::InvokeEx(
    /* [in] */ DISPID /*id*/,
    /* [in] */ LCID /*lcid*/,
    /* [in] */ WORD /*wFlags*/,
    /* [in] */ DISPPARAMS __RPC_FAR* /*pdp*/,
    /* [out] */ VARIANT __RPC_FAR* /*pvarRes*/,
    /* [out] */ EXCEPINFO __RPC_FAR* /*pei*/,
    /* [unique][in] */ IServiceProvider __RPC_FAR* /*pspCaller*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::DeleteMemberByName(
    /* [in] */ BSTR /*bstr*/,
    /* [in] */ DWORD /*grfdex*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::DeleteMemberByDispID(DISPID /*id*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::GetMemberProperties(
    /* [in] */ DISPID /*id*/,
    /* [in] */ DWORD /*grfdexFetch*/,
    /* [out] */ DWORD __RPC_FAR* /*pgrfdex*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::GetMemberName(
    /* [in] */ DISPID /*id*/,
    /* [out] */ BSTR __RPC_FAR* /*pbstrName*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::GetNextDispID(
    /* [in] */ DWORD /*grfdex*/,
    /* [in] */ DISPID /*id*/,
    /* [out] */ DISPID __RPC_FAR* /*pid*/)
{
    return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP InterfaceOleWrapper::GetNameSpaceParent(
    /* [out] */ IUnknown __RPC_FAR *__RPC_FAR* /*ppunk*/)
{
    return ResultFromScode(E_NOTIMPL);
}

// IProvideClassInfo
HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::GetClassInfo (
    /* [out] */ ITypeInfo **ppTI)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetClassInfo");

    if (!ppTI)
        return E_POINTER;

    Reference<ooo::vba::XInterfaceWithIID> xIID(m_xOrigin, UNO_QUERY);
    if (!xIID.is())
        return E_NOTIMPL;

    OUString sIID = xIID->getIID();
    IID aIID;
    if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID)))
        return E_NOTIMPL;

    HRESULT ret;

    CComObject<CXTypeInfo>* pTypeInfo;

    ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
    if (FAILED(ret))
        return ret;

    pTypeInfo->AddRef();

    pTypeInfo->InitForCoclass(m_xOrigin, m_sImplementationName, aIID, m_smgr);

    *ppTI = pTypeInfo;

    return S_OK;
}

// IConnectionPointContainer
HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::EnumConnectionPoints(
    /* [out] */ IEnumConnectionPoints **)
{
    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::EnumConnectionPoints");
    return ResultFromScode(E_NOTIMPL);
}

HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::FindConnectionPoint(
    /* [in] */ REFIID riid,
    /* [out] */ IConnectionPoint **ppCP)
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::FindConnectionPoint(" << riid << ")");

    if (!ppCP)
        return E_POINTER;

    Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);

    // We checked already
    assert(xConnectable.is());
    if (!xConnectable.is())
        return E_NOTIMPL;

    ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();

    IID aIID;
    if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID)))
        return E_INVALIDARG;

    if (!IsEqualIID(riid, aIID))
        return E_INVALIDARG;

    Reference<ooo::vba::XConnectionPoint> xCP = xConnectable->FindConnectionPoint();
    if (!xCP.is())
        return E_INVALIDARG;

    HRESULT ret;

    CComObject<CXConnectionPoint>* pConnectionPoint;

    ret = CComObject<CXConnectionPoint>::CreateInstance(&pConnectionPoint);
    if (FAILED(ret))
        return ret;

    pConnectionPoint->AddRef();

    pConnectionPoint->Init(this, xCP, m_smgr, aTypeAndIID);

    *ppCP = pConnectionPoint;

    return S_OK;
}

// UnoObjectWrapperRemoteOpt ---------------------------------------------------

UnoObjectWrapperRemoteOpt::UnoObjectWrapperRemoteOpt( Reference<XMultiServiceFactory> const & aFactory,
                                                     sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass):
InterfaceOleWrapper( aFactory, unoWrapperClass, comWrapperClass),
m_currentId(1)

{
}
UnoObjectWrapperRemoteOpt::~UnoObjectWrapperRemoteOpt()
{
}

// UnoConversionUtilities
Reference< XInterface > UnoObjectWrapperRemoteOpt::createUnoWrapperInstance()
{
    Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
                                                 m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
    return Reference<XInterface>( xWeak, UNO_QUERY);
}

COM_DECLSPEC_NOTHROW STDMETHODIMP  UnoObjectWrapperRemoteOpt::GetIDsOfNames ( REFIID /*riid*/, LPOLESTR * rgszNames, UINT cNames,
                                LCID /*lcid*/, DISPID * rgdispid )
{
    MutexGuard guard( getBridgeMutex());

    if( ! rgdispid)
        return E_POINTER;
    HRESULT ret = E_UNEXPECTED;

    // _GetValueObject
    if( ! wcscmp( *rgszNames, JSCRIPT_VALUE_FUNC))
    {
        *rgdispid= DISPID_JSCRIPT_VALUE_FUNC;
        return S_OK;
    }
    else if( ! wcscmp( *rgszNames, GET_STRUCT_FUNC))
    {
        *rgdispid= DISPID_GET_STRUCT_FUNC;
        return S_OK;
    }

    if (m_xInvocation.is() && (cNames > 0))
    {
        OUString name(o3tl::toU(rgszNames[0]));
        // has this name been determined as "bad"
        BadNameMap::iterator badIter= m_badNameMap.find( name);
        if( badIter == m_badNameMap.end() )
        {
            // name has not been bad before( member exists
            typedef NameToIdMap::iterator ITnames;
            std::pair< ITnames, bool > pair_id= m_nameToDispIdMap.emplace(name, m_currentId++);
            // new ID inserted ?
            if( pair_id.second )
            {// yes, now create MemberInfo and ad to IdToMemberInfoMap
                MemberInfo d(0, name);
                m_idToMemberInfoMap.emplace(m_currentId - 1, d);
            }

            *rgdispid = pair_id.first->second;
            ret = S_OK;
        }
        else
            ret= DISP_E_UNKNOWNNAME;
    }
    return ret;
}

COM_DECLSPEC_NOTHROW STDMETHODIMP  UnoObjectWrapperRemoteOpt::Invoke ( DISPID dispidMember, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags,
                         DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
                         UINT * puArgErr )
{
    comphelper::Automation::AutomationInvokedZone aAutomationActive;

    HRESULT ret = S_OK;
    try
    {
        bool bHandled= false;
        ret= InvokeGeneral( dispidMember,  wFlags, pdispparams, pvarResult,  pexcepinfo,
                            puArgErr, bHandled);
        if( bHandled)
            return ret;

        if ( dispidMember > 0  && m_xInvocation.is())
        {

            IdToMemberInfoMap::iterator it_MemberInfo= m_idToMemberInfoMap.find( dispidMember);
            if( it_MemberInfo != m_idToMemberInfoMap.end() )
            {
                MemberInfo& info= it_MemberInfo->second;

                Sequence<Any> params; // holds converted any s
                if( ! info.flags )
                { // DISPID called for the first time
                    if( wFlags == DISPATCH_METHOD )
                    {
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );

                        if( FAILED( ret= doInvoke( pdispparams, pvarResult,
                                                   pexcepinfo, puArgErr, info.name, params))
                            && ret == DISP_E_MEMBERNOTFOUND)
                        {
                            // try to get the exact name
                            OUString exactName;
                            if (m_xExactName.is())
                            {
                                exactName = m_xExactName->getExactName( info.name);
                                // invoke again
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
                                                                  pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                        }
                        if( SUCCEEDED( ret ) )
                            info.flags= DISPATCH_METHOD;
                    }
                    else if( wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_PROPERTYPUTREF)
                    {
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
                        if( FAILED( ret= doSetProperty( pdispparams, pvarResult,
                                                        pexcepinfo, puArgErr, info.name, params))
                            && ret == DISP_E_MEMBERNOTFOUND)
                        {
                            // try to get the exact name
                            OUString exactName;
                            if (m_xExactName.is())
                            {
                                exactName = m_xExactName->getExactName( info.name);
                                // invoke again
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
                                                                       pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                        }
                        if( SUCCEEDED( ret ) )
                            info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET;
                    }
                    else if( wFlags == DISPATCH_PROPERTYGET)
                    {
                        if( FAILED( ret= doGetProperty( pdispparams, pvarResult,
                                                        pexcepinfo, info.name))
                            && ret == DISP_E_MEMBERNOTFOUND)
                        {
                            // try to get the exact name
                            OUString exactName;
                            if (m_xExactName.is())
                            {
                                exactName = m_xExactName->getExactName( info.name);
                                // invoke again
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doGetProperty( pdispparams, pvarResult,
                                                                       pexcepinfo, exactName)))
                                        info.name= exactName;
                                }
                            }
                        }
                        if( SUCCEEDED( ret ) )
                            info.flags= DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT;
                    }
                    else if( wFlags & DISPATCH_METHOD &&
                             (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF))
                    {

                        OUString exactName;
                        // convert params for DISPATCH_METHOD or DISPATCH_PROPERTYPUT
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
                        // try first as method
                        if( FAILED( ret= doInvoke( pdispparams, pvarResult,
                                                   pexcepinfo, puArgErr, info.name, params))
                            && ret == DISP_E_MEMBERNOTFOUND)
                        {
                            // try to get the exact name
                            if (m_xExactName.is())
                            {
                                exactName = m_xExactName->getExactName( info.name);
                                // invoke again
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
                                                                  pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                        }
                        if( SUCCEEDED( ret ) )
                            info.flags= DISPATCH_METHOD;

                        // try as property
                        if( FAILED( ret) && pdispparams->cArgs == 1)
                        {
                            if( FAILED( ret= doSetProperty( pdispparams, pvarResult,
                                                            pexcepinfo, puArgErr, info.name, params))
                                && ret == DISP_E_MEMBERNOTFOUND)
                            {
                                // try to get the exact name
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
                                                                       pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                            if( SUCCEEDED( ret ) )
                                info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET;
                        }
                    }
                    else if( wFlags & DISPATCH_METHOD && wFlags & DISPATCH_PROPERTYGET)
                    {
                        OUString exactName;
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );

                        if( FAILED( ret= doInvoke( pdispparams, pvarResult,
                                                   pexcepinfo, puArgErr, info.name, params))
                            && ret == DISP_E_MEMBERNOTFOUND)
                        {
                            // try to get the exact name
                            if (m_xExactName.is())
                            {
                                exactName = m_xExactName->getExactName( info.name);
                                // invoke again
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
                                                                  pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                        }
                        if( SUCCEEDED( ret ) )
                            info.flags= DISPATCH_METHOD;

                        // try as property
                        if( FAILED( ret) && pdispparams->cArgs == 1)
                        {
                            if( FAILED( ret= doGetProperty( pdispparams, pvarResult,
                                                            pexcepinfo, info.name))
                                && ret == DISP_E_MEMBERNOTFOUND)
                            {
                                if( !exactName.isEmpty() )
                                {
                                    if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
                                                                       pexcepinfo, puArgErr, exactName, params)))
                                        info.name= exactName;
                                }
                            }
                            if( SUCCEEDED( ret ) )
                                info.flags= DISPATCH_PROPERTYGET;
                        }
                    }

                    // update information about this member
                    if( ret == DISP_E_MEMBERNOTFOUND)
                    {
                        // Remember the name as not existing
                        // and remove the MemberInfo
                        m_badNameMap[info.name]= false;
                        m_idToMemberInfoMap.erase( it_MemberInfo);
                    }
                } // if( ! info.flags )
                else // IdToMemberInfoMap contains a MemberInfo
                {
                    if( wFlags & DISPATCH_METHOD && info.flags == DISPATCH_METHOD)
                    {
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
                        ret= doInvoke( pdispparams, pvarResult,
                                       pexcepinfo, puArgErr, info.name, params);
                    }
                    else if( (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF )  &&
                             info.flags & DISPATCH_PROPERTYPUT)
                    {
                        convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
                        ret= doSetProperty( pdispparams, pvarResult,
                                            pexcepinfo, puArgErr, info.name, params);
                    }
                    else if( (wFlags & DISPATCH_PROPERTYGET) && ( info.flags & DISPATCH_PROPERTYGET))
                    {
                        ret= doGetProperty( pdispparams, pvarResult,
                                            pexcepinfo, info.name);
                    }
                    else
                    {
                        ret= DISP_E_MEMBERNOTFOUND;
                    }
                }
            }//     if( it_MemberInfo != m_idToMemberInfoMap.end() )
            else
                ret= DISP_E_MEMBERNOTFOUND;
        }
    }
    catch(const BridgeRuntimeError& e)
    {
        writeExcepinfo(pexcepinfo, e.message);
        ret = DISP_E_EXCEPTION;
    }
    catch(const Exception& e)
    {
        OUString message= "UnoObjectWrapperRemoteOpt::Invoke : \n" +
            e.Message;
        writeExcepinfo(pexcepinfo, message);
        ret = DISP_E_EXCEPTION;
    }
    catch(...)
    {
        writeExcepinfo(pexcepinfo, "UnoObjectWrapperRemoteOpt::Invoke : \nUnexpected exception");
        ret = DISP_E_EXCEPTION;
    }

    return ret;
}

HRESULT UnoObjectWrapperRemoteOpt::methodInvoke( DISPID /*dispidMember*/, DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/,
                              EXCEPINFO * /*pexcepinfo*/, unsigned int * /*puArgErr*/, Sequence<Any> const &)
{
    return S_OK;
}

// The returned HRESULT is only appropriate for IDispatch::Invoke
static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr)
{
    HRESULT ret;
    bool bWriteIndex= true;

    switch ( e.Reason)
    {
        case FailReason::OUT_OF_RANGE:
            ret = DISP_E_OVERFLOW;
            break;
        case FailReason::IS_NOT_NUMBER:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::IS_NOT_ENUM:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::IS_NOT_BOOL:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::NO_SUCH_INTERFACE:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::SOURCE_IS_NO_DERIVED_TYPE:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::TYPE_NOT_SUPPORTED:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::INVALID:
            ret = DISP_E_TYPEMISMATCH;
            break;
        case FailReason::NO_DEFAULT_AVAILABLE:
            ret = DISP_E_BADPARAMCOUNT;
            break;
        case FailReason::UNKNOWN:
            ret = E_UNEXPECTED;
            break;
        default:
            ret = E_UNEXPECTED;
            bWriteIndex= false;
            break;
    }

    if( bWriteIndex &&  puArgErr != nullptr)
        *puArgErr = e.ArgumentIndex;
    return ret;
}

// The function maps the TypeClass of the any to VARTYPE: If
// the Any contains STRUCT or INTERFACE then the return value
// is VT_DISPATCH. The function is used from o2u_createUnoObjectWrapper
// and the result is put into the constructor of the uno - wrapper
// object. If a client asks the object for DISPID_VALUE and this
// function returned VT_DISPATCH then the IDispatch of the same
// object is being returned.
// See InterfaceOleWrapper::Invoke, InterfaceOleWrapper::m_defaultValueType
VARTYPE getVarType( const Any& value)
{
    VARTYPE ret= VT_EMPTY;

    switch ( value.getValueTypeClass())
    {
    case TypeClass_STRUCT: ret= VT_DISPATCH; break;
    case TypeClass_INTERFACE: ret= VT_DISPATCH; break;
    default: break;
    }
    return ret;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
