Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/extensions/source/ole/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 124 kB image not shown  

Quelle  unoobjw.cxx   Sprache: C

 
/* -*- 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:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=99 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.