Revision control

++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++ // Qt-Security score:significant reason:default
++
++
++ #include "qaxserverbase_p.h"
++
++ #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 <qmenubar.h>
++ #include <qmenu.h>
++ #include <qmetaobject.h>
++ #include <qpixmap.h>
++ #include <qregularexpression.h>
++ #include <qsizegrip.h>
++ #include <qstatusbar.h>
++ #include <qwhatsthis.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 <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/qaxtypefunctions_p.h>
++ #include <QtAxBase/private/qaxutils_p.h>
++ #include <QtCore/private/qcomobject_p.h>
++
++ #include "qclassfactory_p.h"
++ #include "qaxsignalvec_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 ComPtr<ITypeLib> qAxTypeLibrary;
++ extern unsigned long qAxLock();
++ extern unsigned long qAxUnlock();
++ extern bool qAxOutProcServer;
++
++ #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*);
++
++ 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 QComObject<IUnknown>
++ {
++ Q_DISABLE_COPY_MOVE(QAxServerAggregate)
++ public:
++ QAxServerAggregate(const QString &className, IUnknown *outerUnknown)
++ {
++ object = makeComObject<QAxServerBase>(className, outerUnknown);
++ object->registerActiveObject(this);
++ }
++ virtual ~QAxServerAggregate() = default;
++
++ // IUnknown
++ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
++ {
++ HRESULT result = QComObject::QueryInterface(riid, ppvObject);
++ if (*ppvObject)
++ return result;
++
++ return object->InternalQueryInterface(riid, ppvObject);
++ }
++
++ private:
++ ComPtr<QAxServerBase> object;
++ };
++
++ 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 store and enumerate all connected event listeners.
++ */
++ class QAxConnection : public QComObject<IConnectionPoint, IEnumConnections>
++ {
++ public:
++ QAxConnection(QAxConnection &) = delete;
++ 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)
++ {
++ }
++ virtual ~QAxConnection() = default;
++
++ 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;
++
++ {
++ ComPtr<IDispatch> checkImpl;
++ pUnk->QueryInterface(iid, &checkImpl);
++ if (!checkImpl)
++ return CONNECT_E_CANNOTCONNECT;
++ }
++
++ CONNECTDATA cd;
++ cd.dwCookie = ++nextCookie;
++ 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;
++
++ ComPtr<QAxConnection> copy = makeComObject<QAxConnection>(that, iid);
++ copy->connections = connections;
++ copy->current = current;
++ copy->nextCookie = nextCookie;
++ for (const CONNECTDATA& connection : std::as_const(connections))
++ connection.pUnk->AddRef();
++
++ *ppEnum = copy.Detach();
++
++ return S_OK;
++ }
++
++ private:
++ QAxServerBase *that;
++ QUuid iid;
++ Connections connections;
++ int current = 0;
++ DWORD nextCookie = 0;
++ };
++
++ // 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)
++ {
++ // 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();
++ }
++ }
++
++ 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) {
++ ComPtr<QAxServerAggregate> aggregate = makeComObject<QAxServerAggregate>(className, pUnkOuter);
++ res = aggregate->QueryInterface(iid, ppObject);
++ } 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 (!licensed)
++ return E_NOTIMPL;
++ 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 (!licensed)
++ return E_NOTIMPL;
++ 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)
++ {
++ if (!licensed)
++ return E_NOTIMPL;
++
++ 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;
++ }
++
++ 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;
++ }
++
++ 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;
++ }
++
++ /*
++ class HackMenuData : public QMenuData
++ {
++ friend class QAxServerBase;
++ };
++ */
++
++ class HackWidget : public QWidget
++ {
++ friend class QAxServerBase;
++ };
++
++
++ /*! \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
++ Destroys the QAxServerBase object, releasing all allocated
++ resources and interfaces.
++ */
++ QAxServerBase::~QAxServerBase()
++ {
++ #ifdef QT_DEBUG
++ EnterCriticalSection(&refCountSection);
++ --qaxserverbase_instance_count;
++ LeaveCriticalSection(&refCountSection);
++ #endif
++
++ revokeActiveObject();
++ delete aggregatedObject;
++ aggregatedObject = nullptr;
++ if (theObject) {
++ qt.object->disconnect(this);
++ QObject *aqt = qt.object;
++ qt.object = nullptr;
++ if (ownObject)
++ delete aqt;
++ }
++
++ m_spAdviseSink.Reset();
++ for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
++ adviseSinks.at(i).pAdvSink->Release();
++ }
++ m_spClientSite.Reset();
++ m_spInPlaceFrame.Reset();
++ m_spInPlaceSiteWindowless.Reset();
++ m_spInPlaceSite.Reset();
++
++ DeleteCriticalSection(&refCountSection);
++ DeleteCriticalSection(&createWindowSection);
++
++ qAxUnlock();
++ }
++
++ //**** IUnknown
++ IFACEMETHODIMP QAxServerBase::QueryInterface(REFIID riid, void **ppvObject)
++ {
++ if (m_outerUnknown)
++ return m_outerUnknown->QueryInterface(riid, ppvObject);
++
++ return InternalQueryInterface(riid, ppvObject);
++ }
++
++ IFACEMETHODIMP_(ULONG) QAxServerBase::AddRef()
++ {
++ if (m_outerUnknown)
++ return m_outerUnknown->AddRef();
++
++ return InterlockedIncrement(&ref);
++ }
++
++ IFACEMETHODIMP_(ULONG) QAxServerBase::Release()
++ {
++ if (m_outerUnknown)
++ return m_outerUnknown->Release();
++
++ LONG refCount = InterlockedDecrement(&ref);
++ if (!refCount)
++ delete this;
++
++ return refCount;
++ }
++
++ //**** IAxServerBase
++ IFACEMETHODIMP_(IUnknown *) QAxServerBase::ClientSite() const
++ {
++ return m_spClientSite.Get();
++ }
++
++ /*! \internal
++ Call IPropertyNotifySink of connected clients.
++ \a dispId specifies the ID of the property that changed.
++ */
++ IFACEMETHODIMP_(void) QAxServerBase::EmitPropertyChanged(const char *property)
++ {
++ long dispId = -1;
++
++ ComPtr<IConnectionPoint> cpoint;
++ FindConnectionPoint(IID_IPropertyNotifySink, &cpoint);
++ if (cpoint) {
++ ComPtr<IEnumConnections> clist;
++ 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) {
++ ComPtr<IPropertyNotifySink> sink;
++ c->pUnk->QueryInterface(IID_IPropertyNotifySink, &sink);
++ if (sink) {
++ sink->OnChanged(dispId);
++ }
++ c->pUnk->Release();
++ }
++ clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
++ }
++ }
++ }
++ }
++ dirtyflag = true;
++ }
++
++ /*! \internal
++ Call IPropertyNotifySink of connected clients.
++ \a dispId specifies the ID of the property that changed.
++ */
++ IFACEMETHODIMP_(bool) QAxServerBase::EmitRequestPropertyChange(const char *property)
++ {
++ long dispId = -1;
++
++ ComPtr<IConnectionPoint> cpoint;
++ FindConnectionPoint(IID_IPropertyNotifySink, &cpoint);
++ if (cpoint) {
++ ComPtr<IEnumConnections> clist;
++ 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) {
++ ComPtr<IPropertyNotifySink> sink;
++ c->pUnk->QueryInterface(IID_IPropertyNotifySink, &sink);
++ bool disallows = sink && sink->OnRequestEdit(dispId) == S_FALSE;
++ c->pUnk->Release();
++ if (disallows) { // a client disallows the property to change
++ return false;
++ }
++ }
++ clist->Next(cc, reinterpret_cast<CONNECTDATA *>(&c), &cc);
++ }
++ }
++ }
++ }
++ dirtyflag = true;
++ return true;
++ }
++
++ IFACEMETHODIMP_(QObject *) QAxServerBase::GetQObject() const
++ {
++ return theObject;
++ }
++
++ IFACEMETHODIMP_(void) QAxServerBase::ReportError(int code, const QString &src,
++ const QString &desc, const QString &context)
++ {
++ exception = std::make_unique<QAxExceptInfo>(code, src, desc, context);
++ }
++
++ //**** IDispatch
++ /*
++ Returns the number of class infos for this IDispatch.
++ */
++ IFACEMETHODIMP QAxServerBase::GetTypeInfoCount(UINT *pctinfo)
++ {
++ if (!pctinfo)
++ return E_POINTER;
++
++ *pctinfo = qAxTypeLibrary ? 1 : 0;
++ return S_OK;
++ }
++
++ /*
++ Provides the ITypeInfo for this IDispatch implementation.
++ */
++ IFACEMETHODIMP QAxServerBase::GetTypeInfo(UINT /* iTInfo */, LCID /* lcid */, ITypeInfo **ppTInfo)
++ {
++ if (!ppTInfo)
++ return E_POINTER;
++
++ if (!qAxTypeLibrary)
++ return DISP_E_BADINDEX;
++
++ ensureMetaData();
++
++ *ppTInfo = m_spTypeInfo.Get();
++ (*ppTInfo)->AddRef();
++
++ return S_OK;
++ }
++
++ /*
++ Provides the names of the methods implemented in this IDispatch implementation.
++ */
++ IFACEMETHODIMP 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.
++ */
++ IFACEMETHODIMP 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.Get(), 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) {
++ qt.object->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
++
++ // 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;
++ }
++ }
++ exception.reset();
++ 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;
++ }
++
++ //**** IOleObject
++ /*
++ Stores the client site.
++ */
++ IFACEMETHODIMP QAxServerBase::SetClientSite(IOleClientSite *pClientSite)
++ {
++ // release all client site interfaces
++ m_spInPlaceSiteWindowless.Reset();
++ m_spInPlaceSite.Reset();
++ m_spInPlaceFrame.Reset();
++
++ m_spClientSite = pClientSite;
++ if (m_spClientSite) {
++ m_spClientSite->QueryInterface(IID_IOleInPlaceSite, &m_spInPlaceSite);
++ m_spClientSite->QueryInterface(IID_IOleInPlaceSiteWindowless, &m_spInPlaceSiteWindowless);
++ }
++
++ return S_OK;
++ }
++
++ /*
++ Returns the current client site..
++ */
++ IFACEMETHODIMP QAxServerBase::GetClientSite(IOleClientSite **ppClientSite)
++ {
++ if (!ppClientSite)
++ return E_POINTER;
++ *ppClientSite = m_spClientSite.Get();
++ if (*ppClientSite)
++ (*ppClientSite)->AddRef();
++ return S_OK;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::SetHostNames(LPCOLESTR /* szContainerApp */,
++ LPCOLESTR /* szContainerObj */)
++ {
++ return S_OK;
++ }
++
++ /*
++ Closes the control.
++ */
++ IFACEMETHODIMP 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);
++ }
++
++ m_spInPlaceSiteWindowless.Reset();
++ m_spInPlaceSite.Reset();
++
++ if (m_spAdviseSink)
++ m_spAdviseSink->OnClose();
++ for (qsizetype i = 0; i < adviseSinks.size(); ++i) {
++ adviseSinks.at(i).pAdvSink->OnClose();
++ }
++
++ return S_OK;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::SetMoniker(DWORD /* dwWhichMoniker */, IMoniker * /* pmk */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::GetMoniker(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */,
++ IMoniker ** /* ppmk */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::InitFromData(IDataObject * /* pDataObject */, BOOL /* fCreation */,
++ DWORD /* dwReserved */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::GetClipboardData(DWORD /* dwReserved */,
++ IDataObject ** /* ppDataObject */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Executes the "verb" \a iVerb.
++ */
++ IFACEMETHODIMP QAxServerBase::DoVerb(LONG iVerb, LPMSG /* lpmsg */,
++ IOleClientSite * /* pActiveSite */, LONG /* lindex */,
++ HWND /* hwndParent */, LPCRECT /* lprcPosRect */)
++ {
++ 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;
++ }
++
++ /*
++ Returns an enumerator for the verbs registered for this class.
++ */
++ IFACEMETHODIMP QAxServerBase::EnumVerbs(IEnumOLEVERB **ppEnumOleVerb)
++ {
++ if (!ppEnumOleVerb)
++ return E_POINTER;
++ return OleRegEnumVerbs(qAxFactory()->classID(class_name), ppEnumOleVerb);
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::Update()
++ {
++ return S_OK;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::IsUpToDate()
++ {
++ return S_OK;
++ }
++
++ /*
++ Returns the CLSID of this class.
++ */
++ IFACEMETHODIMP QAxServerBase::GetUserClassID(CLSID *pClsid)
++ {
++ if (!pClsid)
++ return E_POINTER;
++ *pClsid = qAxFactory()->classID(class_name);
++ return S_OK;
++ }
++
++ /*
++ \reimp
++
++ See documentation of IOleObject::GetUserType.
++ */
++ IFACEMETHODIMP 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;
++ }
++
++ /*
++ Tries to set the size of the control.
++ */
++ IFACEMETHODIMP 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;
++ }
++
++ /*
++ Returns the current extent.
++ */
++ IFACEMETHODIMP 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;
++ }
++
++ /*
++ Stores the provided advise sink.
++ */
++ IFACEMETHODIMP QAxServerBase::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
++ {
++ if (!pAdvSink || !pdwConnection)
++ return E_POINTER;
++
++ *pdwConnection = ++m_nextAdviseConnection;
++ STATDATA data = { {0, nullptr, DVASPECT_CONTENT, -1, TYMED_NULL} , 0, pAdvSink, *pdwConnection };
++ adviseSinks.append(data);
++ pAdvSink->AddRef();
++ return S_OK;
++ }
++
++ /*
++ Disconnects an advise sink.
++ */
++ IFACEMETHODIMP 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.
++ */
++ IFACEMETHODIMP QAxServerBase::EnumAdvise(IEnumSTATDATA ** /* ppenumAdvise */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Returns the status flags registered for this control.
++ */
++ IFACEMETHODIMP QAxServerBase::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
++ {
++ return OleRegGetMiscStatus(qAxFactory()->classID(class_name), dwAspect, pdwStatus);
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::SetColorScheme(LOGPALETTE * /* pLogpal */)
++ {
++ return E_NOTIMPL;
++ }
++
++ //**** IOleControl
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::GetControlInfo(CONTROLINFO * /* pCI */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::OnMnemonic(MSG * /* pMsg */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Update the ambient properties of the Qt widget.
++ */
++ IFACEMETHODIMP QAxServerBase::OnAmbientPropertyChange(DISPID dispID)
++ {
++ if (!m_spClientSite || !theObject)
++ return S_OK;
++
++ ComPtr<IDispatch> disp;
++ m_spClientSite->QueryInterface(IID_IDispatch, &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);
++
++ 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;
++ }
++
++ /*
++ Turns event firing on and off.
++ */
++ IFACEMETHODIMP QAxServerBase::FreezeEvents(BOOL bFreeze)
++ {
++ // member of CComControl
++ if (bFreeze)
++ freezeEvents++;
++ else
++ freezeEvents--;
++
++ return S_OK;
++ }
++
++ //**** IViewObject
++ /*
++ Draws the widget into the provided device context.
++ */
++ IFACEMETHODIMP QAxServerBase::Draw(DWORD dwDrawAspect, LONG /* lindex */, void * /* pvAspect */,
++ DVTARGETDEVICE *ptd, HDC hdcTargetDev, HDC hdcDraw,
++ LPCRECTL lprcBounds, LPCRECTL /* lprcWBounds */,
++ BOOL(STDMETHODCALLTYPE * /* pfnContinue */)(ULONG_PTR),
++ ULONG_PTR /* dwContinue */)
++ {
++ if (!lprcBounds)
++ return E_INVALIDARG;
++
++ internalCreate();
++ if (!isWidget || !qt.widget)
++ return OLE_E_BLANK;
++
++ switch (dwDrawAspect) {
++ case DVASPECT_CONTENT:
++ case DVASPECT_OPAQUE:
++ case DVASPECT_TRANSPARENT:
++ break;
++ default:
++ return DV_E_DVASPECT;
++ }
++ if (!ptd)
++ hdcTargetDev = nullptr;
++
++ bool bDeleteDC = false;
++ if (!hdcTargetDev) {
++ hdcTargetDev = ::CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
++ bDeleteDC = (hdcTargetDev != hdcDraw);
++ }
++
++ RECTL rc = *lprcBounds;
++ bool bMetaFile = GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE;
++ if (!bMetaFile)
++ ::LPtoDP(hdcTargetDev, 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(hdcTargetDev);
++
++ return S_OK;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::GetColorSet(DWORD /* dwDrawAspect */, LONG /* lindex */,
++ void * /* pvAspect */, DVTARGETDEVICE * /* ptd */,
++ HDC /* hicTargetDev */, LOGPALETTE ** /* ppColorSet */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::Freeze(DWORD /* dwDrawAspect */, LONG /* lindex */,
++ void * /* pvAspect */, DWORD * /* pdwFreeze */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::Unfreeze(DWORD /* dwFreeze */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Stores the provided advise sink.
++ */
++ IFACEMETHODIMP QAxServerBase::SetAdvise(DWORD /* aspects */, DWORD /* advf */,
++ IAdviseSink *pAdvSink)
++ {
++ m_spAdviseSink = pAdvSink;
++ return S_OK;
++ }
++
++ /*
++ Returns the advise sink.
++ */
++ IFACEMETHODIMP QAxServerBase::GetAdvise(DWORD * /* pAspects */, DWORD * /* pAdvf */,
++ IAdviseSink **ppAdvSink)
++ {
++ if (!ppAdvSink)
++ return E_POINTER;
++
++ *ppAdvSink = m_spAdviseSink.Get();
++ if (*ppAdvSink)
++ (*ppAdvSink)->AddRef();
++ return S_OK;
++ }
++
++ //**** IViewObject2
++ /*
++ Returns the current size ONLY if the widget has already been sized.
++ */
++ IFACEMETHODIMP QAxServerBase::GetExtent(DWORD dwDrawAspect, LONG /* lindex */,
++ DVTARGETDEVICE * /* ptd */, LPSIZEL lpsizel)
++ {
++ if (!isWidget || !qt.widget || !qt.widget->testAttribute(Qt::WA_Resized))
++ return OLE_E_BLANK;
++
++ return GetExtent(dwDrawAspect, lpsizel);
++ }
++
++ //**** IOleWindow
++ /*
++ Returns the HWND of the control.
++ */
++ IFACEMETHODIMP QAxServerBase::GetWindow(HWND *phwnd)
++ {
++ if (!phwnd)
++ return E_POINTER;
++ *phwnd = m_hWnd;
++ return S_OK;
++ }
++
++ /*
++ Enters What's This mode.
++ */
++ IFACEMETHODIMP QAxServerBase::ContextSensitiveHelp(BOOL fEnterMode)
++ {
++ if (fEnterMode)
++ QWhatsThis::enterWhatsThisMode();
++ else
++ QWhatsThis::leaveWhatsThisMode();
++ return S_OK;
++ }
++
++ //**** IOleInPlaceObject
++ /*
++ Deactivates the control in place.
++ */
++ IFACEMETHODIMP 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.
++ */
++ IFACEMETHODIMP 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) {
++ ComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
++ RECT rcPos, rcClip;
++ OLEINPLACEFRAMEINFO frameInfo;
++ frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
++
++ m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
++ if (spInPlaceUIWindow) {
++ spInPlaceUIWindow->SetActiveObject(nullptr, nullptr);
++ }
++ 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.Reset();
++ }
++ }
++ // 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.
++ */
++ IFACEMETHODIMP QAxServerBase::SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
++ {
++ if (lprcPosRect == nullptr || lprcClipRect == 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, lprcPosRect, lprcClipRect);
++ HRGN tempRgn = nullptr;
++ if (b && !EqualRect(&rcIXect, lprcPosRect)) {
++ OffsetRect(&rcIXect, -(lprcPosRect->left), -(lprcPosRect->top));
++ tempRgn = CreateRectRgnIndirect(&rcIXect);
++ }
++
++ ::SetWindowRgn(m_hWnd, tempRgn, true);
++ ::SetWindowPos(m_hWnd, nullptr, lprcPosRect->left, lprcPosRect->top,
++ lprcPosRect->right - lprcPosRect->left, lprcPosRect->bottom - lprcPosRect->top,
++ SWP_NOZORDER | SWP_NOACTIVATE);
++ }
++
++ //Save the new extent.
++ const QRect qr = qaxFromNativeRect(*lprcPosRect, 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.
++ */
++ IFACEMETHODIMP QAxServerBase::ReactivateAndUndo()
++ {
++ return E_NOTIMPL;
++ }
++
++ //**** IOleInPlaceActiveObject
++ IFACEMETHODIMP QAxServerBase::TranslateAccelerator(LPMSG lpmsg)
++ {
++ if (lpmsg->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(lpmsg->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(lpmsg->wParam);
++ // FIXME 4.10.2011: No longer exists in Lighthouse.
++ // if (!(key >= 'A' && key <= 'Z') && !(key >= '0' && key <= '9'))
++ // key = qt_translateKeyCode(lpmsg->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;
++
++ ComPtr<IOleControlSite> controlSite;
++ m_spClientSite->QueryInterface(IID_IOleControlSite, &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(lpmsg->hwnd, GWLP_USERDATA, serverType);
++ #else
++ LONG oldData = SetWindowLong(lpmsg->hwnd, GWL_USERDATA, serverType);
++ #endif
++ HRESULT hres = controlSite->TranslateAcceleratorW(lpmsg, dwKeyMod);
++ // reset the user-data for the window.
++ #ifdef GWLP_USERDATA
++ SetWindowLongPtr(lpmsg->hwnd, GWLP_USERDATA, oldData);
++ #else
++ SetWindowLong(lpmsg->hwnd, GWL_USERDATA, oldData);
++ #endif
++ return hres;
++ }
++
++ IFACEMETHODIMP QAxServerBase::OnFrameWindowActivate(BOOL fActivate)
++ {
++ if (fActivate) {
++ if (wasUIActive)
++ ::SetFocus(m_hWnd);
++ } else {
++ wasUIActive = isUIActive;
++ }
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::OnDocWindowActivate(BOOL /* fActivate */)
++ {
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::ResizeBorder(LPCRECT /* prcBorder */,
++ IOleInPlaceUIWindow * /* pUIWindow */,
++ BOOL /* fFrameWindow */)
++ {
++ return S_OK;
++ }
++
++ IFACEMETHODIMP 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;
++ }
++
++ //**** IProvideClassInfo
++ /*
++ Provide the ITypeInfo implementation for the COM class.
++ */
++ IFACEMETHODIMP QAxServerBase::GetClassInfo(ITypeInfo **ppTI)
++ {
++ if (!ppTI)
++ return E_POINTER;
++
++ *ppTI = nullptr;
++ if (!qAxTypeLibrary)
++ return DISP_E_BADINDEX;
++
++ return qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->classID(class_name), ppTI);
++ }
++
++ //**** IProvideClassInfo2
++ /*
++ Provide the ID of the event interface.
++ */
++ IFACEMETHODIMP 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;
++ }
++
++ //**** IConnectionPointContainer
++ /*
++ Provide the IEnumConnectionPoints implemented in the QAxSignalVec class.
++ */
++ IFACEMETHODIMP QAxServerBase::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
++ {
++ if (!ppEnum)
++ return E_POINTER;
++ *ppEnum = new QAxSignalVec(points);
++ return S_OK;
++ }
++
++ /*
++ Provide the IConnectionPoint implemented in the QAxConnection for \a iid.
++ */
++ IFACEMETHODIMP QAxServerBase::FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP)
++ {
++ if (!ppCP)
++ return E_POINTER;
++
++ auto &cp = points[riid];
++ *ppCP = cp.Get();
++ if (cp) {
++ cp->AddRef();
++ return S_OK;
++ }
++ return CONNECT_E_NOCONNECTION;
++ }
++
++ //**** IPersist
++ IFACEMETHODIMP QAxServerBase::GetClassID(GUID *pClassID)
++ {
++ *pClassID = qAxFactory()->classID(class_name);
++ return S_OK;
++ }
++
++ //**** IPersistStream
++ /*
++ \reimp
++
++ See documentation of IPersistStorage::IsDirty.
++ */
++ IFACEMETHODIMP QAxServerBase::IsDirty()
++ {
++ return dirtyflag ? S_OK : S_FALSE;
++ }
++
++ IFACEMETHODIMP QAxServerBase::Load(LPSTREAM 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;
++ }
++
++ IFACEMETHODIMP QAxServerBase::Save(LPSTREAM pStm, BOOL fClearDirty)
++ {
++ 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 (fClearDirty)
++ dirtyflag = false;
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::GetSizeMax(ULARGE_INTEGER *pCbSize)
++ {
++ const QMetaObject *mo = qt.object->metaObject();
++
++ pCbSize->HighPart = 0;
++ pCbSize->LowPart = DWORD(mo->propertyCount()) * 50;
++
++ return S_OK;
++ }
++
++ //**** IPersistStreamInit
++ /*
++ Initialize the properties of the Qt widget.
++ */
++ IFACEMETHODIMP QAxServerBase::InitNew()
++ {
++ if (initNewCalled)
++ return CO_E_ALREADYINITIALIZED;
++
++ dirtyflag = false;
++ initNewCalled = true;
++ return S_OK;
++ }
++
++ //**** IPersistStorage
++ IFACEMETHODIMP QAxServerBase::InitNew(IStorage *pStg)
++ {
++ if (initNewCalled)
++ return CO_E_ALREADYINITIALIZED;
++
++ dirtyflag = false;
++ initNewCalled = true;
++
++ m_spStorage = pStg;
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::Load(IStorage *pStg)
++ {
++ if (InitNew(pStg) != S_OK)
++ return CO_E_ALREADYINITIALIZED;
++
++ ComPtr<IStream> spStream;
++ 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.Get());
++
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::Save(IStorage *pStgSave, BOOL /* fSameAsLoad */)
++ {
++ ComPtr<IStream> spStream;
++ 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");
++
++ pStgSave->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.Get(), true);
++
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::SaveCompleted(IStorage *pStgNew)
++ {
++ if (pStgNew)
++ m_spStorage = pStgNew;
++
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::HandsOffStorage()
++ {
++ m_spStorage.Reset();
++
++ return S_OK;
++ }
++
++ //**** IPersistPropertyBag
++ /*
++ Set the properties of the Qt widget to the values provided in the \a bag.
++ */
++ IFACEMETHODIMP QAxServerBase::Load(IPropertyBag *pPropBag, IErrorLog * /* pErrorLog */)
++ {
++ if (!pPropBag)
++ 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 = pPropBag->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.
++ */
++ IFACEMETHODIMP QAxServerBase::Save(IPropertyBag *pPropBag, BOOL fClearDirty,
++ BOOL /* fSaveAllProperties */)
++ {
++ if (!pPropBag)
++ return E_POINTER;
++
++ if (fClearDirty)
++ 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);
++ pPropBag->Write(bstr, &var);
++ SysFreeString(bstr);
++ }
++ Q_UNUSED(error);
++ return /*error ? E_FAIL :*/ S_OK;
++ }
++
++ //**** IPersistFile
++ /*
++ */
++ IFACEMETHODIMP QAxServerBase::Load(LPCOLESTR pszFileName, DWORD /* dwMode */)
++ {
++ 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(pszFileName);
++ 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;
++ }
++
++ IFACEMETHODIMP QAxServerBase::Save(LPCOLESTR pszFileName, 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(pszFileName);
++ 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;
++ }
++
++ IFACEMETHODIMP QAxServerBase::SaveCompleted(LPCOLESTR pszFileName)
++ {
++ if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1)
++ return E_NOTIMPL;
++
++ currentFileName = QString::fromWCharArray(pszFileName);
++ return S_OK;
++ }
++
++ IFACEMETHODIMP QAxServerBase::GetCurFile(LPOLESTR *ppszFileName)
++ {
++ if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1)
++ return E_NOTIMPL;
++
++ if (currentFileName.isEmpty()) {
++ *ppszFileName = nullptr;
++ return S_FALSE;
++ }
++ ComPtr<IMalloc> malloc;
++ CoGetMalloc(1, &malloc);
++ if (!malloc)
++ return E_OUTOFMEMORY;
++
++ *ppszFileName = static_cast<wchar_t *>(malloc->Alloc(currentFileName.length() * 2));
++ memcpy(*ppszFileName, currentFileName.unicode(), currentFileName.length() * 2);
++
++ return S_OK;
++ }
++
++ //**** IDataObject
++ /*
++ Calls IViewObject::Draw after setting up the parameters.
++ */
++ IFACEMETHODIMP 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)) {
++ ComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
++ 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();
++ }
++ }
++
++ 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.
++ */
++ IFACEMETHODIMP QAxServerBase::GetDataHere(FORMATETC * /* pformatetc */, STGMEDIUM * /* pmedium */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::QueryGetData(FORMATETC * /* pformatetc */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::GetCanonicalFormatEtc(FORMATETC * /* pformatectIn */,
++ FORMATETC * /* pformatetcOut */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::SetData(FORMATETC * /* pformatetc */, STGMEDIUM * /* pmedium */,
++ BOOL /* fRelease */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::EnumFormatEtc(DWORD /* dwDirection */,
++ IEnumFORMATETC ** /* ppenumFormatEtc */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP 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.
++ */
++ IFACEMETHODIMP QAxServerBase::DUnadvise(DWORD dwConnection)
++ {
++ return Unadvise(dwConnection);
++ }
++
++ /*
++ Not implemented.
++ */
++ IFACEMETHODIMP QAxServerBase::EnumDAdvise(IEnumSTATDATA ** /* ppenumAdvise */)
++ {
++ return E_NOTIMPL;
++ }
++
++ /*! \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] = makeComObject<QAxConnection>(this, IID_IPropertyNotifySink);
++ }
++
++ /* \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 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] = makeComObject<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;
++ }
++ /*
++ 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 (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
++ if (wParam) {
++ 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.Get(), 0, that->m_hWnd, &rcPos);
++ if (that->isUIActive) {
++ ComPtr<IOleControlSite> spSite;
++ that->m_spClientSite->QueryInterface(IID_IOleControlSite, &spSite);
++ if (spSite) {
++ spSite->OnFocus(true);
++ }
++ 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) {
++ ComPtr<IOleControlSite> spSite;
++ that->m_spClientSite->QueryInterface(IID_IOleControlSite, &spSite);
++ if (spSite) {
++ if (!::IsChild(that->m_hWnd, ::GetFocus()))
++ spSite->OnFocus(false);
++ }
++ }
++ }
++ break;
++
++ case WM_MOUSEACTIVATE:
++ if (QAxServerBase *that = axServerBaseFromWindow(hWnd)) {
++ RECT rcPos = that->rcPosRect();
++ that->DoVerb(OLEIVERB_UIACTIVATE, nullptr, that->m_spClientSite.Get(), 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>(QCoreApplicationPrivate::mainInstanceHandle);
++ 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);
++ }
++
++ /*!
++ \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);
++ }
++
++ /*! \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) {
++ ComPtr<ITypeInfo> eventInfo;
++ qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->eventsID(class_name), &eventInfo);
++ if (eventInfo) {
++ QString uni_name = QLatin1String(name);
++ OLECHAR *olename = qaxQString2MutableOleChars(uni_name);
++ eventInfo->GetIDsOfNames(&olename, 1, &eventId);
++ }
++ }
++
++ 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...
++ ComPtr<IConnectionPoint> cpoint;
++ GUID IID_QAxEvents = qAxFactory()->eventsID(class_name);
++ FindConnectionPoint(IID_QAxEvents, &cpoint);
++ if (cpoint) {
++ ComPtr<IEnumConnections> clist;
++ 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) {
++ ComPtr<IDispatch> disp;
++ c->pUnk->QueryInterface(IID_QAxEvents, &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);
++ }
++ }
++ 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);
++ }
++ }
++ }
++
++ return true;
++ }
++
++ Q_GUI_EXPORT int qt_translateKeyCode(int);
++
++ 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, &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) {
++ ComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
++ HWND hwndParent;
++ if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) {
++ // get location in the parent window, as well as some information about the parent
++ 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)) {
++ m_spInPlaceFrame.Reset();
++ 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);
++ }
++ }
++ ShowWindow(m_hWnd, SW_NORMAL);
++ }
++
++ m_spClientSite->ShowObject();
++
++ return S_OK;
++ }
++
++
++ #ifdef QT_SHARED // avoid conflict with symbol in static lib
++ bool qt_sendSpontaneousEvent(QObject *o, QEvent *e)
++ {
++ return QCoreApplication::sendSpontaneousEvent(o, e);
++ }
++ #endif
++
++
++
++ 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);
++ }
++
++ RECT QAxServerBase::rcPosRect() const
++ {
++ RECT result = { 0, 0, 1, 1 };
++ if (qt.widget)
++ result = qaxContentRect(QSize(1, 1) + qaxNativeWidgetSize(qt.widget));
++ return result;
++ }
++
++ QT_END_NAMESPACE