// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include <qabstracteventdispatcher.h>
#include <qapplication.h>
#include <qbuffer.h>
#include <qdatastream.h>
#include <qdebug.h>
#include <qevent.h>
#include <qeventloop.h>
#include <qfile.h>
#include <qpointer.h>
#include <qhash.h>
#include <qmap.h>
#include <qmenubar.h>
#include <qmenu.h>
#include <qmetaobject.h>
#include <qpixmap.h>
#include <qregularexpression.h>
#include <qsizegrip.h>
#include <qstatusbar.h>
#include <qwhatsthis.h>
#include <ocidl.h>
#include <olectl.h>
#include <private/qcoreapplication_p.h>
#include <qwindow.h>
#include <private/qpixmap_win_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <qabstractnativeeventfilter.h>

#include <qlist.h>
#include <private/qguiapplication_p.h>
#include <private/qthread_p.h>

#include "qaxfactory.h"
#include "qaxbindable.h"
#include "qaxaggregated.h"

#include "../shared/qaxtypes_p.h"
#include <QtAxBase/private/qaxutils_p.h>
#include <QtAxBase/private/qaxtypefunctions_p.h>

#include "qclassfactory_p.h"

#if defined Q_CC_GNU
#   include <w32api.h>
#endif

#ifndef Q_OS_WIN64
#define ULONG_PTR DWORD
#endif

// Statusbar messageChanged() signal is connected to a fake slot
// that is custom handled in qt_metacall. Index needs to fit to ushort.
#define STATUSBAR_MESSAGE_CHANGED_SLOT_INDEX 60000

QT_BEGIN_NAMESPACE

// in qaxserver.cpp
extern ITypeLib *qAxTypeLibrary;
extern unsigned long qAxLock();
extern unsigned long qAxUnlock();
extern HANDLE qAxInstance;
extern bool qAxOutProcServer;

static int invokeCount = 0;

#ifdef QT_DEBUG
unsigned long qaxserverbase_instance_count = 0;
#endif

// in qaxserverdll.cpp
extern bool qax_ownQApp;

struct QAxExceptInfo
{
    QAxExceptInfo(int c, const QString &s, const QString &d, const QString &x)
        : code(c), src(s), desc(d), context(x)
    {
    }
    int code;
    QString src;
    QString desc;
    QString context;
};


bool qt_sendSpontaneousEvent(QObject*, QEvent*);

/*
    \class QAxServerBase
    \brief The QAxServerBase class is an ActiveX control hosting a QWidget.

    \internal
*/
class QAxServerBase :
    public QObject,
    public IAxServerBase,
    public IDispatch,
    public IOleObject,
    public IOleControl,
#if defined Q_CC_GNU
#   if (__W32API_MAJOR_VERSION < 2 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION < 5))
    public IViewObject, // this should not be needed as IViewObject2 is meant to inherit from this,
                        // until the mingw headers are fixed this will need to stay.
#   endif
#endif
    public IViewObject2,
    public IOleInPlaceObject,
    public IOleInPlaceActiveObject,
    public IProvideClassInfo2,
    public IConnectionPointContainer,
    public IPersistStream,
    public IPersistStreamInit,
    public IPersistStorage,
    public IPersistPropertyBag,
    public IPersistFile,
    public IDataObject
{
public:
    using ConnectionPoints = QMap<QUuid,IConnectionPoint*>;

    QAxServerBase(const QString &classname, IUnknown *outerUnknown);
    QAxServerBase(QObject *o);

    void init();

    ~QAxServerBase() override;

// Window creation
    HWND create(HWND hWndParent, RECT& rcPos);
    HMENU createPopup(QMenu *popup, HMENU oldMenu = nullptr);
    void createMenu(QMenuBar *menuBar);
    void removeMenu();

    static LRESULT QT_WIN_CALLBACK ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// Object registration with OLE
    void registerActiveObject(IUnknown *object);
    void revokeActiveObject();

// IUnknown
    unsigned long WINAPI AddRef() override
    {
        if (m_outerUnknown)
            return m_outerUnknown->AddRef();

        return InterlockedIncrement(&ref);
    }
    unsigned long WINAPI Release() override
    {
        if (m_outerUnknown)
            return m_outerUnknown->Release();

        LONG refCount = InterlockedDecrement(&ref);
        if (!refCount)
            delete this;

        return refCount;
    }
    HRESULT WINAPI QueryInterface(REFIID iid, void **iface) override;
    HRESULT InternalQueryInterface(REFIID iid, void **iface);

// IAxServerBase
    IUnknown *clientSite() const override
    {
        return m_spClientSite;
    }

    void emitPropertyChanged(const char *) override;
    bool emitRequestPropertyChange(const char *) override;
    QObject *qObject() const override
    {
        return theObject;
    }
    void ensureMetaData();
    bool isPropertyExposed(int index);

    void reportError(int code, const QString &src, const QString &desc,
                     const QString &context) override
    {
        delete exception;
        exception = new QAxExceptInfo(code, src, desc, context);
    }

// IDispatch
    STDMETHOD(GetTypeInfoCount)(UINT* pctinfo) override;
    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) override;
    STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) override;
    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
                LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
                EXCEPINFO* pexcepinfo, UINT* puArgErr) override;

// IProvideClassInfo
    STDMETHOD(GetClassInfo)(ITypeInfo** pptinfo) override;

// IProvideClassInfo2
    STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID) override;

// IOleObject
    STDMETHOD(Advise)(IAdviseSink* pAdvSink, DWORD* pdwConnection) override;
    STDMETHOD(Close)(DWORD dwSaveOption) override;
    STDMETHOD(DoVerb)(LONG iVerb, LPMSG lpmsg, IOleClientSite* pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect) override;
    STDMETHOD(EnumAdvise)(IEnumSTATDATA** ppenumAdvise) override;
    STDMETHOD(EnumVerbs)(IEnumOLEVERB** ppEnumOleVerb) override;
    STDMETHOD(GetClientSite)(IOleClientSite** ppClientSite) override;
    STDMETHOD(GetClipboardData)(DWORD dwReserved, IDataObject** ppDataObject) override;
    STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL* psizel) override;
    STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus) override;
    STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override;
    STDMETHOD(GetUserClassID)(CLSID* pClsid) override;
    STDMETHOD(GetUserType)(DWORD dwFormOfType, LPOLESTR *pszUserType) override;
    STDMETHOD(InitFromData)(IDataObject* pDataObject, BOOL fCreation, DWORD dwReserved) override;
    STDMETHOD(IsUpToDate)() override;
    STDMETHOD(SetClientSite)(IOleClientSite* pClientSite) override;
    STDMETHOD(SetColorScheme)(LOGPALETTE* pLogPal) override;
    STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL* psizel) override;
    STDMETHOD(SetHostNames)(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj) override;
    STDMETHOD(SetMoniker)(DWORD dwWhichMoniker, IMoniker* ppmk) override;
    STDMETHOD(Unadvise)(DWORD dwConnection) override;
    STDMETHOD(Update)() override;

// IViewObject
    STDMETHOD(Draw)(DWORD dwAspect, LONG lIndex, void *pvAspect, DVTARGETDEVICE *ptd,
                    HDC hicTargetDevice, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,
                    BOOL(__stdcall*pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue) override;
    STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd,
                    HDC hicTargetDev, LOGPALETTE **ppColorSet) override;
    STDMETHOD(Freeze)(DWORD dwAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) override;
    STDMETHOD(Unfreeze)(DWORD dwFreeze) override;
    STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink) override;
    STDMETHOD(GetAdvise)(DWORD *aspects, DWORD *advf, IAdviseSink **pAdvSink) override;

// IViewObject2
    STDMETHOD(GetExtent)(DWORD dwAspect, LONG lindex, DVTARGETDEVICE *ptd, LPSIZEL lpsizel) override;

// IOleControl
    STDMETHOD(FreezeEvents)(BOOL) override;
    STDMETHOD(GetControlInfo)(LPCONTROLINFO) override;
    STDMETHOD(OnAmbientPropertyChange)(DISPID) override;
    STDMETHOD(OnMnemonic)(LPMSG) override;

// IOleWindow
    STDMETHOD(GetWindow)(HWND *pHwnd) override;
    STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;

// IOleInPlaceObject
    STDMETHOD(InPlaceDeactivate)() override;
    STDMETHOD(UIDeactivate)() override;
    STDMETHOD(SetObjectRects)(LPCRECT lprcPosRect, LPCRECT lprcClipRect) override;
    STDMETHOD(ReactivateAndUndo)() override;

// IOleInPlaceActiveObject
    STDMETHOD(TranslateAccelerator)(MSG *pMsg) override;
    STDMETHOD(OnFrameWindowActivate)(BOOL) override;
    STDMETHOD(OnDocWindowActivate)(BOOL fActivate) override;
    STDMETHOD(ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow) override;
    STDMETHOD(EnableModeless)(BOOL) override;

// IConnectionPointContainer
    STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints**) override;
    STDMETHOD(FindConnectionPoint)(REFIID, IConnectionPoint**) override;

// IPersist
    STDMETHOD(GetClassID)(GUID*clsid) override
    {
        *clsid = qAxFactory()->classID(class_name);
        return S_OK;
    }

// IPersistStreamInit
    STDMETHOD(InitNew)(VOID) override;
    STDMETHOD(IsDirty)() override;
    STDMETHOD(Load)(IStream *pStm) override;
    STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty) override;
    STDMETHOD(GetSizeMax)(ULARGE_INTEGER *pcbSize) override;

// IPersistPropertyBag
    STDMETHOD(Load)(IPropertyBag *, IErrorLog *) override;
    STDMETHOD(Save)(IPropertyBag *, BOOL, BOOL) override;

// IPersistStorage
    STDMETHOD(InitNew)(IStorage *pStg) override;
    STDMETHOD(Load)(IStorage *pStg) override;
    STDMETHOD(Save)(IStorage *pStg, BOOL fSameAsLoad) override;
    STDMETHOD(SaveCompleted)(IStorage *pStgNew) override;
    STDMETHOD(HandsOffStorage)() override;

// IPersistFile
    STDMETHOD(SaveCompleted)(LPCOLESTR fileName) override;
    STDMETHOD(GetCurFile)(LPOLESTR *currentFile) override;
    STDMETHOD(Load)(LPCOLESTR fileName, DWORD mode) override;
    STDMETHOD(Save)(LPCOLESTR fileName, BOOL fRemember) override;

// IDataObject
    STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) override;
    STDMETHOD(GetDataHere)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */) override;
    STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */) override;
    STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */) override;
    STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */) override;
    STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */) override;
    STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override;
    STDMETHOD(DUnadvise)(DWORD dwConnection) override;
    STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise) override;

// QObject
    int qt_metacall(QMetaObject::Call, int index, void **argv) override;

    bool eventFilter(QObject *o, QEvent *e) override;

    RECT rcPosRect() const
    {
        RECT result = {0, 0, 1, 1};
        if (qt.widget)
            result = qaxContentRect(QSize(1, 1) + qaxNativeWidgetSize(qt.widget));
        return result;
    }

private:
    void update();
    void resize(const QSize &newSize);
    void updateGeometry();
    void updateMask();
    bool internalCreate();
    void internalBind();
    void internalConnect();
    HRESULT internalActivate();

    friend class QAxBindable;
    friend class QAxPropertyPage;
    QAxAggregated *aggregatedObject = nullptr;
    ConnectionPoints points;

    union {
        QWidget *widget;
        QObject *object;
    } qt;
    QPointer<QObject> theObject;
    unsigned isWidget           :1;
    unsigned ownObject          :1;
    unsigned initNewCalled      :1;
    unsigned dirtyflag          :1;
    unsigned hasStockEvents     :1;
    unsigned stayTopLevel       :1;
    unsigned isInPlaceActive    :1;
    unsigned isUIActive         :1;
    unsigned wasUIActive        :1;
    unsigned inDesignMode       :1;
    unsigned canTakeFocus       :1;
    short freezeEvents = 0;

    HWND m_hWnd = nullptr;

    HMENU hmenuShared = nullptr;
    HOLEMENU holemenu = nullptr;
    HWND hwndMenuOwner = nullptr;
    QMap<HMENU, QMenu*> menuMap;
    QMap<UINT, QAction*> actionMap;
    QPointer<QMenuBar> menuBar;
    QPointer<QStatusBar> statusBar;
    QPointer<QMenu> currentPopup;
    QAxExceptInfo *exception = nullptr;

    CRITICAL_SECTION refCountSection;
    CRITICAL_SECTION createWindowSection;

    LONG ref = 0;
    unsigned long ole_ref = 0;

    QString class_name;
    QString currentFileName;

    QHash<long, int> indexCache;
    QHash<int,DISPID> signalCache;

    IUnknown *m_outerUnknown = nullptr;
    IAdviseSink *m_spAdviseSink = nullptr;
    QList<STATDATA> adviseSinks;
    IOleClientSite *m_spClientSite = nullptr;
    IOleInPlaceSite *m_spInPlaceSite = nullptr;
    IOleInPlaceSiteWindowless *m_spInPlaceSiteWindowless = nullptr;
    IOleInPlaceFrame *m_spInPlaceFrame = nullptr;
    ITypeInfo *m_spTypeInfo = nullptr;
    IStorage *m_spStorage = nullptr;
    QSize m_currentExtent; // device independent pixels.
};

static inline QAxServerBase *axServerBaseFromWindow(HWND hWnd)
{
#ifdef GWLP_USERDATA
    return reinterpret_cast<QAxServerBase *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
#else
    return reinterpret_cast<QAxServerBase *>(GetWindowLong(hWnd, GWL_USERDATA));
#endif
}

class QAxServerAggregate : public IUnknown
{
    Q_DISABLE_COPY_MOVE(QAxServerAggregate)
public:
    QAxServerAggregate(const QString &className, IUnknown *outerUnknown)
    {
        object = new QAxServerBase(className, outerUnknown);
        object->registerActiveObject(this);

        InitializeCriticalSection(&refCountSection);
        InitializeCriticalSection(&createWindowSection);
    }
    virtual ~QAxServerAggregate()
    {
        DeleteCriticalSection(&refCountSection);
        DeleteCriticalSection(&createWindowSection);

        delete object;
    }

// IUnknown
    unsigned long WINAPI AddRef() override
    {
        return InterlockedIncrement(&ref);
    }
    unsigned long WINAPI Release() override
    {
        LONG refCount = InterlockedDecrement(&ref);
        if (!refCount)
            delete this;

        return refCount;
    }
    HRESULT WINAPI QueryInterface(REFIID iid, void **iface) override
    {
        *iface = nullptr;

        if (iid == IID_IUnknown) {
            *iface = static_cast<IUnknown *>(this);
            AddRef();
            return S_OK;
        }
        return object->InternalQueryInterface(iid, iface);
    }

private:
    QAxServerBase *object;
    LONG ref = 0;

    CRITICAL_SECTION refCountSection;
    CRITICAL_SECTION createWindowSection;
};

bool QAxFactory::createObjectWrapper(QObject *object, IDispatch **wrapper)
{
    *wrapper = nullptr;
    QAxServerBase *obj = new QAxServerBase(object);
    obj->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(wrapper));
    if (*wrapper)
        return true;

    delete obj;
    return false;
}


/*
    Helper class to enumerate all supported event interfaces.
*/
class QAxSignalVec : public IEnumConnectionPoints
{
public:
    QAxSignalVec &operator=(const QAxSignalVec &) = delete;
    QAxSignalVec &operator=(QAxSignalVec &&) = delete;
    QAxSignalVec(QAxSignalVec &&) = delete;

    QAxSignalVec(const QAxServerBase::ConnectionPoints &points)
        : cpoints(points.values())
    {
        InitializeCriticalSection(&refCountSection);
        for (const auto &point : std::as_const(cpoints))
            point->AddRef();
    }
    QAxSignalVec(const QAxSignalVec &old)
        : cpoints(old.cpoints)
        , current(old.current)
    {
        InitializeCriticalSection(&refCountSection);
        ref = 0;
        for (const auto &point : std::as_const(cpoints))
            point->AddRef();
    }
    virtual ~QAxSignalVec()
    {
        for (const auto &point : std::as_const(cpoints))
            point->Release();

        DeleteCriticalSection(&refCountSection);
    }

    unsigned long __stdcall AddRef() override
    {
        return InterlockedIncrement(&ref);
    }
    unsigned long __stdcall Release() override
    {
        LONG refCount = InterlockedDecrement(&ref);
        if (!refCount)
            delete this;

        return refCount;
    }
    STDMETHOD(QueryInterface)(REFIID iid, void **iface) override
    {
        if (!iface)
            return E_POINTER;
        *iface = nullptr;
        if (iid == IID_IUnknown)
            *iface = this;
        else if (iid == IID_IEnumConnectionPoints)
            *iface = this;
        else
            return E_NOINTERFACE;

        AddRef();
        return S_OK;
    }
    STDMETHOD(Next)(ULONG cConnections, IConnectionPoint **cpoint, ULONG *pcFetched) override
    {
        if (!cpoint)
            return E_POINTER;

        if (!pcFetched && cConnections > 1)
            return E_POINTER;

        const qsizetype count = cpoints.size();
        unsigned long i;
        for (i = 0; i < cConnections; i++) {
            if (current==count)
                break;
            IConnectionPoint *cp = cpoints.at(current);
            cp->AddRef();
            cpoint[i] = cp;
            ++current;
        }
        if (pcFetched)
            *pcFetched = i;
        return i == cConnections ? S_OK : S_FALSE;
    }
    STDMETHOD(Skip)(ULONG cConnections) override
    {
        const qsizetype count = cpoints.size();
        while (cConnections) {
            if (current == count)
                return S_FALSE;
            ++current;
            --cConnections;
        }
        return S_OK;
    }
    STDMETHOD(Reset)() override
    {
        current = 0;
        return S_OK;
    }
    STDMETHOD(Clone)(IEnumConnectionPoints **ppEnum) override
    {
        if (!ppEnum)
            return E_POINTER;
        *ppEnum = new QAxSignalVec(*this);
        (*ppEnum)->AddRef();

        return S_OK;
    }

    QList<IConnectionPoint*> cpoints;
    int current = 0;

private:
    CRITICAL_SECTION refCountSection;

    LONG ref = 0;
};

/*
    Helper class to store and enumerate all connected event listeners.
*/
class QAxConnection : public IConnectionPoint,
                      public IEnumConnections
{
public:
    QAxConnection &operator=(const QAxConnection &) = delete;
    QAxConnection(QAxConnection &&) = delete;
    QAxConnection &operator=(QAxConnection &&) = delete;

    using Connections = QList<CONNECTDATA>;

    QAxConnection(QAxServerBase *parent, const QUuid &uuid)
        : that(parent), iid(uuid)
    {
        InitializeCriticalSection(&refCountSection);
    }
    QAxConnection(const QAxConnection &old)
        : current(old.current)
    {
        InitializeCriticalSection(&refCountSection);
        ref = 0;
        connections = old.connections;
        that = old.that;
        iid = old.iid;
        for (const CONNECTDATA &connection : std::as_const(connections))
            connection.pUnk->AddRef();
    }
    virtual ~QAxConnection()
    {
        DeleteCriticalSection(&refCountSection);
    }

    unsigned long __stdcall AddRef() override
    {
        return InterlockedIncrement(&ref);
    }
    unsigned long __stdcall Release() override
    {
        LONG refCount = InterlockedDecrement(&ref);
        if (!refCount)
            delete this;

        return refCount;
    }
    STDMETHOD(QueryInterface)(REFIID iid, void **iface) override
    {
        if (!iface)
            return E_POINTER;
        *iface = nullptr;
        if (iid == IID_IUnknown)
            *iface = static_cast<IConnectionPoint *>(this);
        else if (iid == IID_IConnectionPoint)
            *iface = this;
        else if (iid == IID_IEnumConnections)
            *iface = this;
        else
            return E_NOINTERFACE;

        AddRef();
        return S_OK;
    }
    STDMETHOD(GetConnectionInterface)(IID *pIID) override
    {
        if (!pIID)
            return E_POINTER;
        *pIID = iid;
        return S_OK;
    }
    STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC) override
    {
        return that->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void **>(ppCPC));
    }
    STDMETHOD(Advise)(IUnknown*pUnk, DWORD *pdwCookie) override
    {
        if (!pUnk || !pdwCookie)
            return E_POINTER;

        {
            IDispatch *checkImpl = nullptr;
            pUnk->QueryInterface(iid, reinterpret_cast<void **>(&checkImpl));
            if (!checkImpl)
                return CONNECT_E_CANNOTCONNECT;
            checkImpl->Release();
        }

        CONNECTDATA cd;
        cd.dwCookie = connections.size() + 1;
        cd.pUnk = pUnk;
        cd.pUnk->AddRef();
        connections.append(cd);
        *pdwCookie = cd.dwCookie;
        return S_OK;
    }
    STDMETHOD(Unadvise)(DWORD dwCookie) override
    {
        const qsizetype count = connections.size();
        for (qsizetype i = 0; i < count; ++i) {
            if (connections.at(i).dwCookie == dwCookie) {
                connections.at(i).pUnk->Release();
                connections.removeAt(i);
                if (current >= i && current != 0)
                    --current;
                return S_OK;
            }
        }
        return CONNECT_E_NOCONNECTION;
    }
    STDMETHOD(EnumConnections)(IEnumConnections **ppEnum) override
    {
        if (!ppEnum)
            return E_POINTER;
        *ppEnum = this;
        AddRef();

        return S_OK;
    }
    STDMETHOD(Next)(ULONG cConnections, CONNECTDATA *cd, ULONG *pcFetched) override
    {
        if (!cd)
            return E_POINTER;

        if (!pcFetched && cConnections > 1)
            return E_POINTER;

        const qsizetype count = connections.size();

        unsigned long i;
        for (i = 0; i < cConnections; i++) {
            if (current == count)
                break;
            cd[i] = connections.at(current);
            cd[i].pUnk->AddRef();
            ++current;
        }
        if (pcFetched)
            *pcFetched = i;
        return i == cConnections ? S_OK : S_FALSE;
    }
    STDMETHOD(Skip)(ULONG cConnections) override
    {
        const qsizetype count = connections.size();
        while (cConnections) {
            if (current == count)
                return S_FALSE;
            ++current;
            --cConnections;
        }
        return S_OK;
    }
    STDMETHOD(Reset)() override
    {
        current = 0;
        return S_OK;
    }
    STDMETHOD(Clone)(IEnumConnections **ppEnum) override
    {
        if (!ppEnum)
            return E_POINTER;
        *ppEnum = new QAxConnection(*this);
        (*ppEnum)->AddRef();

        return S_OK;
    }

private:
    QAxServerBase *that;
    QUuid iid;
    Connections connections;
    int current = 0;

    CRITICAL_SECTION refCountSection;
    LONG ref = 1;
};

// filter for executable case to hook into Qt eventloop
// for DLLs the client calls TranslateAccelerator
class QAxWinEventFilter : public QAbstractNativeEventFilter
{
public:
    bool nativeEventFilter(const QByteArray &, void *message, qintptr *) override;
};

bool QAxWinEventFilter::nativeEventFilter(const QByteArray &, void *message, qintptr *)
{
    MSG *pMsg = static_cast<MSG *>(message);
    if (pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST)
        return false;

    bool ret = false;
    QWidget *aqt = QWidget::find(reinterpret_cast<WId>(pMsg->hwnd));
    if (!aqt)
        return ret;

    // FIXME: 4.10.211: was '::GetParent(hwndForWidget(aqt));'
    HWND baseHwnd = hwndForWidget(aqt);
    QAxServerBase *axbase = nullptr;
    while (!axbase && baseHwnd) {
        axbase = axServerBaseFromWindow(baseHwnd);
        baseHwnd = ::GetParent(baseHwnd);
    }
    if (!axbase)
        return ret;

    HRESULT hres = axbase->TranslateAcceleratorW(pMsg);
    return hres == S_OK;
}

Q_GLOBAL_STATIC(QAxWinEventFilter, qax_winEventFilter);

// COM Factory class, mapping COM requests to ActiveQt requests.
// One instance of this class for each ActiveX the server can provide.
QClassFactory::QClassFactory(CLSID clsid)
{
    InitializeCriticalSection(&refCountSection);

    // COM only knows the CLSID, but QAxFactory is class name based...
    const QStringList keys = qAxFactory()->featureList();
    for (const QString &key : keys) {
        if (qAxFactory()->classID(key) == clsid) {
            className = key;
            break;
        }
    }

    const QMetaObject *mo = qAxFactory()->metaObject(className);
    if (mo) {
        classKey = QLatin1String(mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value());
        licensed = !classKey.isEmpty();
    }
}

QClassFactory::~QClassFactory()
{
    DeleteCriticalSection(&refCountSection);
}

// IUnknown
unsigned long QClassFactory::AddRef()
{
    return InterlockedIncrement(&ref);
}

unsigned long QClassFactory::Release()
{
    LONG refCount = InterlockedDecrement(&ref);
    if (!refCount)
        delete this;

    return refCount;
}

HRESULT QClassFactory::QueryInterface(REFIID iid, LPVOID *iface)
{
    *iface = nullptr;
    if (iid == IID_IUnknown)
        *iface = static_cast<IUnknown *>(this);
    else if (iid == IID_IClassFactory)
        *iface = static_cast<IClassFactory *>(this);
    else if (iid == IID_IClassFactory2 && licensed)
        *iface = static_cast<IClassFactory2 *>(this);
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

HRESULT QClassFactory::CreateInstanceHelper(IUnknown *pUnkOuter, REFIID iid, void **ppObject)
{
    if (pUnkOuter) {
        if (iid != IID_IUnknown)
            return CLASS_E_NOAGGREGATION;
        const QMetaObject *mo = qAxFactory()->metaObject(className);
        if (mo && !qstricmp(mo->classInfo(mo->indexOfClassInfo("Aggregatable")).value(), "no"))
            return CLASS_E_NOAGGREGATION;
    }

    // Make sure a QApplication instance is present (inprocess case)
    if (!qApp) {
        qax_ownQApp = true;
        new QApplication(__argc, __argv);
    }
    QGuiApplication::setQuitOnLastWindowClosed(false);

    if (qAxOutProcServer)
        QAbstractEventDispatcher::instance()->installNativeEventFilter(qax_winEventFilter());
    else
        QGuiApplicationPrivate::instance()->in_exec = true;

    // If we created QApplication instance, ensure native event loop starts properly
    // by calling processEvents.
    if (qax_ownQApp)
        QCoreApplication::processEvents();

    HRESULT res;
    // Create the ActiveX wrapper - aggregate if requested
    if (pUnkOuter) {
        QAxServerAggregate *aggregate = new QAxServerAggregate(className, pUnkOuter);
        res = aggregate->QueryInterface(iid, ppObject);
        if (FAILED(res))
            delete aggregate;
    } else {
        QAxServerBase *activeqt = new QAxServerBase(className, pUnkOuter);
        res = activeqt->QueryInterface(iid, ppObject);
        if (FAILED(res))
            delete activeqt;
        else
            activeqt->registerActiveObject(static_cast<IUnknown*>(static_cast<IDispatch*>(activeqt)));
    }
    return res;
}

// IClassFactory
HRESULT QClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppObject)
{
    // class is licensed
    if (licensed && !qAxFactory()->validateLicenseKey(className, QString()))
        return CLASS_E_NOTLICENSED;

    return CreateInstanceHelper(pUnkOuter, iid, ppObject);
}

HRESULT QClassFactory::LockServer(BOOL fLock)
{
    if (fLock)
        qAxLock();
    else
        qAxUnlock();

    return S_OK;
}

// IClassFactory2
HRESULT QClassFactory::RequestLicKey(DWORD, BSTR *pKey)
{
    if (!pKey)
        return E_POINTER;
    *pKey = nullptr;

    // This of course works only on fully licensed machines
    if (!qAxFactory()->validateLicenseKey(className, QString()))
        return CLASS_E_NOTLICENSED;

    *pKey = QStringToBSTR(classKey);
    return S_OK;
}

HRESULT QClassFactory::GetLicInfo(LICINFO *pLicInfo)
{
    if (!pLicInfo)
        return E_POINTER;
    pLicInfo->cbLicInfo = sizeof(LICINFO);

    // class specific license key?
    const QMetaObject *mo = qAxFactory()->metaObject(className);
    const char *key = mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value();
    pLicInfo->fRuntimeKeyAvail = key && key[0];

    // machine fully licensed?
    pLicInfo->fLicVerified = qAxFactory()->validateLicenseKey(className, QString());

    return S_OK;
}

HRESULT QClassFactory::CreateInstanceLic(IUnknown *pUnkOuter, IUnknown * /* pUnkReserved */, REFIID iid, BSTR bKey, PVOID *ppObject)
{
    QString licenseKey = QString::fromWCharArray(bKey);
    if (!qAxFactory()->validateLicenseKey(className, licenseKey))
        return CLASS_E_NOTLICENSED;
    return CreateInstanceHelper(pUnkOuter, iid, ppObject);
}

void QClassFactory::cleanupCreatedApplication(QCoreApplication &app)
{
    // Cleanup similar to QCoreApplication::exec()
    QCoreApplicationPrivate *priv = static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(&app));
    priv->execCleanup();
}


// Create a QClassFactory object for class \a iid
HRESULT GetClassObject(REFIID clsid, REFIID iid, void **ppUnk)
{
    QClassFactory *factory = new QClassFactory(clsid);
    if (!factory)
        return E_OUTOFMEMORY;
    if (factory->className.isEmpty()) {
        delete factory;
        return E_NOINTERFACE;
    }
    HRESULT res = factory->QueryInterface(iid, ppUnk);
    if (res != S_OK)
        delete factory;
    return res;
}


/*! \internal
    Constructs a QAxServerBase object wrapping the QWidget \a
    classname into an ActiveX control.

    The constructor is called by the QClassFactory object provided by
    the COM server for the respective CLSID.
*/
QAxServerBase::QAxServerBase(const QString &classname, IUnknown *outerUnknown)
    : class_name(classname), m_outerUnknown(outerUnknown)
{
    init();

    internalCreate();
}

/*! \internal
    Constructs a QAxServerBase object wrapping \a o.
*/
QAxServerBase::QAxServerBase(QObject *o)
{
    init();

    qt.object = o;
    if (o) {
        theObject = o;
        isWidget = false;
        class_name = QLatin1String(o->metaObject()->className());
    }
    internalBind();
    internalConnect();
}

/*! \internal
    Initializes data members.
*/
void QAxServerBase::init()
{
    qt.object = nullptr;
    isWidget            = false;
    ownObject           = false;
    initNewCalled       = false;
    dirtyflag           = false;
    hasStockEvents      = false;
    stayTopLevel        = false;
    isInPlaceActive     = false;
    isUIActive          = false;
    wasUIActive         = false;
    inDesignMode        = false;
    canTakeFocus        = false;

    InitializeCriticalSection(&refCountSection);
    InitializeCriticalSection(&createWindowSection);

#ifdef QT_DEBUG
    EnterCriticalSection(&refCountSection);
    ++qaxserverbase_instance_count;
    LeaveCriticalSection(&refCountSection);
#endif

    qAxLock();

    points[IID_IPropertyNotifySink] = new QAxConnection(this, IID_IPropertyNotifySink);
}

/*! \internal
    Destroys the QAxServerBase object, releasing all allocated
    resources and interfaces.
*/
QAxServerBase::~QAxServerBase()
{
#ifdef QT_DEBUG
    EnterCriticalSection(&refCountSection);
    --qaxserverbase_instance_count;
    LeaveCriticalSection(&refCountSection);
#endif

    revokeActiveObject();
    for (auto it = points.cbegin(), end = points.cend(); it != end; ++it) {
        if (IConnectionPoint *point = it.value())
            point->Release();
    }
    delete aggregatedObject;
    aggregatedObject = nullptr;
    if (theObject) {
        qt.object->disconnect(this);
        QObject *aqt = qt.object;
        qt.object = nullptr;
        if (ownObject)
            delete aqt;
    }

    if (m_spAdviseSink) m_spAdviseSink->Release();
    m_spAdviseSink = nullptr;
    for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
        adviseSinks.at(i).pAdvSink->Release();
    }
    if (m_spClientSite) m_spClientSite->Release();
    m_spClientSite = nullptr;
    if (m_spInPlaceFrame) m_spInPlaceFrame->Release();
    m_spInPlaceFrame = nullptr;
    if (m_spInPlaceSiteWindowless)
        m_spInPlaceSiteWindowless->Release();
    m_spInPlaceSiteWindowless = nullptr;
    if (m_spInPlaceSite) m_spInPlaceSite->Release();
    m_spInPlaceSite = nullptr;
    if (m_spTypeInfo) m_spTypeInfo->Release();
    m_spTypeInfo = nullptr;
    if (m_spStorage) m_spStorage->Release();
    m_spStorage = nullptr;

    DeleteCriticalSection(&refCountSection);
    DeleteCriticalSection(&createWindowSection);

    qAxUnlock();
}

/*  \internal
    Registering with OLE
*/
void QAxServerBase::registerActiveObject(IUnknown *object)
{
    if (ole_ref || !qt.object || !qAxOutProcServer)
        return;

    const QMetaObject *mo = qt.object->metaObject();
    if (!qstricmp(mo->classInfo(mo->indexOfClassInfo("RegisterObject")).value(), "yes"))
        RegisterActiveObject(object, qAxFactory()->classID(class_name), ACTIVEOBJECT_WEAK, &ole_ref);
}

void QAxServerBase::revokeActiveObject()
{
    if (!ole_ref)
        return;

    RevokeActiveObject(ole_ref, nullptr);
    ole_ref = 0;
}

/* \internal
    QueryInterface implementation.
*/
HRESULT WINAPI QAxServerBase::QueryInterface(REFIID iid, void **iface)
{
    if (m_outerUnknown)
        return m_outerUnknown->QueryInterface(iid, iface);

    return InternalQueryInterface(iid, iface);
}

HRESULT QAxServerBase::InternalQueryInterface(REFIID iid, void **iface)
{
    *iface = nullptr;

    if (iid == IID_IUnknown) {
        *iface = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
    } else {
        HRESULT res = S_OK;
        if (aggregatedObject)
            res = aggregatedObject->queryInterface(iid, iface);
        if (*iface)
            return res;
    }

    if (!(*iface)) {
        if (iid == qAxFactory()->interfaceID(class_name))
            *iface = static_cast<IDispatch *>(this);
        if (iid == IID_IDispatch)
            *iface = static_cast<IDispatch *>(this);
        else if (iid == IID_IAxServerBase)
            *iface = static_cast<IAxServerBase *>(this);
        else if (iid == IID_IOleObject)
            *iface = static_cast<IOleObject *>(this);
        else if (iid == IID_IConnectionPointContainer)
            *iface = static_cast<IConnectionPointContainer *>(this);
        else if (iid == IID_IProvideClassInfo)
            *iface = static_cast<IProvideClassInfo *>(this);
        else if (iid == IID_IProvideClassInfo2)
            *iface = static_cast<IProvideClassInfo2 *>(this);
        else if (iid == IID_IPersist)
            *iface = static_cast<IPersist *>(static_cast<IPersistStream *>(this));
        else if (iid == IID_IPersistStream)
            *iface = static_cast<IPersistStream *>(this);
        else if (iid == IID_IPersistStreamInit)
            *iface = static_cast<IPersistStreamInit *>(this);
        else if (iid == IID_IPersistStorage)
            *iface = static_cast<IPersistStorage *>(this);
        else if (iid == IID_IPersistPropertyBag)
            *iface = static_cast<IPersistPropertyBag *>(this);
        else if (iid == IID_IPersistFile &&
            qAxFactory()->metaObject(class_name)->indexOfClassInfo("MIME") != -1)
            *iface = static_cast<IPersistFile *>(this);
        else if (iid == IID_IViewObject)
            *iface = static_cast<IViewObject *>(this);
        else if (iid == IID_IViewObject2)
            *iface = static_cast<IViewObject2 *>(this);
        else if (isWidget) {
            if (iid == IID_IOleControl)
                *iface = static_cast<IOleControl *>(this);
            else if (iid == IID_IOleWindow)
                *iface = static_cast<IOleWindow *>(static_cast<IOleInPlaceObject *>(this));
            else if (iid == IID_IOleInPlaceObject)
                *iface = static_cast<IOleInPlaceObject *>(this);
            else if (iid == IID_IOleInPlaceActiveObject)
                *iface = static_cast<IOleInPlaceActiveObject *>(this);
            else if (iid == IID_IDataObject)
                *iface = static_cast<IDataObject *>(this);
        }
    }
    if (!*iface)
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

/*! \internal
    Detects and initilaizes implementation of QAxBindable in objects.
*/
void QAxServerBase::internalBind()
{
    QAxBindable *axb = static_cast<QAxBindable *>(qt.object->qt_metacast("QAxBindable"));
    if (axb) {
        // no addref; this is aggregated
        axb->activex = this;
        if (!aggregatedObject)
            aggregatedObject = axb->createAggregate();
        if (aggregatedObject) {
            aggregatedObject->controlling_unknown = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
            aggregatedObject->the_object = qt.object;
        }
    }
}

/*! \internal
    Connects object signals to event dispatcher.
*/
void QAxServerBase::internalConnect()
{
    QUuid eventsID = qAxFactory()->eventsID(class_name);
    if (!eventsID.isNull()) {
        if (!points[eventsID])
            points[eventsID] = new QAxConnection(this, eventsID);

        // connect the generic slot to all signals of qt.object
        const QMetaObject *mo = qt.object->metaObject();
        for (int isignal = mo->methodCount()-1; isignal >= 0; --isignal) {
            if (mo->method(isignal).methodType() == QMetaMethod::Signal)
                QMetaObject::connect(qt.object, isignal, this, isignal);
        }
    }
}

/*! \internal
    Creates the QWidget for the classname passed to the c'tor.

    All signals of the widget class are connected to the internal event mapper.
    If the widget implements QAxBindable, stock events are also connected.
*/
bool QAxServerBase::internalCreate()
{
    if (qt.object)
        return true;

    qt.object = qAxFactory()->createObject(class_name);
    Q_ASSERT(qt.object);
    if (!qt.object)
        return false;

    theObject = qt.object;
    ownObject = true;
    isWidget = qt.object->isWidgetType();
    hasStockEvents = qAxFactory()->hasStockEvents(class_name);
    stayTopLevel = qAxFactory()->stayTopLevel(class_name);

    internalBind();
    if (isWidget) {
        if (!stayTopLevel) {
            QEvent e(QEvent::EmbeddingControl);
            QCoreApplication::sendEvent(qt.widget, &e);
        }
        qt.widget->setAttribute(Qt::WA_QuitOnClose, false);
        qt.widget->move(0, 0);

        // initialize to sizeHint, but don't set resized flag so that container has a chance to override
        bool wasResized = qt.widget->testAttribute(Qt::WA_Resized);
        updateGeometry();
        if (!wasResized && qt.widget->testAttribute(Qt::WA_Resized)
            && qt.widget->sizePolicy() != QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)) {
            qt.widget->setAttribute(Qt::WA_Resized, false);
        }
    }

    internalConnect();
    // install an event filter for stock events
    if (isWidget) {
        qt.object->installEventFilter(this);
        const QWidgetList children = qt.object->findChildren<QWidget*>();
        for (QWidget *child : children)
            child->installEventFilter(this);
    }
    return true;
}

/*
class HackMenuData : public QMenuData
{
    friend class QAxServerBase;
};
*/

class HackWidget : public QWidget
{
    friend class QAxServerBase;
};
/*
    Message handler. \a hWnd is always the ActiveX widget hosting the Qt widget.
    \a uMsg is handled as follows
    \list
    \li WM_CREATE The ActiveX control is created
    \li WM_DESTROY The QWidget is destroyed
    \li WM_SHOWWINDOW The QWidget is parented into the ActiveX window
    \li WM_PAINT The QWidget is updated
    \li WM_SIZE The QWidget is resized to the new size
    \li WM_SETFOCUS and
    \li WM_KILLFOCUS The client site is notified about the focus transfer
    \li WM_MOUSEACTIVATE The ActiveX is activated
    \endlist

    The semantics of \a wParam and \a lParam depend on the value of \a uMsg.
*/
LRESULT QT_WIN_CALLBACK QAxServerBase::ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_CREATE) {
        CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lParam);
        QAxServerBase *that = static_cast<QAxServerBase *>(cs->lpCreateParams);

#ifdef GWLP_USERDATA
        SetWindowLongPtr(hWnd, GWLP_USERDATA, LONG_PTR(that));
#else
        SetWindowLong(hWnd, GWL_USERDATA, LONG(that));
#endif

        that->m_hWnd = hWnd;

        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    switch (uMsg) {
    case WM_NCDESTROY:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd))
            that->m_hWnd = nullptr;
        break;

    case WM_QUERYENDSESSION:
    case WM_DESTROY:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            if (that->qt.widget) {
                that->qt.widget->hide();
                if (QWindow *widgetWindow = that->qt.widget->windowHandle()) {
                    if (HWND h = reinterpret_cast<HWND>(widgetWindow->winId()))
                        ::SetParent(h, nullptr);
                }
            }
        }
        break;

    case WM_SHOWWINDOW:
        if (wParam) {
            if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
                that->internalCreate();
                if (!that->stayTopLevel) {
                    // Set this property on window to pass the native handle to platform plugin,
                    // so that it can create the window with proper flags instead of thinking
                    // it is toplevel.
                    that->qt.widget->setProperty("_q_embedded_native_parent_handle", WId(that->m_hWnd));

                    if (QWindow *widgetWindow = that->qt.widget->windowHandle()) {
                        // If embedded widget is native, such as QGLWidget, it may have already created
                        // a window before now, probably as an undesired toplevel. In that case set the
                        // proper parent window and set the window frameless to position it correctly.
                        if (that->qt.widget->testAttribute(Qt::WA_WState_Created)
                            && !that->qt.widget->isVisible()) {
                            HWND h = static_cast<HWND>(QGuiApplication::platformNativeInterface()->
                                                       nativeResourceForWindow("handle", widgetWindow));
                            if (h) {
                                ::SetParent(h, that->m_hWnd);
                                // Since the window is already created, we need to set the
                                // property directly to ensure it does not believe it is
                                // toplevel.
                                widgetWindow->setProperty("_q_embedded_native_parent_handle",
                                                          WId(that->m_hWnd));
                            }
                            Qt::WindowFlags flags = widgetWindow->flags();
                            widgetWindow->setFlags(flags | Qt::FramelessWindowHint);
                        }
                    }
                    that->qt.widget->raise();
                    that->qt.widget->move(0, 0);
                }
                that->qt.widget->show();
            } else if (that->qt.widget) {
                that->qt.widget->hide();
            }
        }
        break;

    case WM_ERASEBKGND:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd))
            that->updateMask();
        break;

    case WM_SIZE:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd))
            that->resize(qaxFromNativeSize(that->qt.widget, QSize(LOWORD(lParam), HIWORD(lParam))));
        break;

    case WM_SETFOCUS:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            if (that->isInPlaceActive && that->m_spClientSite && !that->inDesignMode && that->canTakeFocus) {
                RECT rcPos = that->rcPosRect();
                that->DoVerb(OLEIVERB_UIACTIVATE, nullptr, that->m_spClientSite, 0, that->m_hWnd, &rcPos);
                if (that->isUIActive) {
                    IOleControlSite *spSite = nullptr;
                    that->m_spClientSite->QueryInterface(IID_IOleControlSite, reinterpret_cast<void **>(&spSite));
                    if (spSite) {
                        spSite->OnFocus(true);
                        spSite->Release();
                    }
                    QWidget *candidate = that->qt.widget;
                    while (!(candidate->focusPolicy() & Qt::TabFocus)) {
                        candidate = candidate->nextInFocusChain();
                        if (candidate == that->qt.widget) {
                            candidate = nullptr;
                            break;
                        }
                    }
                    if (candidate) {
                        candidate->setFocus();
                        if (::GetKeyState(VK_SHIFT) < 0)
                            static_cast<HackWidget *>(that->qt.widget)->focusNextPrevChild(false);
                    }
                }
            }
        }
        break;

    case WM_KILLFOCUS:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            if (that->isInPlaceActive && that->isUIActive && that->m_spClientSite) {
                IOleControlSite *spSite = nullptr;
                that->m_spClientSite->QueryInterface(IID_IOleControlSite, reinterpret_cast<void **>(&spSite));
                if (spSite) {
                    if (!::IsChild(that->m_hWnd, ::GetFocus()))
                        spSite->OnFocus(false);
                    spSite->Release();
                }
            }
        }
        break;

    case WM_MOUSEACTIVATE:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            RECT rcPos = that->rcPosRect();
            that->DoVerb(OLEIVERB_UIACTIVATE, nullptr, that->m_spClientSite, 0, that->m_hWnd, &rcPos);
        }
        break;

    case WM_INITMENUPOPUP:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            if (that->qt.widget) {
                that->currentPopup = that->menuMap[reinterpret_cast<HMENU>(wParam)];
                if (!that->currentPopup)
                    break;
                const QMetaObject *mo = that->currentPopup->metaObject();
                int index = mo->indexOfSignal("aboutToShow()");
                if (index < 0)
                    break;

                that->currentPopup->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr);
                that->createPopup(that->currentPopup, reinterpret_cast<HMENU>(wParam));
                return 0;
            }
        }
        break;

    case WM_MENUSELECT:
    case WM_COMMAND:
        if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
            if (that->qt.widget) {
                QMenuBar *menuBar = that->menuBar;
                if (!menuBar)
                    break;

                QObject *menuObject = nullptr;
                bool menuClosed = false;

                if (uMsg == WM_COMMAND) {
                    menuObject = that->actionMap.value(UINT(wParam));
                } else if (!lParam) {
                    menuClosed = true;
                    menuObject = that->currentPopup;
                } else {
                    menuObject = that->actionMap.value(LOWORD(wParam));
                }

                if (menuObject) {
                    const QMetaObject *mo = menuObject->metaObject();
                    int index = -1;

                    if (uMsg == WM_COMMAND)
                        index = mo->indexOfSignal("activated()");
                    else if (menuClosed)
                        index = mo->indexOfSignal("aboutToHide()");
                    else
                        index = mo->indexOfSignal("hovered()");

                    if (index < 0)
                        break;

                    menuObject->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr);
                    if (menuClosed || uMsg == WM_COMMAND)
                        that->currentPopup = nullptr;
                    return 0;
                }
            }
        }
        break;

    case WM_DISPLAYCHANGE:
        qaxClearCachedSystemLogicalDpi();
        break;

    default:
        break;
    }

    return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

/*! \internal
    Creates the window hosting the QWidget.
*/
HWND QAxServerBase::create(HWND hWndParent, RECT& rcPos)
{
    Q_ASSERT(isWidget && qt.widget);

    static ATOM atom = 0;
    HINSTANCE hInst = reinterpret_cast<HINSTANCE>(qAxInstance);
    EnterCriticalSection(&createWindowSection);
    QString cn(QLatin1String("QAxControl"));
    cn += QString::number(quintptr(ActiveXProc));
    if (!atom) {
        WNDCLASS wcTemp;
        wcTemp.style = CS_DBLCLKS;
        wcTemp.cbClsExtra = 0;
        wcTemp.cbWndExtra = 0;
        wcTemp.hbrBackground = nullptr;
        wcTemp.hCursor = nullptr;
        wcTemp.hIcon = nullptr;
        wcTemp.hInstance = hInst;
        wcTemp.lpszClassName = reinterpret_cast<const wchar_t *>(cn.utf16());
        wcTemp.lpszMenuName = nullptr;
        wcTemp.lpfnWndProc = ActiveXProc;

        atom = RegisterClass(&wcTemp);
    }
    LeaveCriticalSection(&createWindowSection);
    if (!atom) {
        const DWORD errorCode = GetLastError();
        if (errorCode != ERROR_CLASS_ALREADY_EXISTS) {
            qErrnoWarning(int(errorCode), "%s: RegisterClass() failed", __FUNCTION__);
            return nullptr;
        }
    }

    Q_ASSERT(!m_hWnd);
    // will fail if parent window belongs to a higher integrity level process
    HWND hWnd = ::CreateWindow(reinterpret_cast<const wchar_t *>(cn.utf16()), nullptr,
                               WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                               rcPos.left, rcPos.top, rcPos.right - rcPos.left,
                               rcPos.bottom - rcPos.top, hWndParent, nullptr, hInst, this);
    // m_hWnd is assigned in reponse to WM_CREATE
    if (!hWnd) {
        DWORD err = GetLastError();
        if (err == ERROR_ACCESS_DENIED) {
            // retry without parent window
            // the window will now need to be re-parented in the container process
            hWnd = ::CreateWindow(reinterpret_cast<const wchar_t *>(cn.utf16()), nullptr,
                                  WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                  rcPos.left, rcPos.top, rcPos.right - rcPos.left,
                                  rcPos.bottom - rcPos.top, nullptr, nullptr, hInst, this);
        }
    }

    if (!hWnd) {
        qErrnoWarning("%s: CreateWindow() failed", __FUNCTION__);
        return nullptr;
    }

    Q_ASSERT(m_hWnd == hWnd);

    updateMask();
    EnableWindow(m_hWnd, qt.widget->isEnabled());

    return hWnd;
}

/* \internal
    Recoursively creates Win32 submenus.
*/
HMENU QAxServerBase::createPopup(QMenu *popup, HMENU oldMenu)
{
    HMENU popupMenu = oldMenu ? oldMenu : CreatePopupMenu();
    menuMap.insert(popupMenu, popup);

    if (oldMenu) while (GetMenuItemCount(oldMenu)) {
        DeleteMenu(oldMenu, 0, MF_BYPOSITION);
    }

    const auto actions = popup->actions();
    for (QAction *action : actions) {
        uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED;
        if (action->isSeparator())
            flags |= MF_SEPARATOR;
        else if (action->menu())
            flags |= MF_POPUP;
        else
            flags |= MF_STRING;
        if (action->isChecked())
            flags |= MF_CHECKED;

        ushort itemId;
        if (flags & MF_POPUP) {
            itemId = static_cast<ushort>(
                reinterpret_cast<quintptr>(createPopup(action->menu()))
            );
        } else {
            itemId = static_cast<ushort>(reinterpret_cast<quintptr>(action));
            actionMap.remove(itemId);
            actionMap.insert(itemId, action);
        }
        AppendMenu(popupMenu, flags, itemId, reinterpret_cast<const wchar_t *>(action->text().utf16()));
    }
    if (oldMenu)
        DrawMenuBar(hwndMenuOwner);
    return popupMenu;
}

/*! \internal
    Creates a Win32 menubar.
*/
void QAxServerBase::createMenu(QMenuBar *menuBar)
{
    hmenuShared = ::CreateMenu();

    int edit = 0;
    int object = 0;
    int help = 0;

    const auto actions = menuBar->actions();
    for (QAction *action : actions) {
        uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED;
        if (action->isSeparator())
            flags |= MF_SEPARATOR;
        else if (action->menu())
            flags |= MF_POPUP;
        else
            flags |= MF_STRING;

        if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Edit"))
            edit++;
        else if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Help"))
            help++;
        else
            object++;

        ushort itemId;
        if (flags & MF_POPUP) {
            itemId = static_cast<ushort>(
                reinterpret_cast<quintptr>(createPopup(action->menu()))
            );
        } else {
            itemId = static_cast<ushort>(reinterpret_cast<quintptr>(action));
            actionMap.insert(itemId, action);
        }
        AppendMenu(hmenuShared, flags, itemId, reinterpret_cast<const wchar_t *>(action->text().utf16()));
    }

    OLEMENUGROUPWIDTHS menuWidths = { { 0,edit,0,object,0,help } };
    HRESULT hres = m_spInPlaceFrame->InsertMenus(hmenuShared, &menuWidths);
    if (FAILED(hres)) {
        ::DestroyMenu(hmenuShared);
        hmenuShared = nullptr;
        return;
    }

    m_spInPlaceFrame->GetWindow(&hwndMenuOwner);

    holemenu = OleCreateMenuDescriptor(hmenuShared, &menuWidths);
    hres = m_spInPlaceFrame->SetMenu(hmenuShared, holemenu, m_hWnd);
    if (FAILED(hres)) {
        ::DestroyMenu(hmenuShared);
        hmenuShared = nullptr;
        OleDestroyMenuDescriptor(holemenu);
    }
}

/*! \internal
    Remove the Win32 menubar.
*/
void QAxServerBase::removeMenu()
{
    if (hmenuShared)
        m_spInPlaceFrame->RemoveMenus(hmenuShared);
    holemenu = nullptr;
    m_spInPlaceFrame->SetMenu(nullptr, nullptr, m_hWnd);
    if (hmenuShared) {
        DestroyMenu(hmenuShared);
        hmenuShared = nullptr;
        menuMap.clear();
    }
    hwndMenuOwner = nullptr;
}

extern bool ignoreSlots(const char *test);
extern bool ignoreProps(const char *test);

/*! \internal
    Makes sure the type info is loaded
*/
void QAxServerBase::ensureMetaData()
{
    if (!m_spTypeInfo) {
        qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->interfaceID(class_name), &m_spTypeInfo);
        m_spTypeInfo->AddRef();
    }
}

/*!
    \internal
    Returns true if the property \a index is exposed to COM and should
    be saved/loaded.
*/
bool QAxServerBase::isPropertyExposed(int index)
{
    if (!theObject)
        return false;

    bool result = false;
    const QMetaObject *mo = theObject->metaObject();

    int qtProps = 0;
    if (theObject->isWidgetType())
        qtProps = QWidget::staticMetaObject.propertyCount();
    QMetaProperty property = mo->property(index);
    if (index <= qtProps && ignoreProps(property.name()))
        return result;

    BSTR bstrNames = QStringToBSTR(QLatin1String(property.name()));
    DISPID dispId;
    GetIDsOfNames(IID_NULL, &bstrNames, 1, LOCALE_USER_DEFAULT, &dispId);
    result = dispId != DISPID_UNKNOWN;
    SysFreeString(bstrNames);

    return result;
}


/*!
    \internal
    Updates the view, or asks the client site to do so.
*/
void QAxServerBase::update()
{
    if (isInPlaceActive) {
        if (m_hWnd)
            ::InvalidateRect(m_hWnd, nullptr, true);
        else if (m_spInPlaceSiteWindowless)
            m_spInPlaceSiteWindowless->InvalidateRect(nullptr, true);
    } else if (m_spAdviseSink) {
        m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1);
        for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
            adviseSinks.at(i).pAdvSink->OnViewChange(DVASPECT_CONTENT, -1);
        }
    }
}

/*! \internal
    Resizes the control, faking a QResizeEvent if required
*/
void QAxServerBase::resize(const QSize &size)
{
    if (!isWidget || !qt.widget || !size.isValid() || size == QSize(0, 0))
        return;

    QSize oldSize = qt.widget->size();
    qt.widget->resize(size);
    QSize newSize = qt.widget->size();
    // make sure we get a resize event even if not embedded as a control
    if (!m_hWnd && !qt.widget->isVisible() && newSize != oldSize) {
        QResizeEvent resizeEvent(newSize, oldSize);
#ifndef QT_SHARED // import from static library
        extern bool qt_sendSpontaneousEvent(QObject*,QEvent*);
#endif
        qt_sendSpontaneousEvent(qt.widget, &resizeEvent);
    }
    m_currentExtent = qt.widget->size();
}

/*!
    \internal

    Updates the internal size values.
*/
void QAxServerBase::updateGeometry()
{
    if (!isWidget || !qt.widget)
        return;

    const QSize sizeHint = qt.widget->sizeHint();
    const QSize size = qt.widget->size();
    if (sizeHint.isValid()) { // if provided, adjust to sizeHint
        QSize newSize = size;
        if (!qt.widget->testAttribute(Qt::WA_Resized)) {
            newSize = sizeHint;
        } else { // according to sizePolicy rules if already resized
            QSizePolicy sizePolicy = qt.widget->sizePolicy();
            if (sizeHint.width() > size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag))
                newSize.setWidth(sizeHint.width());
            if (sizeHint.width() < size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag))
                newSize.setWidth(sizeHint.width());
            if (sizeHint.height() > size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag))
                newSize.setHeight(sizeHint.height());
            if (sizeHint.height() < size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag))
                newSize.setHeight(sizeHint.height());
        }
        resize(newSize);

    // set an initial size suitable for embedded controls
    } else if (!qt.widget->testAttribute(Qt::WA_Resized)) {
        resize(QSize(100, 100));
        qt.widget->setAttribute(Qt::WA_Resized, false);
    }
}

/*!
    \internal

    Updates the mask of the widget parent.
*/
void QAxServerBase::updateMask()
{
    if (!isWidget || !qt.widget || qt.widget->mask().isEmpty())
        return;

    QRegion rgn = qt.widget->mask();
    HRGN hrgn = qaxHrgnFromQRegion(rgn, qt.widget);

    // Since SetWindowRegion takes ownership
    HRGN wr = CreateRectRgn(0,0,0,0);
    CombineRgn(wr, hrgn, nullptr, RGN_COPY);
    SetWindowRgn(m_hWnd, wr, true);
    DeleteObject(hrgn);
}

static inline QByteArray paramType(const QByteArray &ptype, bool *out)
{
    *out = ptype.endsWith('&') || ptype.endsWith("**");
    if (*out) {
        QByteArray res(ptype);
        res.truncate(res.length() - 1);
        return res;
    }

    return ptype;
}

/*! \internal
    Catches all signals emitted by the Qt widget and fires the respective COM event.

    \a isignal is the Qt Meta Object index of the received signal, and \a _o the
    signal parameters.
*/
int QAxServerBase::qt_metacall(QMetaObject::Call call, int index, void **argv)
{
    Q_UNUSED(call);
    Q_ASSERT(call == QMetaObject::InvokeMetaMethod);

    if (index == STATUSBAR_MESSAGE_CHANGED_SLOT_INDEX) {
        if (sender() && m_spInPlaceFrame) {
            if (qobject_cast<QStatusBar*>(sender()) != statusBar)
                return true;

            if (statusBar->isHidden()) {
                QString message = *static_cast<const QString *>(argv[1]);
                m_spInPlaceFrame->SetStatusText(QStringToBSTR(message));
            }
        }
        return true;
    }

    if (freezeEvents || inDesignMode)
        return true;

    ensureMetaData();

    // get the signal information.
    const QMetaObject *mo = qt.object->metaObject();
    QMetaMethod signal;
    DISPID eventId = index;
    qsizetype pcount = 0;
    QByteArray type;
    QByteArrayList ptypes;

    switch(index) {
    case DISPID_KEYDOWN:
    case DISPID_KEYUP:
        pcount = 2;
        ptypes << "int&" << "int";
        break;
    case DISPID_KEYPRESS:
        pcount = 1;
        ptypes << "int&";
        break;
    case DISPID_MOUSEDOWN:
    case DISPID_MOUSEMOVE:
    case DISPID_MOUSEUP:
        pcount = 4;
        ptypes << "int" << "int" << "int" << "int";
        break;
    case DISPID_CLICK:
        pcount = 0;
        break;
    case DISPID_DBLCLICK:
        pcount = 0;
        break;
    default:
        {
            signal = mo->method(index);
            Q_ASSERT(signal.methodType() == QMetaMethod::Signal);
            type = signal.typeName();
            QByteArray signature(signal.methodSignature());
            QByteArray name(signature);
            name.truncate(name.indexOf('('));

            eventId = signalCache.value(index, -1);
            if (eventId == -1) {
                ITypeInfo *eventInfo = nullptr;
                qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->eventsID(class_name), &eventInfo);
                if (eventInfo) {
                    QString uni_name = QLatin1String(name);
                    OLECHAR *olename = qaxQString2MutableOleChars(uni_name);
                    eventInfo->GetIDsOfNames(&olename, 1, &eventId);
                    eventInfo->Release();
                }
            }

            signature.remove(0, name.length() + 1);
            signature.truncate(signature.length() - 1);

            if (!signature.isEmpty())
                ptypes = signature.split(',');

            pcount = ptypes.size();
        }
        break;
    }
    if (pcount && !argv) {
        qWarning("QAxServerBase::qt_metacall: Missing %d arguments", int(pcount));
        return false;
    }
    if (eventId == -1)
        return false;

    // For all connected event sinks...
    IConnectionPoint *cpoint = nullptr;
    GUID IID_QAxEvents = qAxFactory()->eventsID(class_name);
    FindConnectionPoint(IID_QAxEvents, &cpoint);
    if (cpoint) {
        IEnumConnections *clist = nullptr;
        cpoint->EnumConnections(&clist);
        if (clist) {
            clist->Reset();
            ULONG cc = 1;
            CONNECTDATA c[1];
            clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
            if (cc) {
                // setup parameters
                unsigned int argErr = 0;
                DISPPARAMS dispParams;
                dispParams.cArgs = UINT(pcount);
                dispParams.cNamedArgs = 0;
                dispParams.rgdispidNamedArgs = nullptr;
                dispParams.rgvarg = nullptr;

                if (pcount) // Use malloc/free for eval package compatibility
                    dispParams.rgvarg = static_cast<VARIANTARG *>(malloc(size_t(pcount) * sizeof(VARIANTARG)));
                qsizetype p = 0;
                for (p = 0; p < pcount; ++p) {
                    VARIANT *arg = dispParams.rgvarg + (pcount - p - 1);
                    VariantInit(arg);

                    bool out;
                    QByteArray ptype = paramType(ptypes.at(p), &out);
                    QVariant variant;
                    if (mo->indexOfEnumerator(ptype) != -1) {
                        // convert enum values to int
                        variant = QVariant(*reinterpret_cast<int *>(argv[p+1]));
                    } else {
                        const QMetaType metaType = QMetaType::fromName(ptype);
                        if (metaType.id() == QMetaType::User) {
                            if (ptype.endsWith('*')) {
                                variant = QVariant(metaType, reinterpret_cast<void **>(argv[p+1]));
                            } else {
                                variant = QVariant(metaType, argv[p+1]);
                            }
                        } else {
                            variant = QVariant(metaType, argv[p + 1]);
                        }
                    }

                    QVariantToVARIANT(variant, *arg, ptype, out);
                }

                VARIANT retval;
                VariantInit(&retval);
                VARIANT *pretval = nullptr;
                if (!type.isEmpty() && type != "void")
                    pretval = &retval;

                // call listeners (through IDispatch)
                while (cc) {
                    if (c->pUnk) {
                        IDispatch *disp = nullptr;
                        c->pUnk->QueryInterface(IID_QAxEvents, reinterpret_cast<void **>(&disp));
                        if (disp) {
                            disp->Invoke(eventId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispParams, pretval, nullptr, &argErr);

                            // update out-parameters and return value
                            if (index > 0) {
                                for (p = 0; p < pcount; ++p) {
                                    bool out;
                                    QByteArray ptype = paramType(ptypes.at(p), &out);
                                    if (out)
                                        QVariantToVoidStar(VARIANTToQVariant(dispParams.rgvarg[pcount - p - 1], ptype), argv[p+1], ptype);
                                }
                                if (pretval)
                                    QVariantToVoidStar(VARIANTToQVariant(retval, type), argv[0], type);
                            }
                            disp->Release();
                        }
                        c->pUnk->Release(); // AddRef'ed by clist->Next implementation
                    }
                    clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
                }

                // clean up
                for (p = 0; p < pcount; ++p)
                    clearVARIANT(dispParams.rgvarg+p);
                free(dispParams.rgvarg);
            }
            clist->Release();
        }
        cpoint->Release();
    }

    return true;
}

/*! \internal
    Call IPropertyNotifySink of connected clients.
    \a dispId specifies the ID of the property that changed.
*/
bool QAxServerBase::emitRequestPropertyChange(const char *property)
{
    long dispId = -1;

    IConnectionPoint *cpoint = nullptr;
    FindConnectionPoint(IID_IPropertyNotifySink, &cpoint);
    if (cpoint) {
        IEnumConnections *clist = nullptr;
        cpoint->EnumConnections(&clist);
        if (clist) {
            clist->Reset();
            ULONG cc = 1;
            CONNECTDATA c[1];
            clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
            if (cc) {
                if (dispId == -1) {
                    BSTR bstr = QStringToBSTR(QLatin1String(property));
                    GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId);
                    SysFreeString(bstr);
                }
                if (dispId != -1) while (cc) {
                    if (c->pUnk) {
                        IPropertyNotifySink *sink = nullptr;
                        c->pUnk->QueryInterface(IID_IPropertyNotifySink, reinterpret_cast<void **>(&sink));
                        bool disallows = sink && sink->OnRequestEdit(dispId) == S_FALSE;
                        sink->Release();
                        c->pUnk->Release();
                        if (disallows) { // a client disallows the property to change
                            clist->Release();
                            cpoint->Release();
                            return false;
                        }
                    }
                    clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
                }
            }
            clist->Release();
        }
        cpoint->Release();
    }
    dirtyflag = true;
    return true;
}

/*! \internal
    Call IPropertyNotifySink of connected clients.
    \a dispId specifies the ID of the property that changed.
*/
void QAxServerBase::emitPropertyChanged(const char *property)
{
    long dispId = -1;

    IConnectionPoint *cpoint = nullptr;
    FindConnectionPoint(IID_IPropertyNotifySink, &cpoint);
    if (cpoint) {
        IEnumConnections *clist = nullptr;
        cpoint->EnumConnections(&clist);
        if (clist) {
            clist->Reset();
            ULONG cc = 1;
            CONNECTDATA c[1];
            clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
            if (cc) {
                if (dispId == -1) {
                    BSTR bstr = QStringToBSTR(QLatin1String(property));
                    GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId);
                    SysFreeString(bstr);
                }
                if (dispId != -1) while (cc) {
                    if (c->pUnk) {
                        IPropertyNotifySink *sink = nullptr;
                        c->pUnk->QueryInterface(IID_IPropertyNotifySink, reinterpret_cast<void **>(&sink));
                        if (sink) {
                            sink->OnChanged(dispId);
                            sink->Release();
                        }
                        c->pUnk->Release();
                    }
                    clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
                }
            }
            clist->Release();
        }
        cpoint->Release();
    }
    dirtyflag = true;
}

//**** IProvideClassInfo
/*
    Provide the ITypeInfo implementation for the COM class.
*/
HRESULT WINAPI QAxServerBase::GetClassInfo(ITypeInfo** pptinfo)
{
    if (!pptinfo)
        return E_POINTER;

    *pptinfo = nullptr;
    if (!qAxTypeLibrary)
        return DISP_E_BADINDEX;

    return qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->classID(class_name), pptinfo);
}

//**** IProvideClassInfo2
/*
    Provide the ID of the event interface.
*/
HRESULT WINAPI QAxServerBase::GetGUID(DWORD dwGuidKind, GUID* pGUID)
{
    if (!pGUID)
        return E_POINTER;

    if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) {
        *pGUID = qAxFactory()->eventsID(class_name);
        return S_OK;
    }
    *pGUID = GUID_NULL;
    return E_FAIL;
}

//**** IDispatch
/*
    Returns the number of class infos for this IDispatch.
*/
HRESULT WINAPI QAxServerBase::GetTypeInfoCount(UINT* pctinfo)
{
    if (!pctinfo)
        return E_POINTER;

    *pctinfo = qAxTypeLibrary ? 1 : 0;
    return S_OK;
}

/*
    Provides the ITypeInfo for this IDispatch implementation.
*/
HRESULT WINAPI QAxServerBase::GetTypeInfo(UINT /* itinfo */, LCID /*lcid*/, ITypeInfo** pptinfo)
{
    if (!pptinfo)
        return E_POINTER;

    if (!qAxTypeLibrary)
        return DISP_E_BADINDEX;

    ensureMetaData();

    *pptinfo = m_spTypeInfo;
    (*pptinfo)->AddRef();

    return S_OK;
}

/*
    Provides the names of the methods implemented in this IDispatch implementation.
*/
HRESULT WINAPI QAxServerBase::GetIDsOfNames(REFIID /* riid */, LPOLESTR* rgszNames, UINT cNames,
                                            LCID /*lcid*/, DISPID* rgdispid)
{
    if (!rgszNames || !rgdispid)
        return E_POINTER;

    if (!qAxTypeLibrary)
        return DISP_E_UNKNOWNNAME;

    ensureMetaData();
    if (!m_spTypeInfo)
        return DISP_E_UNKNOWNNAME;

    return m_spTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
}

/*
    Map the COM call to the Qt slot/property for \a dispidMember.
*/
HRESULT WINAPI QAxServerBase::Invoke(DISPID dispidMember, REFIID riid,
                  LCID /*lcid*/, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult,
                  EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;
    if (!theObject)
        return E_UNEXPECTED;

    HRESULT res = DISP_E_MEMBERNOTFOUND;

    bool uniqueIndex = wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_METHOD;

    int index = uniqueIndex ? indexCache.value(dispidMember, -1) : -1;
    QByteArray name;
    if (index == -1) {
        ensureMetaData();

        // This property or method is invoked when an ActiveX client specifies
        // the object name without a property or method. We only support property.
        if (dispidMember == DISPID_VALUE && (wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT)) {
            const QMetaObject *mo = qt.object->metaObject();
            index = mo->indexOfClassInfo("DefaultProperty");
            if (index != -1) {
                name  = mo->classInfo(index).value();
                index = mo->indexOfProperty(name);
            }
        } else {
            if (!m_spTypeInfo)
                return res;
            name = qaxTypeInfoName(m_spTypeInfo, dispidMember);
            if (name.isEmpty())
                return res;
        }
    }

    const QMetaObject *mo = qt.object->metaObject();
    QSize oldSizeHint;
    if (isWidget)
        oldSizeHint = qt.widget->sizeHint();

    if (wFlags == (DISPATCH_PROPERTYGET|DISPATCH_METHOD)
        && !pvarResult && (pDispParams->cArgs || pDispParams->cNamedArgs)) {
        // some client language might allow "value = object.bar = 'newvalue'", and
        // call us with a get|method but without pvarResult set? QTBUG-106024
        wFlags |= DISPATCH_PROPERTYPUT;
    }

    switch (wFlags) {
    case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
    case DISPATCH_PROPERTYGET:
        {
            if (index == -1) {
                index = mo->indexOfProperty(name);
                if (index == -1 && wFlags == DISPATCH_PROPERTYGET)
                    return res;
            }

            QMetaProperty property;
            if (index < mo->propertyCount())
                property = mo->property(index);

            if (property.isReadable()) {
                if (!pvarResult)
                    return DISP_E_PARAMNOTOPTIONAL;
                if (pDispParams->cArgs ||
                     pDispParams->cNamedArgs)
                    return DISP_E_BADPARAMCOUNT;

                QVariant var = qt.object->property(property.name());
                if (!var.isValid())
                    res =  DISP_E_MEMBERNOTFOUND;
                else if (!QVariantToVARIANT(var, *pvarResult))
                    res = DISP_E_TYPEMISMATCH;
                else
                    res = S_OK;
                break;
            }
            if (wFlags == DISPATCH_PROPERTYGET)
                break;
        }
        Q_FALLTHROUGH(); // Fall through if wFlags == DISPATCH_PROPERTYGET|DISPATCH_METHOD AND not a property.
    case DISPATCH_METHOD:
        {
            int nameLength = 0;
            if (index == -1) {
                nameLength = name.length();
                name += '(';
                // no parameter - shortcut
                if (!pDispParams->cArgs) {
                    const QByteArray slotName = name + ')';
                    index = mo->indexOfSlot(slotName.constData());
                }
                // search
                if (index == -1) {
                    for (int i = 0; i < mo->methodCount(); ++i) {
                        const QMetaMethod slot(mo->method(i));
                        if (slot.methodType() == QMetaMethod::Slot && slot.methodSignature().startsWith(name)) {
                            index = i;
                            break;
                        }
                    }
                    // resolve overloads
                    if (index == -1) {
                        QRegularExpression regexp(QLatin1String("_([0-9])\\("));
                        QRegularExpressionMatch rmatch;
                        if (QString::fromLatin1(name.constData()).lastIndexOf(regexp, -1, &rmatch) != -1) {
                            name.chop(rmatch.capturedLength(0));
                            name += '(';
                            int overload = rmatch.capturedView(1).toInt() + 1;

                            for (int s = 0; s < qt.object->metaObject()->methodCount(); ++s) {
                                QMetaMethod slot = qt.object->metaObject()->method(s);
                                if (slot.methodType() == QMetaMethod::Slot && slot.methodSignature().startsWith(name)) {
                                    if (!--overload) {
                                        index = s;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if (index == -1)
                        return res;
                }
            }

            int lookupIndex = index;

            // get slot info
            QMetaMethod slot(mo->method(index));
             Q_ASSERT(slot.methodType() == QMetaMethod::Slot || slot.methodType() == QMetaMethod::Method);
            QByteArray type = slot.typeName();
            name = slot.methodSignature();
            nameLength = name.indexOf('(');
            QByteArray prototype = name.mid(nameLength + 1);
            prototype.truncate(prototype.length() - 1);
            QByteArrayList ptypes;
            if (!prototype.isEmpty())
                ptypes = prototype.split(',');
            UINT pcount = UINT(ptypes.size());

            // verify parameter count
            if (pcount > pDispParams->cArgs) {
                // count cloned slots immediately following the real thing
                while (index < mo->methodCount()) {
                    ++index;
                    slot = mo->method(index);
                    if (!(slot.attributes() & QMetaMethod::Cloned))
                        break;
                    --pcount;
                    // found a matching overload. ptypes still valid
                    if (pcount <= pDispParams->cArgs)
                        break;
                }
                // still wrong :(
                if (pcount > pDispParams->cArgs)
                    return DISP_E_PARAMNOTOPTIONAL;
            } else if (pcount < pDispParams->cArgs) {
                return DISP_E_BADPARAMCOUNT;
            }

            // setup parameters (pcount + return)
            bool ok = true;
            void *static_argv[QAX_NUM_PARAMS + 1];
            QVariant static_varp[QAX_NUM_PARAMS + 1];
            void *static_argv_pointer[QAX_NUM_PARAMS + 1];

            int totalParam = int(pcount);
            if (!type.isEmpty())
                ++totalParam;

            void **argv = nullptr; // the actual array passed into qt_metacall
            void **argv_pointer = nullptr; // in case we need an additional level of indirection
            QVariant *varp = nullptr; // QVariants to hold the temporary Qt data object for us

            if (totalParam) {
                if (totalParam <= 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_pointer[0] = nullptr;
            }

            for (UINT p = 0; p < pcount; ++p) {
                // map the VARIANT to the void*
                bool out;
                QByteArray ptype = paramType(ptypes.at(int(p)), &out);
                varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype);
                argv_pointer[p + 1] = nullptr;
                if (varp[p + 1].isValid()) {
                    if (varp[p + 1].metaType().id() >= QMetaType::User) {
                        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 {
                    if (puArgErr)
                        *puArgErr = pcount-p-1;
                    ok = false;
                }
            }

            // return value
            if (!type.isEmpty() && type != "void") {
                int vt = QMetaType::fromName(type).id();
                if (vt == QMetaType::QVariant) {
                    argv[0] = varp;
                } else {
                    if (vt == QMetaType::User)
                        vt = QMetaType::UnknownType;
                    else if (vt == QMetaType::UnknownType && mo->indexOfEnumerator(slot.typeName()) != -1)
                        vt = QMetaType::Int;
                    varp[0] = QVariant(QMetaType(vt));
                    if (varp[0].metaType().id() == QMetaType::UnknownType)
                        argv[0] = nullptr;
                    else
                        argv[0] = const_cast<void*>(varp[0].constData());
                }
                if (type.endsWith('*')) {
                    argv_pointer[0] = argv[0];
                    argv[0] = argv_pointer;
                }
            }

            // call the slot if everthing went fine.
            if (ok) {
                ++invokeCount;
                qt.object->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
                if (--invokeCount < 0)
                    invokeCount = 0;

                // update reference parameters and return value
                for (UINT p = 0; p < pcount; ++p) {
                    bool out;
                    QByteArray ptype = paramType(ptypes.at(int(p)), &out);
                    if (out) {
                        if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out))
                            ok = false;
                    }
                }
                if (!type.isEmpty() && type != "void" && pvarResult) {
                    if (argv[0] == argv_pointer && type != "QVariant")
                        varp[0] = QVariant(QMetaType::fromName(type), argv_pointer);
                    ok = QVariantToVARIANT(varp[0], *pvarResult, type);
                }
            }
            if (argv && argv != static_argv) {
                delete []argv;
                delete []argv_pointer;
                delete []varp;
            }

            res = ok ? S_OK : DISP_E_TYPEMISMATCH;

            // reset in case index changed for default-arg handling
            index = lookupIndex;
        }
        break;
    case DISPATCH_PROPERTYPUT:
    case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF:
    case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET|DISPATCH_METHOD:
        {
            if (index == -1) {
                index = mo->indexOfProperty(name);
                if (index == -1)
                    return res;
            }

            QMetaProperty property;
            if (index < mo->propertyCount())
                property = mo->property(index);
            if (!property.isWritable())
                return DISP_E_MEMBERNOTFOUND;
            if (!pDispParams->cArgs)
                return DISP_E_PARAMNOTOPTIONAL;
            if (wFlags != (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET|DISPATCH_METHOD)
                && (pDispParams->cArgs != 1 || pDispParams->cNamedArgs != 1
                    || *pDispParams->rgdispidNamedArgs != DISPID_PROPERTYPUT)) {
                    return DISP_E_BADPARAMCOUNT;
            }

            QVariant var = VARIANTToQVariant(*pDispParams->rgvarg,
                                             property.typeName(), property.metaType().id());
            if (!var.isValid()) {
                if (puArgErr)
                    *puArgErr = 0;
                return DISP_E_BADVARTYPE;
            }
            if (!qt.object->setProperty(property.name(), var)) {
                if (puArgErr)
                    *puArgErr = 0;
                return DISP_E_TYPEMISMATCH;
            }

            res = S_OK;
        }
        break;

    default:
        break;
    }

    // maybe calling a setter? Notify client about changes
    switch(wFlags) {
    case DISPATCH_METHOD:
    case DISPATCH_PROPERTYPUT:
    case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF:
        if (m_spAdviseSink || !adviseSinks.isEmpty()) {
            FORMATETC fmt;
            fmt.cfFormat = 0;
            fmt.ptd = nullptr;
            fmt.dwAspect = DVASPECT_CONTENT;
            fmt.lindex = -1;
            fmt.tymed = TYMED_NULL;

            STGMEDIUM stg;
            stg.tymed = TYMED_NULL;
            stg.pUnkForRelease = nullptr;
            stg.hBitmap = nullptr; // initializes the whole union

            if (m_spAdviseSink) {
                m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1);
                m_spAdviseSink->OnDataChange(&fmt, &stg);
            }
            for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
                adviseSinks.at(i).pAdvSink->OnDataChange(&fmt, &stg);
            }
        }

        dirtyflag = true;
        break;
    default:
        break;
    }

    if (index != -1 && uniqueIndex)
        indexCache.insert(dispidMember, index);

    if (exception) {
        if (pexcepinfo) {
            memset(pexcepinfo, 0, sizeof(EXCEPINFO));

            pexcepinfo->wCode = WORD(exception->code);
            if (!exception->src.isNull())
                pexcepinfo->bstrSource = QStringToBSTR(exception->src);
            if (!exception->desc.isNull())
                pexcepinfo->bstrDescription = QStringToBSTR(exception->desc);
            if (!exception->context.isNull()) {
                QString context = exception->context;
                unsigned contextID = 0;
                const int br = context.indexOf(QLatin1Char('[')); // "error[42]"
                if (br != -1) {
                    contextID = context.mid(br + 1, context.size() - br - 2).toUInt();
                    context.truncate(br-1);
                }
                pexcepinfo->bstrHelpFile = QStringToBSTR(context);
                pexcepinfo->dwHelpContext = contextID;
            }
        }
        delete exception;
        exception = nullptr;
        return DISP_E_EXCEPTION;
    }
    if (isWidget) {
        if (oldSizeHint != qt.widget->sizeHint()) {
            updateGeometry();
            if (m_spInPlaceSite) {
                RECT rect = qaxContentRect(qaxToNativeSize(qt.widget, qt.widget->sizeHint()));
                m_spInPlaceSite->OnPosRectChange(&rect);
            }
        }
        updateMask();
    }

    return res;
}

//**** IConnectionPointContainer
/*
    Provide the IEnumConnectionPoints implemented in the QAxSignalVec class.
*/
HRESULT WINAPI QAxServerBase::EnumConnectionPoints(IEnumConnectionPoints **epoints)
{
    if (!epoints)
        return E_POINTER;
    *epoints = new QAxSignalVec(points);
    (*epoints)->AddRef();
    return S_OK;
}

/*
    Provide the IConnectionPoint implemented in the QAxConnection for \a iid.
*/
HRESULT WINAPI QAxServerBase::FindConnectionPoint(REFIID iid, IConnectionPoint **cpoint)
{
    if (!cpoint)
        return E_POINTER;

    IConnectionPoint *cp = points[iid];
    *cpoint = cp;
    if (cp) {
        cp->AddRef();
        return S_OK;
    }
    return CONNECT_E_NOCONNECTION;
}

//**** IPersistStream
/*
    \reimp

    See documentation of IPersistStorage::IsDirty.
*/
HRESULT WINAPI QAxServerBase::IsDirty()
{
    return dirtyflag ? S_OK : S_FALSE;
}

HRESULT WINAPI QAxServerBase::Load(IStream *pStm)
{
    STATSTG stat;
    HRESULT hres = pStm->Stat(&stat, STATFLAG_DEFAULT);
    bool openAsText = false;
    QByteArray qtarray;
    if (hres == S_OK) {
        QString streamName = QString::fromWCharArray(stat.pwcsName);
        CoTaskMemFree(stat.pwcsName);
        openAsText = streamName == QLatin1String("SomeStreamName");
        if (stat.cbSize.HighPart) // more than 4GB - too large!
            return S_FALSE;

        qtarray.resize(int(stat.cbSize.LowPart));
        ULONG read;
        pStm->Read(qtarray.data(), stat.cbSize.LowPart, &read);
    } else if (hres == E_NOTIMPL) {
        ULONG read = 0;
        while (hres != S_FALSE) {
            QByteArray arrayRead;
            arrayRead.resize(4098);
            hres = pStm->Read(arrayRead.data(), ULONG(arrayRead.size()), &read);
            if (hres != S_OK && hres != S_FALSE) {
                qtarray.resize(0);
                break;
            } else if (read == 0)
                break;
            qtarray.append(arrayRead);
        }
    }
    const QMetaObject *mo = qt.object->metaObject();

    QBuffer qtbuffer(&qtarray);
    QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value();
    if (!mimeType.isEmpty()) {
        mimeType.truncate(mimeType.indexOf(':')); // first type
        QAxBindable *axb = static_cast<QAxBindable *>(qt.object->qt_metacast("QAxBindable"));
        if (axb && axb->readData(&qtbuffer, QString::fromLatin1(mimeType)))
            return S_OK;
    }

    qtbuffer.close(); // resets
    qtbuffer.open(openAsText ? (QIODevice::ReadOnly | QIODevice::Text) : QIODevice::ReadOnly);

    QDataStream qtstream(&qtbuffer);
    int version;
    qtstream >> version;
    qtstream.setVersion(version);
    int more = 0;
    qtstream >> more;

    while (!qtbuffer.atEnd() && more) {
        QString propname;
        QVariant value;
        qtstream >> propname;
        if (propname.isEmpty())
            break;
        qtstream >> value;
        qtstream >> more;

        int idx = mo->indexOfProperty(propname.toLatin1());
        QMetaProperty property = mo->property(idx);
        if (property.isWritable())
            qt.object->setProperty(propname.toLatin1(), value);
    }
    return S_OK;
}

HRESULT WINAPI QAxServerBase::Save(IStream *pStm, BOOL clearDirty)
{
    const QMetaObject *mo = qt.object->metaObject();

    QBuffer qtbuffer;
    bool saved = false;
    QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value();
    if (!mimeType.isEmpty()) {
        QAxBindable *axb = static_cast<QAxBindable *>(qt.object->qt_metacast("QAxBindable"));
        saved = axb && axb->writeData(&qtbuffer);
        qtbuffer.close();
    }

    if (!saved) {
        qtbuffer.open(QIODevice::WriteOnly);
        QDataStream qtstream(&qtbuffer);
        qtstream << qtstream.version();

        for (int prop = 0; prop < mo->propertyCount(); ++prop) {
            if (!isPropertyExposed(prop))
                continue;
            QMetaProperty metaprop = mo->property(prop);
            if (QByteArray(metaprop.typeName()).endsWith('*'))
                continue;
            QString property = QLatin1String(metaprop.name());
            QVariant qvar = qt.object->property(metaprop.name());
            if (qvar.isValid()) {
                qtstream << int(1);
                qtstream << property;
                qtstream << qvar;
            }
        }

        qtstream << int(0);
        qtbuffer.close();
    }

    QByteArray qtarray = qtbuffer.buffer();
    ULONG written = 0;
    const char *data = qtarray.constData();
    ULARGE_INTEGER newsize;
    newsize.HighPart = 0;
    newsize.LowPart = DWORD(qtarray.size());
    pStm->SetSize(newsize);
    pStm->Write(data, ULONG(qtarray.size()), &written);
    pStm->Commit(STGC_ONLYIFCURRENT);

    if (clearDirty)
        dirtyflag = false;
    return S_OK;
}

HRESULT WINAPI QAxServerBase::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
    const QMetaObject *mo = qt.object->metaObject();

    pcbSize->HighPart = 0;
    pcbSize->LowPart = DWORD(mo->propertyCount()) * 50;

    return S_OK;
}

//**** IPersistStorage

HRESULT WINAPI QAxServerBase::InitNew(IStorage *pStg)
{
    if (initNewCalled)
        return CO_E_ALREADYINITIALIZED;

    dirtyflag = false;
    initNewCalled = true;

    m_spStorage = pStg;
    if (m_spStorage)
        m_spStorage->AddRef();
    return S_OK;
}

HRESULT WINAPI QAxServerBase::Load(IStorage *pStg)
{
    if (InitNew(pStg) != S_OK)
        return CO_E_ALREADYINITIALIZED;

    IStream *spStream = nullptr;
    QString streamName = QLatin1String(qt.object->metaObject()->className());
    streamName.replace(QLatin1Char(':'), QLatin1Char('.'));
    /* Also invalid, but not relevant
    streamName.replace(QLatin1Char('/'), QLatin1Char('_'));
    streamName.replace(QLatin1Char('\\'), QLatin1Char('_'));
    */
    streamName += QLatin1String("_Stream4.2");

    pStg->OpenStream(reinterpret_cast<const wchar_t *>(streamName.utf16()), nullptr, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream);
    if (!spStream) // support for streams saved with 4.1 and earlier
        pStg->OpenStream(L"SomeStreamName", nullptr, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream);
    if (!spStream)
        return E_FAIL;

    Load(spStream);
    spStream->Release();

    return S_OK;
}

HRESULT WINAPI QAxServerBase::Save(IStorage *pStg, BOOL /* fSameAsLoad */)
{
    IStream *spStream = nullptr;
    QString streamName = QLatin1String(qt.object->metaObject()->className());
    streamName.replace(QLatin1Char(':'), QLatin1Char('.'));
    /* Also invalid, but not relevant
    streamName.replace(QLatin1Char('/'), QLatin1Char('_'));
    streamName.replace(QLatin1Char('\\'), QLatin1Char('_'));
    */
    streamName += QLatin1String("_Stream4.2");

    pStg->CreateStream(reinterpret_cast<const wchar_t *>(streamName.utf16()), STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &spStream);
    if (!spStream)
        return E_FAIL;

    Save(spStream, true);

    spStream->Release();
    return S_OK;
}

HRESULT WINAPI QAxServerBase::SaveCompleted(IStorage *pStgNew)
{
    if (pStgNew) {
        if (m_spStorage)
            m_spStorage->Release();
        m_spStorage = pStgNew;
        m_spStorage->AddRef();
    }
    return S_OK;
}

HRESULT WINAPI QAxServerBase::HandsOffStorage()
{
    if (m_spStorage) m_spStorage->Release();
    m_spStorage = nullptr;

    return S_OK;
}

//**** IPersistPropertyBag
/*
    Initialize the properties of the Qt widget.
*/
HRESULT WINAPI QAxServerBase::InitNew()
{
    if (initNewCalled)
        return CO_E_ALREADYINITIALIZED;

    dirtyflag = false;
    initNewCalled = true;
    return S_OK;
}

/*
    Set the properties of the Qt widget to the values provided in the \a bag.
*/
HRESULT WINAPI QAxServerBase::Load(IPropertyBag *bag, IErrorLog * /*log*/)
{
    if (!bag)
        return E_POINTER;

    if (InitNew() != S_OK)
        return E_UNEXPECTED;

    bool error = false;
    const QMetaObject *mo = qt.object->metaObject();
    for (int prop = 0; prop < mo->propertyCount(); ++prop) {
        if (!isPropertyExposed(prop))
            continue;
        QMetaProperty property = mo->property(prop);
        const char* pname = property.name();
        BSTR bstr = QStringToBSTR(QLatin1String(pname));
        VARIANT var;
        var.vt = VT_EMPTY;
        HRESULT res = bag->Read(bstr, &var, nullptr);
        if (property.isWritable() && var.vt != VT_EMPTY) {
            if (res != S_OK
                || !qt.object->setProperty(pname, VARIANTToQVariant(var, property.typeName(),
                                                                    property.metaType().id()))) {
                error = true;
            }
        }
        SysFreeString(bstr);
    }

    updateGeometry();

    Q_UNUSED(error);
    return /*error ? E_FAIL :*/ S_OK;
}

/*
    Save the properties of the Qt widget into the \a bag.
*/
HRESULT WINAPI QAxServerBase::Save(IPropertyBag *bag, BOOL clearDirty, BOOL /*saveAll*/)
{
    if (!bag)
        return E_POINTER;

    if (clearDirty)
        dirtyflag = false;
    bool error = false;
    const QMetaObject *mo = qt.object->metaObject();
    for (int prop = 0; prop < mo->propertyCount(); ++prop) {
        if (!isPropertyExposed(prop))
            continue;
        QMetaProperty property = mo->property(prop);
        if (QByteArray(property.typeName()).endsWith('*'))
            continue;

        BSTR bstr = QStringToBSTR(QLatin1String(property.name()));
        QVariant qvar = qt.object->property(property.name());
        if (!qvar.isValid())
            error = true;
        VARIANT var;
        QVariantToVARIANT(qvar, var);
        bag->Write(bstr, &var);
        SysFreeString(bstr);
    }
    Q_UNUSED(error);
    return /*error ? E_FAIL :*/ S_OK;
}

//**** IPersistFile
/*
*/
HRESULT WINAPI QAxServerBase::SaveCompleted(LPCOLESTR fileName)
{
    if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1)
        return E_NOTIMPL;

    currentFileName = QString::fromWCharArray(fileName);
    return S_OK;
}

HRESULT WINAPI QAxServerBase::GetCurFile(LPOLESTR *currentFile)
{
    if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1)
        return E_NOTIMPL;

    if (currentFileName.isEmpty()) {
        *currentFile = nullptr;
        return S_FALSE;
    }
    IMalloc *malloc = nullptr;
    CoGetMalloc(1, &malloc);
    if (!malloc)
        return E_OUTOFMEMORY;

    *currentFile = static_cast<wchar_t *>(malloc->Alloc(currentFileName.length() * 2));
    malloc->Release();
    memcpy(*currentFile, currentFileName.unicode(), currentFileName.length() * 2);

    return S_OK;
}

HRESULT WINAPI QAxServerBase::Load(LPCOLESTR fileName, DWORD /* mode */)
{
    const QMetaObject *mo = qt.object->metaObject();
    int mimeIndex = mo->indexOfClassInfo("MIME");
    if (mimeIndex == -1)
        return E_NOTIMPL;

    QAxBindable *axb = static_cast<QAxBindable *>(qt.object->qt_metacast("QAxBindable"));
    if (!axb) {
        qWarning() << class_name << ": No QAxBindable implementation for mime-type handling";
        return E_NOTIMPL;
    }

    QString loadFileName = QString::fromWCharArray(fileName);
    QString fileExtension = loadFileName.mid(loadFileName.lastIndexOf(QLatin1Char('.')) + 1);
    QFile file(loadFileName);

    QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value());
    QStringList mimeTypes = mimeType.split(QLatin1Char(';'));
    for (qsizetype m = 0; m < mimeTypes.size(); ++m) {
        const QString &mime = mimeTypes.at(m);
        if (mime.count(QLatin1Char(':')) != 2) {
            qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME";
            continue;
        }

        mimeType.truncate(mimeType.indexOf(QLatin1Char(':'))); // first type
        if (mimeType.isEmpty()) {
            qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME";
            continue;
        }
        QString mimeExtension = mime.mid(mimeType.length() + 1);
        mimeExtension.truncate(mimeExtension.indexOf(QLatin1Char(':')));
        if (mimeExtension != fileExtension)
            continue;

        if (axb->readData(&file, mimeType)) {
            currentFileName = loadFileName;
            return S_OK;
        }
    }

    return E_FAIL;
}

HRESULT WINAPI QAxServerBase::Save(LPCOLESTR fileName, BOOL fRemember)
{
    const QMetaObject *mo = qt.object->metaObject();
    int mimeIndex = mo->indexOfClassInfo("MIME");
    if (mimeIndex == -1)
        return E_NOTIMPL;

    QAxBindable *axb = static_cast<QAxBindable *>(qt.object->qt_metacast("QAxBindable"));
    if (!axb) {
        qWarning() << class_name << ": No QAxBindable implementation for mime-type handling";
        return E_NOTIMPL;
    }

    QString saveFileName = QString::fromWCharArray(fileName);
    QString fileExtension = saveFileName.mid(saveFileName.lastIndexOf(QLatin1Char('.')) + 1);
    QFile file(saveFileName);

    QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value());
    QStringList mimeTypes = mimeType.split(QLatin1Char(';'));
    for (qsizetype m = 0; m < mimeTypes.size(); ++m) {
        const QString &mime = mimeTypes.at(m);
        if (mime.count(QLatin1Char(':')) != 2) {
            qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME";
            continue;
        }
        mimeType.truncate(mimeType.indexOf(QLatin1Char(':'))); // first type
        if (mimeType.isEmpty()) {
            qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME";
            continue;
        }
        QString mimeExtension = mime.mid(mimeType.length() + 1);
        mimeExtension.truncate(mimeExtension.indexOf(QLatin1Char(':')));
        if (mimeExtension != fileExtension)
            continue;
        if (axb->writeData(&file)) {
            if (fRemember)
                currentFileName = saveFileName;
            return S_OK;
        }
    }
    return E_FAIL;
}

//**** IViewObject
/*
    Draws the widget into the provided device context.
*/
HRESULT WINAPI QAxServerBase::Draw(DWORD dwAspect, LONG /* lindex */, void * /* pvAspect */, DVTARGETDEVICE *ptd,
                HDC hicTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL /*lprcWBounds*/,
                BOOL(__stdcall* /*pfnContinue*/)(ULONG_PTR), ULONG_PTR /*dwContinue*/)
{
    if (!lprcBounds)
        return E_INVALIDARG;

    internalCreate();
    if (!isWidget || !qt.widget)
        return OLE_E_BLANK;

    switch (dwAspect) {
    case DVASPECT_CONTENT:
    case DVASPECT_OPAQUE:
    case DVASPECT_TRANSPARENT:
        break;
    default:
        return DV_E_DVASPECT;
    }
    if (!ptd)
        hicTargetDev = nullptr;

    bool bDeleteDC = false;
    if (!hicTargetDev) {
        hicTargetDev = ::CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
        bDeleteDC = (hicTargetDev != hdcDraw);
    }

    RECTL rc = *lprcBounds;
    bool bMetaFile = GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE;
    if (!bMetaFile)
        ::LPtoDP(hicTargetDev, reinterpret_cast<LPPOINT>(&rc), 2);

    const QPixmap pm = qt.widget->grab();
    HBITMAP hbm = qt_pixmapToWinHBITMAP(pm);
    HDC hdc = CreateCompatibleDC(nullptr);
    SelectObject(hdc, hbm);
    ::StretchBlt(hdcDraw, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdc, 0, 0,pm.width(), pm.height(), SRCCOPY);
    DeleteDC(hdc);
    DeleteObject(hbm);

    if (bDeleteDC)
        DeleteDC(hicTargetDev);

    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetColorSet(DWORD /* dwDrawAspect */, LONG /* lindex */, void * /* pvAspect */, DVTARGETDEVICE * /* ptd */,
        HDC /* hicTargetDev */, LOGPALETTE ** /* ppColorSet */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::Freeze(DWORD /* dwAspect */, LONG  /* lindex */, void * /* pvAspect */, DWORD * /* pdwFreeze */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::Unfreeze(DWORD /* dwFreeze */)
{
    return E_NOTIMPL;
}

/*
    Stores the provided advise sink.
*/
HRESULT WINAPI QAxServerBase::SetAdvise(DWORD /*aspects*/, DWORD /*advf*/, IAdviseSink *pAdvSink)
{
    if (m_spAdviseSink) m_spAdviseSink->Release();

    m_spAdviseSink = pAdvSink;
    if (m_spAdviseSink) m_spAdviseSink->AddRef();
    return S_OK;
}

/*
    Returns the advise sink.
*/
HRESULT WINAPI QAxServerBase::GetAdvise(DWORD* /*aspects*/, DWORD* /*advf*/, IAdviseSink **ppAdvSink)
{
    if (!ppAdvSink)
        return E_POINTER;

    *ppAdvSink = m_spAdviseSink;
    if (*ppAdvSink)
        (*ppAdvSink)->AddRef();
    return S_OK;
}

//**** IViewObject2
/*
    Returns the current size ONLY if the widget has already been sized.
*/
HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwAspect, LONG /*lindex*/, DVTARGETDEVICE* /*ptd*/, LPSIZEL lpsizel)
{
    if (!isWidget || !qt.widget || !qt.widget->testAttribute(Qt::WA_Resized))
        return OLE_E_BLANK;

    return GetExtent(dwAspect, lpsizel);
}

//**** IOleControl
/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetControlInfo(LPCONTROLINFO)
{
    return E_NOTIMPL;
}

/*
    Turns event firing on and off.
*/
HRESULT WINAPI QAxServerBase::FreezeEvents(BOOL bFreeze)
{
    // member of CComControl
    if (bFreeze)
        freezeEvents++;
    else
        freezeEvents--;

    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::OnMnemonic(LPMSG)
{
    return E_NOTIMPL;
}

/*
    Update the ambient properties of the Qt widget.
*/
HRESULT WINAPI QAxServerBase::OnAmbientPropertyChange(DISPID dispID)
{
    if (!m_spClientSite || !theObject)
        return S_OK;

    IDispatch *disp = nullptr;
    m_spClientSite->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&disp));
    if (!disp)
        return S_OK;

    VARIANT var;
    VariantInit(&var);
    DISPPARAMS params = { nullptr, nullptr, 0, 0 };
    disp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params, &var, nullptr, nullptr);
    disp->Release();
    disp = nullptr;

    switch(dispID) {
    case DISPID_AMBIENT_APPEARANCE:
        break;
    case DISPID_AMBIENT_AUTOCLIP:
        break;
    case DISPID_AMBIENT_BACKCOLOR:
    case DISPID_AMBIENT_FORECOLOR:
        if (isWidget) {
            unsigned rgb;
            if (var.vt == VT_UI4)
                rgb = var.ulVal;
            else if (var.vt == VT_I4)
                rgb = unsigned(var.lVal);
            else
                break;
            QPalette pal = qt.widget->palette();
            pal.setColor(dispID == DISPID_AMBIENT_BACKCOLOR ? QPalette::Window : QPalette::WindowText,
                         OLEColorToQColor(rgb));
            qt.widget->setPalette(pal);
        }
        break;
    case DISPID_AMBIENT_DISPLAYASDEFAULT:
        break;
    case DISPID_AMBIENT_DISPLAYNAME:
        if (var.vt != VT_BSTR || !isWidget)
            break;
        qt.widget->setWindowTitle(QString::fromWCharArray(var.bstrVal));
        break;
    case DISPID_AMBIENT_FONT:
        if (var.vt != VT_DISPATCH || !isWidget)
            break;
        {
            QVariant qvar = VARIANTToQVariant(var, "QFont", QMetaType::QFont);
            QFont qfont = qvariant_cast<QFont>(qvar);
            qt.widget->setFont(qfont);
        }
        break;
    case DISPID_AMBIENT_LOCALEID:
        break;
    case DISPID_AMBIENT_MESSAGEREFLECT:
        if (var.vt != VT_BOOL)
            break;
        if (var.boolVal)
            qt.widget->installEventFilter(this);
        else
            qt.widget->removeEventFilter(this);
        break;
    case DISPID_AMBIENT_PALETTE:
        break;
    case DISPID_AMBIENT_SCALEUNITS:
        break;
    case DISPID_AMBIENT_SHOWGRABHANDLES:
        break;
    case DISPID_AMBIENT_SHOWHATCHING:
        break;
    case DISPID_AMBIENT_SUPPORTSMNEMONICS:
        break;
    case DISPID_AMBIENT_TEXTALIGN:
        break;
    case DISPID_AMBIENT_UIDEAD:
        if (var.vt != VT_BOOL || !isWidget)
            break;
        qt.widget->setEnabled(!var.boolVal);
        break;
    case DISPID_AMBIENT_USERMODE:
        if (var.vt != VT_BOOL)
            break;
        inDesignMode = !var.boolVal;
        break;
    case DISPID_AMBIENT_RIGHTTOLEFT:
        if (var.vt != VT_BOOL)
            break;
        QGuiApplication::setLayoutDirection(var.boolVal ? Qt::RightToLeft : Qt::LeftToRight);
        break;
    }

    return S_OK;
}

//**** IOleWindow
/*
    Returns the HWND of the control.
*/
HRESULT WINAPI QAxServerBase::GetWindow(HWND *pHwnd)
{
    if (!pHwnd)
        return E_POINTER;
    *pHwnd = m_hWnd;
    return S_OK;
}

/*
    Enters What's This mode.
*/
HRESULT WINAPI QAxServerBase::ContextSensitiveHelp(BOOL fEnterMode)
{
    if (fEnterMode)
        QWhatsThis::enterWhatsThisMode();
    else
        QWhatsThis::leaveWhatsThisMode();
    return S_OK;
}

//**** IOleInPlaceObject
/*
    Deactivates the control in place.
*/
HRESULT WINAPI QAxServerBase::InPlaceDeactivate()
{
    if (!isInPlaceActive)
        return S_OK;
    UIDeactivate();

    isInPlaceActive = false;

    // if we have a window, tell it to go away.
    if (m_hWnd) {
        if (::IsWindow(m_hWnd))
            ::DestroyWindow(m_hWnd);
        m_hWnd = nullptr;
    }

    if (m_spInPlaceSite)
        m_spInPlaceSite->OnInPlaceDeactivate();

    return S_OK;
}

/*
    Deactivates the control's user interface.
*/
HRESULT WINAPI QAxServerBase::UIDeactivate()
{
    // if we're not UIActive, not much to do.
    if (!isUIActive || !m_spInPlaceSite)
        return S_OK;

    isUIActive = false;

    // notify frame windows, if appropriate, that we're no longer ui-active.
    HWND hwndParent;
    if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) {
        if (m_spInPlaceFrame) m_spInPlaceFrame->Release();
        m_spInPlaceFrame = nullptr;
        IOleInPlaceUIWindow *spInPlaceUIWindow = nullptr;
        RECT rcPos, rcClip;
        OLEINPLACEFRAMEINFO frameInfo;
        frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);

        m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
        if (spInPlaceUIWindow) {
            spInPlaceUIWindow->SetActiveObject(nullptr, nullptr);
            spInPlaceUIWindow->Release();
        }
        if (m_spInPlaceFrame) {
            removeMenu();
            if (menuBar) {
                menuBar->removeEventFilter(this);
                menuBar = nullptr;
            }
            if (statusBar) {
                statusBar->removeEventFilter(this);
                const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)");
        QMetaObject::disconnect(statusBar, index, this, STATUSBAR_MESSAGE_CHANGED_SLOT_INDEX);
                statusBar = nullptr;
            }
            m_spInPlaceFrame->SetActiveObject(nullptr, nullptr);
            m_spInPlaceFrame->Release();
            m_spInPlaceFrame = nullptr;
        }
    }
    // we don't need to explicitly release the focus here since somebody
    // else grabbing the focus is usually why we are getting called at all
    m_spInPlaceSite->OnUIDeactivate(false);

    return S_OK;
}

/*
    Positions the control, and applies requested clipping.
*/
HRESULT WINAPI QAxServerBase::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip)
{
    if (prcPos == nullptr || prcClip == nullptr)
        return E_POINTER;

    if (m_hWnd) {
        // the container wants us to clip, so figure out if we really need to
        RECT rcIXect;
        BOOL b = IntersectRect(&rcIXect, prcPos, prcClip);
        HRGN tempRgn = nullptr;
        if (b && !EqualRect(&rcIXect, prcPos)) {
            OffsetRect(&rcIXect, -(prcPos->left), -(prcPos->top));
            tempRgn = CreateRectRgnIndirect(&rcIXect);
        }

        ::SetWindowRgn(m_hWnd, tempRgn, true);
        ::SetWindowPos(m_hWnd, nullptr, prcPos->left, prcPos->top,
            prcPos->right - prcPos->left, prcPos->bottom - prcPos->top,
            SWP_NOZORDER | SWP_NOACTIVATE);
    }

    //Save the new extent.
    const QRect qr = qaxFromNativeRect(*prcPos, qt.widget);
    m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), qr.width(), qt.widget->maximumWidth());
    m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), qr.height(), qt.widget->maximumHeight());

    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::ReactivateAndUndo()
{
    return E_NOTIMPL;
}

//**** IOleInPlaceActiveObject

Q_GUI_EXPORT int qt_translateKeyCode(int);

HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg)
{
    if (pMsg->message != WM_KEYDOWN || !isWidget)
        return S_FALSE;

    DWORD dwKeyMod = 0;
    if (::GetKeyState(VK_SHIFT) < 0)
        dwKeyMod |= 1;  // KEYMOD_SHIFT
    if (::GetKeyState(VK_CONTROL) < 0)
        dwKeyMod |= 2;  // KEYMOD_CONTROL
    if (::GetKeyState(VK_MENU) < 0)
        dwKeyMod |= 4;  // KEYMOD_ALT

    switch (LOWORD(pMsg->wParam)) {
    case VK_TAB:
        if (isUIActive) {
            bool shift = ::GetKeyState(VK_SHIFT) < 0;
            bool giveUp = true;
            QWidget *curFocus = qt.widget->focusWidget();
            if (curFocus) {
                if (shift) {
                    if (!curFocus->isWindow()) {
                        QWidget *nextFocus = curFocus->nextInFocusChain();
                        QWidget *topLevel = nullptr;
                        while (nextFocus != curFocus) {
                            if (nextFocus->focusPolicy() & Qt::TabFocus) {
                                topLevel = nullptr;
                            } else if (nextFocus->isWindow()) {
                                topLevel = nextFocus;
                            }
                            nextFocus = nextFocus->nextInFocusChain();
                        }

                        if (!topLevel) {
                            giveUp = false;
                            static_cast<HackWidget *>(curFocus)->focusNextPrevChild(false);
                            curFocus->window()->setAttribute(Qt::WA_KeyboardFocusChange);
                        }
                    }
                } else {
                    QWidget *nextFocus = curFocus;
                    while (true) {
                        nextFocus = nextFocus->nextInFocusChain();
                        if (nextFocus->isWindow())
                            break;
                        if (nextFocus->focusPolicy() & Qt::TabFocus) {
                            giveUp = false;
                            static_cast<HackWidget *>(curFocus)->focusNextPrevChild(true);
                            curFocus->window()->setAttribute(Qt::WA_KeyboardFocusChange);
                            break;
                        }
                    }
                }
            }
            if (giveUp) {
                HWND hwnd = ::GetParent(m_hWnd);
                ::SetFocus(hwnd);
            } else {
                return S_OK;
            }

        }
        break;

    case VK_LEFT:
    case VK_RIGHT:
    case VK_UP:
    case VK_DOWN:
        if (isUIActive)
            return S_FALSE;
        break;

    default:
        if (isUIActive && qt.widget->focusWidget()) {
            int state = Qt::NoButton;
            if (dwKeyMod & 1)
                state |= Qt::ShiftModifier;
            if (dwKeyMod & 2)
                state |= Qt::ControlModifier;
            if (dwKeyMod & 4)
                state |= Qt::AltModifier;

            int key = int(pMsg->wParam);
            // FIXME 4.10.2011: No longer exists in Lighthouse.
            // if (!(key >= 'A' && key <= 'Z') && !(key >= '0' && key <= '9'))
            //    key = qt_translateKeyCode(pMsg->wParam);

            QKeyEvent override(QEvent::ShortcutOverride, key, static_cast<Qt::KeyboardModifiers>(state));
            override.ignore();
            QCoreApplication::sendEvent(qt.widget->focusWidget(), &override);
            if (override.isAccepted())
                return S_FALSE;
        }
        break;
    }

    if (!m_spClientSite)
        return S_FALSE;

    IOleControlSite *controlSite = nullptr;
    m_spClientSite->QueryInterface(IID_IOleControlSite, reinterpret_cast<void **>(&controlSite));
    if (!controlSite)
        return S_FALSE;
    // set server type in the user-data of the window.
#ifdef GWLP_USERDATA
    LONG_PTR serverType = QAX_INPROC_SERVER;
#else
    LONG serverType = QAX_INPROC_SERVER;
#endif
    if (qAxOutProcServer)
        serverType = QAX_OUTPROC_SERVER;
#ifdef GWLP_USERDATA
    LONG_PTR oldData = SetWindowLongPtr(pMsg->hwnd, GWLP_USERDATA, serverType);
#else
    LONG oldData = SetWindowLong(pMsg->hwnd, GWL_USERDATA, serverType);
#endif
    HRESULT hres = controlSite->TranslateAcceleratorW(pMsg, dwKeyMod);
    controlSite->Release();
    // reset the user-data for the window.
#ifdef GWLP_USERDATA
    SetWindowLongPtr(pMsg->hwnd, GWLP_USERDATA, oldData);
#else
    SetWindowLong(pMsg->hwnd, GWL_USERDATA, oldData);
#endif
    return hres;
}

HRESULT WINAPI QAxServerBase::OnFrameWindowActivate(BOOL fActivate)
{
    if (fActivate) {
        if (wasUIActive)
            ::SetFocus(m_hWnd);
    } else {
        wasUIActive = isUIActive;
    }
    return S_OK;
}

HRESULT WINAPI QAxServerBase::OnDocWindowActivate(BOOL /* fActivate */)
{
    return S_OK;
}

HRESULT WINAPI QAxServerBase::ResizeBorder(LPCRECT /* prcBorder */, IOleInPlaceUIWindow * /* pUIWindow */, BOOL /* fFrameWindow */)
{
    return S_OK;
}

HRESULT WINAPI QAxServerBase::EnableModeless(BOOL fEnable)
{
    if (!isWidget)
        return S_OK;

    // FIXME: 4.10.2011 Does this work with the parent's HWND?
    EnableWindow(hwndForWidget(qt.widget), fEnable);
    return S_OK;
}

//**** IOleObject

static inline LPOLESTR QStringToOLESTR(const QString &qstring)
{
    LPOLESTR olestr = static_cast<wchar_t *>(CoTaskMemAlloc(size_t(qstring.length()) * 2 + 2));
    memcpy(olestr, reinterpret_cast<const ushort *>(qstring.unicode()), size_t(qstring.length() * 2));
    olestr[qstring.length()] = 0;
    return olestr;
}

/*
    \reimp

    See documentation of IOleObject::GetUserType.
*/
HRESULT WINAPI QAxServerBase::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType)
{
    if (!pszUserType)
        return E_POINTER;

    switch (dwFormOfType) {
    case USERCLASSTYPE_FULL:
        *pszUserType = QStringToOLESTR(class_name);
        break;
    case USERCLASSTYPE_SHORT:
        if (!qt.widget || !isWidget || qt.widget->windowTitle().isEmpty())
            *pszUserType = QStringToOLESTR(class_name);
        else
            *pszUserType = QStringToOLESTR(qt.widget->windowTitle());
        break;
    case USERCLASSTYPE_APPNAME:
        *pszUserType = QStringToOLESTR(qApp->objectName());
        break;
    }

    return S_OK;
}

/*
    Returns the status flags registered for this control.
*/
HRESULT WINAPI QAxServerBase::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
{
    return OleRegGetMiscStatus(qAxFactory()->classID(class_name), dwAspect, pdwStatus);
}

/*
    Stores the provided advise sink.
*/
HRESULT WINAPI QAxServerBase::Advise(IAdviseSink* pAdvSink, DWORD* pdwConnection)
{
    *pdwConnection = DWORD(adviseSinks.size()) + 1;
    STATDATA data = { {0, nullptr, DVASPECT_CONTENT, -1, TYMED_NULL} , 0, pAdvSink, *pdwConnection };
    adviseSinks.append(data);
    pAdvSink->AddRef();
    return S_OK;
}

/*
    Closes the control.
*/
HRESULT WINAPI QAxServerBase::Close(DWORD dwSaveOption)
{
    if (dwSaveOption != OLECLOSE_NOSAVE && m_spClientSite)
        m_spClientSite->SaveObject();
    if (isInPlaceActive) {
        HRESULT hr = InPlaceDeactivate();
        if (FAILED(hr))
            return hr;
    }
    if (m_hWnd) {
        if (IsWindow(m_hWnd))
            DestroyWindow(m_hWnd);
        m_hWnd = nullptr;
        if (m_spClientSite)
            m_spClientSite->OnShowWindow(false);
    }

    if (m_spInPlaceSiteWindowless)
        m_spInPlaceSiteWindowless->Release();
    m_spInPlaceSiteWindowless = nullptr;
    if (m_spInPlaceSite) m_spInPlaceSite->Release();
    m_spInPlaceSite = nullptr;

    if (m_spAdviseSink)
        m_spAdviseSink->OnClose();
    for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
        adviseSinks.at(i).pAdvSink->OnClose();
    }

    return S_OK;
}

bool qax_disable_inplaceframe = true;

// Find a status bar to hook into, hide and redirect its messages to
// IOleInPlaceFrame if and only if the user did not explicitly show it or
// added some widgets (QTBUG-99294).
static QStatusBar *findStatusBar(QWidget *w)
{
    if (w == nullptr)
        return nullptr;
     QStatusBar *result = w->findChild<QStatusBar*>();
     if (result == nullptr)
         return nullptr;
     if (result->isVisible() && result->testAttribute(Qt::WA_WState_ExplicitShowHide))
         return nullptr;
     for (auto child : result->children()) {
         if (child->isWidgetType() && qobject_cast<QSizeGrip *>(child) == nullptr)
             return nullptr;
     }
     return result;
}

/*
    Executes the steps to activate the control.
*/
HRESULT QAxServerBase::internalActivate()
{
    if (!m_spClientSite)
        return S_OK;
    if (!m_spInPlaceSite)
        m_spClientSite->QueryInterface(IID_IOleInPlaceSite, reinterpret_cast<void **>(&m_spInPlaceSite));
    if (!m_spInPlaceSite)
        return E_FAIL;

    HRESULT hr = E_FAIL;
    if (!isInPlaceActive) {
        hr = m_spInPlaceSite->CanInPlaceActivate();
        if (FAILED(hr))
            return hr;
        if (hr != S_OK)
            return E_FAIL;
        m_spInPlaceSite->OnInPlaceActivate();
    }

    isInPlaceActive = true;
    OnAmbientPropertyChange(DISPID_AMBIENT_USERMODE);

    if (isWidget) {
        IOleInPlaceUIWindow *spInPlaceUIWindow = nullptr;
        HWND hwndParent;
        if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) {
            // get location in the parent window, as well as some information about the parent
            if (m_spInPlaceFrame) m_spInPlaceFrame->Release();
            m_spInPlaceFrame = nullptr;
            RECT rcPos, rcClip;
            OLEINPLACEFRAMEINFO frameInfo;
            frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
            m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
            if (m_hWnd) {
                ::ShowWindow(m_hWnd, SW_SHOW);
                if (!::IsChild(m_hWnd, ::GetFocus()) && qt.widget->focusPolicy() != Qt::NoFocus)
                    ::SetFocus(m_hWnd);
            } else {
                if (!create(hwndParent, rcPos)) {
                    qWarning("%s: Window creation failed.", __FUNCTION__);
                    return E_FAIL;
                }
            }
        }

        // Gone active by now, take care of UIACTIVATE
        canTakeFocus = qt.widget->focusPolicy() != Qt::NoFocus && !inDesignMode;
        if (!canTakeFocus && !inDesignMode) {
            const auto widgets = qt.widget->findChildren<QWidget*>();
            for (const QWidget *widget : widgets) {
                canTakeFocus = widget->focusPolicy() != Qt::NoFocus;
                if (canTakeFocus)
                    break;
            }
        }
        if (!isUIActive && canTakeFocus) {
            isUIActive = true;
            hr = m_spInPlaceSite->OnUIActivate();
            if (FAILED(hr)) {
                if (m_spInPlaceFrame) m_spInPlaceFrame->Release();
                    m_spInPlaceFrame = nullptr;
                if (spInPlaceUIWindow) spInPlaceUIWindow->Release();
                    return hr;
            }

            if (isInPlaceActive) {
                if (!::IsChild(m_hWnd, ::GetFocus()))
                    ::SetFocus(m_hWnd);
            }

            if (m_spInPlaceFrame) {
                hr = m_spInPlaceFrame->SetActiveObject(this, reinterpret_cast<const wchar_t *>(class_name.utf16()));
                if (!FAILED(hr)) {
                    menuBar = (qt.widget && !qax_disable_inplaceframe) ? qt.widget->findChild<QMenuBar*>() : nullptr;
                    if (menuBar && !menuBar->isVisible()) {
                        createMenu(menuBar);
                        menuBar->hide();
                        menuBar->installEventFilter(this);
                    }
                    if (auto *statusBar = findStatusBar(qt.widget)) {
                        const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)");
                        QMetaObject::connect(statusBar, index, this, STATUSBAR_MESSAGE_CHANGED_SLOT_INDEX);
                        statusBar->hide();
                        statusBar->installEventFilter(this);
                    }
                }
            }
            if (spInPlaceUIWindow) {
                spInPlaceUIWindow->SetActiveObject(this, reinterpret_cast<const wchar_t *>(class_name.utf16()));
                spInPlaceUIWindow->SetBorderSpace(nullptr);
            }
        }
        if (spInPlaceUIWindow) spInPlaceUIWindow->Release();
            ShowWindow(m_hWnd, SW_NORMAL);
    }

    m_spClientSite->ShowObject();

    return S_OK;
}

/*
    Executes the "verb" \a iVerb.
*/
HRESULT WINAPI QAxServerBase::DoVerb(LONG iVerb, LPMSG /*lpmsg*/, IOleClientSite* /*pActiveSite*/, LONG /*lindex*/,
                               HWND /*hwndParent*/, LPCRECT /*prcPosRect*/)
{
    HRESULT hr = E_NOTIMPL;
    switch (iVerb)
    {
    case OLEIVERB_SHOW:
        hr = internalActivate();
        if (SUCCEEDED(hr))
            hr = S_OK;
        break;

    case OLEIVERB_PRIMARY:
    case OLEIVERB_INPLACEACTIVATE:
        hr = internalActivate();
        if (SUCCEEDED(hr)) {
            hr = S_OK;
            update();
        }
        break;

    case OLEIVERB_UIACTIVATE:
        if (!isUIActive) {
            hr = internalActivate();
            if (SUCCEEDED(hr))
                hr = S_OK;
        }
        break;

    case OLEIVERB_HIDE:
        UIDeactivate();
        if (m_hWnd)
            ::ShowWindow(m_hWnd, SW_HIDE);
        hr = S_OK;
        return hr;

    default:
        break;
    }
    return hr;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::EnumAdvise(IEnumSTATDATA** /*ppenumAdvise*/)
{
    return E_NOTIMPL;
}

/*
    Returns an enumerator for the verbs registered for this class.
*/
HRESULT WINAPI QAxServerBase::EnumVerbs(IEnumOLEVERB** ppEnumOleVerb)
{
    if (!ppEnumOleVerb)
        return E_POINTER;
    return OleRegEnumVerbs(qAxFactory()->classID(class_name), ppEnumOleVerb);
}

/*
    Returns the current client site..
*/
HRESULT WINAPI QAxServerBase::GetClientSite(IOleClientSite** ppClientSite)
{
    if (!ppClientSite)
        return E_POINTER;
    *ppClientSite = m_spClientSite;
    if (*ppClientSite)
        (*ppClientSite)->AddRef();
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetClipboardData(DWORD, IDataObject**)
{
    return E_NOTIMPL;
}

/*
    Returns the current extent.
*/
HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwDrawAspect, SIZEL* psizel)
{
    if (dwDrawAspect != DVASPECT_CONTENT || !isWidget || !qt.widget)
        return E_FAIL;
    if (!psizel)
        return E_POINTER;

    *psizel = qaxMapPixToLogHiMetrics(m_currentExtent, qt.widget);
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetMoniker(DWORD, DWORD, IMoniker** )
{
    return E_NOTIMPL;
}

/*
    Returns the CLSID of this class.
*/
HRESULT WINAPI QAxServerBase::GetUserClassID(CLSID* pClsid)
{
    if (!pClsid)
        return E_POINTER;
    *pClsid = qAxFactory()->classID(class_name);
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::InitFromData(IDataObject*, BOOL, DWORD)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::IsUpToDate()
{
    return S_OK;
}

/*
    Stores the client site.
*/
HRESULT WINAPI QAxServerBase::SetClientSite(IOleClientSite* pClientSite)
{
    // release all client site interfaces
    if (m_spClientSite) m_spClientSite->Release();
    if (m_spInPlaceSiteWindowless)
        m_spInPlaceSiteWindowless->Release();
    m_spInPlaceSiteWindowless = nullptr;
    if (m_spInPlaceSite) m_spInPlaceSite->Release();
    m_spInPlaceSite = nullptr;
    if (m_spInPlaceFrame) m_spInPlaceFrame->Release();
    m_spInPlaceFrame = nullptr;

    m_spClientSite = pClientSite;
    if (m_spClientSite) {
        m_spClientSite->AddRef();
        m_spClientSite->QueryInterface(IID_IOleInPlaceSite, reinterpret_cast<void **>(&m_spInPlaceSite));
        m_spClientSite->QueryInterface(IID_IOleInPlaceSiteWindowless,
                                       reinterpret_cast<void **>(&m_spInPlaceSiteWindowless));
    }

    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::SetColorScheme(LOGPALETTE*)
{
    return E_NOTIMPL;
}


#ifdef QT_SHARED // avoid conflict with symbol in static lib
bool qt_sendSpontaneousEvent(QObject *o, QEvent *e)
{
    return QCoreApplication::sendSpontaneousEvent(o, e);
}
#endif

/*
    Tries to set the size of the control.
*/
HRESULT WINAPI QAxServerBase::SetExtent(DWORD dwDrawAspect, SIZEL* psizel)
{
    if (dwDrawAspect != DVASPECT_CONTENT)
        return DV_E_DVASPECT;
    if (!psizel)
        return E_POINTER;

    if (!isWidget || !qt.widget) // nothing to do
        return S_OK;

    QSize proposedSize(qaxMapLogHiMetricsToPix(*psizel, qt.widget));

    // can the widget be resized at all?
    if (qt.widget->minimumSize() == qt.widget->maximumSize() && qt.widget->minimumSize() != proposedSize)
        return E_FAIL;
    //Save the extent, bound to the widget restrictions.
    m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), proposedSize.width(), qt.widget->maximumWidth());
    m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), proposedSize.height(), qt.widget->maximumHeight());

    resize(proposedSize);
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::SetHostNames(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */)
{
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::SetMoniker(DWORD, IMoniker*)
{
    return E_NOTIMPL;
}

/*
    Disconnects an advise sink.
*/
HRESULT WINAPI QAxServerBase::Unadvise(DWORD dwConnection)
{
    for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
        STATDATA entry = adviseSinks.at(i);
        if (entry.dwConnection == dwConnection) {
            entry.pAdvSink->Release();
            adviseSinks.removeAt(i);
            return S_OK;
        }
    }
    return OLE_E_NOCONNECTION;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::Update()
{
    return S_OK;
}

//**** IDataObject
/*
    Calls IViewObject::Draw after setting up the parameters.
*/
HRESULT WINAPI QAxServerBase::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
    if (!pmedium)
        return E_POINTER;
    if ((pformatetcIn->tymed & TYMED_MFPICT) == 0)
        return DATA_E_FORMATETC;

    internalCreate();
    if (!isWidget || !qt.widget)
        return E_UNEXPECTED;

    // Container wants to draw, but the size is not defined yet - ask container
    if (m_spInPlaceSite && !qt.widget->testAttribute(Qt::WA_Resized)) {
        IOleInPlaceUIWindow *spInPlaceUIWindow = nullptr;
        RECT rcPos, rcClip;
        OLEINPLACEFRAMEINFO frameInfo;
        frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);

        HRESULT hres = m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
        if (hres == S_OK) {
            resize(qaxFromNativeSize(qt.widget, qaxSizeOfRect(rcPos)));
        } else {
            qt.widget->adjustSize();
        }
        if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); // no need for it
    }

    int width = qt.widget->width();
    int height = qt.widget->height();
    RECTL rectl = {0, 0, width, height};

    HDC hdc = CreateMetaFile(nullptr);
    SaveDC(hdc);
    SetWindowOrgEx(hdc, 0, 0, nullptr);
    SetWindowExtEx(hdc, rectl.right, rectl.bottom, nullptr);

    Draw(pformatetcIn->dwAspect, pformatetcIn->lindex, nullptr, pformatetcIn->ptd, nullptr, hdc, &rectl, &rectl, nullptr, 0);

    RestoreDC(hdc, -1);
    HMETAFILE hMF = CloseMetaFile(hdc);
    if (!hMF)
        return E_UNEXPECTED;

    HGLOBAL hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, sizeof(METAFILEPICT));
    if (!hMem) {
        DeleteMetaFile(hMF);
        return ResultFromScode(STG_E_MEDIUMFULL);
    }

    LPMETAFILEPICT pMF = static_cast<LPMETAFILEPICT>(GlobalLock(hMem));
    pMF->hMF = hMF;
    pMF->mm = MM_ANISOTROPIC;
    const SIZEL sizeL = qaxMapPixToLogHiMetrics(QSize(width, height), qt.widget);
    pMF->xExt = sizeL.cx;
    pMF->yExt = sizeL.cy;
    GlobalUnlock(hMem);

    memset(pmedium, 0, sizeof(STGMEDIUM));
    pmedium->tymed = TYMED_MFPICT;
    pmedium->hGlobal = hMem;
    pmedium->pUnkForRelease = nullptr;

    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::DAdvise(FORMATETC *pformatetc, DWORD advf,
                                      IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
    if (pformatetc->dwAspect != DVASPECT_CONTENT)
        return E_FAIL;

    *pdwConnection = adviseSinks.size() + 1;
    STATDATA data = {
        {pformatetc->cfFormat,pformatetc->ptd,pformatetc->dwAspect,pformatetc->lindex,pformatetc->tymed},
        advf, pAdvSink, *pdwConnection
    };
    adviseSinks.append(data);
    pAdvSink->AddRef();
    return S_OK;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::DUnadvise(DWORD dwConnection)
{
    return Unadvise(dwConnection);
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::EnumDAdvise(IEnumSTATDATA ** /*ppenumAdvise*/)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetDataHere(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::QueryGetData(FORMATETC* /* pformatetc */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::GetCanonicalFormatEtc(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::SetData(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */)
{
    return E_NOTIMPL;
}

/*
    Not implemented.
*/
HRESULT WINAPI QAxServerBase::EnumFormatEtc(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */)
{
    return E_NOTIMPL;
}



static int mapModifiers(Qt::KeyboardModifiers state)
{
    int ole = 0;
    if (state & Qt::ShiftModifier)
        ole |= 1;
    if (state & Qt::ControlModifier)
        ole |= 2;
    if (state & Qt::AltModifier)
        ole |= 4;

    return ole;
}

/*
    \reimp
*/
bool QAxServerBase::eventFilter(QObject *o, QEvent *e)
{
    if (!theObject)
        return QObject::eventFilter(o, e);

    if ((e->type() == QEvent::Show || e->type() == QEvent::Hide) && (o == statusBar || o == menuBar)) {
        if (o == menuBar) {
            if (e->type() == QEvent::Hide) {
                createMenu(menuBar);
            } else if (e->type() == QEvent::Show) {
                removeMenu();
            }
        } else if (statusBar) {
            statusBar->setSizeGripEnabled(false);
        }
        updateGeometry();
        if (m_spInPlaceSite && qt.widget->sizeHint().isValid()) {
            RECT rect = qaxContentRect(qaxToNativeSize(qt.widget, qt.widget->sizeHint()));
            m_spInPlaceSite->OnPosRectChange(&rect);
        }
    }
    switch (e->type()) {
    case QEvent::ChildAdded:
        static_cast<QChildEvent*>(e)->child()->installEventFilter(this);
        break;
    case QEvent::ChildRemoved:
        static_cast<QChildEvent*>(e)->child()->removeEventFilter(this);
        break;
    case QEvent::KeyPress:
        if (o == qt.object && hasStockEvents) {
            const QKeyEvent *ke = static_cast<const QKeyEvent *>(e);
            int key = ke->key();
            int state = ke->modifiers();
            void *argv[] = {
                nullptr,
                &key,
                &state
            };
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYDOWN, argv);
            if (!ke->text().isEmpty())
                qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYPRESS, argv);
        }
        break;
    case QEvent::KeyRelease:
        if (o == qt.object && hasStockEvents) {
            const QKeyEvent *ke = static_cast<const QKeyEvent *>(e);
            int key = ke->key();
            int state = ke->modifiers();
            void *argv[] = {
                nullptr,
                &key,
                &state
            };
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYUP, argv);
        }
        break;
    case QEvent::MouseMove:
        if (o == qt.object && hasStockEvents) {
            const QMouseEvent *me = static_cast<const QMouseEvent *>(e);
            int button = me->buttons() & Qt::MouseButtonMask;
            int state = mapModifiers(me->modifiers());
            const auto pos = me->position().toPoint();
            int x = pos.x();
            int y = pos.y();
            void *argv[] = {
                nullptr,
                &button,
                &state,
                &x,
                &y
            };
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEMOVE, argv);
        }
        break;
    case QEvent::MouseButtonRelease:
        if (o == qt.object && hasStockEvents) {
            const QMouseEvent *me = static_cast<const QMouseEvent *>(e);
            int button = me->button();
            int state = mapModifiers(me->modifiers());
            const auto pos = me->position().toPoint();
            int x = pos.x();
            int y = pos.y();
            void *argv[] = {
                nullptr,
                &button,
                &state,
                &x,
                &y
            };
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEUP, argv);
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_CLICK, nullptr);
        }
        break;
    case QEvent::MouseButtonDblClick:
        if (o == qt.object && hasStockEvents) {
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_DBLCLICK, nullptr);
        }
        break;
    case QEvent::MouseButtonPress:
        if (m_spInPlaceSite && !isUIActive) {
            internalActivate();
        }
        if (o == qt.widget && hasStockEvents) {
            const QMouseEvent *me = static_cast<const QMouseEvent *>(e);
            int button = me->button();
            int state = mapModifiers(me->modifiers());
            const auto pos = me->position().toPoint();
            int x = pos.x();
            int y = pos.y();
            void *argv[] = {
                nullptr,
                &button,
                &state,
                &x,
                &y
            };
            qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEDOWN, argv);
        }
        break;
    case QEvent::Show:
        if (m_hWnd && o == qt.widget)
            ShowWindow(m_hWnd, SW_SHOW);
        updateMask();
        break;
    case QEvent::Hide:
        if (m_hWnd && o == qt.widget)
            ShowWindow(m_hWnd, SW_HIDE);
        break;

    case QEvent::EnabledChange:
        if (m_hWnd && o == qt.widget)
            EnableWindow(m_hWnd, qt.widget->isEnabled());
        Q_FALLTHROUGH();
    case QEvent::FontChange:
    case QEvent::ActivationChange:
    case QEvent::StyleChange:
    case QEvent::IconTextChange:
    case QEvent::ModifiedChange:
    case QEvent::Resize:
        updateMask();
        break;
    case QEvent::WindowBlocked: {
        if (!m_spInPlaceFrame)
            break;
        m_spInPlaceFrame->EnableModeless(FALSE);
        MSG msg;
        // Visual Basic 6.0 posts the message WM_USER+3078 from the EnableModeless().
        // While handling this message, VB will disable all current top-levels. After
        // this we have to re-enable the Qt modal widget to receive input events.
        if (PeekMessage(&msg, nullptr, WM_USER+3078, WM_USER+3078, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            // FIXME: 4.10.2011 Does this work with the parent's HWND?
            QWidget *modalWidget = QApplication::activeModalWidget();
            if (modalWidget && modalWidget->isVisible() && modalWidget->isEnabled()
                && !IsWindowEnabled(hwndForWidget(modalWidget)))
                EnableWindow(hwndForWidget(modalWidget), TRUE);
        }
        break;
        }
    case QEvent::WindowUnblocked:
        if (!m_spInPlaceFrame)
            break;
        m_spInPlaceFrame->EnableModeless(TRUE);
        break;
    default:
        break;
    }
    return QObject::eventFilter(o, e);
}

QT_END_NAMESPACE
