[Libreoffice-commits] core.git: extensions/source oovbaapi/ooo oovbaapi/UnoApi_oovbaapi.mk sw/source

Tor Lillqvist tml at collabora.com
Wed May 30 21:55:33 UTC 2018


 extensions/source/ole/servprov.cxx             |    8 
 extensions/source/ole/unoobjw.cxx              | 1131 +++++++++++++++++++++----
 extensions/source/ole/unoobjw.hxx              |   26 
 oovbaapi/UnoApi_oovbaapi.mk                    |    7 
 oovbaapi/ooo/vba/TypeAndIID.idl                |   30 
 oovbaapi/ooo/vba/XConnectable.idl              |   38 
 oovbaapi/ooo/vba/XConnectionPoint.idl          |   34 
 oovbaapi/ooo/vba/XInterfaceWithIID.idl         |   34 
 oovbaapi/ooo/vba/XSink.idl                     |   36 
 oovbaapi/ooo/vba/XSinkCaller.idl               |   29 
 oovbaapi/ooo/vba/word/XApplication.idl         |    2 
 oovbaapi/ooo/vba/word/XApplicationOutgoing.idl |   36 
 sw/source/ui/vba/vbaapplication.cxx            |  103 ++
 sw/source/ui/vba/vbaapplication.hxx            |   20 
 14 files changed, 1366 insertions(+), 168 deletions(-)

New commits:
commit f7e0297b01f739e17f2f9517bf3d89baaee654ab
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Mar 16 16:39:37 2018 +0200

    Work in progress related to invoking events in Automation clients
    
    XConnectable interfaces need a second IID, for the interface "itself",
    not the coclass. (I am sure there is some catchy short term for that,
    I just can't find it right now.)
    
    Allow several simultaneous sinks for a SwVbaApplication. Not sure in
    what case such would be needed, but you never know about 3rd-party
    client code, and it's trivial to handle anyway, so why not.
    
    Lots of FIXMEs still. There is likely also a lot of leaks. But at
    least an event handler in a simple VBScript script does get invoked.
    
    Note that the changed and added code in extensions/source/ole is
    totally unaware of what outgoing ("event") interfaces Writer or Calc
    implements, it is all handled generically through the UNO interfaces I
    added recently.
    
    One particular thing that needs doing is to actually make Writer (and
    Calc) raise this kind of events when necessary. The current code to
    invoke events handlers in StarBasic (including StarBasic code running
    in "VBA" compaibility) is very much tied to having StarBasic running
    (not surprisingly), which of course is not at all the case when it is
    an Automation client that is manipulating a Writer or Calc instance
    and wants events.
    
    There is demonstration-only code in SwVbaApplication::Documents() to
    raise the "Quit" event. (I would have put that in the SwVbaApplication
    destructor but that doesn't seem to get called.) That should of course
    go away once we invoke other relevant events in appropriate places.
    And the "Quit" event needs to be invoked when the application is
    quitting.
    
    The whole callback mechanism with IConnectionPoint etc is still partly
    a mystery to me. It is entirely possible that even if this now works
    for a simple VBScript client, it won't work for (for instance) a VB6
    client that might exercise the APIs of the COM interfaces we provide
    in a different way.
    
    Add XSinkCaller, for something that perhaps calls one or several
    XSinks.
    
    Change-Id: Ica03344010e374542f4aceff5ec032c78579f937
    Reviewed-on: https://gerrit.libreoffice.org/55093
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Tor Lillqvist <tml at collabora.com>

diff --git a/extensions/source/ole/servprov.cxx b/extensions/source/ole/servprov.cxx
index 220ced0bc2fd..f316e86a9f5c 100644
--- a/extensions/source/ole/servprov.cxx
+++ b/extensions/source/ole/servprov.cxx
@@ -24,7 +24,9 @@
 #include "servprov.hxx"
 #include "unoobjw.hxx"
 #include "oleobjw.hxx"
+
 #include <com/sun/star/script/CannotConvertException.hpp>
+#include <comphelper/windowsdebugoutput.hxx>
 #include <cppuhelper/queryinterface.hxx>
 #include <cppuhelper/supportsservice.hxx>
 #include <o3tl/any.hxx>
@@ -44,7 +46,8 @@ using namespace com::sun::star::bridge::ModelDependent;
 // {82154420-0FBF-11d4-8313-005004526AB4}
 DEFINE_GUID(OID_ServiceManager, 0x82154420, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
 
-// FIXME: This GUID is just the above with the initial part bumped by one. Is that good enough?
+// FIXME: This GUID is just the above OID_ServiceManager with the
+// initial part bumped by one. Is that good enough?
 // {82154421-0FBF-11d4-8313-005004526AB4}
 DEFINE_GUID(OID_LibreOfficeWriterApplication, 0x82154421, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
 
@@ -129,6 +132,8 @@ STDMETHODIMP OneInstanceOleWrapper::CreateInstance(IUnknown FAR* punkOuter,
                                                    REFIID riid,
                                                    void FAR* FAR* ppv)
 {
+    SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::CreateInstance(" << riid << ")");
+
     HRESULT ret = ResultFromScode(E_UNEXPECTED);
     punkOuter = nullptr;
 
@@ -150,6 +155,7 @@ STDMETHODIMP OneInstanceOleWrapper::CreateInstance(IUnknown FAR* punkOuter,
 
             if ((pVariant->vt == VT_UNKNOWN) || (pVariant->vt == VT_DISPATCH))
             {
+                SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::Createbridge: punkVal=" << pVariant->punkVal);
                 ret = pVariant->punkVal->QueryInterface(riid, ppv);
             }
 
diff --git a/extensions/source/ole/unoobjw.cxx b/extensions/source/ole/unoobjw.cxx
index 8e499b8b2dd2..b7aa0fa48cd3 100644
--- a/extensions/source/ole/unoobjw.cxx
+++ b/extensions/source/ole/unoobjw.cxx
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
 /*
  * This file is part of the LibreOffice project.
  *
@@ -17,6 +17,11 @@
  *   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/
+
 // See https://blogs.msdn.microsoft.com/vcblog/2017/12/08/c17-feature-removals-and-deprecations/
 #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
 
@@ -50,12 +55,16 @@
 #include <com/sun/star/script/XInvocation2.hpp>
 #include <com/sun/star/script/MemberType.hpp>
 #include <com/sun/star/reflection/XIdlReflection.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/processfactory.hxx>
 #include <comphelper/profilezone.hxx>
 #include <comphelper/windowsdebugoutput.hxx>
+#include <comphelper/windowserrorstring.hxx>
 #include <o3tl/char16_t2wchar_t.hxx>
 
 #include "comifaces.hxx"
@@ -81,6 +90,73 @@ static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource);
 static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource);
 static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr);
 
+static std::string DumpTypeInfo(ITypeInfo *pTypeInfo, int indentLevel)
+{
+    std::ostringstream os;
+    os << std::string(indentLevel, ' ') << "ITypeInfo@" << std::hex << (void*) pTypeInfo;
+
+    BSTR sName;
+    if (SUCCEEDED(pTypeInfo->GetDocumentation(MEMBERID_NIL, &sName, NULL, NULL, NULL)))
+    {
+        os << ":" << std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(sName);
+    }
+
+    return os.str();
+}
+
+static std::string DumpDispatch(IDispatch *pDispatch, int indentLevel)
+{
+    std::ostringstream os;
+    os << std::string(indentLevel, ' ') << "IDispatch@" << (void*) pDispatch;
+#if 0 // Doesn't work anyway, see comment "Sadly" below
+    // We "know" that we call this on the IUnknown passed to IConnectionPoint::Advise(), so check
+    // the event names we "know" are used in my Events.vbs test script. I.e. this is debug code very
+    // specific to the author's arbitrary transient development environment.
+    LPOLESTR vNames[] = {
+        L"DocumentOpen",
+        L"DocumentChange",
+        L"DocumentBeforeClose",
+        L"Foobar"
+        L"Quit",
+        L"WindowActivate"
+    };
+    for (int i = 0; i < SAL_N_ELEMENTS(vNames); i++)
+    {
+        DISPID nDispId;
+        HRESULT hr;
+        // Sadly it turns out that the IUnknown passed to IConnectionPoint::Advise does have an
+        // IDispatch, but its GetIDsOfNames() returns E_NOTIMPL?! How is the COM object supposed to
+        // be able to invoke events in the outgoing interface when it can't look them up by name?
+        // Need to google harder for informative articles from the early 2000s.
+        hr = pDispatch->GetIDsOfNames(IID_NULL, vNames+i, 1, LOCALE_USER_DEFAULT, &nDispId);
+        if (SUCCEEDED(hr))
+        {
+            os << "\n" << std::string(indentLevel+2, ' ')
+               << std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(vNames[i])
+               << ": " << nDispId;
+        }
+        else if (hr == DISP_E_UNKNOWNNAME)
+        {
+            os << "\n" << std::string(indentLevel+2, ' ')
+               << std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(vNames[i])
+               << ": unknown";
+        }
+    }
+#endif
+
+    UINT nTypeInfoCount = 0;
+    if (SUCCEEDED(pDispatch->GetTypeInfoCount(&nTypeInfoCount)) && nTypeInfoCount == 1)
+    {
+        ITypeInfo *pTypeInfo = NULL;
+        if (SUCCEEDED(pDispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &pTypeInfo)) && pTypeInfo != NULL)
+        {
+            os << "\n" << DumpTypeInfo(pTypeInfo, indentLevel);
+        }
+    }
+
+    return os.str();
+}
+
 /* Does not throw any exceptions.
    Param pInfo can be NULL.
  */
@@ -123,16 +199,35 @@ STDMETHODIMP InterfaceOleWrapper::QueryInterface(REFIID riid, LPVOID FAR * ppv)
     {
         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) &&
+             m_sImplementationName == "SwVbaApplication")
+    {
+        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;
@@ -200,229 +295,678 @@ class CXTypeInfo : public ITypeInfo,
                    public CComObjectRoot
 {
 public:
+    enum class Kind { COCLASS, MAIN, OUTGOING };
+
     BEGIN_COM_MAP(CXTypeInfo)
         COM_INTERFACE_ENTRY(ITypeInfo)
     END_COM_MAP()
 
     DECLARE_NOT_AGGREGATABLE(CXTypeInfo)
 
-    void Init(InterfaceOleWrapper* pInterfaceOleWrapper)
+    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);
+    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:
+    BEGIN_COM_MAP(CXTypeLib)
+        COM_INTERFACE_ENTRY(ITypeLib)
+    END_COM_MAP()
+
+    DECLARE_NOT_AGGREGATABLE(CXTypeLib)
+
+    void Init(Reference<XInterface> xOrigin,
+              const OUString& sImplementationName,
+              Reference<XMultiServiceFactory> xMSF)
     {
-        SAL_INFO("extensions.olebridge", "CXTypeInfo::Init() this=" << this << " for " << pInterfaceOleWrapper->getImplementationName());
-        mpInterfaceOleWrapper = pInterfaceOleWrapper;
+        SAL_INFO("extensions.olebridge", "CXTypeLib::Init() this=" << this << " for " << sImplementationName);
+        mxOrigin = xOrigin;
+        msImplementationName = sImplementationName;
+        mxMSF = xMSF;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetTypeAttr(TYPEATTR **ppTypeAttr) override
+    virtual UINT STDMETHODCALLTYPE GetTypeInfoCount() override
     {
-        (void) ppTypeAttr;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetTypeAttr: NOTIMPL");
-        return E_NOTIMPL;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetTypeInfoCount()");
+        return 1;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **ppTComp) override
+    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT index,
+                                                  ITypeInfo **ppTInfo) override
     {
-        (void) ppTComp;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetTypeComp: NOTIMPL");
+        (void) index;
+        (void) ppTInfo;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetTypeInfo: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetFuncDesc(UINT index,
-                                                  FUNCDESC **ppFuncDesc) override
+    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoType(UINT index,
+                                                      TYPEKIND *pTKind) override
     {
         (void) index;
-        (void) ppFuncDesc;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetFuncDesc: NOTIMPL");
+        (void) pTKind;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetTypeInfoType: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetVarDesc(UINT index,
-                                                 VARDESC **ppVarDesc) override
+    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(REFGUID guid,
+                                                        ITypeInfo **ppTInfo) override
     {
-        (void) index;
-        (void) ppVarDesc;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetVarDesc: NOTIMPL");
+        SAL_INFO("extensions.olebridge", "CXTypeLib@" << this << "::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((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 **ppTLibAttr) override
+    {
+        (void) ppTLibAttr;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetLibAttr: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetNames(MEMBERID memid,
-                                               BSTR *rgBstrNames,
-                                               UINT cMaxNames,
-                                               UINT *pcNames) override
+    virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **ppTComp) override
     {
-        (void) memid;
-        (void) rgBstrNames;
-        (void) cMaxNames;
-        (void) pcNames;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetNames: NOTIMPL");
+        (void) ppTComp;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetTypeComp: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetRefTypeOfImplType(UINT index,
-                                                           HREFTYPE *pRefType) override
+    virtual HRESULT STDMETHODCALLTYPE GetDocumentation(INT index,
+                                                       BSTR *pBstrName,
+                                                       BSTR *pBstrDocString,
+                                                       DWORD *pdwHelpContext,
+                                                       BSTR *pBstrHelpFile) override
     {
         (void) index;
-        (void) pRefType;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetRefTypeOfImplType: NOTIMPL");
+        (void) pBstrName;
+        (void) pBstrDocString;
+        (void) pdwHelpContext;
+        (void) pBstrHelpFile;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::GetDocumentation: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetImplTypeFlags(UINT index,
-                                                       INT *pImplTypeFlags) override
+    virtual HRESULT STDMETHODCALLTYPE IsName(LPOLESTR szNameBuf,
+                                             ULONG lHashVal,
+                                             BOOL *pfName) override
     {
-        (void) index;
-        (void) pImplTypeFlags;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetImplTypeFlags: NOTIMPL");
+        (void) szNameBuf;
+        (void) lHashVal;
+        (void) pfName;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::IsName: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(LPOLESTR *rgszNames,
-                                                    UINT cNames,
-                                                    MEMBERID *pMemId) override
+    virtual HRESULT STDMETHODCALLTYPE FindName(LPOLESTR szNameBuf,
+                                               ULONG lHashVal,
+                                               ITypeInfo **ppTInfo,
+                                               MEMBERID *rgMemId,
+                                               USHORT *pcFound) override
     {
-        (void) rgszNames;
-        (void) cNames;
-        (void) pMemId;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetIDsOfNames: NOTIMPL");
+        (void) szNameBuf;
+        (void) lHashVal;
+        (void) ppTInfo;
+        (void) rgMemId;
+        (void) pcFound;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::FindName: NOTIMPL");
         return E_NOTIMPL;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE Invoke(PVOID pvInstance,
+    virtual void STDMETHODCALLTYPE ReleaseTLibAttr(TLIBATTR *pTLibAttr) override
+    {
+        (void) pTLibAttr;
+        SAL_WARN("extensions.olebridge", "CXTypeLib@" << this << "::ReleaseTLibAttr: 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", "CXTypeInfo::InitForCoclass() this=" << this << " for " << rIID << " (" << sImplementationName << ")");
+    meKind = Kind::COCLASS;
+    mxOrigin = xOrigin;
+    msImplementationName = sImplementationName;
+    maIID = rIID;
+    mxMSF = xMSF;
+}
+
+void CXTypeInfo::InitForClassItself(Reference<XInterface> xOrigin,
+                                    const OUString& sImplementationName,
+                                    const IID& rIID)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo::InitForClassItself() this=" << this << " for " << rIID << " (" << sImplementationName << ")");
+    meKind = Kind::MAIN;
+    mxOrigin = xOrigin;
+    msImplementationName = sImplementationName;
+    maIID = rIID;
+}
+
+void CXTypeInfo::InitForOutgoing(Reference<XInterface> xOrigin,
+                                 const OUString& sInterfaceName,
+                                 const IID& rIID,
+                                 Reference<XMultiServiceFactory> xMSF,
+                                 Type aType)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo::InitForOutgoing() this=" << this << " for " << rIID << " (" << sInterfaceName << ")");
+    meKind = Kind::OUTGOING;
+    mxOrigin = xOrigin;
+    msInterfaceName = sInterfaceName;
+    maIID = rIID;
+    mxMSF = xMSF;
+    maType = aType;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeAttr(TYPEATTR **ppTypeAttr)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::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", "CXTypeInfo@" << this << "::GetTypeAttr(): " << pTypeAttr);
+
+    *ppTypeAttr = pTypeAttr;
+
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeComp(ITypeComp **ppTComp)
+{
+    (void) ppTComp;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetTypeComp: NOTIMPL");
+    return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetFuncDesc(UINT index,
+                                                  FUNCDESC **ppFuncDesc)
+{
+    (void) index;
+    (void) ppFuncDesc;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetFuncDesc: NOTIMPL");
+    return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetVarDesc(UINT index,
+                                                 VARDESC **ppVarDesc)
+{
+    (void) index;
+    (void) ppVarDesc;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetVarDesc: NOTIMPL");
+    return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetNames(MEMBERID memid,
+                                               BSTR *rgBstrNames,
+                                               UINT cMaxNames,
+                                               UINT *pcNames)
+{
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::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;
+
+    rgBstrNames[0] = SysAllocString((LPOLESTR) aMethods[memid + 2]->getName().pData->buffer);
+    *pcNames = 1;
+
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeOfImplType(UINT index,
+                                                           HREFTYPE *pRefType)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::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)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::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 *rgszNames,
+                                                    UINT cNames,
+                                                    MEMBERID *pMemId)
+{
+    (void) rgszNames;
+    (void) cNames;
+    (void) pMemId;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetIDsOfNames: NOTIMPL");
+    return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::Invoke(PVOID pvInstance,
                                              MEMBERID memid,
                                              WORD wFlags,
                                              DISPPARAMS *pDispParams,
                                              VARIANT *pVarResult,
                                              EXCEPINFO *pExcepInfo,
-                                             UINT *puArgErr) override
-    {
-        (void) pvInstance;
-        (void) memid;
-        (void) wFlags;
-        (void) pDispParams;
-        (void) pVarResult;
-        (void) pExcepInfo;
-        (void) puArgErr;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::Invoke: NOTIMPL");
-        return E_NOTIMPL;
-    }
+                                             UINT *puArgErr)
+{
+    (void) pvInstance;
+    (void) memid;
+    (void) wFlags;
+    (void) pDispParams;
+    (void) pVarResult;
+    (void) pExcepInfo;
+    (void) puArgErr;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::Invoke: NOTIMPL");
+    return E_NOTIMPL;
+}
 
-    virtual HRESULT STDMETHODCALLTYPE GetDocumentation(MEMBERID memid,
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDocumentation(MEMBERID memid,
                                                        BSTR *pBstrName,
                                                        BSTR *pBstrDocString,
                                                        DWORD *pdwHelpContext,
-                                                       BSTR *pBstrHelpFile) override
+                                                       BSTR *pBstrHelpFile)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::GetDocumentation(" << memid << ")");
+    if (pBstrName)
     {
-        SAL_INFO("extensions.olebridge", "CXTypeInfo::GetDocumentation(" << memid << ")");
-        if (pBstrName)
+        if (memid == MEMBERID_NIL)
         {
-            if (memid == MEMBERID_NIL)
-            {
-                *pBstrName = SysAllocString(o3tl::toW(mpInterfaceOleWrapper->getImplementationName().getStr()));
-            }
-            else if (memid == DISPID_VALUE)
-            {
-                // MEMBERIDs are the same as DISPIDs, apparently?
-                *pBstrName = SysAllocString(L"Value");
-            }
-            else
-            {
-                *pBstrName = SysAllocString(L"Unknown");
-            }
+            *pBstrName = SysAllocString(o3tl::toW(msImplementationName.getStr()));
+        }
+        else if (memid == DISPID_VALUE)
+        {
+            // MEMBERIDs are the same as DISPIDs, apparently?
+            *pBstrName = SysAllocString(L"Value");
+        }
+        else
+        {
+            *pBstrName = SysAllocString(L"Unknown");
         }
-        if (pBstrDocString)
-            *pBstrDocString = SysAllocString(L"");;
-        if (pdwHelpContext)
-            *pdwHelpContext = 0;
-        if (pBstrHelpFile)
-            *pBstrHelpFile = NULL;
-
-        return S_OK;
     }
+    if (pBstrDocString)
+        *pBstrDocString = SysAllocString(L"");;
+    if (pdwHelpContext)
+        *pdwHelpContext = 0;
+    if (pBstrHelpFile)
+        *pBstrHelpFile = NULL;
 
-    virtual HRESULT STDMETHODCALLTYPE GetDllEntry(MEMBERID memid,
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDllEntry(MEMBERID memid,
                                                   INVOKEKIND invKind,
                                                   BSTR *pBstrDllName,
                                                   BSTR *pBstrName,
-                                                  WORD *pwOrdinal) override
-    {
-        (void) memid;
-        (void) invKind;
-        (void) pBstrDllName;
-        (void) pBstrName;
-        (void) pwOrdinal;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetDllEntry: NOTIMPL");
+                                                  WORD *pwOrdinal)
+{
+    (void) memid;
+    (void) invKind;
+    (void) pBstrDllName;
+    (void) pBstrName;
+    (void) pwOrdinal;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetDllEntry: NOTIMPL");
+    return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeInfo(HREFTYPE hRefType,
+                                                     ITypeInfo **ppTInfo)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::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;
-    }
 
-    virtual HRESULT STDMETHODCALLTYPE GetRefTypeInfo(HREFTYPE hRefType,
-                                                     ITypeInfo **ppTInfo) override
-    {
-        (void) hRefType;
-        (void) ppTInfo;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetRefTypeInfo: NOTIMPL");
+    ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();
+
+    IID aIID;
+    if (!SUCCEEDED(IIDFromString((LPOLESTR)aTypeAndIID.IID.pData->buffer, &aIID)))
         return E_NOTIMPL;
-    }
 
-    virtual HRESULT STDMETHODCALLTYPE AddressOfMember(MEMBERID memid,
+    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 memid,
                                                       INVOKEKIND invKind,
-                                                      PVOID *ppv) override
-    {
-        (void) memid;
-        (void) invKind;
-        (void) ppv;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::AddressOfMember: NOTIMPL");
-        return E_NOTIMPL;
-    }
+                                                      PVOID *ppv)
+{
+    (void) memid;
+    (void) invKind;
+    (void) ppv;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::AddressOfMember: NOTIMPL");
+    return E_NOTIMPL;
+}
 
-    virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter,
+HRESULT STDMETHODCALLTYPE CXTypeInfo::CreateInstance(IUnknown *pUnkOuter,
                                                      REFIID riid,
-                                                     PVOID *ppvObj) override
-    {
-        (void) pUnkOuter;
-        (void) riid;
-        (void) ppvObj;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::CreateInstance: NOTIMPL");
-        return E_NOTIMPL;
-    }
+                                                     PVOID *ppvObj)
+{
+    (void) pUnkOuter;
+    (void) riid;
+    (void) ppvObj;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::CreateInstance: NOTIMPL");
+    return E_NOTIMPL;
+}
 
-    virtual HRESULT STDMETHODCALLTYPE GetMops(MEMBERID memid,
-                                              BSTR *pBstrMops) override
-    {
-        (void) memid;
-        (void) pBstrMops;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetMops: NOTIMPL");
-        return E_NOTIMPL;
-    }
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetMops(MEMBERID memid,
+                                              BSTR *pBstrMops)
+{
+    (void) memid;
+    (void) pBstrMops;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::GetMops: NOTIMPL");
+    return E_NOTIMPL;
+}
 
-    virtual HRESULT STDMETHODCALLTYPE GetContainingTypeLib(ITypeLib **ppTLib,
-                                                           UINT *pIndex) override
-    {
-        (void) ppTLib;
-        (void) pIndex;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::GetContainingTypeLib: 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.
 
-    virtual void STDMETHODCALLTYPE ReleaseTypeAttr(TYPEATTR *pTypeAttr) override
-    {
-        (void) pTypeAttr;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::ReleaseTypeAttr: NOTIMPL");
-    }
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetContainingTypeLib(ITypeLib **ppTLib,
+                                                           UINT *pIndex)
+{
+    SAL_INFO("extensions.olebridge", "CXTypeInfo@" << this << "::GetContainingTypeLib");
 
-    virtual void STDMETHODCALLTYPE ReleaseFuncDesc(FUNCDESC *pFuncDesc) override
-    {
-        (void) pFuncDesc;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::ReleaseFuncDesc: NOTIMPL");
-    }
+    if (!ppTLib || !pIndex)
+        return E_POINTER;
 
-    virtual void STDMETHODCALLTYPE ReleaseVarDesc(VARDESC *pVarDesc) override
-    {
-        (void) pVarDesc;
-        SAL_WARN("extensions.olebridge", "CXTypeInfo::ReleaseVarDesc: NOTIMPL");
-    }
+    HRESULT ret;
 
-private:
-    InterfaceOleWrapper* mpInterfaceOleWrapper;
-};
+    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", "CXTypeInfo@" << this << "::ReleaseTypeAttr(" << pTypeAttr << ")");
+
+    delete pTypeAttr;
+}
+
+void STDMETHODCALLTYPE CXTypeInfo::ReleaseFuncDesc(FUNCDESC *pFuncDesc)
+{
+    (void) pFuncDesc;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::ReleaseFuncDesc: NOTIMPL");
+}
+
+void STDMETHODCALLTYPE CXTypeInfo::ReleaseVarDesc(VARDESC *pVarDesc)
+{
+    (void) pVarDesc;
+    SAL_WARN("extensions.olebridge", "CXTypeInfo@" << this << "::ReleaseVarDesc: NOTIMPL");
+}
 
 STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(unsigned int iTInfo, LCID, ITypeInfo ** ppTInfo)
 {
@@ -434,7 +978,16 @@ STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(unsigned int iTInfo, LCID, ITypeIn
     if (iTInfo != 0)
         return E_NOTIMPL;
 
-    HRESULT ret = S_OK;
+    Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
+    if (!xConnectable.is())
+        return E_NOTIMPL;
+
+    OUString sIID = xConnectable->GetIIDForClassItselfNotCoclass();
+    IID aIID;
+    if (!SUCCEEDED(IIDFromString((LPOLESTR)sIID.pData->buffer, &aIID)))
+        return E_NOTIMPL;
+
+    HRESULT ret;
 
     CComObject<CXTypeInfo>* pTypeInfo;
 
@@ -444,7 +997,7 @@ STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(unsigned int iTInfo, LCID, ITypeIn
 
     pTypeInfo->AddRef();
 
-    pTypeInfo->Init(this);
+    pTypeInfo->InitForClassItself(m_xOrigin, m_sImplementationName, aIID);
 
     *ppTInfo = pTypeInfo;
 
@@ -468,7 +1021,7 @@ STDMETHODIMP InterfaceOleWrapper::GetIDsOfNames(REFIID /*riid*/,
         if( ! rgdispid)
             return E_POINTER;
 
-        // FIXME: Handle the cNames > 1 case
+        // 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))
@@ -1414,6 +1967,182 @@ private:
     Reference<XEnumeration> mxEnumeration;
 };
 
+class Sink : public cppu::WeakImplHelper<ooo::vba::XSink>
+{
+public:
+    Sink(IUnknown* pUnkSink,
+         Reference<XMultiServiceFactory> xMSF,
+         ooo::vba::TypeAndIID aTypeAndIID);
+
+    // XSink
+    void SAL_CALL Call( const OUString& Method, const Sequence< Any >& Arguments ) override;
+
+private:
+    IUnknown* mpUnkSink;
+    Reference<XMultiServiceFactory> mxMSF;
+    ooo::vba::TypeAndIID maTypeAndIID;
+};
+
+Sink::Sink(IUnknown* pUnkSink,
+           Reference<XMultiServiceFactory> xMSF,
+           ooo::vba::TypeAndIID aTypeAndIID) :
+    mpUnkSink(pUnkSink),
+    mxMSF(xMSF),
+    maTypeAndIID(aTypeAndIID)
+{
+    mpUnkSink->AddRef();
+}
+
+void SAL_CALL
+Sink::Call( const OUString& Method, const Sequence< Any >& Arguments )
+{
+    SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << ", " << Arguments.getLength() << " arguments)");
+
+    IDispatch* pDispatch;
+    HRESULT nResult = mpUnkSink->QueryInterface(IID_IDispatch, (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;
+    // Skip the three XInterface methods
+    for (int i = 3; i < aMethods.getLength(); i++)
+    {
+        if (aMethods[i]->getName() == Method)
+        {
+            DISPPARAMS aDispParams;
+            aDispParams.rgvarg = NULL;
+            aDispParams.rgdispidNamedArgs = NULL;
+            aDispParams.cArgs = 0;
+            aDispParams.cNamedArgs = 0;
+            VARIANT aVarResult;
+            VariantInit(&aVarResult);
+            UINT uArgErr;
+            nResult = pDispatch->Invoke(nMemId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &aDispParams, &aVarResult, NULL, &uArgErr);
+            SAL_WARN_IF(!SUCCEEDED(nResult), "extensions.olebridge", "Call to " << Method << " failed: " << WindowsErrorStringFromHRESULT(nResult));
+            return;
+        }
+        nMemId++;
+    }
+    SAL_WARN("extensions.olebridge", "Sink::Call: Uknown method '" << Method << "'");
+}
+
+class CXConnectionPoint : public IConnectionPoint,
+                          public CComObjectRoot
+{
+public:
+    BEGIN_COM_MAP(CXConnectionPoint)
+        COM_INTERFACE_ENTRY(IConnectionPoint)
+    END_COM_MAP()
+
+    DECLARE_NOT_AGGREGATABLE(CXConnectionPoint)
+
+    void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
+              Reference<ooo::vba::XConnectionPoint>& xCP,
+              Reference<XMultiServiceFactory>& xMSF,
+              ooo::vba::TypeAndIID aTypeAndIID)
+    {
+        SAL_INFO("extensions.olebridge", "CXConnectionPoint::Init() this=" << this << " for " << pInterfaceOleWrapper->getImplementationName());
+
+        IUnknown *pUnknown;
+        if (SUCCEEDED(QueryInterface(IID_IUnknown, (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", "CXConnectionPoint@" << this << "::GetConnectionInterface(" << *pIID << "): NOTIMPL");
+
+        // FIXME: Needed?
+
+        return E_NOTIMPL;
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(IConnectionPointContainer **ppCPC) override
+    {
+        (void) ppCPC;
+        SAL_WARN("extensions.olebridge", "CXConnectionPoint@" << this << "::GetConnectionInterface: NOTIMPL");
+
+        // FIXME: Needed?
+
+        return E_NOTIMPL;
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE Advise(IUnknown *pUnkSink,
+                                             DWORD *pdwCookie) override
+    {
+        SAL_INFO("extensions.olebridge", "CXConnectionPoint@" << this << "::Advise(" << pUnkSink << ")");
+
+        if (!pdwCookie)
+            return E_POINTER;
+
+        Reference<ooo::vba::XSink> xSink(new Sink(pUnkSink, mxMSF, maTypeAndIID));
+
+        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
+    {
+        SAL_INFO("extensions.olebridge", "CXConnectionPoint@" << this << "::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
+    {
+        (void) ppEnum;
+        SAL_WARN("extensions.olebridge", "CXConnectionPoint@" << this << "::EnumConnections: NOTIMPL");
+        return E_NOTIMPL;
+    }
+
+private:
+    InterfaceOleWrapper* mpInterfaceOleWrapper;
+    std::vector<IUnknown*> mvISinks;
+    std::vector<Reference<ooo::vba::XSink>> mvXSinks;
+    std::vector<sal_uInt32> 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)
@@ -1545,7 +2274,7 @@ HRESULT InterfaceOleWrapper::InvokeGeneral( DISPID dispidMember, unsigned short
             pvarResult->vt = VT_UNKNOWN;
             pvarResult->punkVal = NULL;
 
-            ret = pEnumVar->QueryInterface(IID_IUnknown, (void**)&pvarResult->punkVal);
+            ret = pEnumVar->QueryInterface(IID_IUnknown, (void**) &pvarResult->punkVal);
             if (FAILED(ret))
             {
                 pEnumVar->Release();
@@ -1633,6 +2362,96 @@ STDMETHODIMP InterfaceOleWrapper::GetNameSpaceParent(
     return ResultFromScode(E_NOTIMPL);
 }
 
+// IProvideClassInfo
+HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::GetClassInfo (
+    /* [out] */ ITypeInfo **ppTI)
+{
+    SAL_INFO("extensions.olebridge", "InterfaceOleWrapper@" << this << "::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((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 **ppEnum)
+{
+    (void) ppEnum;
+    SAL_INFO("extensions.olebridge", "InterfaceOleWrapper@" << this << "::EnumConnectionPoints");
+    return ResultFromScode(E_NOTIMPL);
+}
+
+HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::FindConnectionPoint(
+    /* [in] */ REFIID riid,
+    /* [out] */ IConnectionPoint **ppCP)
+{
+    SAL_INFO("extensions.olebridge", "InterfaceOleWrapper@" << this << "::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((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,
diff --git a/extensions/source/ole/unoobjw.hxx b/extensions/source/ole/unoobjw.hxx
index f77d2aba10d5..2457694d4e3d 100644
--- a/extensions/source/ole/unoobjw.hxx
+++ b/extensions/source/ole/unoobjw.hxx
@@ -78,21 +78,21 @@ typedef std::unordered_map
 
 class InterfaceOleWrapper : public WeakImplHelper<XBridgeSupplier2, XInitialization>,
                             public IDispatchEx,
+                            public IProvideClassInfo,
+                            public IConnectionPointContainer,
                             public UnoConversionUtilities<InterfaceOleWrapper>,
                             public IUnoObjectWrapper
 {
 public:
-
-
     InterfaceOleWrapper(Reference<XMultiServiceFactory> const & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass);
     ~InterfaceOleWrapper() override;
 
-    /* IUnknown methods */
+    // IUnknown
     STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR * ppvObj) override;
     STDMETHOD_(ULONG, AddRef)() override;
     STDMETHOD_(ULONG, Release)() override;
 
-    /* IDispatch methods */
+    // IDispatch
     STDMETHOD( GetTypeInfoCount )( unsigned int * pctinfo ) override;
     STDMETHOD( GetTypeInfo )( unsigned int itinfo, LCID lcid, ITypeInfo ** pptinfo ) override;
     STDMETHOD( GetIDsOfNames )( REFIID riid, OLECHAR ** rgszNames, unsigned int cNames,
@@ -101,8 +101,7 @@ public:
                          DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
                          unsigned int * puArgErr ) override;
 
-    /* IDispatchEx methods */
-
+    // IDispatchEx
     virtual HRESULT STDMETHODCALLTYPE GetDispID(
         /* [in] */ BSTR bstrName,
         /* [in] */ DWORD grfdex,
@@ -141,13 +140,24 @@ public:
     virtual HRESULT STDMETHODCALLTYPE GetNameSpaceParent(
         /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk) override;
 
-    // XBridgeSupplier2 ---------------------------------------------------
+    // IProvideClassInfo
+    virtual HRESULT STDMETHODCALLTYPE GetClassInfo(
+        /* [out] */ ITypeInfo **ppTI) override;
+
+    // IConnectionPointContainer
+    virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints(
+        /* [out] */ IEnumConnectionPoints **ppEnum) override;
+    virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint(
+        /* [in] */ REFIID riid,
+        /* [out] */ IConnectionPoint **ppCP) override;
+
+    // XBridgeSupplier2
     virtual Any SAL_CALL createBridge(const Any& modelDepObject,
                                 const Sequence<sal_Int8>& ProcessId,
                                 sal_Int16 sourceModelType,
                                 sal_Int16 destModelType) override;
 
-    //XInitialization -----------------------------------------------------
+    // XInitialization
     virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
 
     // IUnoObjectWrapper
diff --git a/oovbaapi/UnoApi_oovbaapi.mk b/oovbaapi/UnoApi_oovbaapi.mk
index f977f44d0b08..39c558257153 100644
--- a/oovbaapi/UnoApi_oovbaapi.mk
+++ b/oovbaapi/UnoApi_oovbaapi.mk
@@ -44,6 +44,7 @@ $(eval $(call gb_UnoApi_add_idlfiles_noheader,oovbaapi,ooo/vba/excel,\
 
 $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba,\
     FormShowConstants \
+    TypeAndIID \
     VbAppWinStyle \
     VbCalendar \
     VbCallType \
@@ -70,6 +71,8 @@ $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba,\
 	XCommandBarControls \
 	XCommandBarPopup \
 	XCommandBars \
+	XConnectable \
+	XConnectionPoint \
 	XControlProvider \
 	XDialogBase \
 	XDialogsBase \
@@ -82,8 +85,11 @@ $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba,\
 	XFontBase \
 	XGlobalsBase \
 	XHelperInterface \
+	XInterfaceWithIID \
 	XPageSetupBase \
 	XPropValue \
+	XSink \
+	XSinkCaller \
 	XVBAToOOEventDescGen \
 	XWindowBase \
 ))
@@ -1028,6 +1034,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
 	XAddin \
 	XAddins \
 	XApplication \
+	XApplicationOutgoing \
 	XAutoTextEntries \
 	XAutoTextEntry \
 	XBookmark \
diff --git a/oovbaapi/ooo/vba/TypeAndIID.idl b/oovbaapi/ooo/vba/TypeAndIID.idl
new file mode 100644
index 000000000000..61fdc5219336
--- /dev/null
+++ b/oovbaapi/ooo/vba/TypeAndIID.idl
@@ -0,0 +1,30 @@
+/* -*- 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/.
+ */
+
+#ifndef __ooo_vba_TypeAndIID_idl__
+#define __ooo_vba_TypeAndIID_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+struct TypeAndIID
+{
+    type Type;
+    string IID;
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/XConnectable.idl b/oovbaapi/ooo/vba/XConnectable.idl
new file mode 100644
index 000000000000..d6bcfd9173ec
--- /dev/null
+++ b/oovbaapi/ooo/vba/XConnectable.idl
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __ooo_vba_XConnectable_idl__
+#define __ooo_vba_XConnectable_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+// An object that implements this interface should intend to be usable from an Automation client
+// that wants the object to do callbacks, i.e. generate "events" in the client (for instance
+// implemented in VBScript, VB6, or even C++).
+
+interface XConnectable
+{
+    interface XInterfaceWithIID;
+    // Silly name yes, but I can't find what the proper term for this thing is. This is not a
+    // published interface anyway.
+    string GetIIDForClassItselfNotCoclass();
+    TypeAndIID GetConnectionPoint();
+    XConnectionPoint FindConnectionPoint();
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/XConnectionPoint.idl b/oovbaapi/ooo/vba/XConnectionPoint.idl
new file mode 100644
index 000000000000..b3471c6445ff
--- /dev/null
+++ b/oovbaapi/ooo/vba/XConnectionPoint.idl
@@ -0,0 +1,34 @@
+/* -*- 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/.
+ */
+
+#ifndef __ooo_vba_XConnectionPoint_idl__
+#define __ooo_vba_XConnectionPoint_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+// This is quite different from css::lang::XConnectionPoint.
+
+// This is tailored to be slimmer and match the use case from Automation clients much better.
+
+interface XConnectionPoint
+{
+    unsigned long Advise([in] XSink Sink);
+    void Unadvise([in] unsigned long Cookie);
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/XInterfaceWithIID.idl b/oovbaapi/ooo/vba/XInterfaceWithIID.idl
new file mode 100644
index 000000000000..0752cced0c56
--- /dev/null
+++ b/oovbaapi/ooo/vba/XInterfaceWithIID.idl
@@ -0,0 +1,34 @@
+/* -*- 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/.
+ */
+
+#ifndef __ooo_vba_XInterfaceWithIID_idl__
+#define __ooo_vba_XInterfaceWithIID_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+// An interface that has an IID.
+
+interface XInterfaceWithIID : com::sun::star::uno::XInterface
+{
+    // The IID of the interface, in the string form with braces, as
+    // accepted by IIDFromString, for instance
+    // "{82154421-0fbf-11d4-8313-005004526ab4}"
+    [attribute, readonly] string IID;
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/XSink.idl b/oovbaapi/ooo/vba/XSink.idl
new file mode 100644
index 000000000000..110c9b6a573f
--- /dev/null
+++ b/oovbaapi/ooo/vba/XSink.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __ooo_vba_XConnectionPointSink_idl__
+#define __ooo_vba_XConnectionPointSink_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+// This is the interface LibreOffice code actually uses when doing "outgoing" calls to Automation
+// clients (for events).
+
+interface XSink
+{
+    // This will be massaged by the Automation-UNO bridge into a call into the sink provided by the
+    // Automation client.
+
+    // FIXME: Add "out" arguments, and perhaps exceptions?
+    void Call([in] string Method, [in] sequence<any> Arguments);
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/XSinkCaller.idl b/oovbaapi/ooo/vba/XSinkCaller.idl
new file mode 100644
index 000000000000..33be504fbe62
--- /dev/null
+++ b/oovbaapi/ooo/vba/XSinkCaller.idl
@@ -0,0 +1,29 @@
+/* -*- 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/.
+ */
+
+#ifndef __ooo_vba_XSinkCaller_idl__
+#define __ooo_vba_XSinkCaller_idl__
+
+module ooo {  module vba {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+interface XSinkCaller
+{
+    void CallSinks([in] string Method, [in] sequence<any> Arguments);
+};
+
+}; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XApplication.idl b/oovbaapi/ooo/vba/word/XApplication.idl
index 0d667551e4a3..de6fafc085a7 100644
--- a/oovbaapi/ooo/vba/word/XApplication.idl
+++ b/oovbaapi/ooo/vba/word/XApplication.idl
@@ -29,7 +29,7 @@ interface XWindow;
 interface XSystem;
 interface XOptions;
 interface XSelection;
-interface XApplication : com::sun::star::uno::XInterface
+interface XApplication : XConnectable
 {
     [attribute, readonly] XDocument ActiveDocument;
     [attribute, readonly] XWindow ActiveWindow;
diff --git a/oovbaapi/ooo/vba/word/XApplicationOutgoing.idl b/oovbaapi/ooo/vba/word/XApplicationOutgoing.idl
new file mode 100644
index 000000000000..11ac1da2a540
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XApplicationOutgoing.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __ooo_vba_word_XApplicationOutgoing_idl__
+#define __ooo_vba_word_XApplicationOutgoing_idl__
+
+module ooo {  module vba {  module word {
+
+// Despite being here in ooo::vba, this has nothing to do with "VBA" (Visual Basic for
+// Applications), or the VBA compatibility in StarBasic. This is related to using LibreOffice from
+// (OLE) Automation clients. It is here anyway because much of the API available to such clients
+// is identical to that offered to StarBasic code written in a VBA-like fashion.
+
+// This interface exists for technical reasons only, we don't actually have any UNO object that
+// would implemenrt (inherit from) this. We just advertise the type of this interface from Writer to
+// the Automation-UNO bridge code (in extensions/source/ole) so that it can tell the Automation
+// client what the Automation server expects and construct a corresponding COM interface that can
+// receive the callbacks. Or something like that.
+
+interface XApplicationOutgoing : XInterfaceWithIID
+{
+    void DocumentChange();
+    void Quit();
+};
+
+}; }; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbaapplication.cxx b/sw/source/ui/vba/vbaapplication.cxx
index 4ffd6766d7af..f86032a249e7 100644
--- a/sw/source/ui/vba/vbaapplication.cxx
+++ b/sw/source/ui/vba/vbaapplication.cxx
@@ -27,7 +27,9 @@
 #include "vbadocuments.hxx"
 #include "vbaaddins.hxx"
 #include "vbadialogs.hxx"
+#include <ooo/vba/XConnectionPoint.hpp>
 #include <ooo/vba/word/WdEnableCancelKey.hpp>
+#include <ooo/vba/word/XApplicationOutgoing.hpp>
 #include <basic/sbuno.hxx>
 #include <editeng/acorrcfg.hxx>
 #include "wordvbahelper.hxx"
@@ -38,12 +40,48 @@ using namespace ::ooo;
 using namespace ::ooo::vba;
 using namespace ::com::sun::star;
 
-SwVbaApplication::SwVbaApplication( uno::Reference<uno::XComponentContext >& xContext ): SwVbaApplication_BASE( xContext )
+class SwVbaApplicationOutgoingConnectionPoint : public cppu::WeakImplHelper<XConnectionPoint>
+{
+private:
+    SwVbaApplication* mpApp;
+
+public:
+    SwVbaApplicationOutgoingConnectionPoint( SwVbaApplication* pApp );
+
+    // XConnectionPoint
+    sal_uInt32 SAL_CALL Advise(const uno::Reference< XSink >& Sink ) override;
+    void SAL_CALL Unadvise( sal_uInt32 Cookie ) override;
+};
+
+SwVbaApplication::SwVbaApplication( uno::Reference<uno::XComponentContext >& xContext ):
+    SwVbaApplication_BASE( xContext )
 {
 }
 
 SwVbaApplication::~SwVbaApplication()
 {
+    // FIXME: Sadly this is not the place to do this, this dtor is never called, it seems
+    for (auto& i : mvSinks)
+    {
+        if (i.is())
+            i->Call("Quit", uno::Sequence<uno::Any>());
+    }
+}
+
+sal_uInt32
+SwVbaApplication::AddSink( const css::uno::Reference< XSink >& xSink )
+{
+    mvSinks.push_back(xSink);
+    return mvSinks.size();;
+}
+
+void
+SwVbaApplication::RemoveSink( sal_uInt32 nNumber )
+{
+    if (nNumber < 1 || nNumber > mvSinks.size())
+        return;
+
+    mvSinks[nNumber-1] = uno::Reference< XSink >();
 }
 
 OUString SAL_CALL
@@ -94,6 +132,13 @@ SwVbaApplication::getSelection()
 uno::Any SAL_CALL
 SwVbaApplication::Documents( const uno::Any& index )
 {
+    // FIXME DUMMY just to test calling this somewhere... the dtor is never called
+    for (auto& i : mvSinks)
+    {
+        if (i.is())
+            i->Call("Quit", uno::Sequence<uno::Any>());
+    }
+
     uno::Reference< XCollection > xCol( new SwVbaDocuments( this, mxContext ) );
     if ( index.hasValue() )
         return xCol->Item( index, uno::Any() );
@@ -159,12 +204,48 @@ void SAL_CALL SwVbaApplication::ShowMe()
     // No idea what we should or could do
 }
 
+// XInterfaceWithIID
+
+OUString SAL_CALL
+SwVbaApplication::getIID()
+{
+    return OUString("{82154421-0FBF-11d4-8313-005004526AB4}");
+}
+
 uno::Reference< frame::XModel >
 SwVbaApplication::getCurrentDocument()
 {
     return getCurrentWordDoc( mxContext );
 }
 
+// XConnectable
+
+OUString SAL_CALL
+SwVbaApplication::GetIIDForClassItselfNotCoclass()
+{
+    return OUString("{82154423-0FBF-11D4-8313-005004526AB4}");
+}
+
+TypeAndIID SAL_CALL
+SwVbaApplication::GetConnectionPoint()
+{
+    TypeAndIID aResult =
+        { word::XApplicationOutgoing::static_type(),
+          "{82154422-0FBF-11D4-8313-005004526AB4}"
+        };
+
+    return aResult;
+}
+
+uno::Reference<XConnectionPoint> SAL_CALL
+SwVbaApplication::FindConnectionPoint()
+{
+    uno::Reference<XConnectionPoint> xCP(new SwVbaApplicationOutgoingConnectionPoint(this));
+    return xCP;
+}
+
+// XHelperInterface
+
 OUString
 SwVbaApplication::getServiceImplName()
 {
@@ -183,4 +264,24 @@ SwVbaApplication::getServiceNames()
     return aServiceNames;
 }
 
+SwVbaApplicationOutgoingConnectionPoint::SwVbaApplicationOutgoingConnectionPoint( SwVbaApplication* pApp ) :
+    mpApp(pApp)
+{
+}
+
+// SwVbaApplicationOutgoingConnectionPoint
+
+// XConnectionPoint
+sal_uInt32 SAL_CALL
+SwVbaApplicationOutgoingConnectionPoint::Advise( const uno::Reference< XSink >& Sink )
+{
+    return mpApp->AddSink(Sink);
+}
+
+void SAL_CALL
+SwVbaApplicationOutgoingConnectionPoint::Unadvise( sal_uInt32 Cookie )
+{
+    mpApp->RemoveSink( Cookie );
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbaapplication.hxx b/sw/source/ui/vba/vbaapplication.hxx
index 161a5284d86b..73e16c5f71d1 100644
--- a/sw/source/ui/vba/vbaapplication.hxx
+++ b/sw/source/ui/vba/vbaapplication.hxx
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
 /*
  * This file is part of the LibreOffice project.
  *
@@ -19,6 +19,9 @@
 #ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAAPPLICATION_HXX
 #define INCLUDED_SW_SOURCE_UI_VBA_VBAAPPLICATION_HXX
 
+#include <vector>
+
+#include <ooo/vba/XSink.hpp>
 #include <ooo/vba/word/XApplication.hpp>
 #include <ooo/vba/word/XDocument.hpp>
 #include <ooo/vba/word/XWindow.hpp>
@@ -34,10 +37,16 @@ typedef cppu::ImplInheritanceHelper< VbaApplicationBase, ooo::vba::word::XApplic
 
 class SwVbaApplication : public SwVbaApplication_BASE
 {
+    // FIXME: We allow just one sink at a time
+    std::vector<css::uno::Reference< ooo::vba::XSink >> mvSinks;
+
 public:
     explicit SwVbaApplication( css::uno::Reference< css::uno::XComponentContext >& m_xContext );
     virtual ~SwVbaApplication() override;
 
+    sal_uInt32 AddSink( const css::uno::Reference< ooo::vba::XSink >& xSink );
+    void RemoveSink( sal_uInt32 nNumber );
+
     // XApplication
     virtual OUString SAL_CALL getName() override;
     virtual css::uno::Reference< ooo::vba::word::XSystem > SAL_CALL getSystem() override;
@@ -56,6 +65,15 @@ public:
     virtual void SAL_CALL setEnableCancelKey( sal_Int32 _enableCancelKey ) override;
     virtual float SAL_CALL CentimetersToPoints( float Centimeters ) override;
     virtual void SAL_CALL ShowMe() override;
+
+    // XInterfaceWithIID
+    virtual OUString SAL_CALL getIID() override;
+
+    // XConnectable
+    virtual OUString SAL_CALL GetIIDForClassItselfNotCoclass() override;
+    virtual ov::TypeAndIID SAL_CALL GetConnectionPoint() override;
+    virtual css::uno::Reference<ov::XConnectionPoint> SAL_CALL FindConnectionPoint() override;
+
     // XHelperInterface
     virtual OUString getServiceImplName() override;
     virtual css::uno::Sequence<OUString> getServiceNames() override;


More information about the Libreoffice-commits mailing list