Revision control
++ // Copyright (C) 2015 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++ // Qt-Security score:significant reason:default
++
++
++ #include <ocidl.h>
++ #include <olectl.h>
++
++ #include "qaxtypes_p.h"
++ #include "../control/iaxserverbase_p.h"
++ #include <QtAxBase/private/qaxutils_p.h>
++ #include <QtAxBase/private/qaxtypefunctions_p.h>
++
++ #include <qcursor.h>
++ #include <qpixmap.h>
++ #include <qpainter.h>
++ #include <private/qpixmap_win_p.h>
++ #include <qobject.h>
++ #include <qdebug.h>
++ #ifdef QAX_SERVER
++ # include <qaxfactory.h>
++ # include <private/qsystemlibrary_p.h>
++ #else
++ # include <quuid.h>
++ # include <qaxobject.h>
++ #endif
++
++ #include <QtCore/private/qcomptr_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ #ifdef QAX_SERVER
++ # define QVariantToVARIANT QVariantToVARIANT_server
++ # define VARIANTToQVariant VARIANTToQVariant_server
++ extern ComPtr<ITypeLib> qAxTypeLibrary;
++
++ CLSID CLSID_QRect = { 0x34030f30, 0xe359, 0x4fe6, {0xab, 0x82, 0x39, 0x76, 0x6f, 0x5d, 0x91, 0xee } };
++ CLSID CLSID_QSize = { 0xcb5f84b3, 0x29e5, 0x491d, {0xba, 0x18, 0x54, 0x72, 0x48, 0x8e, 0xef, 0xba } };
++ CLSID CLSID_QPoint = { 0x3be838a3, 0x3fac, 0xbfc4, {0x4c, 0x6c, 0x37, 0xc4, 0x4d, 0x03, 0x02, 0x52 } };
++ #else
++ # define QVariantToVARIANT QVariantToVARIANT_container
++ # define VARIANTToQVariant VARIANTToQVariant_container
++ extern void *qax_createObjectWrapper(int metaType, IUnknown *iface);
++ #endif
++
++ static IFontDisp *QFontToIFont(const QFont &font)
++ {
++ FONTDESC fdesc;
++ memset(&fdesc, 0, sizeof(fdesc));
++ fdesc.cbSizeofstruct = sizeof(FONTDESC);
++ fdesc.cySize.Lo = font.pointSize() * 10000;
++ fdesc.fItalic = font.italic();
++ fdesc.fStrikethrough = font.strikeOut();
++ fdesc.fUnderline = font.underline();
++ fdesc.lpstrName = QStringToBSTR(font.family());
++ fdesc.sWeight = font.weight() * 10;
++
++ ComPtr<IFontDisp> f;
++ HRESULT res = OleCreateFontIndirect(&fdesc, IID_IFontDisp, &f);
++ if (res != S_OK) {
++ f.Reset();
++ #if defined(QT_CHECK_STATE)
++ qWarning("QFontToIFont: Failed to create IFont");
++ #endif
++ }
++ return f.Detach();
++ }
++
++ static QFont IFontToQFont(IFont *f)
++ {
++ BSTR name;
++ BOOL bold;
++ SHORT charset;
++ BOOL italic;
++ CY size;
++ BOOL strike;
++ BOOL underline;
++ SHORT weight;
++ f->get_Name(&name);
++ f->get_Bold(&bold);
++ f->get_Charset(&charset);
++ f->get_Italic(&italic);
++ f->get_Size(&size);
++ f->get_Strikethrough(&strike);
++ f->get_Underline(&underline);
++ f->get_Weight(&weight);
++ QFont font(QString::fromWCharArray(name), size.Lo/9750, weight / 97, italic);
++ font.setBold(bold);
++ font.setStrikeOut(strike);
++ font.setUnderline(underline);
++ SysFreeString(name);
++
++ return font;
++ }
++
++ static IPictureDisp *QPixmapToIPicture(const QPixmap &pixmap)
++ {
++ ComPtr<IPictureDisp> pic;
++
++ PICTDESC desc;
++ desc.cbSizeofstruct = sizeof(PICTDESC);
++ desc.picType = PICTYPE_BITMAP;
++
++ desc.bmp.hbitmap = nullptr;
++ desc.bmp.hpal = nullptr;
++
++ if (!pixmap.isNull()) {
++ desc.bmp.hbitmap = qt_pixmapToWinHBITMAP(pixmap);
++ Q_ASSERT(desc.bmp.hbitmap);
++ }
++
++ HRESULT res = OleCreatePictureIndirect(&desc, IID_IPictureDisp, true, &pic);
++ if (res != S_OK) {
++ pic.Reset();
++ #if defined(QT_CHECK_STATE)
++ qWarning("QPixmapToIPicture: Failed to create IPicture");
++ #endif
++ }
++ return pic.Detach();
++ }
++
++ static QPixmap IPictureToQPixmap(IPicture *ipic)
++ {
++ SHORT type;
++ ipic->get_Type(&type);
++ if (type != PICTYPE_BITMAP)
++ return QPixmap();
++
++ HBITMAP hbm = nullptr;
++ ipic->get_Handle(reinterpret_cast<OLE_HANDLE*>(&hbm));
++ if (!hbm)
++ return QPixmap();
++
++ return qt_pixmapFromWinHBITMAP(hbm);
++ }
++
++ static QDateTime DATEToQDateTime(DATE ole)
++ {
++ SYSTEMTIME stime;
++ if (ole >= 949998 || !VariantTimeToSystemTime(ole, &stime))
++ return QDateTime();
++
++ QDate date(stime.wYear, stime.wMonth, stime.wDay);
++ QTime time(stime.wHour, stime.wMinute, stime.wSecond, stime.wMilliseconds);
++ return QDateTime(date, time);
++ }
++
++ static DATE QDateTimeToDATE(const QDateTime &dt)
++ {
++ if (!dt.isValid() || dt.isNull())
++ return 949998;
++
++ SYSTEMTIME stime;
++ memset(&stime, 0, sizeof(stime));
++ QDate date = dt.date();
++ QTime time = dt.time();
++ if (date.isValid() && !date.isNull()) {
++ stime.wDay = date.day();
++ stime.wMonth = date.month();
++ stime.wYear = date.year();
++ }
++ if (time.isValid() && !time.isNull()) {
++ stime.wMilliseconds = time.msec();
++ stime.wSecond = time.second();
++ stime.wMinute = time.minute();
++ stime.wHour = time.hour();
++ }
++
++ double vtime;
++ SystemTimeToVariantTime(&stime, &vtime);
++
++ return vtime;
++ }
++
++ static QByteArray msgOutParameterNotSupported(const QByteArray &type)
++ {
++ return QByteArrayLiteral("QVariantToVARIANT: out-parameter not supported for \"")
++ + type + QByteArrayLiteral("\".");
++ }
++
++ /*
++ Converts \a var to \a arg, and tries to coerce \a arg to \a type.
++
++ Used by
++
++ QAxServerBase:
++ - QAxServerBase::qt_metacall
++ - IDispatch::Invoke(PROPERTYGET, METHOD)
++ - IPersistPropertyBag::Save
++
++ QAxBase:
++ - IDispatch::Invoke (QAxEventSink)
++ - QAxBase::internalProperty(WriteProperty)
++ - QAxBase::internalInvoke()
++ - QAxBase::dynamicCallHelper()
++ - IPropertyBag::Read (QtPropertyBag)
++
++ Also called recoursively for lists.
++ */
++
++ // Convenience macro for function QVariantToVARIANT()
++ // storing a POD QVariant value in the VARIANT arg.
++ #define QVARIANT_TO_VARIANT_POD(type, value, out, varType, varMember, varPointerMember) \
++ if (out && arg.vt == ((varType) | VT_BYREF)) { \
++ *arg.varPointerMember = value; /* pre-allocated out-parameter */ \
++ } else { \
++ if (out) { \
++ arg.vt = (varType) | VT_BYREF; \
++ arg.varPointerMember = new type(value); \
++ } else { \
++ arg.vt = (varType); \
++ arg.varMember = value; \
++ } \
++ }
++
++ static qsizetype columnCount2D(const QVariantList &list)
++ {
++ if (!list.isEmpty()) {
++ const auto &firstElement = list.at(0);
++ const auto type = firstElement.typeId();
++ if (type != QMetaType::QString && type != QMetaType::QByteArray
++ && firstElement.canConvert<QVariantList>()) {
++ return firstElement.toList().size();
++ }
++ }
++ return 0;
++ }
++
++ bool QVariantToVARIANT(const QVariant &var, VARIANT &arg, const QByteArray &typeName, bool out)
++ {
++ QVariant qvar = var;
++ // "type" is the expected type, so coerce if necessary
++ const int proptype = typeName.isEmpty()
++ ? QMetaType::UnknownType
++ : QMetaType::fromName(typeName).id();
++ if (proptype != QMetaType::UnknownType
++ && proptype != QMetaType::User
++ && proptype != QMetaType::QVariant
++ && proptype != qvar.metaType().id()) {
++ const QMetaType metaType(proptype);
++ if (qvar.canConvert(metaType))
++ qvar.convert(metaType);
++ else
++ qvar = QVariant(metaType);
++ }
++
++ if (out && arg.vt == (VT_VARIANT|VT_BYREF) && arg.pvarVal) {
++ return QVariantToVARIANT(var, *arg.pvarVal, typeName, false);
++ }
++
++ if (out && proptype == QMetaType::QVariant) {
++ VARIANT *pVariant = new VARIANT;
++ QVariantToVARIANT(var, *pVariant, QByteArray(), false);
++ arg.vt = VT_VARIANT|VT_BYREF;
++ arg.pvarVal = pVariant;
++ return true;
++ }
++
++ switch (qvar.metaType().id()) {
++ case QMetaType::QString:
++ if (out && arg.vt == (VT_BSTR|VT_BYREF)) {
++ if (*arg.pbstrVal)
++ SysFreeString(*arg.pbstrVal);
++ *arg.pbstrVal = QStringToBSTR(qvar.toString());
++ arg.vt = VT_BSTR|VT_BYREF;
++ } else {
++ arg.vt = VT_BSTR;
++ arg.bstrVal = QStringToBSTR(qvar.toString());
++ if (out) {
++ arg.pbstrVal = new BSTR(arg.bstrVal);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ break;
++
++ case QMetaType::Char:
++ QVARIANT_TO_VARIANT_POD(char, char(qvar.toInt()), out, VT_I1, cVal, pcVal)
++ break;
++
++ case QMetaType::UChar:
++ QVARIANT_TO_VARIANT_POD(BYTE, uchar(qvar.toUInt()), out, VT_UI1, bVal, pbVal)
++ break;
++
++ case QMetaType::Short:
++ QVARIANT_TO_VARIANT_POD(short, qvariant_cast<short>(qvar), out, VT_I2, iVal, piVal)
++ break;
++
++ case QMetaType::UShort:
++ QVARIANT_TO_VARIANT_POD(ushort, qvariant_cast<ushort>(qvar), out, VT_UI2, uiVal, puiVal)
++ break;
++
++ case QMetaType::Int:
++ QVARIANT_TO_VARIANT_POD(long, qvar.toInt(), out, VT_I4, lVal, plVal)
++ break;
++
++ case QMetaType::UInt:
++ QVARIANT_TO_VARIANT_POD(uint, qvar.toUInt(), out, VT_UI4, uintVal, puintVal)
++ break;
++
++ case QMetaType::LongLong:
++ if (out && arg.vt == (VT_CY|VT_BYREF)) { // VT_CY: Currency
++ arg.pcyVal->int64 = qvar.toLongLong();
++ } else {
++ QVARIANT_TO_VARIANT_POD(LONGLONG, qvar.toLongLong(), out, VT_I8, llVal, pllVal)
++ }
++ break;
++
++ case QMetaType::ULongLong:
++ if (out && arg.vt == (VT_CY|VT_BYREF)) { // VT_CY: Currency
++ arg.pcyVal->int64 = qvar.toULongLong();
++ } else {
++ QVARIANT_TO_VARIANT_POD(ULONGLONG, qvar.toULongLong(), out, VT_UI8, ullVal, pullVal)
++ }
++ break;
++
++ case QMetaType::Bool:
++ QVARIANT_TO_VARIANT_POD(short, short(qvar.toBool() ? VARIANT_TRUE : VARIANT_FALSE),
++ out, VT_BOOL, boolVal, pboolVal)
++ break;
++
++ case QMetaType::Float:
++ QVARIANT_TO_VARIANT_POD(float, float(qvar.toDouble()), out, VT_R4, fltVal, pfltVal)
++ break;
++
++ case QMetaType::Double:
++ QVARIANT_TO_VARIANT_POD(double, qvar.toDouble(), out, VT_R8, dblVal, pdblVal)
++ break;
++
++ case QMetaType::QColor:
++ QVARIANT_TO_VARIANT_POD(long, QColorToOLEColor(qvariant_cast<QColor>(qvar)),
++ out, VT_COLOR, lVal, plVal)
++ break;
++
++ case QMetaType::QDate:
++ case QMetaType::QTime:
++ case QMetaType::QDateTime: // DATE = double
++ QVARIANT_TO_VARIANT_POD(DATE, QDateTimeToDATE(qvar.toDateTime()),
++ out, VT_DATE, date, pdate)
++ break;
++
++ case QMetaType::QFont:
++ if (out && arg.vt == (VT_DISPATCH|VT_BYREF)) {
++ if (*arg.ppdispVal)
++ (*arg.ppdispVal)->Release();
++ *arg.ppdispVal = QFontToIFont(qvariant_cast<QFont>(qvar));
++ } else {
++ arg.vt = VT_DISPATCH;
++ arg.pdispVal = QFontToIFont(qvariant_cast<QFont>(qvar));
++ if (out) {
++ arg.ppdispVal = new IDispatch*(arg.pdispVal);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ break;
++
++ case QMetaType::QPixmap:
++ if (out && arg.vt == (VT_DISPATCH|VT_BYREF)) {
++ if (*arg.ppdispVal)
++ (*arg.ppdispVal)->Release();
++ *arg.ppdispVal = QPixmapToIPicture(qvariant_cast<QPixmap>(qvar));
++ } else {
++ arg.vt = VT_DISPATCH;
++ arg.pdispVal = QPixmapToIPicture(qvariant_cast<QPixmap>(qvar));
++ if (out) {
++ arg.ppdispVal = new IDispatch*(arg.pdispVal);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ break;
++
++ case QMetaType::QCursor:
++ {
++ #ifndef QT_NO_CURSOR
++ int shape = qvariant_cast<QCursor>(qvar).shape();
++ if (out && (arg.vt & VT_BYREF)) {
++ switch(arg.vt & ~VT_BYREF) {
++ case VT_I4:
++ *arg.plVal = shape;
++ break;
++ case VT_I2:
++ *arg.piVal = shape;
++ break;
++ case VT_UI4:
++ *arg.pulVal = shape;
++ break;
++ case VT_UI2:
++ *arg.puiVal = shape;
++ break;
++ case VT_INT:
++ *arg.pintVal = shape;
++ break;
++ case VT_UINT:
++ *arg.puintVal = shape;
++ break;
++ }
++ } else {
++ arg.vt = VT_I4;
++ arg.lVal = shape;
++ if (out) {
++ arg.plVal = new long(arg.lVal);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ #endif
++ }
++ break;
++
++ case QMetaType::QVariantList:
++ {
++ const auto list = qvar.toList();
++ const qsizetype count = list.size();
++ VARTYPE vt = VT_VARIANT;
++ int listType = QMetaType::QVariant;
++ if (!typeName.isEmpty() && typeName.startsWith("QList<")) {
++ const QByteArray listTypeName = typeName.mid(6, typeName.length() - 7); // QList<int> -> int
++ listType = QMetaType::fromName(listTypeName).id();
++ }
++
++ VARIANT variant;
++ void *pElement = &variant;
++ switch(listType) {
++ case QMetaType::Int:
++ vt = VT_I4;
++ pElement = &variant.lVal;
++ break;
++ case QMetaType::Double:
++ vt = VT_R8;
++ pElement = &variant.dblVal;
++ break;
++ case QMetaType::QDateTime:
++ vt = VT_DATE;
++ pElement = &variant.date;
++ break;
++ case QMetaType::Bool:
++ vt = VT_BOOL;
++ pElement = &variant.boolVal;
++ break;
++ case QMetaType::LongLong:
++ vt = VT_I8;
++ pElement = &variant.llVal;
++ break;
++ default:
++ break;
++ }
++ SAFEARRAY *array = nullptr;
++ // If the first element in the array is a list the whole list is
++ // treated as a 2D array. The column count is taken from the 1st element.
++ if (qsizetype maxColumns = columnCount2D(list)) {
++ SAFEARRAYBOUND rgsabound[2] = { {0, 0}, {0, 0} };
++ rgsabound[0].cElements = count;
++ rgsabound[1].cElements = maxColumns;
++ array = SafeArrayCreate(VT_VARIANT, 2, rgsabound);
++ LONG rgIndices[2];
++ for (LONG i = 0; i < count; ++i) {
++ rgIndices[0] = i;
++ QVariantList columns = list.at(i).toList();
++ qsizetype columnCount = qMin(maxColumns, columns.size());
++ for (LONG j = 0; j < columnCount; ++j) {
++ const QVariant &elem = columns.at(j);
++ VariantInit(&variant);
++ QVariantToVARIANT(elem, variant, elem.typeName());
++ rgIndices[1] = j;
++ SafeArrayPutElement(array, rgIndices, pElement);
++ clearVARIANT(&variant);
++ }
++ }
++ } else {
++ array = SafeArrayCreateVector(vt, 0, count);
++ for (LONG index = 0; index < count; ++index) {
++ QVariant elem = list.at(index);
++ if (listType != QMetaType::QVariant)
++ elem.convert(QMetaType(listType));
++ VariantInit(&variant);
++ QVariantToVARIANT(elem, variant, elem.typeName());
++ SafeArrayPutElement(array, &index, pElement);
++ clearVARIANT(&variant);
++ }
++ }
++ if (out && arg.vt == (VT_ARRAY|vt|VT_BYREF)) {
++ if (*arg.pparray)
++ SafeArrayDestroy(*arg.pparray);
++ *arg.pparray = array;
++ } else {
++ arg.vt = VT_ARRAY|vt;
++ arg.parray = array;
++ if (out) {
++ arg.pparray = new SAFEARRAY*(arg.parray);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ }
++ break;
++
++ case QMetaType::QStringList:
++ {
++ const QStringList list = qvar.toStringList();
++ const qsizetype count = list.size();
++ SAFEARRAY *array = SafeArrayCreateVector(VT_BSTR, 0, count);
++ for (LONG index = 0; index < count; ++index) {
++ QString elem = list.at(index);
++ BSTR bstr = QStringToBSTR(elem);
++ SafeArrayPutElement(array, &index, bstr);
++ SysFreeString(bstr);
++ }
++
++ if (out && arg.vt == (VT_ARRAY|VT_BSTR|VT_BYREF)) {
++ if (*arg.pparray)
++ SafeArrayDestroy(*arg.pparray);
++ *arg.pparray = array;
++ } else {
++ arg.vt = VT_ARRAY|VT_BSTR;
++ arg.parray = array;
++ if (out) {
++ arg.pparray = new SAFEARRAY*(arg.parray);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ }
++ break;
++
++ case QMetaType::QByteArray:
++ {
++ const QByteArray bytes = qvar.toByteArray();
++ const uint count = static_cast<uint>(bytes.size());
++ SAFEARRAY *array = SafeArrayCreateVector(VT_UI1, 0, count);
++ if (count) {
++ const char *data = bytes.constData();
++ char *dest;
++ SafeArrayAccessData(array, reinterpret_cast<void**>(&dest));
++ memcpy(dest, data, count);
++ SafeArrayUnaccessData(array);
++ }
++
++ if (out && arg.vt == (VT_ARRAY|VT_UI1|VT_BYREF)) {
++ if (*arg.pparray)
++ SafeArrayDestroy(*arg.pparray);
++ *arg.pparray = array;
++ } else {
++ arg.vt = VT_ARRAY|VT_UI1;
++ arg.parray = array;
++ if (out) {
++ arg.pparray = new SAFEARRAY*(arg.parray);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ }
++ break;
++
++ #ifdef QAX_SERVER
++ case QMetaType::QRect:
++ case QMetaType::QSize:
++ case QMetaType::QPoint:
++ {
++ typedef HRESULT(WINAPI* PGetRecordInfoFromTypeInfo)(ITypeInfo *, IRecordInfo **);
++ static PGetRecordInfoFromTypeInfo pGetRecordInfoFromTypeInfo = 0;
++ static bool resolved = false;
++ if (!resolved) {
++ resolved = true;
++ pGetRecordInfoFromTypeInfo = (PGetRecordInfoFromTypeInfo)QSystemLibrary::resolve(QLatin1String("oleaut32"),
++ "GetRecordInfoFromTypeInfo");
++ }
++ if (!pGetRecordInfoFromTypeInfo)
++ break;
++
++ ComPtr<ITypeInfo> typeInfo;
++ IRecordInfo *recordInfo = 0;
++ const int vType = qvar.metaType().id();
++ CLSID clsid = vType == QMetaType::QRect
++ ? CLSID_QRect
++ : vType == QMetaType::QSize ? CLSID_QSize : CLSID_QPoint;
++ qAxTypeLibrary->GetTypeInfoOfGuid(clsid, &typeInfo);
++ if (!typeInfo)
++ break;
++ pGetRecordInfoFromTypeInfo(typeInfo.Get(), &recordInfo);
++ if (!recordInfo)
++ break;
++
++ void *record = 0;
++ switch (qvar.metaType().id()) {
++ case QMetaType::QRect:
++ {
++ QRect qrect(qvar.toRect());
++ recordInfo->RecordCreateCopy(&qrect, &record);
++ }
++ break;
++ case QMetaType::QSize:
++ {
++ QSize qsize(qvar.toSize());
++ recordInfo->RecordCreateCopy(&qsize, &record);
++ }
++ break;
++ case QMetaType::QPoint:
++ {
++ QPoint qpoint(qvar.toPoint());
++ recordInfo->RecordCreateCopy(&qpoint, &record);
++ }
++ break;
++ default:
++ break;
++ }
++
++ if (out) {
++ qWarning().noquote() << msgOutParameterNotSupported("records");
++ arg.vt = VT_EMPTY;
++ arg.byref = nullptr;
++ return false;
++ }
++ arg.vt = VT_RECORD;
++ arg.pRecInfo = recordInfo,
++ arg.pvRecord = record;
++ }
++ break;
++ #endif // QAX_SERVER
++
++ case QMetaType::UnknownType: // default-parameters not set
++ if (out && arg.vt == (VT_ERROR|VT_BYREF)) {
++ *arg.plVal = DISP_E_PARAMNOTFOUND;
++ } else {
++ arg.vt = VT_ERROR;
++ arg.lVal = DISP_E_PARAMNOTFOUND;
++ if (out) {
++ arg.plVal = new long(arg.lVal);
++ arg.vt |= VT_BYREF;
++ }
++ }
++ break;
++
++ default:
++ if (qvar.metaType().flags().testFlag(QMetaType::IsEnumeration)) {
++ // A COM enum has no dedicated QMetaType::Type id of its own, so it
++ // ends up here with an id() >= QMetaType::User. It must not be
++ // mistaken for a QAxObject-wrapping container below: pack the
++ // underlying integer value instead.
++ QVARIANT_TO_VARIANT_POD(long, qvar.toInt(), out, VT_I4, lVal, plVal)
++ break;
++ }
++ if (qvar.metaType().id() >= QMetaType::User) {
++ QByteArray subType = qvar.typeName();
++ #ifdef QAX_SERVER
++ if (subType.endsWith('*'))
++ subType.truncate(subType.length() - 1);
++ #endif
++ if (!qstrcmp(qvar.typeName(), "IDispatch*")) {
++ if (out) {
++ qWarning().noquote() << msgOutParameterNotSupported(qvar.typeName());
++ arg.vt = VT_EMPTY;
++ arg.byref = nullptr;
++ return false;
++ }
++ arg.vt = VT_DISPATCH;
++ arg.pdispVal = *static_cast<IDispatch**>(qvar.data());
++ if (arg.pdispVal)
++ arg.pdispVal->AddRef();
++ } else if (!qstrcmp(qvar.typeName(), "IDispatch**")) {
++ arg.vt = VT_DISPATCH;
++ arg.ppdispVal = *static_cast<IDispatch***>(qvar.data());
++ if (out)
++ arg.vt |= VT_BYREF;
++ } else if (!qstrcmp(qvar.typeName(), "IUnknown*")) {
++ if (out) {
++ qWarning().noquote() << msgOutParameterNotSupported(qvar.typeName());
++ arg.vt = VT_EMPTY;
++ arg.byref = nullptr;
++ return false;
++ }
++ arg.vt = VT_UNKNOWN;
++ arg.punkVal = *static_cast<IUnknown**>(qvar.data());
++ if (arg.punkVal)
++ arg.punkVal->AddRef();
++ #ifdef QAX_SERVER
++ } else if (qAxFactory()->metaObject(QString::fromLatin1(subType.constData()))) {
++ if (out) {
++ qWarning().noquote() << msgOutParameterNotSupported("subtype");
++ arg.vt = VT_EMPTY;
++ arg.byref = nullptr;
++ return false;
++ }
++ arg.vt = VT_DISPATCH;
++ void *user = *(void**)qvar.constData();
++ // qVariantGet(qvar, user, qvar.typeName());
++ if (!user) {
++ arg.pdispVal = 0;
++ } else {
++ qAxFactory()->createObjectWrapper(static_cast<QObject*>(user), &arg.pdispVal);
++ }
++ #else
++ } else if (QMetaType::fromName(subType).id() != QMetaType::UnknownType) {
++ if (out) {
++ qWarning().noquote() << msgOutParameterNotSupported("subtype");
++ arg.vt = VT_EMPTY;
++ arg.byref = nullptr;
++ return false;
++ }
++ QAxObject *object = *static_cast<QAxObject**>(qvar.data());
++ // qVariantGet(qvar, object, subType);
++ arg.vt = VT_DISPATCH;
++ object->queryInterface(IID_IDispatch, reinterpret_cast<void**>(&arg.pdispVal));
++ #endif
++ } else {
++ return false;
++ }
++ } else { // >= User
++ return false;
++ }
++ break;
++ }
++
++ Q_ASSERT(!out || (arg.vt & VT_BYREF));
++ return true;
++ }
++
++ #ifdef QAX_SERVER
++ static QVariant axServer(IUnknown *unknown, const QByteArray &typeName)
++ {
++ ComPtr<IAxServerBase> iface;
++ if (unknown && typeName != "IDispatch*" && typeName != "IUnknown*")
++ unknown->QueryInterface(IID_IAxServerBase, &iface);
++ if (iface == nullptr)
++ return {};
++
++ auto *qObj = iface->GetQObject();
++ QByteArray pointerType = qObj ? QByteArray(qObj->metaObject()->className()) + '*' : typeName;
++ QMetaType pointerMetaType = QMetaType::fromName(pointerType);
++ if (pointerMetaType.id() == QMetaType::UnknownType)
++ pointerMetaType = QMetaType(qRegisterMetaType<QObject *>(pointerType));
++ return QVariant(pointerMetaType, &qObj);
++ }
++ #endif // QAX_SERVER
++
++ #undef QVARIANT_TO_VARIANT_POD
++
++ /*
++ Returns \a arg as a QVariant of type \a typeName or \a type.
++
++ NOTE: If a \a typeName is specified, value type is assumed. to
++ get/create a pointer type, provide the type id in the \a type argument.
++
++ Used by
++
++ QAxServerBase:
++ - QAxServerBase::qt_metacall(update out parameters/return value)
++ - IDispatch::Invoke(METHOD, PROPERTYPUT)
++ - IPersistPropertyBag::Load
++
++ QAxBase:
++ - IDispatch::Invoke (QAxEventSink)
++ - QAxBase::internalProperty(ReadProperty)
++ - QAxBase::internalInvoke(update out parameters/return value)
++ - QAxBase::dynamicCallHelper(update out parameters)
++ - QAxBase::dynamicCall(return value)
++ - IPropertyBag::Write (QtPropertyBag)
++ */
++ QVariant VARIANTToQVariant(const VARIANT &arg, const QByteArray &typeName, int type)
++ {
++ int nameTypeId = QMetaType::UnknownType;
++ if (type == QMetaType::UnknownType && !typeName.isEmpty()) {
++ auto name = typeName.endsWith('*')
++ ? QByteArrayView{typeName.constData(), typeName.size() - 1}
++ : QByteArrayView{typeName};
++ nameTypeId = QMetaType::fromName(name).id();
++ }
++
++ QVariant var;
++ switch(arg.vt) {
++ case VT_BSTR:
++ var = QString::fromWCharArray(arg.bstrVal);
++ break;
++ case VT_BSTR|VT_BYREF:
++ var = QString::fromWCharArray(*arg.pbstrVal);
++ break;
++ case VT_BOOL:
++ var = QVariant((bool)arg.boolVal);
++ break;
++ case VT_BOOL|VT_BYREF:
++ var = QVariant((bool)*arg.pboolVal);
++ break;
++ case VT_I1:
++ var = arg.cVal;
++ if (typeName == "char")
++ type = QMetaType::Int;
++ break;
++ case VT_I1|VT_BYREF:
++ var = *arg.pcVal;
++ if (typeName == "char")
++ type = QMetaType::Int;
++ break;
++ case VT_I2:
++ var = arg.iVal;
++ if (typeName == "short")
++ type = QMetaType::Int;
++ break;
++ case VT_I2|VT_BYREF:
++ var = *arg.piVal;
++ if (typeName == "short")
++ type = QMetaType::Int;
++ break;
++ case VT_I4:
++ if (type == QMetaType::QColor || nameTypeId == QMetaType::QColor)
++ var = QVariant::fromValue(OLEColorToQColor(arg.lVal));
++ #ifndef QT_NO_CURSOR
++ else if (type == QMetaType::QCursor || nameTypeId == QMetaType::QCursor)
++ var = QVariant::fromValue(QCursor(static_cast<Qt::CursorShape>(arg.lVal)));
++ #endif
++ else
++ var = (int)arg.lVal;
++ break;
++ case VT_I4|VT_BYREF:
++ if (type == QMetaType::QColor || nameTypeId == QMetaType::QColor)
++ var = QVariant::fromValue(OLEColorToQColor((int)*arg.plVal));
++ #ifndef QT_NO_CURSOR
++ else if (type == QMetaType::QCursor || nameTypeId == QMetaType::QCursor)
++ var = QVariant::fromValue(QCursor(static_cast<Qt::CursorShape>(*arg.plVal)));
++ #endif
++ else
++ var = (int)*arg.plVal;
++ break;
++ case VT_INT:
++ var = arg.intVal;
++ break;
++ case VT_INT|VT_BYREF:
++ var = *arg.pintVal;
++ break;
++ case VT_UI1:
++ var = arg.bVal;
++ break;
++ case VT_UI1|VT_BYREF:
++ var = *arg.pbVal;
++ break;
++ case VT_UI2:
++ var = arg.uiVal;
++ break;
++ case VT_UI2|VT_BYREF:
++ var = *arg.puiVal;
++ break;
++ case VT_UI4:
++ if (type == QMetaType::QColor || nameTypeId == QMetaType::QColor)
++ var = QVariant::fromValue(OLEColorToQColor(arg.ulVal));
++ #ifndef QT_NO_CURSOR
++ else if (type == QMetaType::QCursor || nameTypeId == QMetaType::QCursor)
++ var = QVariant::fromValue(QCursor(static_cast<Qt::CursorShape>(arg.ulVal)));
++ #endif
++ else
++ var = (int)arg.ulVal;
++ break;
++ case VT_UI4|VT_BYREF:
++ if (type == QMetaType::QColor || nameTypeId == QMetaType::QColor)
++ var = QVariant::fromValue(OLEColorToQColor((uint)*arg.pulVal));
++ #ifndef QT_NO_CURSOR
++ else if (type == QMetaType::QCursor || nameTypeId == QMetaType::QCursor)
++ var = QVariant::fromValue(QCursor(static_cast<Qt::CursorShape>(*arg.pulVal)));
++ #endif
++ else
++ var = (int)*arg.pulVal;
++ break;
++ case VT_UINT:
++ var = arg.uintVal;
++ break;
++ case VT_UINT|VT_BYREF:
++ var = *arg.puintVal;
++ break;
++ case VT_CY:
++ var = arg.cyVal.int64;
++ break;
++ case VT_CY|VT_BYREF:
++ var = arg.pcyVal->int64;
++ break;
++ case VT_I8:
++ var = arg.llVal;
++ break;
++ case VT_I8|VT_BYREF:
++ var = *arg.pllVal;
++ break;
++ case VT_UI8:
++ var = arg.ullVal;
++ break;
++ case VT_UI8|VT_BYREF:
++ var = *arg.pullVal;
++ break;
++ case VT_R4:
++ var = arg.fltVal;
++ break;
++ case VT_R4|VT_BYREF:
++ var = *arg.pfltVal;
++ break;
++ case VT_R8:
++ var = arg.dblVal;
++ break;
++ case VT_R8|VT_BYREF:
++ var = *arg.pdblVal;
++ break;
++ case VT_DATE:
++ var = DATEToQDateTime(arg.date);
++ if (type == QMetaType::QDate || nameTypeId == QMetaType::QDate) {
++ var.convert(QMetaType(QMetaType::QDate));
++ } else if (type == QMetaType::QTime || nameTypeId == QMetaType::QTime) {
++ var.convert(QMetaType(QMetaType::QTime));
++ }
++ break;
++ case VT_DATE|VT_BYREF:
++ var = DATEToQDateTime(*arg.pdate);
++ if (type == QMetaType::QDate || nameTypeId == QMetaType::QDate) {
++ var.convert(QMetaType(QMetaType::QDate));
++ } else if (type == QMetaType::QTime || nameTypeId == QMetaType::QTime) {
++ var.convert(QMetaType(QMetaType::QTime));
++ }
++ break;
++ case VT_VARIANT:
++ case VT_VARIANT|VT_BYREF:
++ if (arg.pvarVal)
++ var = VARIANTToQVariant(*arg.pvarVal, typeName);
++ break;
++
++ case VT_DISPATCH:
++ case VT_DISPATCH|VT_BYREF:
++ {
++ // pdispVal and ppdispVal are a union
++ IDispatch *disp = nullptr;
++ if (arg.vt & VT_BYREF)
++ disp = *arg.ppdispVal;
++ else
++ disp = arg.pdispVal;
++ if (type == QMetaType::QFont || nameTypeId == QMetaType::QFont) {
++ ComPtr<IFont> ifont;
++ if (disp)
++ disp->QueryInterface(IID_IFont, &ifont);
++ if (ifont) {
++ var = QVariant::fromValue(IFontToQFont(ifont.Get()));
++ } else {
++ var = QVariant::fromValue(QFont());
++ }
++ } else if (type == QMetaType::QPixmap || nameTypeId == QMetaType::QPixmap) {
++ ComPtr<IPicture> ipic;
++ if (disp)
++ disp->QueryInterface(IID_IPicture, &ipic);
++ if (ipic) {
++ var = QVariant::fromValue(IPictureToQPixmap(ipic.Get()));
++ } else {
++ var = QVariant::fromValue(QPixmap());
++ }
++ } else {
++ #ifdef QAX_SERVER
++ if (auto axs = axServer(disp, typeName); axs.isValid()) {
++ var = axs;
++ } else
++ #endif
++ {
++ if (!typeName.isEmpty()) {
++ if (arg.vt & VT_BYREF) {
++ // When the dispinterface is a return value, just assign it to a QVariant
++ static const int dispatchId = qRegisterMetaType<IDispatch**>("IDispatch**");
++ var = QVariant(QMetaType(dispatchId), &arg.ppdispVal);
++ } else {
++ #ifndef QAX_SERVER
++ if (typeName == "QVariant") {
++ // If a QVariant is requested, wrap the dispinterface in a QAxObject
++ QAxObject *object = new QAxObject(disp);
++ var = QVariant::fromValue<QAxObject*>(object);
++ } else if (typeName != "IDispatch*" && QMetaType::fromName(typeName).id() != QMetaType::UnknownType) {
++ // Conversion from IDispatch* to a wrapper type is requested. Here, the requested
++ // wrapper type is constructed around the dispinterface, and then returned as
++ // a QVariant containing a pointer to the wrapper type.
++
++ // Calculate the value type from a potential pointer type
++ QByteArray valueTypeStr = QByteArray(typeName);
++ int pIndex = typeName.lastIndexOf('*');
++ if (pIndex != -1)
++ valueTypeStr = typeName.left(pIndex);
++
++ const QMetaType metaValueType = QMetaType::fromName(valueTypeStr);
++ Q_ASSERT(metaValueType.id() != QMetaType::UnknownType);
++
++ auto object = static_cast<QAxObject*>(qax_createObjectWrapper(metaValueType.id(), disp));
++
++ // Return object as the original type
++ const QMetaType returnType = QMetaType::fromName(typeName);
++ Q_ASSERT(metaValueType.id() != QMetaType::UnknownType);
++
++ var = QVariant(returnType, &object);
++
++ // The result must be a pointer to an instance derived from QObject
++ Q_ASSERT((var.metaType().flags() & QMetaType::PointerToQObject) != 0);
++ } else {
++ #endif
++ // An IDispatch pointer is requested, no conversion required, just return as QVariant
++ // containing the pointer.
++ static const int dispatchId = qRegisterMetaType<IDispatch*>(typeName.constData());
++ var = QVariant(QMetaType(dispatchId), &disp);
++ #ifndef QAX_SERVER
++ }
++ #endif
++ }
++ }
++ }
++ }
++ }
++ break;
++ case VT_UNKNOWN:
++ case VT_UNKNOWN|VT_BYREF:
++ {
++ IUnknown *unkn = nullptr;
++ if (arg.vt & VT_BYREF)
++ unkn = *arg.ppunkVal;
++ else
++ unkn = arg.punkVal;
++ var.setValue(unkn);
++ #ifdef QAX_SERVER
++ if (auto axs = axServer(unkn, typeName); axs.isValid())
++ var = axs;
++ #endif
++ }
++ break;
++ case VT_ARRAY|VT_VARIANT:
++ case VT_ARRAY|VT_VARIANT|VT_BYREF:
++ {
++ SAFEARRAY *array = nullptr;
++ if ( arg.vt & VT_BYREF )
++ array = *arg.pparray;
++ else
++ array = arg.parray;
++
++ UINT cDims = array ? SafeArrayGetDim(array) : 0;
++ switch(cDims) {
++ case 1:
++ {
++ QVariantList list;
++
++ long lBound, uBound;
++ SafeArrayGetLBound( array, 1, &lBound );
++ SafeArrayGetUBound( array, 1, &uBound );
++
++ for ( long i = lBound; i <= uBound; ++i ) {
++ VARIANT var;
++ VariantInit( &var );
++ SafeArrayGetElement( array, &i, &var );
++
++ QVariant qvar = VARIANTToQVariant( var, nullptr );
++ clearVARIANT( &var );
++ list << qvar;
++ }
++
++ var = list;
++ }
++ break;
++
++ case 2:
++ {
++ QVariantList listList; // a list of lists
++ long dimIndices[2];
++
++ long xlBound, xuBound, ylBound, yuBound;
++ SafeArrayGetLBound(array, 1, &xlBound);
++ SafeArrayGetUBound(array, 1, &xuBound);
++ SafeArrayGetLBound(array, 2, &ylBound);
++ SafeArrayGetUBound(array, 2, &yuBound);
++
++ for (long x = xlBound; x <= xuBound; ++x) {
++ QVariantList list;
++
++ dimIndices[0] = x;
++ for (long y = ylBound; y <= yuBound; ++y) {
++ VARIANT var;
++ VariantInit(&var);
++ dimIndices[1] = y;
++ SafeArrayGetElement(array, dimIndices, &var);
++
++ QVariant qvar = VARIANTToQVariant(var, nullptr);
++ clearVARIANT(&var);
++ list << qvar;
++ }
++
++ listList << QVariant(list);
++ }
++ var = listList;
++ }
++ break;
++ default:
++ var = QVariantList();
++ break;
++ }
++ }
++ break;
++
++ case VT_ARRAY|VT_BSTR:
++ case VT_ARRAY|VT_BSTR|VT_BYREF:
++ {
++ SAFEARRAY *array = nullptr;
++ if (arg.vt & VT_BYREF)
++ array = *arg.pparray;
++ else
++ array = arg.parray;
++
++ QStringList strings;
++ if (!array || array->cDims != 1) {
++ var = strings;
++ break;
++ }
++
++ long lBound, uBound;
++ SafeArrayGetLBound(array, 1, &lBound);
++ SafeArrayGetUBound(array, 1, &uBound);
++
++ for (long i = lBound; i <= uBound; ++i) {
++ BSTR bstr;
++ SafeArrayGetElement(array, &i, &bstr);
++ strings << QString::fromWCharArray(bstr);
++ SysFreeString(bstr);
++ }
++
++ var = strings;
++ }
++ break;
++
++ case VT_ARRAY|VT_UI1:
++ case VT_ARRAY|VT_UI1|VT_BYREF:
++ {
++ SAFEARRAY *array = nullptr;
++ if (arg.vt & VT_BYREF)
++ array = *arg.pparray;
++ else
++ array = arg.parray;
++
++ QByteArray bytes;
++ if (!array || array->cDims != 1) {
++ var = bytes;
++ break;
++ }
++
++ long lBound, uBound;
++ SafeArrayGetLBound(array, 1, &lBound);
++ SafeArrayGetUBound(array, 1, &uBound);
++
++ if (uBound != -1) { // non-empty array
++ bytes.resize(uBound - lBound + 1);
++ char *data = bytes.data();
++ char *src;
++ SafeArrayAccessData(array, reinterpret_cast<void**>(&src));
++ memcpy(data, src, bytes.size());
++ SafeArrayUnaccessData(array);
++ }
++
++ var = bytes;
++ }
++ break;
++
++ #if defined(QAX_SERVER)
++ case VT_RECORD:
++ case VT_RECORD|VT_BYREF:
++ if (arg.pvRecord && arg.pRecInfo) {
++ IRecordInfo *recordInfo = arg.pRecInfo;
++ void *record = arg.pvRecord;
++ GUID guid;
++ recordInfo->GetGuid(&guid);
++
++ if (guid == CLSID_QRect) {
++ QRect qrect;
++ recordInfo->RecordCopy(record, &qrect);
++ var = qrect;
++ } else if (guid == CLSID_QSize) {
++ QSize qsize;
++ recordInfo->RecordCopy(record, &qsize);
++ var = qsize;
++ } else if (guid == CLSID_QPoint) {
++ QPoint qpoint;
++ recordInfo->RecordCopy(record, &qpoint);
++ var = qpoint;
++ }
++ }
++ break;
++ #endif // QAX_SERVER
++ default:
++ // support for any SAFEARRAY(Type) where Type can be converted to a QVariant
++ // -> QVariantList
++ if (arg.vt & VT_ARRAY) {
++ SAFEARRAY *array = nullptr;
++ if (arg.vt & VT_BYREF)
++ array = *arg.pparray;
++ else
++ array = arg.parray;
++
++ QVariantList list;
++ if (!array || array->cDims != 1) {
++ var = list;
++ break;
++ }
++
++ // find out where to store the element
++ VARTYPE vt;
++ VARIANT variant;
++ SafeArrayGetVartype(array, &vt);
++
++ void *pElement = nullptr;
++ switch(vt) {
++ case VT_BSTR: Q_ASSERT(false); break; // already covered
++ case VT_BOOL: pElement = &variant.boolVal; break;
++ case VT_I1: pElement = &variant.cVal; break;
++ case VT_I2: pElement = &variant.iVal; break;
++ case VT_I4: pElement = &variant.lVal; break;
++ case VT_I8: pElement = &variant.llVal; break;
++ case VT_UI8: pElement = &variant.ullVal; break;
++ case VT_INT: pElement = &variant.intVal; break;
++ case VT_UI1: Q_ASSERT(false); break; // already covered
++ case VT_UI2: pElement = &variant.uiVal; break;
++ case VT_UI4: pElement = &variant.ulVal; break;
++ case VT_UINT: pElement = &variant.uintVal; break;
++ case VT_CY: pElement = &variant.cyVal; break;
++ case VT_R4: pElement = &variant.fltVal; break;
++ case VT_R8: pElement = &variant.dblVal; break;
++ case VT_DATE: pElement = &variant.date; break;
++ case VT_VARIANT: Q_ASSERT(false); break; // already covered
++ default:
++ break;
++ }
++ if (!pElement) {
++ var = list;
++ break;
++ }
++
++ long lBound, uBound;
++ SafeArrayGetLBound( array, 1, &lBound );
++ SafeArrayGetUBound( array, 1, &uBound );
++
++ for ( long i = lBound; i <= uBound; ++i ) {
++ variant.vt = vt;
++ SafeArrayGetElement(array, &i, pElement);
++ QVariant qvar = VARIANTToQVariant(variant, nullptr);
++ clearVARIANT(&variant);
++ list << qvar;
++ }
++
++ var = list;
++ }
++ break;
++ }
++
++ const int proptype = type != QMetaType::UnknownType || typeName.isEmpty() || typeName == "QVariant"
++ ? type : nameTypeId;
++
++ if (proptype != QMetaType::QVariant && proptype != QMetaType::UnknownType
++ && var.metaType().id() != proptype) {
++ QMetaType propertyMetaType(proptype);
++ if (var.canConvert(propertyMetaType)) {
++ QVariant oldvar = var;
++ if (oldvar.convert(propertyMetaType))
++ var = oldvar;
++ } else if (proptype == QMetaType::QStringList
++ && var.metaType().id() == QMetaType::QVariantList) {
++ bool allStrings = true;
++ QStringList strings;
++ const QVariantList list(var.toList());
++ for (const QVariant &variant : list) {
++ if (variant.canConvert<QString>())
++ strings << variant.toString();
++ else
++ allStrings = false;
++ }
++ if (allStrings)
++ var = strings;
++ } else {
++ var = QVariant();
++ }
++ }
++ return var;
++ }
++
++ QT_END_NAMESPACE