Blob Blame History Raw
diff -rupN --no-dereference qtactiveqt-everywhere-src-5.15.13/src/activeqt/container/qaxbase.cpp qtactiveqt-everywhere-src-5.15.13-new/src/activeqt/container/qaxbase.cpp
--- qtactiveqt-everywhere-src-5.15.13/src/activeqt/container/qaxbase.cpp	2024-01-04 20:21:40.000000000 +0100
+++ qtactiveqt-everywhere-src-5.15.13-new/src/activeqt/container/qaxbase.cpp	2024-05-01 13:20:01.043261658 +0200
@@ -2203,6 +2203,10 @@ void MetaObjectGenerator::readClassInfo(
             const QStringList versions = controls.childGroups();
             for (const QString &version : versions) {
                 tlfile = controls.value(QLatin1Char('/') + version + QLatin1String("/0/win32/.")).toString();
+                if(tlfile.isEmpty())
+                {
+                  tlfile = controls.value(QLatin1Char('/') + version + QLatin1String("/0/win64/.")).toString();
+                }
                 if (!tlfile.isEmpty())
                     break;
             }
diff -rupN --no-dereference qtactiveqt-everywhere-src-5.15.13/src/activeqt/container/qaxbase.cpp.orig qtactiveqt-everywhere-src-5.15.13-new/src/activeqt/container/qaxbase.cpp.orig
--- qtactiveqt-everywhere-src-5.15.13/src/activeqt/container/qaxbase.cpp.orig	1970-01-01 01:00:00.000000000 +0100
+++ qtactiveqt-everywhere-src-5.15.13-new/src/activeqt/container/qaxbase.cpp.orig	2024-01-04 20:21:40.000000000 +0100
@@ -0,0 +1,4572 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the ActiveQt framework of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of The Qt Company Ltd nor the names of its
+**     contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QAX_NO_CLASSINFO
+
+#define QT_CHECK_STATE
+
+#include "qaxobject.h"
+
+#include <qfile.h>
+#include <qwidget.h>
+#include <quuid.h>
+#include <qhash.h>
+#include <qset.h>
+#include <qpair.h>
+#include <qbitarray.h>
+#include <qmetaobject.h>
+#include <qsettings.h>
+#include <qdebug.h>
+#include <QGuiApplication>
+#include <qpa/qplatformnativeinterface.h>
+
+#ifndef QT_NO_THREAD
+#   include <qmutex.h>
+#endif
+
+#include <private/qobject_p.h>
+#include <private/qmetaobject_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <private/qtools_p.h>
+
+#include <qt_windows.h>
+#include <ocidl.h>
+#include <ctype.h>
+
+#include "../shared/qaxtypes.h"
+#include "../shared/qaxutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static inline HRESULT Invoke(IDispatch *disp,
+                             DISPID dispIdMember,
+                             REFIID riid,
+                             LCID lcid,
+                             DWORD wFlags,
+                             DISPPARAMS *pDispParams,
+                             VARIANT *pVarResult,
+                             EXCEPINFO *pExcepInfo,
+                             unsigned int *puArgErr)
+{
+    if ((wFlags & DISPATCH_PROPERTYPUT) &&
+        pDispParams &&
+        pDispParams->cArgs == 1 &&
+        pDispParams->cNamedArgs == 1 &&
+        pDispParams->rgdispidNamedArgs &&
+        *pDispParams->rgdispidNamedArgs == DISPID_PROPERTYPUT &&
+        pDispParams->rgvarg) {
+        VARTYPE vt = pDispParams->rgvarg->vt;
+
+        if (vt == VT_UNKNOWN || vt == VT_DISPATCH || (vt & VT_ARRAY) || (vt & VT_BYREF)) {
+            const HRESULT hr =
+                disp->Invoke(dispIdMember, riid, lcid, (WORD(wFlags) & ~DISPATCH_PROPERTYPUT) | DISPATCH_PROPERTYPUTREF,
+                             pDispParams, pVarResult, pExcepInfo, puArgErr);
+            if (SUCCEEDED(hr))
+                return hr;
+        }
+    }
+
+    return disp->Invoke(dispIdMember, riid, lcid, WORD(wFlags), pDispParams, pVarResult, pExcepInfo, puArgErr);
+}
+
+/*
+    \internal
+    \class QAxMetaObject
+
+    \brief The QAxMetaObject class stores extended information
+*/
+struct QAxMetaObject : public QMetaObject
+{
+    QAxMetaObject()
+    {
+        d.data = nullptr;
+        d.stringdata = nullptr;
+    }
+    ~QAxMetaObject()
+    {
+        delete [] d.data;
+        delete [] reinterpret_cast<char *>(const_cast<QByteArrayData *>(d.stringdata));
+    }
+
+    int numParameter(const QByteArray &prototype);
+    QByteArray paramType(const QByteArray &signature, int index, bool *out = nullptr);
+    QByteArray propertyType(const QByteArray &propertyName);
+    void parsePrototype(const QByteArray &prototype);
+    DISPID dispIDofName(const QByteArray &name, IDispatch *disp);
+
+private:
+    friend class MetaObjectGenerator;
+    // save information about QAxEventSink connections, and connect when found in cache
+    QVector<QUuid> connectionInterfaces;
+    // DISPID -> signal name
+    QMap< QUuid, QMap<DISPID, QByteArray> > sigs;
+    // DISPID -> property changed signal name
+    QMap< QUuid, QMap<DISPID, QByteArray> > propsigs;
+    // DISPID -> property name
+    QMap< QUuid, QMap<DISPID, QByteArray> > props;
+
+    // Prototype -> member info
+    QHash<QByteArray, QByteArrayList> memberInfo;
+    QMap<QByteArray, QByteArray> realPrototype;
+
+    // DISPID cache
+    QHash<QByteArray, DISPID> dispIDs;
+};
+
+void QAxMetaObject::parsePrototype(const QByteArray &prototype)
+{
+    QByteArray realProto = realPrototype.value(prototype, prototype);
+    QByteArray parameters = realProto.mid(realProto.indexOf('(') + 1);
+    parameters.truncate(parameters.length() - 1);
+
+    if (parameters.isEmpty())
+        memberInfo.insert(prototype, {});
+    else
+        memberInfo.insert(prototype, parameters.split(','));
+}
+
+inline QByteArray QAxMetaObject::propertyType(const QByteArray &propertyName)
+{
+    return realPrototype.value(propertyName);
+}
+
+int QAxMetaObject::numParameter(const QByteArray &prototype)
+{
+    if (!memberInfo.contains(prototype))
+        parsePrototype(prototype);
+
+    return memberInfo.value(prototype).count();
+}
+
+QByteArray QAxMetaObject::paramType(const QByteArray &prototype, int index, bool *out)
+{
+    if (!memberInfo.contains(prototype))
+        parsePrototype(prototype);
+
+    if (out)
+        *out = false;
+
+    const auto plist = memberInfo.value(prototype);
+    if (index > plist.count() - 1)
+        return QByteArray();
+
+    QByteArray param(plist.at(index));
+    if (param.isEmpty())
+        return QByteArray();
+
+    bool byRef = param.endsWith('&') || param.endsWith("**");
+    if (byRef) {
+        param.truncate(param.length() - 1);
+        if (out)
+            *out = true;
+    }
+
+    return param;
+}
+
+inline DISPID QAxMetaObject::dispIDofName(const QByteArray &name, IDispatch *disp)
+{
+    DISPID dispid = dispIDs.value(name, DISPID_UNKNOWN);
+    if (dispid == DISPID_UNKNOWN) {
+        // get the Dispatch ID from the object
+        QString unicodeName = QLatin1String(name);
+        OLECHAR *names = reinterpret_cast<wchar_t *>(const_cast<ushort *>(unicodeName.utf16()));
+        disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid);
+        if (dispid != DISPID_UNKNOWN)
+            dispIDs.insert(name, dispid);
+    }
+    return dispid;
+}
+
+
+static QHash<QString, QAxMetaObject*> mo_cache;
+static QHash<QUuid, QMap<QByteArray, QList<QPair<QByteArray, int> > > > enum_cache;
+static int mo_cache_ref = 0;
+static QMutex cache_mutex;
+
+
+static const char *const type_conversion[][2] =
+{
+    { "float", "double"},
+    { "short", "int"},
+    { "char", "int"},
+    { "QList<int>", "QVariantList" },
+    { "QList<uint>", "QVariantList" },
+    { "QList<double>", "QVariantList" },
+    { "QList<bool>", "QVariantList" },
+    { "QList<QDateTime>", "QVariantList" },
+    { "QList<qlonglong>", "QVariantList" },
+    { nullptr, nullptr }
+};
+
+/*
+    \internal
+    \class QAxEventSink
+
+    \brief The QAxEventSink class implements the event sink for all
+           IConnectionPoints implemented in the COM object.
+*/
+
+class QAxEventSink : public IDispatch, public IPropertyNotifySink
+{
+    Q_DISABLE_COPY_MOVE(QAxEventSink)
+public:
+    explicit QAxEventSink(QAxBase *com) : combase(com) {}
+    virtual ~QAxEventSink()
+    {
+        Q_ASSERT(!cpoint);
+    }
+
+    QUuid connectionInterface() const
+    {
+        return ciid;
+    }
+    QMap<DISPID, QByteArray> signalMap() const
+    {
+        return sigs;
+    }
+    QMap<DISPID, QByteArray> propertyMap() const
+    {
+        return props;
+    }
+    QMap<DISPID, QByteArray> propSignalMap() const
+    {
+        return propsigs;
+    }
+
+    // add a connection
+    void advise(IConnectionPoint *cp, IID iid)
+    {
+        cpoint = cp;
+        cpoint->AddRef();
+        ciid = iid;
+        cpoint->Advise(static_cast<IUnknown *>(static_cast<IDispatch *>(this)), &cookie);
+    }
+
+    // disconnect from all connection points
+    void unadvise()
+    {
+        combase = nullptr;
+        if (cpoint) {
+            cpoint->Unadvise(cookie);
+            cpoint->Release();
+            cpoint = nullptr;
+        }
+    }
+
+    void addSignal(DISPID memid, const char *name)
+    {
+        QByteArray signalname = name;
+        int pi = signalname.indexOf('(');
+        int i = 0;
+        while (type_conversion[i][0]) {
+            int ti = pi;
+            int len = int(strlen(type_conversion[i][0]));
+            while ((ti = signalname.indexOf(type_conversion[i][0], ti)) != -1)
+                signalname.replace(ti, len, type_conversion[i][1]);
+            ++i;
+        }
+
+        sigs.insert(memid, signalname);
+        const DISPID id = propsigs.key(signalname, -1);
+        if (id != -1)
+            propsigs.remove(id);
+    }
+    void addProperty(DISPID propid, const char *name, const char *signal)
+    {
+        props.insert(propid, name);
+        propsigs.insert(propid, signal);
+    }
+
+    // IUnknown
+    unsigned long __stdcall AddRef() override
+    {
+        return InterlockedIncrement(&ref);
+    }
+    unsigned long __stdcall Release() override
+    {
+        LONG refCount = InterlockedDecrement(&ref);
+        if (!refCount)
+            delete this;
+
+        return refCount;
+    }
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject) override
+    {
+        *ppvObject = nullptr;
+        if (riid == IID_IUnknown)
+            *ppvObject = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
+        else if (riid == IID_IPropertyNotifySink)
+            *ppvObject = static_cast<IPropertyNotifySink *>(this);
+        else if (riid == IID_IDispatch || ciid == riid)
+            *ppvObject = static_cast<IDispatch *>(this);
+        else
+            return E_NOINTERFACE;
+
+        AddRef();
+        return S_OK;
+    }
+
+    // IDispatch
+    HRESULT __stdcall GetTypeInfoCount(unsigned int *) override
+    { return E_NOTIMPL; }
+    HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) override
+    { return E_NOTIMPL; }
+    HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int,
+                                    unsigned long, long *) override
+    { return E_NOTIMPL; }
+
+    HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID,
+                             WORD wFlags, DISPPARAMS *pDispParams,
+                             VARIANT *, EXCEPINFO *, UINT *) override
+    {
+        // verify input
+        if (riid != IID_NULL)
+            return DISP_E_UNKNOWNINTERFACE;
+        if (!(wFlags & DISPATCH_METHOD))
+            return DISP_E_MEMBERNOTFOUND;
+        if (!combase)
+            return E_UNEXPECTED;
+
+        QByteArray signame = sigs.value(dispIdMember);
+        if (signame.isEmpty())
+            return DISP_E_MEMBERNOTFOUND;
+
+        QObject *qobject = combase->qObject();
+        if (qobject->signalsBlocked())
+            return S_OK;
+
+        QAxMetaObject *axmeta = combase->internalMetaObject();
+        const QMetaObject *meta = combase->metaObject();
+
+        int index = -1;
+        // emit the generic signal "as is"
+        if (signalHasReceivers(qobject, "signal(QString,int,void*)")) {
+            index = meta->indexOfSignal("signal(QString,int,void*)");
+            Q_ASSERT(index != -1);
+
+            QString nameString = QLatin1String(signame);
+            void *argv[] = {nullptr, &nameString, &pDispParams->cArgs, &pDispParams->rgvarg};
+            QAxBase::qt_static_metacall(combase, QMetaObject::InvokeMetaMethod,
+                                        index - meta->methodOffset(), argv);
+        }
+
+        HRESULT hres = S_OK;
+
+        // get the signal information from the metaobject
+        index = -1;
+        if (signalHasReceivers(qobject, signame)) {
+            index = meta->indexOfSignal(signame);
+            Q_ASSERT(index != -1);
+            const QMetaMethod signal = meta->method(index);
+            Q_ASSERT(signal.methodType() == QMetaMethod::Signal);
+            Q_ASSERT(signame == signal.methodSignature());
+            // verify parameter count
+            const int pcount = axmeta->numParameter(signame);
+            const int argcount = int(pDispParams->cArgs);
+            if (pcount > argcount)
+                return DISP_E_PARAMNOTOPTIONAL;
+            if (pcount < argcount)
+                return DISP_E_BADPARAMCOUNT;
+
+            // setup parameters (no return values in signals)
+            bool ok = true;
+            void *static_argv[QAX_NUM_PARAMS + 1];
+            void *static_argv_pointer[QAX_NUM_PARAMS + 1];
+            QVariant static_varp[QAX_NUM_PARAMS + 1];
+
+            void **argv = nullptr;
+            void **argv_pointer = nullptr; // in case we need an additional level of indirection
+            QVariant *varp = nullptr;
+
+            if (pcount) {
+                if (pcount <= QAX_NUM_PARAMS) {
+                    argv = static_argv;
+                    argv_pointer = static_argv_pointer;
+                    varp = static_varp;
+                } else {
+                    argv = new void*[pcount + 1];
+                    argv_pointer = new void*[pcount + 1];
+                    varp = new QVariant[pcount + 1];
+                }
+
+                argv[0] = nullptr;
+                argv_pointer[0] = nullptr;
+            }
+
+            int p;
+            for (p = 0; p < pcount && ok; ++p) {
+                // map the VARIANT to the void*
+                QByteArray ptype = axmeta->paramType(signame, p);
+                varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype);
+                argv_pointer[p + 1] = nullptr;
+                if (varp[p + 1].isValid()) {
+                    if (varp[p + 1].type() == QVariant::UserType) {
+                        argv[p + 1] = varp[p + 1].data();
+                    } else if (ptype == "QVariant") {
+                        argv[p + 1] = varp + p + 1;
+                    } else {
+                        argv[p + 1] = const_cast<void*>(varp[p + 1].constData());
+                        if (ptype.endsWith('*')) {
+                            argv_pointer[p + 1] = argv[p + 1];
+                            argv[p + 1] = argv_pointer + p + 1;
+                        }
+                    }
+                } else if (ptype == "QVariant") {
+                    argv[p + 1] = varp + p + 1;
+                } else {
+                    ok = false;
+                }
+            }
+
+            if (ok) {
+                // emit the generated signal if everything went well
+                QAxBase::qt_static_metacall(combase, QMetaObject::InvokeMetaMethod, index - meta->methodOffset(), argv);
+                // update the VARIANT for references and free memory
+                for (p = 0; p < pcount; ++p) {
+                    bool out;
+                    QByteArray ptype = axmeta->paramType(signame, p, &out);
+                    if (out) {
+                        if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out))
+                            ok = false;
+                    }
+                }
+            }
+
+            if (argv != static_argv) {
+                delete [] argv;
+                delete [] argv_pointer;
+                delete [] varp;
+            }
+            hres = ok ? S_OK : (ok ? DISP_E_MEMBERNOTFOUND : DISP_E_TYPEMISMATCH);
+        }
+
+        return hres;
+    }
+
+    QByteArray findProperty(DISPID dispID);
+
+    // IPropertyNotifySink
+    HRESULT __stdcall OnChanged(DISPID dispID) override
+    {
+        // verify input
+        if (dispID == DISPID_UNKNOWN || !combase)
+            return S_OK;
+
+        const QMetaObject *meta = combase->metaObject();
+        if (!meta)
+            return S_OK;
+
+        QByteArray propname(findProperty(dispID));
+        if (propname.isEmpty())
+            return S_OK;
+
+        QObject *qobject = combase->qObject();
+        if (qobject->signalsBlocked())
+            return S_OK;
+
+        // emit the generic signal
+        int index = meta->indexOfSignal("propertyChanged(QString)");
+        if (index != -1) {
+            QString propnameString = QString::fromLatin1(propname);
+            void *argv[] = {nullptr, &propnameString};
+            QAxBase::qt_static_metacall(combase, QMetaObject::InvokeMetaMethod,
+                                        index - meta->methodOffset(), argv);
+        }
+
+        QByteArray signame = propsigs.value(dispID);
+        if (signame.isEmpty())
+            return S_OK;
+
+        index = meta->indexOfSignal(signame);
+        if (index == -1) // bindable but not marked as bindable in typelib
+            return S_OK;
+
+        // get the signal information from the metaobject
+        if (signalHasReceivers(qobject, signame)) {
+            index = meta->indexOfSignal(signame);
+            Q_ASSERT(index != -1);
+            // setup parameters
+            QVariant var = qobject->property(propname);
+            if (!var.isValid())
+                return S_OK;
+
+            const QMetaProperty metaProp = meta->property(meta->indexOfProperty(propname));
+            void *argv[] = {nullptr, var.data()};
+            if (metaProp.type() == QVariant::Type(QMetaType::QVariant) || metaProp.type() == QVariant::LastType)
+                argv[1] = &var;
+
+            // emit the "changed" signal
+            QAxBase::qt_static_metacall(combase, QMetaObject::InvokeMetaMethod,
+                                        index - meta->methodOffset(), argv);
+        }
+        return S_OK;
+    }
+    HRESULT __stdcall OnRequestEdit(DISPID dispID) override
+    {
+        if (dispID == DISPID_UNKNOWN || !combase)
+            return S_OK;
+
+        QByteArray propname(findProperty(dispID));
+        if (propname.isEmpty())
+            return S_OK;
+
+        return combase->propertyWritable(propname) ? S_OK : S_FALSE;
+    }
+
+    static bool signalHasReceivers(QObject *qobject, const char *signalName)
+    {
+        Q_ASSERT(qobject);
+        const QByteArray name = QByteArray::number(QSIGNAL_CODE) + signalName;
+        return static_cast<QAxObject *>(qobject)->receivers(name.constData()) > 0;
+    }
+
+    IConnectionPoint *cpoint = nullptr;
+    IID ciid = IID_NULL;
+    ULONG cookie = 0;
+
+    QMap<DISPID, QByteArray> sigs;
+    QMap<DISPID, QByteArray> propsigs;
+    QMap<DISPID, QByteArray> props;
+
+    QAxBase *combase = nullptr;
+    LONG ref = 1;
+};
+
+/*
+    \internal
+    \class QAxBasePrivate
+*/
+
+class QAxBasePrivate
+{
+    Q_DISABLE_COPY_MOVE(QAxBasePrivate)
+public:
+    using UuidEventSinkHash = QHash<QUuid, QAxEventSink*>;
+
+    QAxBasePrivate()
+        : useEventSink(true), useMetaObject(true), useClassInfo(true),
+        cachedMetaObject(false), initialized(false), tryCache(false)
+    {
+        // protect initialization
+        QMutexLocker locker(&cache_mutex);
+        mo_cache_ref++;
+
+        qRegisterMetaType<IUnknown*>("IUnknown*", &ptr);
+        qRegisterMetaType<IDispatch*>("IDispatch*", &disp);
+    }
+
+    ~QAxBasePrivate()
+    {
+        Q_ASSERT(!ptr);
+        Q_ASSERT(!disp);
+
+        // protect cleanup
+        QMutexLocker locker(&cache_mutex);
+        if (!--mo_cache_ref) {
+            qDeleteAll(mo_cache);
+            mo_cache.clear();
+        }
+
+        CoFreeUnusedLibraries();
+    }
+
+    inline IDispatch *dispatch() const
+    {
+        if (disp)
+            return disp;
+
+        if (ptr)
+            ptr->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&disp));
+        return disp;
+    }
+
+    QString ctrl;
+    UuidEventSinkHash eventSink;
+    uint useEventSink       :1;
+    uint useMetaObject      :1;
+    uint useClassInfo       :1;
+    uint cachedMetaObject   :1;
+    uint initialized        :1;
+    uint tryCache           :1;
+    unsigned long classContext = CLSCTX_SERVER;
+
+    IUnknown *ptr = nullptr;
+    mutable IDispatch *disp = nullptr;
+
+    QMap<QByteArray, bool> propWritable;
+
+    inline QAxMetaObject *metaObject()
+    {
+        if (!metaobj)
+            metaobj = new QAxMetaObject;
+        return metaobj;
+    }
+
+    mutable QMap<QString, LONG> verbs;
+
+    QAxMetaObject *metaobj = nullptr;
+};
+
+
+QByteArray QAxEventSink::findProperty(DISPID dispID)
+{
+    // look up in cache, and fall back to
+    // type info for precompiled metaobjects
+    QByteArray propname(props.value(dispID));
+
+    if (!propname.isEmpty())
+        return propname;
+
+    IDispatch *dispatch = combase->d->dispatch();
+    ITypeInfo *typeinfo = nullptr;
+    if (dispatch)
+        dispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo);
+    if (!typeinfo)
+        return propname;
+
+
+    const QByteArray propnameI = qaxTypeInfoName(typeinfo, dispID);
+    if (!propnameI.isEmpty())
+        propname = propnameI;
+    typeinfo->Release();
+
+    QByteArray propsignal(propname + "Changed(");
+    const QMetaObject *mo = combase->metaObject();
+    int index = mo->indexOfProperty(propname);
+    const QMetaProperty prop = mo->property(index);
+    propsignal += prop.typeName();
+    propsignal += ')';
+    addProperty(dispID, propname, propsignal);
+
+    return propname;
+}
+
+/*!
+    \class QAxBase
+    \brief The QAxBase class is an abstract class that provides an API
+    to initialize and access a COM object.
+
+    \inmodule QAxContainer
+
+    QAxBase is an abstract class that cannot be used directly, and is
+    instantiated through the subclasses QAxObject and QAxWidget. This
+    class provides the API to access the COM object directly
+    through its IUnknown implementation. If the COM object implements
+    the IDispatch interface, the properties and methods of that object
+    become available as Qt properties and slots.
+
+    \snippet src_activeqt_container_qaxbase.cpp 0
+
+    Properties exposed by the object's IDispatch implementation can
+    be read and written through the property system provided by the
+    Qt Object Model (both subclasses are \l{QObject}s, so you can use
+    QObject::setProperty() and QObject::property()). Properties with
+    multiple parameters are not supported.
+
+    \snippet src_activeqt_container_qaxbase.cpp 1
+
+    Write-functions for properties and other methods exposed by the
+    object's IDispatch implementation can be called directly using
+    dynamicCall(), or indirectly as slots connected to a signal.
+
+    \snippet src_activeqt_container_qaxbase.cpp 2
+
+    Outgoing events supported by the COM object are emitted as
+    standard Qt signals.
+
+    \snippet src_activeqt_container_qaxbase.cpp 3
+
+    QAxBase transparently converts between COM data types and the
+    equivalent Qt data types. Some COM types have no equivalent Qt data structure.
+
+    Supported COM datatypes are listed in the first column of following table.
+    The second column is the Qt type that can be used with the QObject property
+    functions. The third column is the Qt type that is used in the prototype of
+    generated signals and slots for in-parameters, and the last column is the Qt
+    type that is used in the prototype of signals and slots for out-parameters.
+    \table
+    \header
+    \li COM type
+    \li Qt property
+    \li in-parameter
+    \li out-parameter
+    \row
+    \li VARIANT_BOOL
+    \li bool
+    \li bool
+    \li bool&
+    \row
+    \li BSTR
+    \li QString
+    \li const QString&
+    \li QString&
+    \row
+    \li char, short, int, long
+    \li int
+    \li int
+    \li int&
+    \row
+    \li uchar, ushort, uint, ulong
+    \li uint
+    \li uint
+    \li uint&
+    \row
+    \li float, double
+    \li double
+    \li double
+    \li double&
+    \row
+    \li DATE
+    \li QDateTime
+    \li const QDateTime&
+    \li QDateTime&
+    \row
+    \li CY
+    \li qlonglong
+    \li qlonglong
+    \li qlonglong&
+    \row
+    \li OLE_COLOR
+    \li QColor
+    \li const QColor&
+    \li QColor&
+    \row
+    \li SAFEARRAY(VARIANT)
+    \li QList\<QVariant\>
+    \li const QList\<QVariant\>&
+    \li QList\<QVariant\>&
+    \row
+    \li SAFEARRAY(int), SAFEARRAY(double), SAFEARRAY(Date)
+    \li QList\<QVariant\>
+    \li const QList\<QVariant\>&
+    \li QList\<QVariant\>&
+    \row
+    \li SAFEARRAY(BYTE)
+    \li QByteArray
+    \li const QByteArray&
+    \li QByteArray&
+    \row
+    \li SAFEARRAY(BSTR)
+    \li QStringList
+    \li const QStringList&
+    \li QStringList&
+    \row
+    \li VARIANT
+    \li type-dependent
+    \li const QVariant&
+    \li QVariant&
+    \row
+    \li IFontDisp*
+    \li QFont
+    \li const QFont&
+    \li QFont&
+    \row
+    \li IPictureDisp*
+    \li QPixmap
+    \li const QPixmap&
+    \li QPixmap&
+    \row
+    \li IDispatch*
+    \li QAxObject*
+    \li \c QAxBase::asVariant()
+    \li QAxObject* (return value)
+    \row
+    \li IUnknown*
+    \li QAxObject*
+    \li \c QAxBase::asVariant()
+    \li QAxObject* (return value)
+    \row
+    \li SCODE, DECIMAL
+    \li \e unsupported
+    \li \e unsupported
+    \li \e unsupported
+    \row
+    \li VARIANT* (Since Qt 4.5)
+    \li \e unsupported
+    \li \e QVariant&
+    \li \e QVariant&
+    \endtable
+
+    Supported are also enumerations, and typedefs to supported types.
+
+    To call the methods of a COM interface described by the following IDL
+
+    \snippet src_activeqt_container_qaxbase.cpp 4
+
+    use the QAxBase API like this:
+
+    \snippet src_activeqt_container_qaxbase.cpp 5
+
+    Note that the QList the object should fill has to be provided as an
+    element in the parameter list of \l{QVariant}s.
+
+    If you need to access properties or pass parameters of
+    unsupported datatypes you must access the COM object directly
+    through its \c IDispatch implementation or other interfaces.
+    Those interfaces can be retrieved through queryInterface().
+
+    \snippet src_activeqt_container_qaxbase.cpp 6
+
+    To get the definition of the COM interfaces you will have to use the header
+    files provided with the component you want to use. Some compilers can also
+    import type libraries using the #import compiler directive. See the component
+    documentation to find out which type libraries you have to import, and how to use
+    them.
+
+    If you need to react to events that pass parameters of unsupported
+    datatypes you can use the generic signal that delivers the event
+    data as provided by the COM event.
+
+    \sa QAxObject, QAxWidget, QAxScript, {ActiveQt Framework}
+*/
+
+/*!
+    \typedef QAxBase::PropertyBag
+
+    A QMap<QString,QVariant> that can store properties as name:value pairs.
+*/
+
+/*!
+    Creates a QAxBase object that wraps the COM object \a iface. If \a
+    iface is 0 (the default), use setControl() to instantiate a COM
+    object.
+*/
+QAxBase::QAxBase(IUnknown *iface)
+{
+    d = new QAxBasePrivate();
+    d->ptr = iface;
+    if (d->ptr) {
+        d->ptr->AddRef();
+        d->initialized = true;
+    }
+}
+
+/*!
+    Shuts down the COM object and destroys the QAxBase object.
+
+    \sa clear()
+*/
+QAxBase::~QAxBase()
+{
+
+    clear();
+
+    delete d;
+    d = nullptr;
+}
+
+/*!
+    \internal
+
+    Used by subclasses generated with dumpcpp to balance reference count.
+*/
+void QAxBase::internalRelease()
+{
+    if (d->ptr)
+        d->ptr->Release();
+}
+
+/*!
+    \internal
+
+    Used by subclasses generated with dumpcpp to implement cast-operators.
+*/
+void QAxBase::initializeFrom(QAxBase *that)
+{
+    if (d->ptr)
+        return;
+
+    d->ptr = that->d->ptr;
+    if (d->ptr) {
+        d->ptr->AddRef();
+        d->initialized = true;
+    }
+}
+
+
+QAxMetaObject *QAxBase::internalMetaObject() const
+{
+    return d->metaObject();
+}
+
+/*!
+    \property QAxBase::control
+    \brief the name of the COM object wrapped by this QAxBase object.
+
+    Setting this property initializes the COM object. Any COM object
+    previously set is shut down.
+
+    The most efficient way to set this property is by using the
+    registered component's UUID, e.g.
+
+    \snippet src_activeqt_container_qaxbase.cpp 7
+
+    The second fastest way is to use the registered control's class
+    name (with or without version number), e.g.
+
+    \snippet src_activeqt_container_qaxbase.cpp 8
+
+    The slowest, but easiest way to use is to use the control's full
+    name, e.g.
+
+    \snippet src_activeqt_container_qaxbase.cpp 9
+
+    It is also possible to initialize the object from a file, e.g.
+
+    \snippet src_activeqt_container_qaxbase.cpp 10
+
+    If the component's UUID is used the following patterns can be used
+    to initialize the control on a remote machine, to initialize a
+    licensed control or to connect to a running object:
+    \list
+    \li To initialize the control on a different machine use the following
+    pattern:
+
+    \snippet src_activeqt_container_qaxbase.cpp 11
+
+    \li To initialize a licensed control use the following pattern:
+
+    \snippet src_activeqt_container_qaxbase.cpp 12
+
+    \li To connect to an already running object use the following pattern:
+
+    \snippet src_activeqt_container_qaxbase.cpp 13
+
+    \endlist
+    The first two patterns can be combined, e.g. to initialize a licensed
+    control on a remote machine:
+
+    \snippet src_activeqt_container_qaxbase.cpp 14
+
+    The control's read function always returns the control's UUID, if provided including the license
+    key, and the name of the server, but not including the username, the domain or the password.
+
+    \sa setClassContext()
+*/
+bool QAxBase::setControl(const QString &c)
+{
+    if (!c.compare(d->ctrl, Qt::CaseInsensitive))
+        return !d->ctrl.isEmpty();
+
+    QString search = c;
+    // don't waste time for DCOM requests
+    int dcomIDIndex = search.indexOf(QLatin1String("/{"));
+    if ((dcomIDIndex == -1 || dcomIDIndex != search.length()-39) && !search.endsWith(QLatin1String("}&"))) {
+        QUuid uuid(search);
+        if (uuid.isNull()) {
+            CLSID clsid;
+            HRESULT res = CLSIDFromProgID(reinterpret_cast<const wchar_t *>(c.utf16()), &clsid);
+            if (res == S_OK)
+                search = QUuid(clsid).toString();
+            else {
+                QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\"), QSettings::NativeFormat);
+                search = controls.value(c + QLatin1String("/CLSID/Default")).toString();
+                if (search.isEmpty()) {
+                    controls.beginGroup(QLatin1String("/CLSID"));
+                    const QStringList clsids = controls.childGroups();
+                    for (const QString &clsid : clsids) {
+                        const QString name = controls.value(clsid + QLatin1String("/Default")).toString();
+                        if (name == c) {
+                            search = clsid;
+                            break;
+                        }
+                    }
+                    controls.endGroup();
+                }
+            }
+        }
+        if (search.isEmpty())
+            search = c;
+    }
+
+    if (!search.compare(d->ctrl, Qt::CaseInsensitive))
+        return !d->ctrl.isEmpty();
+
+    clear();
+    d->ctrl = search;
+
+    d->tryCache = true;
+    if (!initialize(&d->ptr))
+        d->initialized = true;
+    if (isNull()) {
+        qWarning("QAxBase::setControl: requested control %s could not be instantiated", c.toLatin1().data());
+        clear();
+        return false;
+    }
+    return true;
+}
+
+QString QAxBase::control() const
+{
+    return d->ctrl;
+}
+
+/*!
+    Disables the event sink implementation for this ActiveX container.
+    If you don't intend to listen to the ActiveX control's events use
+    this function to speed up the meta object generation.
+
+    Some ActiveX controls might be unstable when connected to an event
+    sink. To get OLE events you must use standard COM methods to
+    register your own event sink. Use queryInterface() to get access
+    to the raw COM object.
+
+    Note that this function should be called immediately after
+    construction of the object.
+*/
+void QAxBase::disableEventSink()
+{
+    d->useEventSink = false;
+}
+
+/*!
+    \since 5.13
+
+    \return the context the ActiveX control will run in (default CLSCTX_SERVER).
+*/
+unsigned long QAxBase::classContext() const
+{
+    return d->classContext;
+}
+
+/*!
+    \since 5.13
+
+    Sets the context the ActiveX control will run in to \a classContext
+
+    Affects the "dwClsContext" argument when calling CoCreateInstance.
+    This can be used to control in-proc vs. out-of-proc startup for controls
+    supporting both alternatives. Also, it can be used to modify/reduce control
+    permissions when used with CLSCTX_ENABLE_CLOAKING and an impersonation token.
+
+    Note that this function must be called before setControl() to have any
+    effect.
+*/
+void QAxBase::setClassContext(unsigned long classContext)
+{
+    d->classContext = classContext;
+}
+
+/*!
+    Disables the meta object generation for this ActiveX container.
+    This also disables the event sink and class info generation. If
+    you don't intend to use the Qt meta object implementation call
+    this function to speed up instantiation of the control. You will
+    still be able to call the object through \l dynamicCall(), but
+    signals, slots and properties will not be available with QObject
+    APIs.
+
+    Some ActiveX controls might be unstable when used with OLE
+    automation. Use standard COM methods to use those controls through
+    the COM interfaces provided by queryInterface().
+
+    Note that this function must be called immediately after
+    construction of the object.
+*/
+void QAxBase::disableMetaObject()
+{
+    d->useMetaObject    = false;
+    d->useEventSink     = false;
+    d->useClassInfo     = false;
+}
+
+/*!
+    Disables the class info generation for this ActiveX container. If
+    you don't require any class information about the ActiveX control
+    use this function to speed up the meta object generation.
+
+    Note that this function must be called immediately after
+    construction of the object
+*/
+void QAxBase::disableClassInfo()
+{
+    d->useClassInfo = false;
+}
+
+/*!
+    Disconnects and destroys the COM object.
+
+    If you reimplement this function you must also reimplement the
+    destructor to call clear(), and call this implementation at the
+    end of your clear() function.
+*/
+void QAxBase::clear()
+{
+    for (auto it = d->eventSink.cbegin(), end = d->eventSink.cend(); it != end; ++it) {
+        if (QAxEventSink *eventSink = it.value()) {
+            eventSink->unadvise();
+            eventSink->Release();
+        }
+    }
+    d->eventSink.clear();
+    if (d->disp) {
+        d->disp->Release();
+        d->disp = nullptr;
+    }
+    if (d->ptr) {
+        d->ptr->Release();
+        d->ptr = nullptr;
+        d->initialized = false;
+    }
+
+    d->ctrl.clear();
+
+    if (!d->cachedMetaObject)
+        delete d->metaobj;
+    d->metaobj = nullptr;
+}
+
+/*!
+    \since 4.1
+
+    Returns the list of verbs that the COM object can execute. If
+    the object does not implement IOleObject, or does not support
+    any verbs, then this function returns an empty stringlist.
+
+    Note that the OLE default verbs (OLEIVERB_SHOW etc) are not
+    included in the list.
+*/
+QStringList QAxBase::verbs() const
+{
+    if (!d->ptr)
+        return QStringList();
+
+    if (d->verbs.isEmpty()) {
+        IOleObject *ole = nullptr;
+        d->ptr->QueryInterface(IID_IOleObject, reinterpret_cast<void **>(&ole));
+        if (ole) {
+            IEnumOLEVERB *enumVerbs = nullptr;
+            ole->EnumVerbs(&enumVerbs);
+            if (enumVerbs) {
+                enumVerbs->Reset();
+                ULONG c;
+                OLEVERB verb;
+                while (enumVerbs->Next(1, &verb, &c) == S_OK) {
+                    if (!verb.lpszVerbName)
+                        continue;
+                    QString verbName = QString::fromWCharArray(verb.lpszVerbName);
+                    if (!verbName.isEmpty())
+                        d->verbs.insert(verbName, verb.lVerb);
+                }
+                enumVerbs->Release();
+            }
+            ole->Release();
+        }
+    }
+
+    return d->verbs.keys();
+}
+
+/*!
+    \internal
+*/
+
+long QAxBase::indexOfVerb(const QString &verb) const
+{
+    return d->verbs.value(verb);
+}
+
+/*!
+    This virtual function is called by setControl() and creates the
+    requested COM object. \a ptr is set to the object's IUnknown
+    implementation. The function returns true if the object
+    initialization succeeded; otherwise the function returns false.
+
+    The default implementation interprets the string returned by
+    control(), and calls initializeRemote(), initializeLicensed()
+    or initializeActive() if the string matches the respective
+    patterns. If control() is the name of an existing file,
+    initializeFromFile() is called. If no pattern is matched, or
+    if remote or licensed initialization fails, CoCreateInstance
+    is used directly to create the object.
+
+    See the \l control property documentation for details about
+    supported patterns.
+
+    The interface returned in \a ptr must be referenced exactly once
+    when this function returns. The interface provided by e.g.
+    CoCreateInstance is already referenced, and there is no need to
+    reference it again.
+*/
+bool QAxBase::initialize(IUnknown **ptr)
+{
+    if (*ptr || control().isEmpty())
+        return false;
+
+    // Request asynchronous expose events to be used when application uses ActiveQt objects.
+    // Otherwise painter can get corrupted if Invoke or some other COM method that cause Windows
+    // messages to be processed is called during an existing paint operation when WM_PAINT is
+    // also in the queue.
+    static bool asyncExposeSet = false;
+    if (!asyncExposeSet && QGuiApplication::platformNativeInterface()) {
+        QGuiApplication::platformNativeInterface()->setProperty("asyncExpose", QVariant(true));
+        asyncExposeSet = true;
+    }
+
+    *ptr = nullptr;
+
+    bool res = false;
+
+    const QString ctrl(d->ctrl);
+    if (ctrl.contains(QLatin1String("/{"))) // DCOM request
+        res = initializeRemote(ptr);
+    else if (ctrl.contains(QLatin1String("}:"))) // licensed control
+        res = initializeLicensed(ptr);
+    else if (ctrl.contains(QLatin1String("}&"))) // running object
+        res = initializeActive(ptr);
+    else if (QFile::exists(ctrl)) // existing file
+        res = initializeFromFile(ptr);
+
+    if (!res) { // standard
+        HRESULT hres = CoCreateInstance(QUuid(ctrl), nullptr, d->classContext, IID_IUnknown,
+                                        reinterpret_cast<void **>(ptr));
+        res = S_OK == hres;
+#ifndef QT_NO_DEBUG
+        if (!res)
+            qErrnoWarning(hres, "CoCreateInstance failure");
+#endif
+    }
+
+    return *ptr != nullptr;
+}
+
+/*!
+    Creates an instance of a licensed control, and returns the IUnknown interface
+    to the object in \a ptr. This functions returns true if successful, otherwise
+    returns false.
+
+    This function is called by initialize() if the control string contains the
+    substring "}:". The license key needs to follow this substring.
+
+    \sa initialize()
+*/
+bool QAxBase::initializeLicensed(IUnknown** ptr)
+{
+    int at = control().lastIndexOf(QLatin1String("}:"));
+
+    QString clsid(control().left(at));
+    QString key(control().mid(at+2));
+
+    IClassFactory *factory = nullptr;
+    CoGetClassObject(QUuid(clsid), CLSCTX_SERVER, nullptr, IID_IClassFactory,
+                     reinterpret_cast<void **>(&factory));
+    if (!factory)
+        return false;
+    initializeLicensedHelper(factory, key, ptr);
+    factory->Release();
+
+    return *ptr != nullptr;
+}
+
+/* \internal
+    Called by initializeLicensed and initializedRemote to create an object
+    via IClassFactory2.
+*/
+bool QAxBase::initializeLicensedHelper(void *f, const QString &key, IUnknown **ptr)
+{
+    IClassFactory *factory = reinterpret_cast<IClassFactory *>(f);
+    IClassFactory2 *factory2 = nullptr;
+    factory->QueryInterface(IID_IClassFactory2, reinterpret_cast<void **>(&factory2));
+    if (factory2) {
+        BSTR bkey = QStringToBSTR(key);
+        HRESULT hres = factory2->CreateInstanceLic(nullptr, nullptr, IID_IUnknown, bkey,
+                                                   reinterpret_cast<void **>(ptr));
+        SysFreeString(bkey);
+#ifdef QT_DEBUG
+        LICINFO licinfo;
+        licinfo.cbLicInfo = sizeof(LICINFO);
+        factory2->GetLicInfo(&licinfo);
+
+        if (hres != S_OK) {
+            SetLastError(DWORD(hres));
+            qErrnoWarning("CreateInstanceLic failed");
+            if (!licinfo.fLicVerified) {
+                qWarning("Wrong license key specified, and machine is not fully licensed.");
+            } else if (licinfo.fRuntimeKeyAvail) {
+                BSTR licenseKey;
+                factory2->RequestLicKey(0, &licenseKey);
+                QString qlicenseKey = QString::fromWCharArray(licenseKey);
+                SysFreeString(licenseKey);
+                qWarning("Use license key is '%s' to create object on unlicensed machine.",
+                    qlicenseKey.toLatin1().constData());
+            }
+        } else if (licinfo.fLicVerified) {
+            qWarning("Machine is fully licensed for '%s'", control().toLatin1().constData());
+            if (licinfo.fRuntimeKeyAvail) {
+                BSTR licenseKey;
+                factory2->RequestLicKey(0, &licenseKey);
+                QString qlicenseKey = QString::fromWCharArray(licenseKey);
+                SysFreeString(licenseKey);
+
+                if (qlicenseKey != key)
+                    qWarning("Runtime license key is '%s'", qlicenseKey.toLatin1().constData());
+            }
+        }
+#else
+        Q_UNUSED(hres);
+#endif
+        factory2->Release();
+    } else {  // give it a shot without license
+        factory->CreateInstance(nullptr, IID_IUnknown, reinterpret_cast<void **>(ptr));
+    }
+    return *ptr != nullptr;
+}
+
+
+/*!
+    Connects to an active instance running on the current machine, and returns the
+    IUnknown interface to the running object in \a ptr. This function returns true
+    if successful, otherwise returns false.
+
+    This function is called by initialize() if the control string contains the
+    substring "}&".
+
+    \sa initialize()
+*/
+bool QAxBase::initializeActive(IUnknown** ptr)
+{
+    int at = control().lastIndexOf(QLatin1String("}&"));
+    QString clsid(control().left(at));
+
+    GetActiveObject(QUuid(clsid), nullptr, ptr);
+
+    return *ptr != nullptr;
+}
+
+#ifdef Q_CC_GNU
+#   ifndef OLEPENDER_NONE
+#   define OLERENDER_NONE 0
+#   endif
+#endif
+
+/*!
+    Creates the COM object handling the filename in the control property, and
+    returns the IUnknown interface to the object in \a ptr. This function returns
+    true if successful, otherwise returns false.
+
+    This function is called by initialize() if the control string is the name of
+    an existing file.
+
+    \sa initialize()
+*/
+bool QAxBase::initializeFromFile(IUnknown** ptr)
+{
+    IStorage *storage = nullptr;
+    ILockBytes * bytes = nullptr;
+    HRESULT hres = ::CreateILockBytesOnHGlobal(nullptr, TRUE, &bytes);
+    hres = ::StgCreateDocfileOnILockBytes(bytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage);
+
+    hres = OleCreateFromFile(CLSID_NULL, reinterpret_cast<const wchar_t*>(control().utf16()),
+                             IID_IUnknown, OLERENDER_NONE, nullptr, nullptr, storage,
+                             reinterpret_cast<void **>(ptr));
+
+    storage->Release();
+    bytes->Release();
+
+    return hres == S_OK;
+}
+
+
+// There seams to be a naming problem in mingw headers
+#if defined(Q_CC_GNU) && !defined(COAUTHIDENTITY) && !defined(__MINGW64_VERSION_MAJOR)
+#define COAUTHIDENTITY AUTH_IDENTITY
+#endif
+
+
+/*!
+    Creates the instance on a remote server, and returns the IUnknown interface
+    to the object in \a ptr. This function returns true if successful, otherwise
+    returns false.
+
+    This function is called by initialize() if the control string contains the
+    substring "/{". The information about the remote machine needs to be provided
+    in front of the substring.
+
+    \sa initialize()
+*/
+bool QAxBase::initializeRemote(IUnknown** ptr)
+{
+    int at = control().lastIndexOf(QLatin1String("/{"));
+
+    QString server(control().left(at));
+    QString clsid(control().mid(at+1));
+
+    QString user;
+    QString domain;
+    QString passwd;
+    QString key;
+
+    at = server.indexOf(QChar::fromLatin1('@'));
+    if (at != -1) {
+        user = server.left(at);
+        server.remove(0, at + 1);
+
+        at = user.indexOf(QChar::fromLatin1(':'));
+        if (at != -1) {
+            passwd = user.mid(at+1);
+            user.truncate(at);
+        }
+        at = user.indexOf(QChar::fromLatin1('/'));
+        if (at != -1) {
+            domain = user.left(at);
+            user.remove(0, at + 1);
+        }
+    }
+
+    at = clsid.lastIndexOf(QLatin1String("}:"));
+    if (at != -1) {
+        key = clsid.mid(at+2);
+        clsid.truncate(at);
+    }
+
+    d->ctrl = server + QChar::fromLatin1('/') + clsid;
+    if (!key.isEmpty())
+        d->ctrl = d->ctrl + QChar::fromLatin1(':') + key;
+
+    COAUTHIDENTITY authIdentity;
+    authIdentity.UserLength = ULONG(user.length());
+    authIdentity.User = authIdentity.UserLength
+        ? const_cast<ushort *>(user.utf16()) : nullptr;
+    authIdentity.DomainLength = ULONG(domain.length());
+    authIdentity.Domain = authIdentity.DomainLength
+        ? const_cast<ushort *>(domain.utf16()) : nullptr;
+    authIdentity.PasswordLength = ULONG(passwd.length());
+    authIdentity.Password = authIdentity.PasswordLength
+        ? const_cast<ushort *>(passwd.utf16()) : nullptr;
+    authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+
+    COAUTHINFO authInfo;
+    authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
+    authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
+    authInfo.pwszServerPrincName = nullptr;
+    authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT;
+    authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
+    authInfo.pAuthIdentityData = &authIdentity;
+    authInfo.dwCapabilities = 0;
+
+    COSERVERINFO serverInfo;
+    serverInfo.dwReserved1 = 0;
+    serverInfo.dwReserved2 = 0;
+    serverInfo.pAuthInfo = &authInfo;
+    serverInfo.pwszName = reinterpret_cast<wchar_t *>(const_cast<ushort *>(server.utf16()));
+
+    IClassFactory *factory = nullptr;
+    HRESULT res = CoGetClassObject(QUuid(clsid), CLSCTX_REMOTE_SERVER, &serverInfo,
+                                   IID_IClassFactory, reinterpret_cast<void **>(&factory));
+    if (factory) {
+        if (!key.isEmpty())
+            initializeLicensedHelper(factory, key, ptr);
+        else
+            res = factory->CreateInstance(nullptr, IID_IUnknown, reinterpret_cast<void **>(ptr));
+        factory->Release();
+    }
+#ifndef QT_NO_DEBUG
+    if (res != S_OK)
+        qErrnoWarning(res, "initializeRemote Failed");
+#endif
+
+    return res == S_OK;
+}
+
+/*!
+    Requests the interface \a uuid from the COM object and sets the
+    value of \a iface to the provided interface, or to 0 if the
+    requested interface could not be provided.
+
+    Returns the result of the QueryInterface implementation of the COM object.
+
+    \sa control
+*/
+long QAxBase::queryInterface(const QUuid &uuid, void **iface) const
+{
+    *iface = nullptr;
+    if (!d->ptr) {
+        const_cast<QAxBase *>(this)->initialize(&d->ptr);
+        d->initialized = true;
+    }
+
+    if (d->ptr && !uuid.isNull())
+        return d->ptr->QueryInterface(uuid, iface);
+
+    return E_NOTIMPL;
+}
+
+class MetaObjectGenerator
+{
+public:
+    MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr);
+    MetaObjectGenerator(ITypeLib *typelib, ITypeInfo *typeinfo);
+    ~MetaObjectGenerator();
+
+    QMetaObject *metaObject(const QMetaObject *parentObject, const QByteArray &className = QByteArray());
+
+    void readClassInfo();
+    void readEnumInfo();
+    void readInterfaceInfo();
+    void readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs);
+    void readVarsInfo(ITypeInfo *typeinfo, ushort nVars);
+    void readEventInfo();
+    void readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint);
+
+    inline void addClassInfo(const QByteArray &key, const  QByteArray &value)
+    {
+        classinfo_list.insert(key, value);
+    }
+
+private:
+    using ByteArrayIntPair = QPair<QByteArray, int>;
+    using ByteArrayIntPairList = QList<ByteArrayIntPair>;
+
+    void init();
+
+
+    QMetaObject *tryCache();
+
+    QByteArray createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QByteArrayList &names,
+                               QByteArray &type, QByteArrayList &parameters);
+
+    QByteArray usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function);
+    QByteArray guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function);
+
+    // ActiveQt's extensions to the PropertyFlags defined in qmetaobject_p.h.
+    // This will break if new overlapping flags are added in qmetaobject_p.h!
+    enum ProperyFlags  {
+        RequestingEdit          = 0x01000000,
+        Bindable                = 0x02000000
+    };
+
+    static inline QByteArrayList paramList(const QByteArray &prototype)
+    {
+        QByteArray parameters = prototype.mid(prototype.indexOf('(') + 1);
+        parameters.truncate(parameters.length() - 1);
+        if (parameters.isEmpty() || parameters == "void")
+            return {};
+        return parameters.split(',');
+    }
+
+    inline QByteArray replaceType(const QByteArray &type)
+    {
+        if (type.isEmpty())
+            return QByteArray("void");
+        int i = 0;
+        while (type_conversion[i][0]) {
+            int len = int(strlen(type_conversion[i][0]));
+            int ti;
+            if ((ti = type.indexOf(type_conversion[i][0])) != -1) {
+                QByteArray rtype(type);
+                rtype.replace(ti, len, type_conversion[i][1]);
+                return rtype;
+            }
+            ++i;
+        }
+        return type;
+    }
+
+    QByteArray replacePrototype(const QByteArray &prototype)
+    {
+        QByteArray proto(prototype);
+
+        const auto plist = paramList(prototype);
+        for (const QByteArray &param : plist) {
+            if (param != replaceType(param)) {
+                int type = 0;
+                while (type_conversion[type][0]) {
+                    int paren = proto.indexOf('(');
+                    while ((paren = proto.indexOf(type_conversion[type][0])) != -1) {
+                        proto.replace(paren, int(qstrlen(type_conversion[type][0])),
+                                      type_conversion[type][1]);
+                    }
+                    ++type;
+                }
+                break;
+            }
+        }
+
+        return proto;
+    }
+
+    QMap<QByteArray, QByteArray> classinfo_list;
+
+    inline bool hasClassInfo(const char *key)
+    {
+        return classinfo_list.contains(key);
+    }
+
+    struct Method {
+        QByteArray type;
+        QByteArray parameters;
+        int flags = 0;
+        QByteArray realPrototype;
+    };
+    QMap<QByteArray, Method> signal_list;
+    inline void addSignal(const QByteArray &prototype, const QByteArray &parameters)
+    {
+        QByteArray proto(replacePrototype(prototype));
+
+        Method &signal = signal_list[proto];
+        signal.type = "void";
+        signal.parameters = parameters;
+        signal.flags = QMetaMethod::Public | MethodSignal;
+        if (proto != prototype)
+            signal.realPrototype = prototype;
+    }
+
+    void addChangedSignal(const QByteArray &function, const QByteArray &type, int memid);
+
+    inline bool hasSignal(const QByteArray &prototype)
+    {
+        return signal_list.contains(prototype);
+    }
+
+    QMap<QByteArray, Method> slot_list;
+    inline void addSlot(const QByteArray &type, const QByteArray &prototype, const QByteArray &parameters, int flags = QMetaMethod::Public)
+    {
+        QByteArray proto = replacePrototype(prototype);
+
+        Method &slot = slot_list[proto];
+
+        slot.type = replaceType(type);
+        slot.parameters = parameters;
+        slot.flags = flags | MethodSlot;
+        if (proto != prototype)
+            slot.realPrototype = prototype;
+    }
+
+    void addSetterSlot(const QByteArray &property);
+
+    inline bool hasSlot(const QByteArray &prototype)
+    {
+        return slot_list.contains(prototype);
+    }
+
+    static int aggregateParameterCount(const QMap<QByteArray, Method> &map);
+
+    struct Property {
+        Property() = default;
+
+        QByteArray type;
+        uint flags = 0;
+        QByteArray realType;
+    };
+    QMap<QByteArray, Property> property_list;
+    void addProperty(const QByteArray &type, const QByteArray &name, uint flags)
+    {
+        QByteArray propertyType(type);
+        if (propertyType.endsWith('&'))
+            propertyType.chop(1);
+
+        Property &prop = property_list[name];
+        if (!propertyType.isEmpty() && propertyType != "HRESULT") {
+            prop.type = replaceType(propertyType);
+            if (prop.type != propertyType)
+                prop.realType = propertyType;
+        }
+        if (flags & Writable)
+            flags |= Stored;
+        prop.flags |= flags;
+    }
+
+    inline bool hasProperty(const QByteArray &name)
+    {
+        return property_list.contains(name);
+    }
+
+    inline QByteArray propertyType(const QByteArray &name)
+    {
+        return property_list.value(name).type;
+    }
+
+    QMap<QByteArray, ByteArrayIntPairList> enum_list;
+
+    inline void addEnumValue(const QByteArray &enumname, const QByteArray &key, int value)
+    {
+        enum_list[enumname].append(ByteArrayIntPair(key, value));
+    }
+
+    inline bool hasEnum(const QByteArray &enumname)
+    {
+        return enum_list.contains(enumname);
+    }
+
+    QAxBase *that = nullptr;
+    QAxBasePrivate *d = nullptr;
+
+    IDispatch *disp = nullptr;
+    ITypeInfo *dispInfo = nullptr;
+    ITypeInfo *classInfo = nullptr;
+    ITypeLib *typelib = nullptr;
+    QByteArray current_typelib;
+
+    QSettings iidnames;
+    QString cacheKey;
+    QByteArray debugInfo;
+
+    QUuid iid_propNotifySink;
+
+    friend QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject);
+};
+
+QMetaObject *qax_readEnumInfo(ITypeLib *typeLib, const QMetaObject *parentObject)
+{
+    MetaObjectGenerator generator(typeLib, nullptr);
+
+    generator.readEnumInfo();
+    return generator.metaObject(parentObject, "EnumInfo");
+}
+
+QMetaObject *qax_readInterfaceInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject)
+{
+    MetaObjectGenerator generator(typeLib, typeInfo);
+
+    QString className;
+    BSTR bstr;
+    if (S_OK != typeInfo->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr))
+        return nullptr;
+
+    className = QString::fromWCharArray(bstr);
+    SysFreeString(bstr);
+
+    generator.readEnumInfo();
+    generator.readFuncsInfo(typeInfo, 0);
+    generator.readVarsInfo(typeInfo, 0);
+
+    return generator.metaObject(parentObject, className.toLatin1());
+}
+
+QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject)
+{
+    MetaObjectGenerator generator(typeLib, nullptr);
+    generator.addSignal("exception(int,QString,QString,QString)", "code,source,disc,help");
+    generator.addSignal("propertyChanged(QString)", "name");
+
+    QString className;
+    BSTR bstr;
+    if (S_OK != classInfo->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr))
+        return nullptr;
+
+    className = QString::fromWCharArray(bstr);
+    SysFreeString(bstr);
+
+    generator.readEnumInfo();
+
+    TYPEATTR *typeattr;
+    classInfo->GetTypeAttr(&typeattr);
+    if (typeattr) {
+        const UINT nInterfaces = typeattr->cImplTypes;
+        classInfo->ReleaseTypeAttr(typeattr);
+
+        for (UINT index = 0; index < nInterfaces; ++index) {
+            HREFTYPE refType;
+            if (S_OK != classInfo->GetRefTypeOfImplType(index, &refType))
+                continue;
+
+            int flags = 0;
+            classInfo->GetImplTypeFlags(index, &flags);
+            if (flags & IMPLTYPEFLAG_FRESTRICTED)
+                continue;
+
+            ITypeInfo *interfaceInfo = nullptr;
+            classInfo->GetRefTypeInfo(refType, &interfaceInfo);
+            if (!interfaceInfo)
+                continue;
+
+            interfaceInfo->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr);
+            QString interfaceName = QString::fromWCharArray(bstr);
+            SysFreeString(bstr);
+            QByteArray key;
+
+            TYPEATTR *typeattr = nullptr;
+            interfaceInfo->GetTypeAttr(&typeattr);
+
+            if (flags & IMPLTYPEFLAG_FSOURCE) {
+                if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN))
+                    key = "Event Interface " + QByteArray::number(index);
+                generator.readEventInterface(interfaceInfo, nullptr);
+            } else {
+                if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN))
+                    key = "Interface " + QByteArray::number(index);
+                generator.readFuncsInfo(interfaceInfo, 0);
+                generator.readVarsInfo(interfaceInfo, 0);
+            }
+            if (!key.isEmpty())
+                generator.addClassInfo(key.data(), interfaceName.toLatin1());
+
+            if (typeattr)
+                interfaceInfo->ReleaseTypeAttr(typeattr);
+            interfaceInfo->Release();
+        }
+    }
+
+    return generator.metaObject(parentObject, className.toLatin1());
+}
+
+void qax_deleteMetaObject(QMetaObject *metaObject)
+{
+    delete static_cast<QAxMetaObject *>(metaObject);
+}
+
+MetaObjectGenerator::MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr)
+    : that(ax), d(dptr),
+      iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat)
+{
+    init();
+}
+
+MetaObjectGenerator::MetaObjectGenerator(ITypeLib *tlib, ITypeInfo *tinfo)
+    : dispInfo(tinfo), typelib(tlib),
+      iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat)
+{
+    init();
+
+    if (dispInfo)
+        dispInfo->AddRef();
+    if (typelib) {
+        typelib->AddRef();
+        BSTR bstr;
+        typelib->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr);
+        current_typelib = QString::fromWCharArray(bstr).toLatin1();
+        SysFreeString(bstr);
+    }
+    readClassInfo();
+}
+
+void MetaObjectGenerator::init()
+{
+    if (d)
+        disp = d->dispatch();
+
+    iid_propNotifySink = IID_IPropertyNotifySink;
+
+    addSignal("signal(QString,int,void*)", "name,argc,argv");
+    addSignal("exception(int,QString,QString,QString)", "code,source,disc,help");
+    addSignal("propertyChanged(QString)", "name");
+    if (d || dispInfo) {
+        addProperty("QString", "control", Readable|Writable|Designable|Scriptable|Stored|Editable|StdCppSet);
+    }
+}
+
+MetaObjectGenerator::~MetaObjectGenerator()
+{
+    if (dispInfo) dispInfo->Release();
+    if (classInfo) classInfo->Release();
+    if (typelib) typelib->Release();
+}
+
+bool qax_dispatchEqualsIDispatch = true;
+QByteArrayList qax_qualified_usertypes;
+
+QByteArray MetaObjectGenerator::usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function)
+{
+    HREFTYPE usertype = tdesc.hreftype;
+    if (tdesc.vt != VT_USERDEFINED)
+        return nullptr;
+
+    QByteArray typeName;
+    ITypeInfo *usertypeinfo = nullptr;
+    info->GetRefTypeInfo(usertype, &usertypeinfo);
+    if (usertypeinfo) {
+        ITypeLib *usertypelib = nullptr;
+        UINT index;
+        usertypeinfo->GetContainingTypeLib(&usertypelib, &index);
+        if (usertypelib) {
+            // get type library name
+            BSTR typelibname = nullptr;
+            usertypelib->GetDocumentation(-1, &typelibname, nullptr, nullptr, nullptr);
+            QByteArray typeLibName = QString::fromWCharArray(typelibname).toLatin1();
+            SysFreeString(typelibname);
+
+            // get type name
+            BSTR usertypename = nullptr;
+            usertypelib->GetDocumentation(INT(index), &usertypename, nullptr, nullptr, nullptr);
+            QByteArray userTypeName = QString::fromWCharArray(usertypename).toLatin1();
+            SysFreeString(usertypename);
+
+            if (hasEnum(userTypeName)) // known enum?
+                typeName = userTypeName;
+            else if (userTypeName == "OLE_COLOR" || userTypeName == "VB_OLE_COLOR")
+                typeName = "QColor";
+            else if (userTypeName == "IFontDisp" || userTypeName == "IFontDisp*" || userTypeName == "IFont" || userTypeName == "IFont*")
+                typeName = "QFont";
+            else if (userTypeName == "Picture" || userTypeName == "Picture*")
+                typeName = "QPixmap";
+
+            if (typeName.isEmpty()) {
+                TYPEATTR *typeattr = nullptr;
+                usertypeinfo->GetTypeAttr(&typeattr);
+                if (typeattr) {
+                    switch(typeattr->typekind) {
+                    case TKIND_ALIAS:
+                        userTypeName = guessTypes(typeattr->tdescAlias, usertypeinfo, function);
+                        break;
+                    case TKIND_DISPATCH:
+                    case TKIND_COCLASS:
+                        if (qax_dispatchEqualsIDispatch) {
+                            userTypeName = "IDispatch";
+                        } else {
+                            if (typeLibName != current_typelib)
+                                userTypeName.prepend(typeLibName + "::");
+                            if (!qax_qualified_usertypes.contains(userTypeName))
+                                qax_qualified_usertypes << userTypeName;
+                        }
+                        break;
+                    case TKIND_ENUM:
+                        if (typeLibName != current_typelib)
+                            userTypeName.prepend(typeLibName + "::");
+                        if (!qax_qualified_usertypes.contains("enum " + userTypeName))
+                            qax_qualified_usertypes << "enum " + userTypeName;
+                        break;
+                    case TKIND_INTERFACE:
+                        if (typeLibName != current_typelib)
+                            userTypeName.prepend(typeLibName + "::");
+                        if (!qax_qualified_usertypes.contains(userTypeName))
+                            qax_qualified_usertypes << userTypeName;
+                        break;
+                    case TKIND_RECORD:
+                        if (!qax_qualified_usertypes.contains("struct " + userTypeName))
+                            qax_qualified_usertypes << "struct "+ userTypeName;
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                usertypeinfo->ReleaseTypeAttr(typeattr);
+                typeName = userTypeName;
+            }
+            usertypelib->Release();
+        }
+        usertypeinfo->Release();
+    }
+
+    return typeName;
+}
+
+#define VT_UNHANDLED(x) case VT_##x: qWarning("QAxBase: Unhandled type %s", #x); str = #x; break;
+
+QByteArray MetaObjectGenerator::guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function)
+{
+    QByteArray str;
+    switch (tdesc.vt) {
+    case VT_EMPTY:
+        break;
+    case VT_VOID:
+        str = "void";
+        break;
+    case VT_LPWSTR:
+        str = "wchar_t *";
+        break;
+    case VT_BSTR:
+        str = "QString";
+        break;
+    case VT_BOOL:
+        str = "bool";
+        break;
+    case VT_I1:
+        str = "char";
+        break;
+    case VT_I2:
+        str = "short";
+        break;
+    case VT_I4:
+    case VT_INT:
+        str = "int";
+        break;
+    case VT_I8:
+        str = "qlonglong";
+        break;
+    case VT_UI1:
+    case VT_UI2:
+    case VT_UI4:
+    case VT_UINT:
+        str = "uint";
+        break;
+    case VT_UI8:
+        str = "qulonglong";
+        break;
+    case VT_CY:
+        str = "qlonglong";
+        break;
+    case VT_R4:
+        str = "float";
+        break;
+    case VT_R8:
+        str = "double";
+        break;
+    case VT_DATE:
+        str = "QDateTime";
+        break;
+    case VT_DISPATCH:
+        str = "IDispatch*";
+        break;
+    case VT_VARIANT:
+        str = "QVariant";
+        break;
+    case VT_UNKNOWN:
+        str = "IUnknown*";
+        break;
+    case VT_HRESULT:
+        str = "HRESULT";
+        break;
+    case VT_PTR:
+        str = guessTypes(*tdesc.lptdesc, info, function);
+        switch(tdesc.lptdesc->vt) {
+        case VT_VOID:
+            str = "void*";
+            break;
+        case VT_VARIANT:
+        case VT_BSTR:
+        case VT_I1:
+        case VT_I2:
+        case VT_I4:
+        case VT_I8:
+        case VT_UI1:
+        case VT_UI2:
+        case VT_UI4:
+        case VT_UI8:
+        case VT_BOOL:
+        case VT_R4:
+        case VT_R8:
+        case VT_INT:
+        case VT_UINT:
+        case VT_CY:
+            str += '&';
+            break;
+        case VT_PTR:
+            if (str == "QFont" || str == "QPixmap") {
+                str += '&';
+                break;
+            } else if (str == "void*") {
+                str = "void **";
+                break;
+            }
+            // FALLTHROUGH
+        default:
+            if (str == "QColor")
+                str += '&';
+            else if (str == "QDateTime")
+                str += '&';
+            else if (str == "QVariantList")
+                str += '&';
+            else if (str == "QByteArray")
+                str += '&';
+            else if (str == "QStringList")
+                str += '&';
+            else if (!str.isEmpty() && hasEnum(str))
+                str += '&';
+            else if (!str.isEmpty() && str != "QFont" && str != "QPixmap" && str != "QVariant")
+                str += '*';
+        }
+        break;
+    case VT_SAFEARRAY:
+        switch(tdesc.lpadesc->tdescElem.vt) {
+        // some shortcuts, and generic support for lists of QVariant-supported types
+        case VT_UI1:
+            str = "QByteArray";
+            break;
+        case VT_BSTR:
+            str = "QStringList";
+            break;
+        case VT_VARIANT:
+            str = "QVariantList";
+            break;
+        default:
+            str = guessTypes(tdesc.lpadesc->tdescElem, info, function);
+            if (!str.isEmpty())
+                str = "QList<" + str + '>';
+            break;
+        }
+        break;
+    case VT_CARRAY:
+        str = guessTypes(tdesc.lpadesc->tdescElem, info, function);
+        if (!str.isEmpty()) {
+            for (int index = 0; index < tdesc.lpadesc->cDims; ++index)
+                str += '[' + QByteArray::number(int(tdesc.lpadesc->rgbounds[index].cElements)) + ']';
+        }
+        break;
+    case VT_USERDEFINED:
+        str = usertypeToString(tdesc, info, function);
+        break;
+
+    VT_UNHANDLED(FILETIME);
+    VT_UNHANDLED(BLOB);
+    VT_UNHANDLED(ERROR);
+    VT_UNHANDLED(DECIMAL);
+    VT_UNHANDLED(LPSTR);
+    default:
+        break;
+    }
+
+    if (tdesc.vt & VT_BYREF)
+        str += '&';
+
+    str.replace("&*", "**");
+    return str;
+}
+
+void MetaObjectGenerator::readClassInfo()
+{
+    // Read class information
+    IProvideClassInfo *provideClassInfo = nullptr;
+    if (d)
+        d->ptr->QueryInterface(IID_IProvideClassInfo, reinterpret_cast<void **>(&provideClassInfo));
+    if (provideClassInfo) {
+        provideClassInfo->GetClassInfo(&classInfo);
+        TYPEATTR *typeattr = nullptr;
+        if (classInfo)
+            classInfo->GetTypeAttr(&typeattr);
+
+        QString coClassID;
+        if (typeattr) {
+            QUuid clsid(typeattr->guid);
+            coClassID = clsid.toString().toUpper();
+#ifndef QAX_NO_CLASSINFO
+            // UUID
+            if (d->useClassInfo && !hasClassInfo("CoClass")) {
+                QString coClassIDstr = iidnames.value(QLatin1String("/CLSID/") + coClassID + QLatin1String("/Default"), coClassID).toString();
+                addClassInfo("CoClass", coClassIDstr.isEmpty() ? coClassID.toLatin1() : coClassIDstr.toLatin1());
+                QByteArray version = QByteArray::number(typeattr->wMajorVerNum) + '.' + QByteArray::number(typeattr->wMinorVerNum);
+                if (version != "0.0")
+                    addClassInfo("Version", version);
+            }
+#endif
+            classInfo->ReleaseTypeAttr(typeattr);
+        }
+        provideClassInfo->Release();
+        provideClassInfo = nullptr;
+
+        if (d->tryCache && !coClassID.isEmpty())
+            cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(coClassID)
+                .arg(d->useEventSink).arg(d->useClassInfo).arg(int(qax_dispatchEqualsIDispatch));
+    }
+
+    UINT index = 0;
+    if (disp && !dispInfo)
+        disp->GetTypeInfo(index, LOCALE_USER_DEFAULT, &dispInfo);
+
+    if (dispInfo && !typelib)
+        dispInfo->GetContainingTypeLib(&typelib, &index);
+
+    if (!typelib && !that->control().isEmpty()) {
+        QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software"), QSettings::NativeFormat);
+        QString tlid = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/TypeLib/.")).toString();
+        QString tlfile;
+        if (!tlid.isEmpty()) {
+            controls.beginGroup(QLatin1String("/Classes/TypeLib/") + tlid);
+            const QStringList versions = controls.childGroups();
+            for (const QString &version : versions) {
+                tlfile = controls.value(QLatin1Char('/') + version + QLatin1String("/0/win32/.")).toString();
+                if (!tlfile.isEmpty())
+                    break;
+            }
+            controls.endGroup();
+        } else {
+            tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/InprocServer32/.")).toString();
+            if (tlfile.isEmpty())
+                tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/LocalServer32/.")).toString();
+        }
+        if (!tlfile.isEmpty()) {
+            LoadTypeLib(reinterpret_cast<const OLECHAR *>(tlfile.utf16()), &typelib);
+            if (!typelib) {
+                tlfile.truncate(tlfile.lastIndexOf(QLatin1Char('.')));
+                tlfile += QLatin1String(".tlb");
+                LoadTypeLib(reinterpret_cast<const OLECHAR *>(tlfile.utf16()), &typelib);
+            }
+            if (!typelib) {
+                tlfile.truncate(tlfile.lastIndexOf(QLatin1Char('.')));
+                tlfile.append(QLatin1String(".olb"));
+                LoadTypeLib(reinterpret_cast<const OLECHAR *>(tlfile.utf16()), &typelib);
+            }
+        }
+    }
+
+    if (!classInfo && typelib && that)
+        typelib->GetTypeInfoOfGuid(QUuid(that->control()), &classInfo);
+
+    if (classInfo && !dispInfo) {
+        TYPEATTR *classAttr;
+        classInfo->GetTypeAttr(&classAttr);
+        if (classAttr) {
+            for (UINT i = 0; i < classAttr->cImplTypes; ++i) {
+                int typeFlags = 0;
+                classInfo->GetImplTypeFlags(i, &typeFlags);
+                if (typeFlags & IMPLTYPEFLAG_FSOURCE)
+                    continue;
+
+                HREFTYPE hrefType;
+                if (S_OK == classInfo->GetRefTypeOfImplType(i, &hrefType))
+                    classInfo->GetRefTypeInfo(hrefType, &dispInfo);
+                if (dispInfo) {
+                    TYPEATTR *ifaceAttr;
+                    dispInfo->GetTypeAttr(&ifaceAttr);
+                    WORD typekind = ifaceAttr->typekind;
+                    dispInfo->ReleaseTypeAttr(ifaceAttr);
+
+                    if (typekind & TKIND_DISPATCH) {
+                        break;
+                    } else {
+                        dispInfo->Release();
+                        dispInfo = nullptr;
+                    }
+                }
+            }
+            classInfo->ReleaseTypeAttr(classAttr);
+        }
+    }
+
+    if (!d || !dispInfo || !cacheKey.isEmpty() || !d->tryCache) {
+        if (disp && !dispInfo)
+            qWarning("%s: IDispatch %p does not provide interface information", Q_FUNC_INFO, disp);
+        return;
+    }
+
+    TYPEATTR *typeattr = nullptr;
+    dispInfo->GetTypeAttr(&typeattr);
+
+    QString interfaceID;
+    if (typeattr) {
+        QUuid iid(typeattr->guid);
+        interfaceID = iid.toString().toUpper();
+
+        dispInfo->ReleaseTypeAttr(typeattr);
+        // ### event interfaces!!
+        if (!interfaceID.isEmpty())
+            cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(interfaceID)
+                .arg(d->useEventSink).arg(d->useClassInfo).arg(int(qax_dispatchEqualsIDispatch));
+    }
+}
+
+void MetaObjectGenerator::readEnumInfo()
+{
+    if (!typelib)
+        return;
+
+    QUuid libUuid;
+
+    if (d && d->tryCache) {
+        TLIBATTR *libAttr = nullptr;
+        typelib->GetLibAttr(&libAttr);
+        if (libAttr) {
+            libUuid = QUuid(libAttr->guid);
+            typelib->ReleaseTLibAttr(libAttr);
+            enum_list = enum_cache.value(libUuid);
+            if (!enum_list.isEmpty())
+                return;
+        }
+    }
+
+    int valueindex = 0;
+    QSet<QString> clashCheck;
+    int clashIndex = 0;
+
+    int enum_serial = 0;
+    UINT index = typelib->GetTypeInfoCount();
+    for (UINT i = 0; i < index; ++i) {
+        TYPEKIND typekind;
+        typelib->GetTypeInfoType(i, &typekind);
+        if (typekind == TKIND_ENUM) {
+            // Get the type information for the enum
+            ITypeInfo *enuminfo = nullptr;
+            typelib->GetTypeInfo(i, &enuminfo);
+            if (!enuminfo)
+                continue;
+
+            // Get the name of the enumeration
+            BSTR enumname;
+            QByteArray enumName;
+            if (typelib->GetDocumentation(INT(i), &enumname, nullptr, nullptr, nullptr) == S_OK) {
+                enumName = QString::fromWCharArray(enumname).toLatin1();
+                SysFreeString(enumname);
+            } else {
+                enumName = "enum" + QByteArray::number(++enum_serial);
+            }
+
+            // Get the attributes of the enum type
+            TYPEATTR *typeattr = nullptr;
+            enuminfo->GetTypeAttr(&typeattr);
+            if (typeattr) {
+                // Get all values of the enumeration
+                for (UINT vd = 0; vd < typeattr->cVars; ++vd) {
+                    VARDESC *vardesc = nullptr;
+                    enuminfo->GetVarDesc(vd, &vardesc);
+                    if (vardesc && vardesc->varkind == VAR_CONST) {
+                        int value = vardesc->lpvarValue->lVal;
+                        int memid = vardesc->memid;
+                        // Get the name of the value
+                        QByteArray valueName = qaxTypeInfoName(enuminfo, memid);
+                        if (valueName.isEmpty())
+                            valueName = "value" + QByteArray::number(valueindex++);
+                        if (clashCheck.contains(QString::fromLatin1(valueName)))
+                            valueName += QByteArray::number(++clashIndex);
+
+                        clashCheck.insert(QString::fromLatin1(valueName));
+                        addEnumValue(enumName, valueName, value);
+                    }
+                    enuminfo->ReleaseVarDesc(vardesc);
+                }
+            }
+            enuminfo->ReleaseTypeAttr(typeattr);
+            enuminfo->Release();
+        }
+    }
+
+    if (!libUuid.isNull())
+        enum_cache.insert(libUuid, enum_list);
+}
+
+void MetaObjectGenerator::addChangedSignal(const QByteArray &function, const QByteArray &type, int memid)
+{
+    QAxEventSink *eventSink = nullptr;
+    if (d) {
+        eventSink = d->eventSink.value(iid_propNotifySink);
+        if (!eventSink && d->useEventSink) {
+            eventSink = new QAxEventSink(that);
+            d->eventSink.insert(iid_propNotifySink, eventSink);
+        }
+    }
+    // generate changed signal
+    QByteArray signalName(function);
+    signalName += "Changed";
+    QByteArray signalProto = signalName + '(' + replaceType(type) + ')';
+    if (!hasSignal(signalProto))
+        addSignal(signalProto, function);
+    if (eventSink)
+        eventSink->addProperty(memid, function, signalProto);
+}
+
+void MetaObjectGenerator::addSetterSlot(const QByteArray &property)
+{
+    QByteArray prototype(property);
+    if (isupper(prototype.at(0))) {
+        prototype.insert(0, "Set");
+    } else {
+        prototype[0] = QtMiscUtils::toAsciiUpper(prototype[0]);
+        prototype.insert(0, "set");
+    }
+    const QByteArray type = propertyType(property);
+    if (type.isEmpty() || type == "void") {
+        qWarning("%s: Invalid property '%s' of type '%s' encountered.",
+                 Q_FUNC_INFO, property.constData(), type.constData());
+    } else {
+        prototype += '(';
+        prototype += type;
+        prototype += ')';
+        if (!hasSlot(prototype))
+            addSlot("void", prototype, property);
+    }
+}
+
+QByteArray MetaObjectGenerator::createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QByteArrayList &names,
+                                                QByteArray &type, QByteArrayList &parameters)
+{
+    const QByteArray &function = names.at(0);
+    const QByteArray hresult("HRESULT");
+    // get function prototype
+    type = guessTypes(funcdesc->elemdescFunc.tdesc, typeinfo, function);
+    if ((type.isEmpty() || type == hresult || type == "void") &&
+        (funcdesc->invkind == INVOKE_PROPERTYPUT || funcdesc->invkind == INVOKE_PROPERTYPUTREF) &&
+        funcdesc->lprgelemdescParam) {
+        type = guessTypes(funcdesc->lprgelemdescParam->tdesc, typeinfo, function);
+    }
+
+    QByteArray prototype = function + '(';
+    if (funcdesc->invkind == INVOKE_FUNC && type == hresult)
+        type = nullptr;
+
+    int p;
+    for (p = 1; p < names.count(); ++p) {
+        // parameter
+        QByteArray paramName = names.at(p);
+        bool optional = p > (funcdesc->cParams - funcdesc->cParamsOpt);
+        TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc;
+        PARAMDESC pdesc = funcdesc->lprgelemdescParam[p-1].paramdesc;
+
+        QByteArray ptype = guessTypes(tdesc, typeinfo, function);
+        if (pdesc.wParamFlags & PARAMFLAG_FRETVAL) {
+            if (ptype.endsWith('&')) {
+                ptype.truncate(ptype.length() - 1);
+            } else if (ptype.endsWith("**")) {
+                ptype.truncate(ptype.length() - 1);
+            }
+            type = ptype;
+        } else {
+            prototype += ptype;
+            if (pdesc.wParamFlags & PARAMFLAG_FOUT && !ptype.endsWith('&') && !ptype.endsWith("**"))
+                prototype += '&';
+            if (optional || pdesc.wParamFlags & PARAMFLAG_FOPT)
+                paramName += "=0";
+            else if (pdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
+                // ### get the value from pdesc.pparamdescex
+                paramName += "=0";
+            }
+            parameters << paramName;
+        }
+        if (p < funcdesc->cParams && !(pdesc.wParamFlags & PARAMFLAG_FRETVAL))
+            prototype += ',';
+    }
+
+    if (!prototype.isEmpty()) {
+        if (prototype.endsWith(',')) {
+            if ((funcdesc->invkind == INVOKE_PROPERTYPUT || funcdesc->invkind == INVOKE_PROPERTYPUTREF) &&
+                p == funcdesc->cParams) {
+                TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc;
+                QByteArray ptype = guessTypes(tdesc, typeinfo, function);
+                prototype += ptype;
+                prototype += ')';
+                parameters << "rhs";
+            } else {
+                prototype[prototype.length()-1] = ')';
+            }
+        } else {
+            prototype += ')';
+        }
+    }
+
+    return prototype;
+}
+
+void MetaObjectGenerator::readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs)
+{
+    if (!nFuncs) {
+        TYPEATTR *typeattr = nullptr;
+        typeinfo->GetTypeAttr(&typeattr);
+        if (typeattr) {
+            nFuncs = typeattr->cFuncs;
+            typeinfo->ReleaseTypeAttr(typeattr);
+        }
+    }
+
+    // get information about all functions
+    for (ushort fd = 0; fd < nFuncs ; ++fd) {
+        FUNCDESC *funcdesc = nullptr;
+        typeinfo->GetFuncDesc(fd, &funcdesc);
+        if (!funcdesc)
+            break;
+
+        QByteArray type;
+        QByteArray prototype;
+        QByteArrayList parameters;
+
+        // parse function description
+        const QByteArrayList names = qaxTypeInfoNames(typeinfo, funcdesc->memid);
+        const int maxNamesOut = names.size();
+        // function name
+        const QByteArray &function = names.at(0);
+        if ((maxNamesOut == 3 && function == "QueryInterface") ||
+            (maxNamesOut == 1 && function == "AddRef") ||
+            (maxNamesOut == 1 && function == "Release") ||
+            (maxNamesOut == 9 && function == "Invoke") ||
+            (maxNamesOut == 6 && function == "GetIDsOfNames") ||
+            (maxNamesOut == 2 && function == "GetTypeInfoCount") ||
+            (maxNamesOut == 4 && function == "GetTypeInfo")) {
+            typeinfo->ReleaseFuncDesc(funcdesc);
+            continue;
+        }
+
+        prototype = createPrototype(/*in*/ funcdesc, typeinfo, names, /*out*/type, parameters);
+
+        // get type of function
+        switch(funcdesc->invkind) {
+        case INVOKE_PROPERTYGET: // property
+        case INVOKE_PROPERTYPUT:
+        case INVOKE_PROPERTYPUTREF:
+            if (funcdesc->cParams - funcdesc->cParamsOpt <= 1) {
+                bool dontBreak = false;
+                // getter with non-default-parameters -> fall through to function handling
+                if (funcdesc->invkind == INVOKE_PROPERTYGET && parameters.count() && funcdesc->cParams - funcdesc->cParamsOpt) {
+                    dontBreak = true;
+                } else {
+                    uint flags = Readable;
+                    if (funcdesc->invkind != INVOKE_PROPERTYGET)
+                        flags |= Writable;
+                    if (!(funcdesc->wFuncFlags & (FUNCFLAG_FNONBROWSABLE | FUNCFLAG_FHIDDEN)))
+                        flags |= Designable;
+                    if (!(funcdesc->wFuncFlags & FUNCFLAG_FRESTRICTED))
+                        flags |= Scriptable;
+                    if (funcdesc->wFuncFlags & FUNCFLAG_FREQUESTEDIT)
+                        flags |= RequestingEdit;
+                    if (hasEnum(type))
+                        flags |= EnumOrFlag;
+
+                    if (funcdesc->wFuncFlags & FUNCFLAG_FBINDABLE && funcdesc->invkind == INVOKE_PROPERTYGET) {
+                        addChangedSignal(function, type, funcdesc->memid);
+                        flags |= Bindable;
+                    }
+                    // Don't generate code for properties without type
+                    if (type.isEmpty() || type == "void")
+                        break;
+                    addProperty(type, function, flags);
+
+                    // more parameters -> function handling
+                    if (funcdesc->invkind == INVOKE_PROPERTYGET && funcdesc->cParams)
+                        dontBreak = true;
+                }
+
+                if (!funcdesc->cParams) {
+                    // don't generate slots for incomplete properties
+                    if (type.isEmpty())
+                        break;
+
+                    // Done for getters
+                    if (funcdesc->invkind == INVOKE_PROPERTYGET)
+                        break;
+
+                    // generate setter slot
+                    if ((funcdesc->invkind == INVOKE_PROPERTYPUT || funcdesc->invkind == INVOKE_PROPERTYPUTREF) &&
+                        hasProperty(function)) {
+                        addSetterSlot(function);
+                        break;
+                    }
+                } else if ((funcdesc->invkind == INVOKE_PROPERTYPUT || funcdesc->invkind == INVOKE_PROPERTYPUTREF) &&
+                           hasProperty(function)) {
+                    addSetterSlot(function);
+                    // more parameters -> function handling
+                    if (funcdesc->cParams > 1)
+                        dontBreak = true;
+                }
+                if (!dontBreak)
+                    break;
+            }
+            if (funcdesc->invkind == INVOKE_PROPERTYPUT || funcdesc->invkind == INVOKE_PROPERTYPUTREF) {
+                // remove the typename guessed for property setters
+                // its done only for setter's with more than one parameter.
+                if (funcdesc->cParams - funcdesc->cParamsOpt > 1) {
+                    type.clear();
+                }
+                QByteArray set;
+                if (isupper(prototype.at(0))) {
+                    set = "Set";
+                } else {
+                    set = "set";
+                    prototype[0] = QtMiscUtils::toAsciiUpper(prototype[0]);
+                }
+
+                prototype = set + prototype;
+            }
+            Q_FALLTHROUGH(); // Fall through to support multi-variate properties
+        case INVOKE_FUNC: // method
+            {
+                bool cloned = false;
+                bool defargs;
+                do {
+                    QByteArray pnames;
+                    for (int p = 0; p < parameters.count(); ++p) {
+                        pnames += parameters.at(p);
+                        if (p < parameters.count() - 1)
+                            pnames += ',';
+                    }
+                    defargs = pnames.contains("=0");
+                    int flags = QMetaMethod::Public;
+                    if (cloned)
+                        flags |= QMetaMethod::Cloned << 4;
+                    cloned |= defargs;
+                    addSlot(type, prototype, pnames.replace("=0", ""), flags);
+
+                    if (defargs) {
+                        parameters.takeLast();
+                        int lastParam = prototype.lastIndexOf(',');
+                        if (lastParam == -1)
+                            lastParam = prototype.indexOf('(') + 1;
+                        prototype.truncate(lastParam);
+                        prototype += ')';
+                    }
+                } while (defargs);
+            }
+            break;
+
+        default:
+            break;
+        }
+#if 0 // documentation in metaobject would be cool?
+        // get function documentation
+        BSTR bstrDocu;
+        info->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0);
+        QString strDocu = QString::fromWCharArray(bstrDocu);
+        SysFreeString(bstrDocu);
+        if (!!strDocu)
+            desc += '[' + strDocu + ']';
+        desc += '\n';
+#endif
+        typeinfo->ReleaseFuncDesc(funcdesc);
+    }
+}
+
+void MetaObjectGenerator::readVarsInfo(ITypeInfo *typeinfo, ushort nVars)
+{
+    if (!nVars) {
+        TYPEATTR *typeattr = nullptr;
+        typeinfo->GetTypeAttr(&typeattr);
+        if (typeattr) {
+            nVars = typeattr->cVars;
+            typeinfo->ReleaseTypeAttr(typeattr);
+        }
+    }
+
+    // get information about all variables
+    for (ushort vd = 0; vd < nVars; ++vd) {
+        VARDESC *vardesc;
+        typeinfo->GetVarDesc(vd, &vardesc);
+        if (!vardesc)
+            break;
+
+        // no use if it's not a dispatched variable
+        if (vardesc->varkind != VAR_DISPATCH) {
+            typeinfo->ReleaseVarDesc(vardesc);
+            continue;
+        }
+
+        // get variable name
+        const QByteArray variableName = qaxTypeInfoName(typeinfo, vardesc->memid);
+        if (variableName.isEmpty()) {
+            typeinfo->ReleaseVarDesc(vardesc);
+            continue;
+        }
+
+        uint flags = 0;
+
+        // get variable type
+        TYPEDESC typedesc = vardesc->elemdescVar.tdesc;
+        const QByteArray variableType = guessTypes(typedesc, typeinfo, variableName);
+
+        // generate meta property
+        if (!hasProperty(variableName)) {
+            flags = Readable;
+            if (!(vardesc->wVarFlags & VARFLAG_FREADONLY))
+                flags |= Writable;
+            if (!(vardesc->wVarFlags & (VARFLAG_FNONBROWSABLE | VARFLAG_FHIDDEN)))
+                flags |= Designable;
+            if (!(vardesc->wVarFlags & VARFLAG_FRESTRICTED))
+                flags |= Scriptable;
+            if (vardesc->wVarFlags & VARFLAG_FREQUESTEDIT)
+                flags |= RequestingEdit;
+            if (hasEnum(variableType))
+                flags |= EnumOrFlag;
+
+            if (vardesc->wVarFlags & VARFLAG_FBINDABLE) {
+                addChangedSignal(variableName, variableType, vardesc->memid);
+                flags |= Bindable;
+            }
+            addProperty(variableType, variableName, flags);
+        }
+
+        // generate a set slot
+        if (!(vardesc->wVarFlags & VARFLAG_FREADONLY))
+            addSetterSlot(variableName);
+
+#if 0 // documentation in metaobject would be cool?
+        // get function documentation
+        BSTR bstrDocu;
+        info->GetDocumentation(vardesc->memid, 0, &bstrDocu, 0, 0);
+        QString strDocu = QString::fromWCharArray(bstrDocu);
+        SysFreeString(bstrDocu);
+        if (!!strDocu)
+            desc += '[' + strDocu + ']';
+        desc += '\n';
+#endif
+        typeinfo->ReleaseVarDesc(vardesc);
+    }
+}
+
+void MetaObjectGenerator::readInterfaceInfo()
+{
+    ITypeInfo *typeinfo = dispInfo;
+    if (!typeinfo)
+        return;
+    typeinfo->AddRef();
+    int interface_serial = 0;
+    while (typeinfo) {
+        ushort nFuncs = 0;
+        ushort nVars = 0;
+        ushort nImpl = 0;
+        // get information about type
+        TYPEATTR *typeattr;
+        typeinfo->GetTypeAttr(&typeattr);
+        bool interesting = true;
+        if (typeattr) {
+            // get number of functions, variables, and implemented interfaces
+            nFuncs = typeattr->cFuncs;
+            nVars = typeattr->cVars;
+            nImpl = typeattr->cImplTypes;
+
+            if ((typeattr->typekind == TKIND_DISPATCH || typeattr->typekind == TKIND_INTERFACE) &&
+                (typeattr->guid != IID_IDispatch && typeattr->guid != IID_IUnknown)) {
+#ifndef QAX_NO_CLASSINFO
+                if (d && d->useClassInfo) {
+                    // UUID
+                    QUuid uuid(typeattr->guid);
+                    QString uuidstr = uuid.toString().toUpper();
+                    uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString();
+                    addClassInfo("Interface " + QByteArray::number(++interface_serial), uuidstr.toLatin1());
+                }
+#endif
+                typeinfo->ReleaseTypeAttr(typeattr);
+            } else {
+                interesting = false;
+                typeinfo->ReleaseTypeAttr(typeattr);
+            }
+        }
+
+        if (interesting) {
+            readFuncsInfo(typeinfo, nFuncs);
+            readVarsInfo(typeinfo, nVars);
+        }
+
+        if (!nImpl) {
+            typeinfo->Release();
+            typeinfo = nullptr;
+            break;
+        }
+
+        // go up one base class
+        HREFTYPE pRefType;
+        typeinfo->GetRefTypeOfImplType(0, &pRefType);
+        ITypeInfo *baseInfo = nullptr;
+        typeinfo->GetRefTypeInfo(pRefType, &baseInfo);
+        typeinfo->Release();
+        if (typeinfo == baseInfo) { // IUnknown inherits IUnknown ???
+            baseInfo->Release();
+            typeinfo = nullptr;
+            break;
+        }
+        typeinfo = baseInfo;
+    }
+}
+
+void MetaObjectGenerator::readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint)
+{
+    TYPEATTR *eventattr;
+    eventinfo->GetTypeAttr(&eventattr);
+    if (!eventattr)
+        return;
+    if (eventattr->typekind != TKIND_DISPATCH) {
+        eventinfo->ReleaseTypeAttr(eventattr);
+        return;
+    }
+
+    QAxEventSink *eventSink = nullptr;
+    if (d) {
+        IID conniid;
+        cpoint->GetConnectionInterface(&conniid);
+        eventSink = d->eventSink.value(QUuid(conniid));
+        if (!eventSink) {
+            eventSink = new QAxEventSink(that);
+            d->eventSink.insert(QUuid(conniid), eventSink);
+            eventSink->advise(cpoint, conniid);
+        }
+    }
+
+    // get information about all event functions
+    for (UINT fd = 0; fd < eventattr->cFuncs; ++fd) {
+        FUNCDESC *funcdesc;
+        eventinfo->GetFuncDesc(fd, &funcdesc);
+        if (!funcdesc)
+            break;
+        if (funcdesc->invkind != INVOKE_FUNC ||
+            funcdesc->funckind != FUNC_DISPATCH) {
+            eventinfo->ReleaseFuncDesc(funcdesc);
+            continue;
+        }
+
+        QByteArray prototype;
+        QByteArrayList parameters;
+
+        // parse event function description, get event function prototype
+        const QByteArrayList names = qaxTypeInfoNames(eventinfo, funcdesc->memid);
+
+        QByteArray type; // dummy - we don't care about return values for signals
+        prototype = createPrototype(/*in*/ funcdesc, eventinfo, names, /*out*/type, parameters);
+        if (!hasSignal(prototype)) {
+            QByteArray pnames;
+            for (int p = 0; p < parameters.count(); ++p) {
+                pnames += parameters.at(p);
+                if (p < parameters.count() - 1)
+                    pnames += ',';
+            }
+            addSignal(prototype, pnames);
+        }
+        if (eventSink)
+            eventSink->addSignal(funcdesc->memid, prototype);
+
+#if 0 // documentation in metaobject would be cool?
+        // get function documentation
+        BSTR bstrDocu;
+        eventinfo->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0);
+        QString strDocu = QString::fromWCharArray(bstrDocu);
+        SysFreeString(bstrDocu);
+        if (!!strDocu)
+            desc += '[' + strDocu + ']';
+        desc += '\n';
+#endif
+        eventinfo->ReleaseFuncDesc(funcdesc);
+    }
+    eventinfo->ReleaseTypeAttr(eventattr);
+}
+
+void MetaObjectGenerator::readEventInfo()
+{
+    int event_serial = 0;
+    IConnectionPointContainer *cpoints = nullptr;
+    if (d && d->useEventSink)
+        d->ptr->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void **>(&cpoints));
+    if (cpoints) {
+        // Get connection point enumerator
+        IEnumConnectionPoints *epoints = nullptr;
+        cpoints->EnumConnectionPoints(&epoints);
+        if (epoints) {
+            ULONG c = 1;
+            IConnectionPoint *cpoint = nullptr;
+            epoints->Reset();
+            QList<QUuid> cpointlist;
+            do {
+                if (cpoint) cpoint->Release();
+                cpoint = nullptr;
+                HRESULT hr = epoints->Next(c, &cpoint, &c);
+                if (!c || hr != S_OK)
+                    break;
+
+                IID conniid;
+                cpoint->GetConnectionInterface(&conniid);
+                // workaround for typelibrary bug of Word.Application
+                QUuid connuuid(conniid);
+                if (cpointlist.contains(connuuid))
+                    break;
+
+#ifndef QAX_NO_CLASSINFO
+                if (d->useClassInfo) {
+                    QString uuidstr = connuuid.toString().toUpper();
+                    uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString();
+                    addClassInfo("Event Interface " + QByteArray::number(++event_serial), uuidstr.toLatin1());
+                }
+#endif
+
+                // get information about type
+                if (conniid == IID_IPropertyNotifySink) {
+                    // test whether property notify sink has been created already, and advise on it
+                    QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink);
+                    if (eventSink)
+                        eventSink->advise(cpoint, conniid);
+                    continue;
+                }
+
+                ITypeInfo *eventinfo = nullptr;
+                if (typelib)
+                    typelib->GetTypeInfoOfGuid(conniid, &eventinfo);
+
+                if (eventinfo) {
+                    // avoid recursion (see workaround above)
+                    cpointlist.append(connuuid);
+
+                    readEventInterface(eventinfo, cpoint);
+                    eventinfo->Release();
+                }
+            } while (c);
+            if (cpoint) cpoint->Release();
+            epoints->Release();
+        } else if (classInfo) { // no enumeration - search source interfaces and ask for those
+            TYPEATTR *typeattr = nullptr;
+            classInfo->GetTypeAttr(&typeattr);
+            if (typeattr) {
+                for (UINT i = 0; i < typeattr->cImplTypes; ++i) {
+                    int flags = 0;
+                    classInfo->GetImplTypeFlags(i, &flags);
+                    if (!(flags & IMPLTYPEFLAG_FSOURCE))
+                        continue;
+                    HREFTYPE reference;
+                    if (S_OK != classInfo->GetRefTypeOfImplType(i, &reference))
+                        continue;
+                    ITypeInfo *eventInfo = nullptr;
+                    classInfo->GetRefTypeInfo(reference, &eventInfo);
+                    if (!eventInfo)
+                        continue;
+                    TYPEATTR *eventattr = nullptr;
+                    eventInfo->GetTypeAttr(&eventattr);
+                    if (eventattr) {
+                        IConnectionPoint *cpoint = nullptr;
+                        cpoints->FindConnectionPoint(eventattr->guid, &cpoint);
+                        if (cpoint) {
+                            if (eventattr->guid == IID_IPropertyNotifySink) {
+                                // test whether property notify sink has been created already, and advise on it
+                                QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink);
+                                if (eventSink)
+                                    eventSink->advise(cpoint, eventattr->guid);
+                                continue;
+                            }
+
+                            readEventInterface(eventInfo, cpoint);
+                            cpoint->Release();
+                        }
+                        eventInfo->ReleaseTypeAttr(eventattr);
+                    }
+                    eventInfo->Release();
+                }
+                classInfo->ReleaseTypeAttr(typeattr);
+            }
+        }
+        cpoints->Release();
+    }
+}
+
+QMetaObject *MetaObjectGenerator::tryCache()
+{
+    if (!cacheKey.isEmpty()) {
+        d->metaobj = mo_cache.value(cacheKey);
+        if (d->metaobj) {
+            d->cachedMetaObject = true;
+
+            IConnectionPointContainer *cpoints = nullptr;
+            d->ptr->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void **>(&cpoints));
+            if (cpoints) {
+                for (const QUuid &iid : qAsConst(d->metaobj->connectionInterfaces)) {
+                    IConnectionPoint *cpoint = nullptr;
+                    cpoints->FindConnectionPoint(iid, &cpoint);
+                    if (cpoint) {
+                        QAxEventSink *sink = new QAxEventSink(that);
+                        sink->advise(cpoint, iid);
+                        d->eventSink.insert(iid, sink);
+                        sink->sigs = d->metaobj->sigs.value(iid);
+                        sink->props = d->metaobj->props.value(iid);
+                        sink->propsigs = d->metaobj->propsigs.value(iid);
+                        cpoint->Release();
+                    }
+                }
+                cpoints->Release();
+            }
+
+            return d->metaobj;
+        }
+    }
+    return nullptr;
+}
+
+static int nameToBuiltinType(const QByteArray &typeName)
+{
+    int id = QMetaType::type(typeName);
+    return (id < QMetaType::User) ? id : QMetaType::UnknownType;
+}
+
+static uint nameToTypeInfo(const QByteArray &typeName, QMetaStringTable &strings)
+{
+    int id = nameToBuiltinType(typeName);
+    const int result = id != QMetaType::UnknownType
+        ? id : (IsUnresolvedType) | strings.enter(typeName);
+    return uint(result);
+}
+
+// Returns the sum of all parameters (including return type) for the given
+// \a map of methods. This is needed for calculating the size of the methods'
+// parameter type/name meta-data.
+int MetaObjectGenerator::aggregateParameterCount(const QMap<QByteArray, Method> &map)
+{
+    int sum = 0;
+    QMap<QByteArray, Method>::const_iterator it;
+    for (it = map.constBegin(); it != map.constEnd(); ++it)
+        sum += paramList(it.key()).size() + 1; // +1 for return type
+    return sum;
+}
+
+QMetaObject *MetaObjectGenerator::metaObject(const QMetaObject *parentObject, const QByteArray &className)
+{
+    if (that) {
+        readClassInfo();
+        if (typelib) {
+            BSTR bstr;
+            typelib->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr);
+            current_typelib = QString::fromWCharArray(bstr).toLatin1();
+            SysFreeString(bstr);
+        }
+        if (d->tryCache && tryCache())
+            return d->metaobj;
+        readEnumInfo();
+        readInterfaceInfo();
+        readEventInfo();
+    }
+
+    current_typelib = QByteArray();
+
+#ifndef QAX_NO_CLASSINFO
+    if (!debugInfo.isEmpty() && d->useClassInfo)
+        addClassInfo("debugInfo", debugInfo);
+#endif
+
+    QAxMetaObject *metaobj = new QAxMetaObject;
+
+    int paramsDataSize =
+            ((aggregateParameterCount(signal_list)
+              + aggregateParameterCount(slot_list)) * 2) // types and parameter names
+            - signal_list.count() // return "parameters" don't have names
+            - slot_list.count(); // ditto
+
+    int int_data_size = MetaObjectPrivateFieldCount;
+    int_data_size += classinfo_list.count() * 2;
+    int_data_size += (signal_list.count() + slot_list.count()) * 5 + paramsDataSize;
+    int_data_size += property_list.count() * 3;
+    int_data_size += enum_list.count() * 5;
+    for (auto it = enum_list.cbegin(), end = enum_list.cend(); it != end; ++it)
+        int_data_size += it.value().count() * 2;
+    ++int_data_size; // eod
+
+    uint *int_data = new uint[int_data_size];
+    QMetaObjectPrivate *header = reinterpret_cast<QMetaObjectPrivate *>(int_data);
+    Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 8, "QtDBus meta-object generator should generate the same version as moc");
+    header->revision = QMetaObjectPrivate::OutputRevision;
+    header->className = 0;
+    header->classInfoCount = classinfo_list.count();
+    header->classInfoData = MetaObjectPrivateFieldCount;
+    header->methodCount = signal_list.count() + slot_list.count();
+    header->methodData = header->classInfoData + header->classInfoCount * 2;
+    header->propertyCount = property_list.count();
+    header->propertyData = header->methodData + header->methodCount * 5 + paramsDataSize;
+    header->enumeratorCount = enum_list.count();
+    header->enumeratorData = header->propertyData + header->propertyCount * 3;
+    header->constructorCount = 0;
+    header->constructorData = 0;
+    header->flags = 0;
+    header->signalCount = signal_list.count();
+
+    QByteArray classNameForMetaObject = className;
+    if (that)
+        classNameForMetaObject = that->className();
+    QMetaStringTable strings(classNameForMetaObject);
+
+    int offset = header->classInfoData;
+
+    // each class info in form key\0value\0
+    for (auto it = classinfo_list.cbegin(), cend = classinfo_list.cend(); it != cend; ++it) {
+        int_data[offset++] = uint(strings.enter(it.key()));
+        int_data[offset++] = uint(strings.enter(it.value()));
+    }
+    Q_ASSERT(offset == header->methodData);
+
+    int paramsOffset = offset + header->methodCount * 5;
+    // add each method:
+    for (int x = 0; x < 2; ++x) {
+        // Signals must be added before other methods, to match moc.
+        const QMap<QByteArray, Method> &map = (x == 0) ? signal_list : slot_list;
+        for (QMap<QByteArray, Method>::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) {
+            QByteArray prototype(QMetaObject::normalizedSignature(it.key()));
+            QByteArray name = prototype.left(prototype.indexOf('('));
+            const auto paramTypeNames = paramList(prototype);
+            const QByteArrayList paramNames = it.value().parameters.isEmpty()
+                ? QByteArrayList() : it.value().parameters.split(',');
+            Q_ASSERT(paramTypeNames.size() == paramNames.size());
+            if (!it.value().realPrototype.isEmpty())
+                metaobj->realPrototype.insert(prototype, it.value().realPrototype);
+            int argc = paramTypeNames.size();
+            QByteArray tag;
+            int_data[offset++] = uint(strings.enter(name));
+            int_data[offset++] = uint(argc);
+            int_data[offset++] = uint(paramsOffset);
+            int_data[offset++] = uint(strings.enter(tag));
+            int_data[offset++] = uint(it.value().flags);
+
+            // Parameter types
+            for (int i = -1; i < argc; ++i) {
+                QByteArray typeName = (i < 0) ? it.value().type : paramTypeNames.at(i);
+                int_data[paramsOffset++] = nameToTypeInfo(typeName, strings);
+            }
+            // Parameter names
+            for (int i = 0; i < argc; ++i)
+                int_data[paramsOffset++] = uint(strings.enter(paramNames.at(i)));
+        }
+    }
+    Q_ASSERT(offset == header->methodData + header->methodCount * 5);
+    Q_ASSERT(paramsOffset = header->propertyData);
+    offset += paramsDataSize;
+    Q_ASSERT(offset == header->propertyData);
+
+    // each property in form name\0type\0
+    for (auto it = property_list.cbegin(), end = property_list.cend(); it != end; ++it) {
+        const QByteArray &name = it.key();
+        const QByteArray &type = it.value().type;
+        Q_ASSERT(!type.isEmpty());
+        QByteArray realType(it.value().realType);
+        if (!realType.isEmpty() && realType != type)
+            metaobj->realPrototype.insert(name, realType);
+        int_data[offset++] = uint(strings.enter(name));
+        int_data[offset++] = nameToTypeInfo(type, strings);
+        int_data[offset++] = uint(it.value().flags);
+    }
+    Q_ASSERT(offset == header->enumeratorData);
+
+    int value_offset = offset + enum_list.count() * 5;
+    // each enum in form name\0
+    for (auto it = enum_list.cbegin(), end = enum_list.cend(); it != end; ++it) {
+        QByteArray name(it.key());
+        int count = it.value().count();
+
+        uint nameId = uint(strings.enter(name));
+        int_data[offset++] = nameId;
+        int_data[offset++] = nameId;
+        int_data[offset++] = 0x0; // 0x1 for flag?
+        int_data[offset++] = uint(count);
+        int_data[offset++] = uint(value_offset);
+        value_offset += count * 2;
+    }
+    Q_ASSERT(offset == header->enumeratorData + enum_list.count() * 5);
+
+    // each enum value in form key\0
+    for (auto it = enum_list.cbegin(), end = enum_list.cend(); it != end; ++it) {
+        for (const auto &e : it.value()) {
+            int_data[offset++] = uint(strings.enter(e.first));
+            int_data[offset++] = uint(e.second);
+        }
+    }
+    Q_ASSERT(offset == int_data_size-1);
+    int_data[offset] = 0; // eod
+
+    char *string_data = new char[strings.blobSize()];
+    strings.writeBlob(string_data);
+
+    // put the metaobject together
+    metaobj->d.data = int_data;
+    metaobj->d.extradata = nullptr;
+    metaobj->d.stringdata = reinterpret_cast<const QByteArrayData *>(string_data);
+    metaobj->d.static_metacall = nullptr;
+    metaobj->d.relatedMetaObjects = nullptr;
+    metaobj->d.superdata = parentObject;
+
+    if (d)
+        d->metaobj = metaobj;
+
+    if (!cacheKey.isEmpty()) {
+        mo_cache.insert(cacheKey, d->metaobj);
+        d->cachedMetaObject = true;
+        for (auto it = d->eventSink.cbegin(), end = d->eventSink.cend(); it != end; ++it) {
+            if (QAxEventSink *sink = it.value()) {
+                QUuid ciid = sink->connectionInterface();
+
+                d->metaobj->connectionInterfaces.append(ciid);
+                d->metaobj->sigs.insert(ciid, sink->signalMap());
+                d->metaobj->props.insert(ciid, sink->propertyMap());
+                d->metaobj->propsigs.insert(ciid, sink->propSignalMap());
+            }
+        }
+    }
+
+    return metaobj;
+}
+
+#define QT_MOC_LITERAL(idx, ofs, len) { \
+    Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \
+    offsetof(qt_meta_stringdata_QAxBase_t, stringdata) + ofs \
+        - idx * sizeof(QByteArrayData) \
+    }
+const QAxBase::qt_meta_stringdata_QAxBase_t QAxBase::qt_meta_stringdata_QAxBase = {
+    {
+QT_MOC_LITERAL(0, 0, 7),
+QT_MOC_LITERAL(1, 8, 6),
+QT_MOC_LITERAL(2, 15, 0),
+QT_MOC_LITERAL(3, 16, 4),
+QT_MOC_LITERAL(4, 21, 4),
+QT_MOC_LITERAL(5, 26, 4),
+QT_MOC_LITERAL(6, 31, 15),
+QT_MOC_LITERAL(7, 47, 9),
+QT_MOC_LITERAL(8, 57, 4),
+QT_MOC_LITERAL(9, 62, 6),
+QT_MOC_LITERAL(10, 69, 4),
+QT_MOC_LITERAL(11, 74, 4),
+QT_MOC_LITERAL(12, 79, 7)
+    },
+    "QAxBase\0signal\0\0name\0argc\0argv\0"
+    "propertyChanged\0exception\0code\0source\0"
+    "desc\0help\0control\0"
+};
+#undef QT_MOC_LITERAL
+
+/*!
+    \fn const QMetaObject *QAxBase::fallbackMetaObject() const
+    \internal
+*/
+
+/*!
+    \internal
+    \class QAxBase::qt_meta_stringdata_QAxBase_t
+*/
+
+const uint QAxBase::qt_meta_data_QAxBase[] = {
+
+ // content:
+       7,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       3,   14, // methods
+       1,   48, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: name, argc, parameters, tag, flags
+       1,    3,   29,    2, 0x05,
+       6,    1,   36,    2, 0x05,
+       7,    4,   39,    2, 0x05,
+
+ // signals: parameters
+    QMetaType::Void, QMetaType::QString, QMetaType::Int, QMetaType::VoidStar,    3,    4,    5,
+    QMetaType::Void, QMetaType::QString,    3,
+    QMetaType::Void, QMetaType::Int, QMetaType::QString, QMetaType::QString, QMetaType::QString,    8,    9,   10,   11,
+
+ // properties: name, type, flags
+      12, QMetaType::QString, 0x00095000,
+
+       0        // eod
+};
+
+/*!
+    \internal
+
+    The metaobject is generated on the fly from the information
+    provided by the IDispatch and ITypeInfo interface implementations
+    in the COM object.
+*/
+const QMetaObject *QAxBase::metaObject() const
+{
+    if (d->metaobj)
+        return d->metaobj;
+    const QMetaObject* parentObject = parentMetaObject();
+
+    if (!d->ptr && !d->initialized) {
+        const_cast<QAxBase*>(this)->initialize(&d->ptr);
+        d->initialized = true;
+    }
+
+#ifndef QT_NO_THREAD
+    // only one thread at a time can generate meta objects
+    QMutexLocker locker(&cache_mutex);
+#endif
+
+    // return the default meta object if not yet initialized
+    if (!d->ptr || !d->useMetaObject)
+        return fallbackMetaObject();
+
+    MetaObjectGenerator generator(const_cast<QAxBase *>(this), d);
+    return generator.metaObject(parentObject);
+}
+
+/*!
+    \internal
+
+    Connects to all event interfaces of the object.
+
+    Called by the subclasses' connectNotify() reimplementations, so
+    at this point the connection as actually been created already.
+*/
+void QAxBase::connectNotify()
+{
+    if (d->eventSink.count()) // already listening
+        return;
+
+    IEnumConnectionPoints *epoints = nullptr;
+    if (d->ptr && d->useEventSink) {
+        IConnectionPointContainer *cpoints = nullptr;
+        d->ptr->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void **>(&cpoints));
+        if (!cpoints)
+            return;
+
+        cpoints->EnumConnectionPoints(&epoints);
+        cpoints->Release();
+    }
+
+    if (!epoints)
+        return;
+
+    UINT index;
+    IDispatch *disp = d->dispatch();
+    ITypeInfo *typeinfo = nullptr;
+    ITypeLib  *typelib = nullptr;
+    if (disp)
+        disp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo);
+    if (typeinfo)
+        typeinfo->GetContainingTypeLib(&typelib, &index);
+
+    if (!typelib) {
+        epoints->Release();
+        return;
+    }
+
+    MetaObjectGenerator generator(this, d);
+    bool haveEnumInfo = false;
+
+    ULONG c = 1;
+    IConnectionPoint *cpoint = nullptr;
+    epoints->Reset();
+    do {
+        if (cpoint) cpoint->Release();
+        cpoint = nullptr;
+        epoints->Next(c, &cpoint, &c);
+        if (!c || !cpoint)
+            break;
+
+        IID conniid;
+        cpoint->GetConnectionInterface(&conniid);
+        // workaround for typelibrary bug of Word.Application
+        const QUuid connuuid(conniid);
+        if (d->eventSink.contains(connuuid))
+            break;
+
+        // Get ITypeInfo for source-interface, and skip if not supporting IDispatch
+        ITypeInfo *eventinfo = nullptr;
+        typelib->GetTypeInfoOfGuid(conniid, &eventinfo);
+        if (eventinfo) {
+            TYPEATTR *eventAttr;
+            eventinfo->GetTypeAttr(&eventAttr);
+            if (!eventAttr) {
+                eventinfo->Release();
+                break;
+            }
+
+            TYPEKIND eventKind = eventAttr->typekind;
+            eventinfo->ReleaseTypeAttr(eventAttr);
+            if (eventKind != TKIND_DISPATCH) {
+                eventinfo->Release();
+                break;
+            }
+        }
+
+        // always into the cache to avoid recoursion
+        QAxEventSink *eventSink = eventinfo ? new QAxEventSink(this) : nullptr;
+        d->eventSink.insert(connuuid, eventSink);
+
+        if (!eventinfo)
+            continue;
+
+        // have to get type info to support signals with enum parameters
+        if (!haveEnumInfo) {
+            bool wasTryCache = d->tryCache;
+            d->tryCache = true;
+            generator.readClassInfo();
+            generator.readEnumInfo();
+            d->tryCache = wasTryCache;
+            haveEnumInfo = true;
+        }
+        generator.readEventInterface(eventinfo, cpoint);
+        eventSink->advise(cpoint, conniid);
+
+        eventinfo->Release();
+    } while (c);
+    if (cpoint) cpoint->Release();
+    epoints->Release();
+
+    typelib->Release();
+
+    // make sure we don't try again
+    if (!d->eventSink.count())
+        d->eventSink.insert(QUuid{}, nullptr);
+}
+
+/*!
+    \fn QString QAxBase::generateDocumentation()
+
+    Returns a rich text string with documentation for the
+    wrapped COM object. Dump the string to an HTML-file,
+    or use it in e.g. a QTextBrowser widget.
+*/
+
+static bool checkHRESULT(HRESULT hres, EXCEPINFO *exc, QAxBase *that, const QString &name, uint argerr)
+{
+    switch(hres) {
+    case S_OK:
+        return true;
+    case DISP_E_BADPARAMCOUNT:
+        qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name.toLatin1().data());
+        return false;
+    case DISP_E_BADVARTYPE:
+        qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name.toLatin1().data());
+        return false;
+    case DISP_E_EXCEPTION:
+        {
+            bool printWarning = true;
+            unsigned int code = uint(-1);
+            QString source, desc, help;
+            const QMetaObject *mo = that->metaObject();
+            int exceptionSignal = mo->indexOfSignal("exception(int,QString,QString,QString)");
+            if (exceptionSignal >= 0) {
+                if (exc->pfnDeferredFillIn)
+                    exc->pfnDeferredFillIn(exc);
+
+                code = exc->wCode ? exc->wCode : exc->scode;
+                source = QString::fromWCharArray(exc->bstrSource);
+                desc = QString::fromWCharArray(exc->bstrDescription);
+                help = QString::fromWCharArray(exc->bstrHelpFile);
+                uint helpContext = exc->dwHelpContext;
+
+                if (helpContext && !help.isEmpty())
+                    help += QString::fromLatin1(" [%1]").arg(helpContext);
+
+                if (QAxEventSink::signalHasReceivers(that->qObject(), "exception(int,QString,QString,QString)")) {
+                    void *argv[] = {nullptr, &code, &source, &desc, &help};
+                    QAxBase::qt_static_metacall(that, QMetaObject::InvokeMetaMethod,
+                                                exceptionSignal - mo->methodOffset(), argv);
+                    printWarning = false;
+                }
+            }
+            if (printWarning) {
+                qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name.toLatin1().data());
+                qWarning("             Code       : %d", code);
+                qWarning("             Source     : %s", source.toLatin1().data());
+                qWarning("             Description: %s", desc.toLatin1().data());
+                qWarning("             Help       : %s", help.toLatin1().data());
+                qWarning("         Connect to the exception(int,QString,QString,QString) signal to catch this exception");
+            }
+        }
+        return false;
+    case DISP_E_MEMBERNOTFOUND:
+        qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name.toLatin1().data());
+        return false;
+    case DISP_E_NONAMEDARGS:
+        qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name.toLatin1().data());
+        return false;
+    case DISP_E_OVERFLOW:
+        qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name.toLatin1().data());
+        return false;
+    case DISP_E_PARAMNOTFOUND:
+        qWarning("QAxBase: Error calling IDispatch member %s: Parameter %d not found", name.toLatin1().data(), argerr);
+        return false;
+    case DISP_E_TYPEMISMATCH:
+        qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch in parameter %d", name.toLatin1().data(), argerr);
+        return false;
+    case DISP_E_UNKNOWNINTERFACE:
+        qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name.toLatin1().data());
+        return false;
+    case DISP_E_UNKNOWNLCID:
+        qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name.toLatin1().data());
+        return false;
+    case DISP_E_PARAMNOTOPTIONAL:
+        qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name.toLatin1().data());
+        return false;
+    default:
+        qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name.toLatin1().data());
+        return false;
+    }
+}
+
+/*!
+    \internal
+*/
+int QAxBase::internalProperty(QMetaObject::Call call, int index, void **v)
+{
+    const QMetaObject *mo = metaObject();
+    const QMetaProperty prop = mo->property(index + mo->propertyOffset());
+    QByteArray propname = prop.name();
+
+    // hardcoded control property
+    if (propname == "control") {
+        switch(call) {
+        case QMetaObject::ReadProperty:
+            *static_cast<QString*>(*v) = control();
+            break;
+        case QMetaObject::WriteProperty:
+            setControl(*static_cast<const QString*>(*v));
+            break;
+        case QMetaObject::ResetProperty:
+            clear();
+            break;
+        default:
+            break;
+        }
+        return index - mo->propertyCount();
+    }
+
+    // get the IDispatch
+    if (!d->ptr || !prop.isValid())
+        return index;
+    IDispatch *disp = d->dispatch();
+    if (!disp)
+        return index;
+
+    DISPID dispid = d->metaObject()->dispIDofName(propname, disp);
+    if (dispid == DISPID_UNKNOWN)
+        return index;
+
+    Q_ASSERT(d->metaobj);
+    // property found, so everthing that goes wrong now should not bother the caller
+    index -= mo->propertyCount();
+
+    VARIANTARG arg;
+    VariantInit(&arg);
+    DISPPARAMS params;
+    EXCEPINFO excepinfo;
+    memset(&excepinfo, 0, sizeof(excepinfo));
+    UINT argerr = 0;
+    HRESULT hres = E_FAIL;
+
+    QByteArray proptype(prop.typeName());
+    switch (call) {
+    case QMetaObject::ReadProperty:
+        {
+            params.cArgs = 0;
+            params.cNamedArgs = 0;
+            params.rgdispidNamedArgs = nullptr;
+            params.rgvarg = nullptr;
+
+            hres = Invoke(disp, dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params, &arg, &excepinfo, nullptr);
+
+            // map result VARIANTARG to void*
+            uint type = QVariant::Int;
+            if (!prop.isEnumType())
+                type = prop.type();
+            QVariantToVoidStar(VARIANTToQVariant(arg, proptype, type), *v, proptype, type);
+            if ((arg.vt != VT_DISPATCH && arg.vt != VT_UNKNOWN) || type == QVariant::Pixmap || type == QVariant::Font)
+                clearVARIANT(&arg);
+        }
+        break;
+
+    case QMetaObject::WriteProperty:
+        {
+            DISPID dispidNamed = DISPID_PROPERTYPUT;
+            params.cArgs = 1;
+            params.cNamedArgs = 1;
+            params.rgdispidNamedArgs = &dispidNamed;
+            params.rgvarg = &arg;
+
+            arg.vt = VT_ERROR;
+            arg.scode = DISP_E_TYPEMISMATCH;
+
+            // map void* to VARIANTARG via QVariant
+            QVariant qvar;
+            if (prop.isEnumType()) {
+                qvar = *reinterpret_cast<const int *>(v[0]);
+                proptype = nullptr;
+            } else {
+                int typeId = prop.userType();
+                if (typeId == int(QMetaType::QVariant)) {
+                    qvar = *reinterpret_cast<const QVariant *>(v[0]);
+                    proptype = nullptr;
+                } else {
+                    qvar = QVariant(typeId, v[0]);
+                    if (typeId < QMetaType::User)
+                        proptype = d->metaObject()->propertyType(propname);
+                }
+            }
+
+            QVariantToVARIANT(qvar, arg, proptype);
+            if (arg.vt == VT_EMPTY || arg.vt == VT_ERROR) {
+                qWarning("QAxBase::setProperty: Unhandled property type %s", prop.typeName());
+                break;
+            }
+        }
+        hres = Invoke(disp, dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &params, nullptr, &excepinfo, &argerr);
+        clearVARIANT(&arg);
+        break;
+
+    default:
+        break;
+    }
+
+    checkHRESULT(hres, &excepinfo, this, QLatin1String(propname), argerr);
+    return index;
+}
+
+int QAxBase::internalInvoke(QMetaObject::Call call, int index, void **v)
+{
+    Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+    Q_UNUSED(call);
+
+    // get the IDispatch
+    IDispatch *disp = d->dispatch();
+    if (!disp)
+        return index;
+
+    const QMetaObject *mo = metaObject();
+    // get the slot information
+    const QMetaMethod slot = mo->method(index + mo->methodOffset());
+    Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
+
+    QByteArray signature(slot.methodSignature());
+    QByteArray slotname(signature);
+    slotname.truncate(slotname.indexOf('('));
+
+    // Get the Dispatch ID of the method to be called
+    bool isProperty = false;
+    DISPID dispid = d->metaObject()->dispIDofName(slotname, disp);
+
+    Q_ASSERT(d->metaobj);
+
+    if (dispid == DISPID_UNKNOWN && slotname.toLower().startsWith("set")) {
+        // see if we are calling a property set function as a slot
+        slotname.remove(0, 3);
+        dispid = d->metaobj->dispIDofName(slotname, disp);
+        isProperty = true;
+    }
+    if (dispid == DISPID_UNKNOWN)
+        return index;
+
+    // slot found, so everthing that goes wrong now should not bother the caller
+    index -= mo->methodCount();
+
+    // setup the parameters
+    DISPPARAMS params;
+    DISPID dispidNamed = DISPID_PROPERTYPUT;
+    params.cArgs = UINT(d->metaobj->numParameter(signature));
+    params.cNamedArgs = isProperty ? 1 : 0;
+    params.rgdispidNamedArgs = isProperty ? &dispidNamed : nullptr;
+    params.rgvarg = nullptr;
+    VARIANTARG static_rgvarg[QAX_NUM_PARAMS];
+    if (params.cArgs) {
+        if (params.cArgs <= QAX_NUM_PARAMS)
+            params.rgvarg = static_rgvarg;
+        else
+            params.rgvarg = new VARIANTARG[params.cArgs];
+    }
+    for (VARIANTARG *vp = params.rgvarg, *vEnd = params.rgvarg + params.cArgs; vp < vEnd; ++vp)
+        VariantInit(vp);
+
+    int p;
+    for (p = 0; p < int(params.cArgs); ++p) {
+        bool out;
+        QByteArray type = d->metaobj->paramType(signature, p, &out);
+        QVariant::Type vt = QVariant::nameToType(type);
+        QVariant qvar;
+        if (vt != QVariant::UserType && vt != int(QMetaType::QVariant))
+            qvar = QVariant(vt, v[p + 1]);
+
+        if (!qvar.isValid()) {
+            if (type == "IDispatch*") {
+                if (out)
+                    qvar.setValue(*reinterpret_cast<IDispatch ***>(v[p+1]));
+                else
+                    qvar.setValue(*reinterpret_cast<IDispatch **>(v[p+1]));
+            } else if (type == "IUnknown*") {
+                    qvar.setValue(*reinterpret_cast<IUnknown **>(v[p+1]));
+            } else if (type == "QVariant") {
+                qvar = *reinterpret_cast<const QVariant *>(v[p + 1]);
+            } else if (mo->indexOfEnumerator(type) != -1) {
+                qvar = *reinterpret_cast<const int *>(v[p + 1]);
+            } else {
+                qvar = QVariant(QMetaType::type(type), v[p + 1]);
+            }
+        }
+
+        QVariantToVARIANT(qvar, params.rgvarg[int(params.cArgs) - p - 1], type, out);
+    }
+
+    // call the method
+    VARIANT ret;
+    VariantInit(&ret);
+    UINT argerr = 0;
+    HRESULT hres = E_FAIL;
+    EXCEPINFO excepinfo;
+    memset(&excepinfo, 0, sizeof(excepinfo));
+
+    WORD wFlags = isProperty ? DISPATCH_PROPERTYPUT : DISPATCH_METHOD | DISPATCH_PROPERTYGET;
+    hres = Invoke(disp, dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags, &params, &ret, &excepinfo, &argerr);
+
+    // get return value
+    if (hres == S_OK && ret.vt != VT_EMPTY) {
+        QVariantToVoidStar(VARIANTToQVariant(ret, slot.typeName()), v[0], slot.typeName());
+        if (ret.vt != VT_DISPATCH)
+            clearVARIANT(&ret);
+        else
+            VariantInit(&ret);
+    }
+
+    // update out parameters
+    for (p = 0; p < int(params.cArgs); ++p) {
+        bool out;
+        QByteArray ptype = d->metaobj->paramType(signature, p, &out);
+        if (out) {
+            VARIANTARG &var = params.rgvarg[int(params.cArgs) - p - 1];
+            QVariantToVoidStar(VARIANTToQVariant(var, ptype), v[p+1], ptype);
+            if (var.vt == (VT_DISPATCH | VT_BYREF))
+                VariantInit(&var); // Prevent clearVARIANT() from releasing returned IDispatch* out parameters.
+        }
+    }
+    // clean up
+    for (p = 0; p < int(params.cArgs); ++p)
+        clearVARIANT(params.rgvarg+p);
+    if (params.rgvarg != static_rgvarg)
+        delete [] params.rgvarg;
+
+    checkHRESULT(hres, &excepinfo, this, QString::fromLatin1(slotname), params.cArgs-argerr-1);
+    return index;
+}
+
+/*!
+    \internal
+*/
+int QAxBase::qt_static_metacall(QAxBase *_t, QMetaObject::Call _c, int _id, void **_a)
+{
+    Q_ASSERT(_t != nullptr);
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        const QMetaObject *mo = _t->metaObject();
+        switch (mo->method(_id + mo->methodOffset()).methodType()) {
+        case QMetaMethod::Signal:
+            QMetaObject::activate(_t->qObject(), mo, _id, _a);
+            return _id - mo->methodCount();
+        case QMetaMethod::Method:
+        case QMetaMethod::Slot:
+            return _t->internalInvoke(_c, _id, _a);
+        default:
+            break;
+        }
+    }
+    return 0;
+}
+
+/*!
+    \internal
+*/
+int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v)
+{
+    const QMetaObject *mo = metaObject();
+    if (isNull() && mo->property(id + mo->propertyOffset()).name() != QByteArray("control")) {
+        qWarning("QAxBase::qt_metacall: Object is not initialized, or initialization failed");
+        return id;
+    }
+
+    switch(call) {
+    case QMetaObject::InvokeMetaMethod:
+        id = qt_static_metacall(this, call, id, v);
+        break;
+    case QMetaObject::ReadProperty:
+    case QMetaObject::WriteProperty:
+    case QMetaObject::ResetProperty:
+        id = internalProperty(call, id, v);
+        break;
+    case QMetaObject::QueryPropertyScriptable:
+    case QMetaObject::QueryPropertyDesignable:
+    case QMetaObject::QueryPropertyStored:
+    case QMetaObject::QueryPropertyEditable:
+    case QMetaObject::QueryPropertyUser:
+        id -= mo->propertyCount();
+        break;
+    default:
+        break;
+    }
+    Q_ASSERT(id < 0);
+    return id;
+}
+
+#ifdef QT_CHECK_STATE
+static void qax_noSuchFunction(int disptype, const QByteArray &name, const QByteArray &function, const QAxBase *that)
+{
+    const QMetaObject *metaObject = that->metaObject();
+    const char *coclass = metaObject->classInfo(metaObject->indexOfClassInfo("CoClass")).value();
+
+    if (disptype == DISPATCH_METHOD) {
+        qWarning("QAxBase::dynamicCallHelper: %s: No such method in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown");
+        qWarning("\tCandidates are:");
+        for (int i = 0; i < metaObject->methodCount(); ++i) {
+            const QMetaMethod slot(metaObject->method(i));
+            if (slot.methodType() != QMetaMethod::Slot)
+                continue;
+            QByteArray signature = slot.methodSignature();
+            if (signature.toLower().startsWith(function.toLower()))
+                qWarning("\t\t%s", signature.data());
+        }
+    } else {
+        qWarning("QAxBase::dynamicCallHelper: %s: No such property in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown");
+        if (!function.isEmpty()) {
+            qWarning("\tCandidates are:");
+            char f0 = function.toLower().at(0);
+            for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) {
+                QByteArray signature(metaObject->property(i).name());
+                if (!signature.isEmpty() && signature.toLower().at(0) == f0)
+                    qWarning("\t\t%s", signature.data());
+            }
+        }
+    }
+}
+#endif
+
+/*!
+    \internal
+
+    \a name is already normalized?
+*/
+bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList<QVariant> &vars,
+                                QByteArray &type, unsigned flags)
+{
+    if (isNull()) {
+        qWarning("QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed");
+        return false;
+    }
+
+    IDispatch *disp = d->dispatch();
+    if (!disp) {
+        qWarning("QAxBase::dynamicCallHelper: Object does not support automation");
+        return false;
+    }
+
+    const QMetaObject *mo = metaObject();
+    d->metaObject();
+    Q_ASSERT(d->metaobj);
+
+    int varc = vars.count();
+
+    QByteArray normFunction = QMetaObject::normalizedSignature(name);
+    QByteArray function(normFunction);
+    VARIANT staticarg[QAX_NUM_PARAMS];
+    VARIANT *arg = nullptr;
+    VARIANTARG *res = reinterpret_cast<VARIANTARG *>(inout);
+
+    unsigned short disptype;
+
+    int id = -1;
+    bool parse = false;
+
+    if (function.contains('(')) {
+        disptype = DISPATCH_METHOD;
+        if (!(flags & NoPropertyGet))
+            disptype |= DISPATCH_PROPERTYGET; // Support Excel/VB.
+        if (d->useMetaObject)
+            id = mo->indexOfSlot(function);
+        if (id >= 0) {
+            const QMetaMethod slot = mo->method(id);
+            Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
+            function = slot.methodSignature();
+            type = slot.typeName();
+        }
+        function.truncate(function.indexOf('('));
+        parse = !varc && normFunction.length() > function.length() + 2;
+        if (parse) {
+            QString args = QLatin1String(normFunction);
+            args.remove(0, function.length() + 1);
+            // parse argument string int list of arguments
+            QString curArg;
+            const QChar *c = args.unicode();
+            int index = 0;
+            bool inString = false;
+            bool inEscape = false;
+            while (index < args.length()) {
+                QChar cc = *c;
+                ++c;
+                ++index;
+                switch(cc.toLatin1()) {
+                case 'n':
+                    if (inEscape)
+                        cc = QLatin1Char('\n');
+                    break;
+                case 'r':
+                    if (inEscape)
+                        cc = QLatin1Char('\r');
+                    break;
+                case 't':
+                    if (inEscape)
+                        cc = QLatin1Char('\t');
+                    break;
+                case '\\':
+                    if (!inEscape && inString) {
+                        inEscape = true;
+                        continue;
+                    }
+                    break;
+                case '"':
+                    if (!inEscape) {
+                        inString = !inString;
+                        curArg += cc;
+                        continue;
+                    }
+                    break;
+                case ' ':
+                    if (!inString && curArg.isEmpty())
+                        continue;
+                    break;
+                case ',':
+                case ')':
+                    if (inString)
+                        break;
+                    curArg = curArg.trimmed();
+                    if (curArg.at(0) == QLatin1Char('\"') && curArg.at(curArg.length()-1) == QLatin1Char('\"')) {
+                        vars << curArg.mid(1, curArg.length() - 2);
+                    } else {
+                        bool isNumber = false;
+                        bool isDouble = false;
+                        int number = curArg.toInt(&isNumber);
+                        double dbl = curArg.toDouble(&isDouble);
+                        if (isNumber) {
+                            vars << number;
+                        } else if (isDouble) {
+                            vars << dbl;
+                        } else {
+                            bool isEnum = false;
+                            for (int enumIndex = 0; enumIndex < mo->enumeratorCount(); ++enumIndex) {
+                                QMetaEnum metaEnum =mo->enumerator(enumIndex);
+                                int value = metaEnum.keyToValue(curArg.toLatin1());
+                                if (value != -1 && !QByteArray(metaEnum.valueToKey(value)).isEmpty()) {
+                                    vars << value;
+                                    isEnum = true;
+                                    break;
+                                }
+                            }
+                            if (!isEnum)
+                                vars << curArg;
+                        }
+                    }
+                    curArg.clear();
+                    continue;
+                default:
+                    break;
+                }
+                inEscape = false;
+                curArg += cc;
+            }
+
+            varc = vars.count();
+        }
+    } else {
+        if (d->useMetaObject)
+            id = mo->indexOfProperty(normFunction);
+
+        if (id >= 0) {
+            const QMetaProperty prop =mo->property(id);
+            type = prop.typeName();
+        }
+        if (varc == 1) {
+            res = nullptr;
+            disptype = DISPATCH_PROPERTYPUT;
+        } else {
+            disptype = DISPATCH_PROPERTYGET;
+        }
+    }
+    QBitArray outArgs;
+    if (varc) {
+        varc = qMin(varc, d->metaobj->numParameter(normFunction));
+        arg = varc <= QAX_NUM_PARAMS ? staticarg : new VARIANT[varc];
+        outArgs = QBitArray(varc);
+        for (int i = 0; i < varc; ++i) {
+            const QVariant &var = vars.at(i);
+            VariantInit(arg + (varc - i - 1));
+            bool out = false;
+            QByteArray paramType;
+            if (disptype == DISPATCH_PROPERTYPUT)
+                paramType = type;
+            else if (parse || disptype == DISPATCH_PROPERTYGET)
+                paramType = nullptr;
+            else
+                paramType = d->metaobj->paramType(normFunction, i, &out);
+
+            if ((!parse && d->useMetaObject && var.type() == QVariant::String) || var.type() == QVariant::ByteArray) {
+                int enumIndex =mo->indexOfEnumerator(paramType);
+                if (enumIndex != -1) {
+                    QMetaEnum metaEnum =mo->enumerator(enumIndex);
+                    QVariantToVARIANT(metaEnum.keyToValue(var.toByteArray()), arg[varc - i - 1], "int", out);
+                }
+            }
+
+            if (arg[varc - i - 1].vt == VT_EMPTY)
+                QVariantToVARIANT(var, arg[varc - i - 1], paramType, out);
+            outArgs[i] = out;
+        }
+    }
+
+    DISPID dispid = d->metaobj->dispIDofName(function, disp);
+    if (dispid == DISPID_UNKNOWN && function.toLower().startsWith("set")) {
+        function = function.mid(3);
+        dispid = d->metaobj->dispIDofName(function, disp);
+        disptype = DISPATCH_PROPERTYPUT;
+    }
+
+    if (dispid == DISPID_UNKNOWN) {
+#ifdef QT_CHECK_STATE
+        qax_noSuchFunction(disptype, normFunction, function, this);
+#endif
+        return false;
+    }
+
+    DISPPARAMS params;
+    DISPID dispidNamed = DISPID_PROPERTYPUT;
+
+    params.cArgs = UINT(varc);
+    params.cNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? 1 : 0;
+    params.rgdispidNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? &dispidNamed : nullptr;
+    params.rgvarg = arg;
+    EXCEPINFO excepinfo;
+    memset(&excepinfo, 0, sizeof(excepinfo));
+    UINT argerr = 0;
+
+    HRESULT hres = Invoke(disp, dispid, IID_NULL, LOCALE_USER_DEFAULT, disptype, &params, res, &excepinfo, &argerr);
+    if (hres == DISP_E_MEMBERNOTFOUND && (disptype & DISPATCH_METHOD) != 0) {
+        // Retry with pVarResult = 0, workaround for "Word.Application.WordBasic.DisableAutoMacros(bool)"
+        memset(&excepinfo, 0, sizeof(excepinfo));
+        argerr = 0;
+        hres = Invoke(disp, dispid, IID_NULL, LOCALE_USER_DEFAULT, disptype, &params, nullptr, &excepinfo, &argerr);
+    }
+
+    if (disptype == (DISPATCH_METHOD|DISPATCH_PROPERTYGET) && hres == S_OK && varc) {
+        for (int i = 0; i < varc; ++i)
+            if ((arg[varc-i-1].vt & VT_BYREF) || outArgs[i]) // update out-parameters
+                vars[i] = VARIANTToQVariant(arg[varc-i-1], vars.at(i).typeName());
+    }
+
+    // clean up
+    for (int i = 0; i < varc; ++i)
+        clearVARIANT(params.rgvarg+i);
+    if (arg && arg != staticarg)
+        delete[] arg;
+
+    return checkHRESULT(hres, &excepinfo, this, QLatin1String(function), uint(varc) - argerr - 1);
+}
+
+/*!
+    \internal
+*/
+QVariantList QAxBase::argumentsToList(const QVariant &var1, const QVariant &var2,
+                                      const QVariant &var3, const QVariant &var4,
+                                      const QVariant &var5, const QVariant &var6,
+                                      const QVariant &var7, const QVariant &var8)
+{
+    QVariantList vars;
+    QVariant var = var1;
+    int argc = 1;
+    while (var.isValid()) {
+        vars << var;
+        switch (++argc) {
+        case 2: var = var2; break;
+        case 3: var = var3; break;
+        case 4: var = var4; break;
+        case 5: var = var5; break;
+        case 6: var = var6; break;
+        case 7: var = var7; break;
+        case 8: var = var8; break;
+        default:var = QVariant(); break;
+        }
+    }
+    return vars;
+}
+
+/*!
+    Calls the COM object's method \a function, passing the
+    parameters \a var1, \a var1, \a var2, \a var3, \a var4, \a var5,
+    \a var6, \a var7 and \a var8, and returns the value returned by
+    the method, or an invalid QVariant if the method does not return
+    a value or when the function call failed.
+
+    If \a function is a method of the object the string must be provided
+    as the full prototype, for example as it would be written in a
+    QObject::connect() call.
+
+    \snippet src_activeqt_container_qaxbase.cpp 15
+
+    Alternatively a function can be called passing the parameters embedded
+    in the string, e.g. above function can also be invoked using
+
+    \snippet src_activeqt_container_qaxbase.cpp 16
+
+    All parameters are passed as strings; it depends on the control whether
+    they are interpreted correctly, and is slower than using the prototype
+    with correctly typed parameters.
+
+    If \a function is a property the string has to be the name of the
+    property. The property setter is called when \a var1 is a valid QVariant,
+    otherwise the getter is called.
+
+    \snippet src_activeqt_container_qaxbase.cpp 17
+
+    Note that it is faster to get and set properties using
+    QObject::property() and QObject::setProperty().
+
+    dynamicCall() can also be used to call objects with a
+    \l{QAxBase::disableMetaObject()}{disabled metaobject} wrapper,
+    which can improve performance significantely, esp. when calling many
+    different objects of different types during an automation process.
+    ActiveQt will then however not validate parameters.
+
+    It is only possible to call functions through dynamicCall() that
+    have parameters or return values of datatypes supported by
+    QVariant. See the QAxBase class documentation for a list of
+    supported and unsupported datatypes. If you want to call functions
+    that have unsupported datatypes in the parameter list, use
+    queryInterface() to retrieve the appropriate COM interface, and
+    use the function directly.
+
+    \snippet src_activeqt_container_qaxbase.cpp 18
+
+    This is also more efficient.
+*/
+QVariant QAxBase::dynamicCall(const char *function,
+                              const QVariant &var1,
+                              const QVariant &var2,
+                              const QVariant &var3,
+                              const QVariant &var4,
+                              const QVariant &var5,
+                              const QVariant &var6,
+                              const QVariant &var7,
+                              const QVariant &var8)
+{
+    QVariantList vars = QAxBase::argumentsToList(var1, var2, var3, var4, var5, var6, var7, var8);
+    return dynamicCall(function, vars); // Use overload taking "QVariantList &" to avoid recursion
+}
+
+/*!
+    \overload
+
+    Calls the COM object's method \a function, passing the
+    parameters in \a vars, and returns the value returned by
+    the method. If the method does not return a value or when
+    the function call failed this function returns an invalid
+    QVariant object.
+
+    The QVariant objects in \a vars are updated when the method has
+    out-parameters.
+*/
+QVariant QAxBase::dynamicCall(const char *function, QList<QVariant> &vars)
+{
+    return dynamicCall(function, vars, 0);
+}
+
+/*!
+    \internal
+*/
+QVariant QAxBase::dynamicCall(const char *function, QList<QVariant> &vars, unsigned flags)
+{
+    VARIANTARG res;
+    VariantInit(&res);
+
+    QByteArray rettype;
+    if (!dynamicCallHelper(function, &res, vars, rettype, flags))
+        return QVariant();
+
+    QVariant qvar = VARIANTToQVariant(res, rettype);
+    if ((res.vt != VT_DISPATCH && res.vt != VT_UNKNOWN) || qvar.type() == QVariant::Pixmap || qvar.type() == QVariant::Font)
+        clearVARIANT(&res);
+
+    return qvar;
+}
+
+/*!
+    Returns a pointer to a QAxObject wrapping the COM object provided
+    by the method or property \a name, passing passing the parameters
+    \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, \a var6,
+    \a var7 and \a var8.
+
+    If \a name is provided by a method the string must include the
+    full function prototype.
+
+    If \a name is a property the string must be the name of the property,
+    and \a var1, ... \a var8 are ignored.
+
+    The returned QAxObject is a child of this object (which is either of
+    type QAxObject or QAxWidget), and is deleted when this object is
+    deleted. It is however safe to delete the returned object yourself,
+    and you should do so when you iterate over lists of subobjects.
+
+    COM enabled applications usually have an object model publishing
+    certain elements of the application as dispatch interfaces. Use
+    this method to navigate the hierarchy of the object model, e.g.
+
+    \snippet src_activeqt_container_qaxbase.cpp 19
+*/
+QAxObject *QAxBase::querySubObject(const char *name,
+                                   const QVariant &var1,
+                                   const QVariant &var2,
+                                   const QVariant &var3,
+                                   const QVariant &var4,
+                                   const QVariant &var5,
+                                   const QVariant &var6,
+                                   const QVariant &var7,
+                                   const QVariant &var8)
+{
+    QVariantList vars;
+    QVariant var = var1;
+    int argc = 1;
+    while(var.isValid()) {
+        vars << var;
+        switch(++argc) {
+        case 2: var = var2; break;
+        case 3: var = var3; break;
+        case 4: var = var4; break;
+        case 5: var = var5; break;
+        case 6: var = var6; break;
+        case 7: var = var7; break;
+        case 8: var = var8; break;
+        default:var = QVariant(); break;
+        }
+    }
+
+    return querySubObject(name, vars);
+}
+
+/*!
+    \overload
+
+    The QVariant objects in \a vars are updated when the method has
+    out-parameters.
+*/
+QAxObject *QAxBase::querySubObject(const char *name, QList<QVariant> &vars)
+{
+    QAxObject *object = nullptr;
+    VARIANTARG res;
+    VariantInit(&res);
+
+    QByteArray rettype;
+    if (!dynamicCallHelper(name, &res, vars, rettype))
+        return nullptr;
+
+    switch (res.vt) {
+    case VT_DISPATCH:
+        if (res.pdispVal) {
+            if (rettype.isEmpty() || rettype == "IDispatch*" || rettype == "QVariant") {
+                object = new QAxObject(res.pdispVal, qObject());
+            } else if (QMetaType::type(rettype)) {
+                QVariant qvar = VARIANTToQVariant(res, rettype, 0);
+                object = *static_cast<QAxObject**>(qvar.data());
+//                qVariantGet(qvar, object, rettype);
+                res.pdispVal->AddRef();
+            }
+            if (object)
+                static_cast<QAxBase*>(object)->d->tryCache = true;
+        }
+        break;
+    case VT_UNKNOWN:
+        if (res.punkVal) {
+            if (rettype.isEmpty() || rettype == "IUnknown*") {
+                object = new QAxObject(res.punkVal, qObject());
+            } else if (QMetaType::type(rettype)) {
+                QVariant qvar = VARIANTToQVariant(res, rettype, 0);
+                object = *static_cast<QAxObject**>(qvar.data());
+//                qVariantGet(qvar, object, rettype);
+                res.punkVal->AddRef();
+            }
+            if (object)
+                static_cast<QAxBase*>(object)->d->tryCache = true;
+        }
+        break;
+    case VT_EMPTY:
+#ifdef QT_CHECK_STATE
+        {
+            const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value();
+            qWarning("QAxBase::querySubObject: %s: Error calling function or property in %s (%s)"
+                , name, control().toLatin1().data(), coclass ? coclass: "unknown");
+        }
+#endif
+        break;
+    default:
+#ifdef QT_CHECK_STATE
+        {
+            const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value();
+            qWarning("QAxBase::querySubObject: %s: Method or property is not of interface type in %s (%s)"
+                , name, control().toLatin1().data(), coclass ? coclass: "unknown");
+        }
+#endif
+        break;
+    }
+
+    clearVARIANT(&res);
+    return object;
+}
+
+class QtPropertyBag : public IPropertyBag
+{
+    Q_DISABLE_COPY_MOVE(QtPropertyBag)
+public:
+    QtPropertyBag() = default;
+    virtual ~QtPropertyBag() = default;
+
+    HRESULT __stdcall QueryInterface(REFIID iid, LPVOID *iface) override
+    {
+        *iface = nullptr;
+        if (iid == IID_IUnknown)
+            *iface = this;
+        else if (iid == IID_IPropertyBag)
+            *iface = this;
+        else
+            return E_NOINTERFACE;
+
+        AddRef();
+        return S_OK;
+    }
+    unsigned long __stdcall AddRef() override
+    {
+        return InterlockedIncrement(&ref);
+    }
+    unsigned long __stdcall Release() override
+    {
+        LONG refCount = InterlockedDecrement(&ref);
+        if (!refCount)
+            delete this;
+
+        return refCount;
+    }
+
+    HRESULT __stdcall Read(LPCOLESTR name, VARIANT *var, IErrorLog *) override
+    {
+        if (!var)
+            return E_POINTER;
+
+        QString property = QString::fromWCharArray(name);
+        QVariant qvar = map.value(property);
+        QVariantToVARIANT(qvar, *var);
+        return S_OK;
+    }
+    HRESULT __stdcall Write(LPCOLESTR name, VARIANT *var) override
+    {
+        if (!var)
+            return E_POINTER;
+        QString property = QString::fromWCharArray(name);
+        QVariant qvar = VARIANTToQVariant(*var, nullptr);
+        map[property] = qvar;
+
+        return S_OK;
+    }
+
+    QAxBase::PropertyBag map;
+
+private:
+    LONG ref = 0;
+};
+
+/*!
+    Returns a name:value map of all the properties exposed by the COM
+    object.
+
+    This is more efficient than getting multiple properties
+    individually if the COM object supports property bags.
+
+    \warning It is not guaranteed that the property bag implementation
+    of the COM object returns all properties, or that the properties
+    returned are the same as those available through the IDispatch
+    interface.
+*/
+QAxBase::PropertyBag QAxBase::propertyBag() const
+{
+    PropertyBag result;
+
+    if (!d->ptr && !d->initialized) {
+        const_cast<QAxBase *>(this)->initialize(&d->ptr);
+        d->initialized = true;
+    }
+
+    if (isNull())
+        return result;
+    IPersistPropertyBag *persist = nullptr;
+    d->ptr->QueryInterface(IID_IPersistPropertyBag, reinterpret_cast<void **>(&persist));
+    if (persist) {
+        QtPropertyBag *pbag = new QtPropertyBag();
+        pbag->AddRef();
+        persist->Save(pbag, false, true);
+        result = pbag->map;
+        pbag->Release();
+        persist->Release();
+        return result;
+    }
+    const QMetaObject *mo = metaObject();
+    for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) {
+        const QMetaProperty property = mo->property(p);
+        QVariant var = qObject()->property(property.name());
+        result.insert(QLatin1String(property.name()), var);
+    }
+    return result;
+}
+
+/*!
+    Sets the properties of the COM object to the corresponding values
+    in \a bag.
+
+    \warning
+    You should only set property bags that have been returned by the
+    propertyBag function, as it cannot be guaranteed that the property
+    bag implementation of the COM object supports the same properties
+    that are available through the IDispatch interface.
+
+    \sa propertyBag()
+*/
+void QAxBase::setPropertyBag(const PropertyBag &bag)
+{
+    if (!d->ptr && !d->initialized) {
+        initialize(&d->ptr);
+        d->initialized = true;
+    }
+
+    if (isNull())
+        return;
+    IPersistPropertyBag *persist = nullptr;
+    d->ptr->QueryInterface(IID_IPersistPropertyBag, reinterpret_cast<void **>(&persist));
+    if (persist) {
+        QtPropertyBag *pbag = new QtPropertyBag();
+        pbag->map = bag;
+        pbag->AddRef();
+        persist->Load(pbag, nullptr);
+        pbag->Release();
+        persist->Release();
+    } else {
+        const QMetaObject *mo = metaObject();
+        for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) {
+            const QMetaProperty property = mo->property(p);
+            QVariant var = bag.value(QLatin1String(property.name()));
+            qObject()->setProperty(property.name(), var);
+        }
+    }
+}
+
+/*!
+    Returns true if the property \a prop is writable; otherwise
+    returns false. By default, all properties are writable.
+
+    \warning
+    Depending on the control implementation this setting might be
+    ignored for some properties.
+
+    \sa setPropertyWritable(), propertyChanged()
+*/
+bool QAxBase::propertyWritable(const char *prop) const
+{
+    return d->propWritable.value(prop, true);
+}
+
+/*!
+    Sets the property \a prop to writable if \a ok is true, otherwise
+    sets \a prop to be read-only. By default, all properties are
+    writable.
+
+    \warning
+    Depending on the control implementation this setting might be
+    ignored for some properties.
+
+    \sa propertyWritable(), propertyChanged()
+*/
+void QAxBase::setPropertyWritable(const char *prop, bool ok)
+{
+    d->propWritable[prop] = ok;
+}
+
+/*!
+    Returns true if there is no COM object loaded by this wrapper;
+    otherwise return false.
+
+    \sa control
+*/
+bool QAxBase::isNull() const
+{
+    return !d->ptr;
+}
+
+/*!
+    Returns a QVariant that wraps the COM object. The variant can
+    then be used as a parameter in e.g. dynamicCall().
+*/
+QVariant QAxBase::asVariant() const
+{
+    if (!d->ptr && !d->initialized) {
+        const_cast<QAxBase *>(this)->initialize(&d->ptr);
+        d->initialized = true;
+    }
+
+    QVariant qvar;
+    QByteArray cn(className());
+    if (cn == "QAxObject" || cn == "QAxWidget" || cn == "QAxBase") {
+        if (d->dispatch())
+            qvar.setValue(d->dispatch());
+        else if (d->ptr)
+            qvar.setValue(d->ptr);
+    } else {
+        cn.remove(0, cn.lastIndexOf(':') + 1);
+        cn += '*';
+        QObject *object = qObject();
+        int typeId = QMetaType::type(cn);
+        if (typeId == QMetaType::UnknownType)
+            typeId = qRegisterMetaType<QObject *>(cn);
+        qvar = QVariant(typeId, &object);
+    }
+
+    return qvar;
+}
+
+// internal function that creates a QAxObject from an iface
+// used by type-conversion code (types.cpp)
+void *qax_createObjectWrapper(int metaType, IUnknown *iface)
+{
+    if (!iface)
+        return nullptr;
+
+    void *object = QMetaType::create(metaType, nullptr);
+    QAxBasePrivate *d = reinterpret_cast<const QAxObject *>(object)->d;
+
+    d->ptr = iface;
+    d->initialized = true;
+
+    // no release, since no addref
+
+    return object;
+}
+
+/*!
+    \fn void QAxBase::signal(const QString &name, int argc, void *argv)
+
+    This generic signal gets emitted when the COM object issues the
+    event \a name. \a argc is the number of parameters provided by the
+    event (DISPPARAMS.cArgs), and \a argv is the pointer to the
+    parameter values (DISPPARAMS.rgvarg). Note that the order of parameter
+    values is turned around, ie. the last element of the array is the first
+    parameter in the function.
+
+    \snippet src_activeqt_container_qaxbase.cpp 20
+
+    Use this signal if the event has parameters of unsupported data
+    types. Otherwise, connect directly to the signal \a name.
+*/
+
+/*!
+    \fn void QAxBase::propertyChanged(const QString &name)
+
+    If the COM object supports property notification, this signal gets
+    emitted when the property called \a name is changed.
+*/
+
+/*!
+    \fn void QAxBase::exception(int code, const QString &source, const QString &desc, const QString &help)
+
+    This signal is emitted when the COM object throws an exception while called using the OLE automation
+    interface IDispatch. \a code, \a source, \a desc and \a help provide information about the exception as
+    provided by the COM server and can be used to provide useful feedback to the end user. \a help includes
+    the help file, and the help context ID in brackets, e.g. "filename [id]".
+*/
+
+/*!
+    \fn QObject *QAxBase::qObject() const
+    \internal
+*/
+
+/*!
+    \fn const char *QAxBase::className() const
+    \internal
+*/
+
+QT_END_NAMESPACE
diff -rupN --no-dereference qtactiveqt-everywhere-src-5.15.13/tools/dumpcpp/main.cpp qtactiveqt-everywhere-src-5.15.13-new/tools/dumpcpp/main.cpp
--- qtactiveqt-everywhere-src-5.15.13/tools/dumpcpp/main.cpp	2024-01-04 20:21:40.000000000 +0100
+++ qtactiveqt-everywhere-src-5.15.13-new/tools/dumpcpp/main.cpp	2024-05-01 13:20:01.044261655 +0200
@@ -1586,6 +1586,10 @@ int main(int argc, char **argv)
             typeLib = settings.value(keyPrefix + QLatin1String("win32/.")).toString();
             if (QFile::exists(typeLib))
                 break;
+            typeLib = settings.value(QLatin1String("/") + codes.at(c) + QLatin1String("/0/win64/.")).toByteArray();
+            if (QFile::exists(typeLib)) {
+                break;
+            }
         }
 
         if (!typeLib.isEmpty())
@@ -1650,6 +1654,10 @@ int main(int argc, char **argv)
             typeLib = settings.value(keyPrefix + QLatin1String("win32/.")).toString();
             if (QFile::exists(typeLib))
                 break;
+            typeLib = settings.value(key + QLatin1String("/") + codes.at(c) + QLatin1String("/win64/.")).toByteArray();
+            if (QFile::exists(typeLib)) {
+                break;
+            }
         }
     }