Revision control
Copy as Markdown
// 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, ¶ms, &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