Revision control
++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++ // Qt-Security score:significant reason:default
++
++
++ #include "qaxbindable.h"
++ #include "qaxfactory.h"
++
++ #include <QtAxBase/private/qaxutils_p.h>
++
++ #include <qapplication.h>
++ #include <qdatetime.h>
++ #include <qdir.h>
++ #include <qmap.h>
++ #include <qmenubar.h>
++ #include <qmetaobject.h>
++ #include <qscopedpointer.h>
++ #include <qfileinfo.h>
++ #include <qsettings.h>
++ #include <qvariant.h>
++ #include <qtextstream.h>
++ #include <qloggingcategory.h>
++ #include <qdebug.h>
++ #include <QScopeGuard>
++
++ #include <qt_windows.h>
++ #include <olectl.h>
++ #include <QtCore/private/qcomptr_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ #define Q_REQUIRED_RPCNDR_H_VERSION 475
++
++ // Some global variables to store module information
++ bool qAxIsServer = false;
++ ComPtr<ITypeLib> qAxTypeLibrary;
++ wchar_t qAxModuleFilename[MAX_PATH];
++ bool qAxOutProcServer = false;
++
++ // The QAxFactory instance
++ static QAxFactory* qax_factory = nullptr;
++ extern CLSID CLSID_QRect;
++ extern CLSID CLSID_QSize;
++ extern CLSID CLSID_QPoint;
++ extern void qax_shutDown();
++ extern bool qax_ownQApp;
++
++
++ extern QAxFactory *qax_instantiate();
++
++ QAxFactory *qAxFactory()
++ {
++ if (!qax_factory) {
++ // QAxFactory is a QObject; Qt requires QCoreApplication to exist first.
++ // In the in-process DLL case (qAxIsServer set by DllMain), COM may call into us
++ // via DllGetClassObject on the COM apartment thread before the host application
++ // creates QApplication. Create one here so the factory QObject is not instantiated
++ // before QCoreApplication, which Qt does not support.
++ if (!qApp && qAxIsServer) {
++ new QApplication(__argc, __argv);
++ qax_ownQApp = true;
++ }
++ qax_factory = qax_instantiate();
++
++ // register all types with metatype system as pointers
++ QStringList keys(qax_factory->featureList());
++ for (int i = 0; i < keys.size(); ++i) {
++ QByteArray pointerType = keys.at(i).toLatin1() + '*';
++ if (QMetaType::fromName(pointerType).id() == QMetaType::UnknownType)
++ qRegisterMetaType<void *>(pointerType);
++ }
++ }
++ return qax_factory;
++ }
++
++ // Some local variables to handle module lifetime
++ static unsigned long qAxModuleRef = 0;
++ static CRITICAL_SECTION qAxModuleSection;
++
++
++ /////////////////////////////////////////////////////////////////////////////
++ // Server control
++ /////////////////////////////////////////////////////////////////////////////
++
++ static int initCount = 0;
++
++ Q_STATIC_LOGGING_CATEGORY(lcAxRegistration, "qt.activeqt.registration")
++
++ QString qAxInit()
++ {
++ static QString libFile;
++
++ if (initCount++)
++ return libFile;
++
++ InitializeCriticalSection(&qAxModuleSection);
++
++ libFile = QString::fromWCharArray(qAxModuleFilename);
++ libFile = libFile.toLower();
++ if (LoadTypeLibEx(reinterpret_cast<const wchar_t *>(libFile.utf16()),
++ REGKIND_NONE, &qAxTypeLibrary) == S_OK)
++ return libFile;
++
++ const int lastDot = libFile.lastIndexOf(QLatin1Char('.'));
++ libFile.truncate(lastDot);
++ libFile += QLatin1String(".tlb");
++ if (LoadTypeLibEx(reinterpret_cast<const wchar_t *>(libFile.utf16()),
++ REGKIND_NONE, &qAxTypeLibrary) == S_OK)
++ return libFile;
++
++ libFile.truncate(lastDot);
++ libFile += QLatin1String(".olb");
++ if (LoadTypeLibEx(reinterpret_cast<const wchar_t *>(libFile.utf16()),
++ REGKIND_NONE, &qAxTypeLibrary) == S_OK)
++ return libFile;
++
++ return QString();
++ }
++
++ void qAxCleanup()
++ {
++ if (!initCount)
++ qWarning("qAxInit/qAxCleanup mismatch");
++
++ if (--initCount)
++ return;
++
++ delete qax_factory;
++ qax_factory = nullptr;
++
++ qAxTypeLibrary.Reset();
++
++ DeleteCriticalSection(&qAxModuleSection);
++ }
++
++ unsigned long qAxLock()
++ {
++ EnterCriticalSection(&qAxModuleSection);
++ unsigned long ref = ++qAxModuleRef;
++ LeaveCriticalSection(&qAxModuleSection);
++ return ref;
++ }
++
++ unsigned long qAxUnlock()
++ {
++ if (!initCount) // cleaned up already
++ return 0;
++
++ EnterCriticalSection(&qAxModuleSection);
++ unsigned long ref = --qAxModuleRef;
++ LeaveCriticalSection(&qAxModuleSection);
++
++ if (!ref)
++ qax_shutDown();
++ return ref;
++ }
++
++ unsigned long qAxLockCount()
++ {
++ return qAxModuleRef;
++ }
++
++ /////////////////////////////////////////////////////////////////////////////
++ // Registry
++ /////////////////////////////////////////////////////////////////////////////
++
++ extern bool qax_disable_inplaceframe;
++
++ QString qax_clean_type(const QString &type, const QMetaObject *mo)
++ {
++ if (mo) {
++ int classInfoIdx = mo->indexOfClassInfo("CoClassAlias");
++ if (classInfoIdx != -1) {
++ const QMetaClassInfo classInfo = mo->classInfo(classInfoIdx);
++ return QLatin1String(classInfo.value());
++ }
++ }
++
++ QString alias(type);
++ alias.remove(QLatin1String("::"));
++ return alias;
++ }
++
++ static void UpdateRegistryKeys(bool bRegister, const QString keyPath, QScopedPointer<QSettings> & settings)
++ {
++ const QChar dot(QLatin1Char('.'));
++ const QChar slash(QLatin1Char('/'));
++ QString file = QString::fromWCharArray(qAxModuleFilename);
++ const QString module = QFileInfo(file).baseName();
++
++ const QString appId = qAxFactory()->appID().toString().toUpper();
++ const QString libId = qAxFactory()->typeLibID().toString().toUpper();
++
++ if (bRegister) {
++ settings->setValue(QLatin1String("/AppID/") + appId + QLatin1String("/."), module);
++ if (qAxOutProcServer)
++ settings->setValue(QLatin1String("/AppID/") + module + QLatin1String(".EXE/AppID"), appId);
++ else
++ settings->setValue(QLatin1String("/AppID/") + appId + QLatin1String("/DllSurrogate"), QString());
++
++ const QStringList keys = qAxFactory()->featureList();
++ for (const QString &classNameIn : keys) {
++ QObject *object = qAxFactory()->createObject(classNameIn);
++ const QMetaObject *mo = qAxFactory()->metaObject(classNameIn);
++ const QString classId = qAxFactory()->classID(classNameIn).toString().toUpper();
++
++ const QString className = qax_clean_type(classNameIn, mo);
++
++ if (object) { // don't register subobject classes
++ QString classVersion = mo ? QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("Version")).value()) : QString();
++ if (classVersion.isNull())
++ classVersion = QLatin1String("1.0");
++ bool insertable = mo && !qstricmp(mo->classInfo(mo->indexOfClassInfo("Insertable")).value(), "yes");
++ bool control = object->isWidgetType();
++ QString classMajorVersion = classVersion.left(classVersion.indexOf(dot));
++ uint olemisc = OLEMISC_SETCLIENTSITEFIRST
++ |OLEMISC_ACTIVATEWHENVISIBLE
++ |OLEMISC_INSIDEOUT
++ |OLEMISC_CANTLINKINSIDE
++ |OLEMISC_RECOMPOSEONRESIZE;
++ if (!control)
++ olemisc |= OLEMISC_INVISIBLEATRUNTIME;
++ else if (object->findChild<QMenuBar*>() && !qax_disable_inplaceframe)
++ olemisc |= OLEMISC_WANTSTOMENUMERGE;
++
++ const QString versionLessProgId = module + dot + className;
++ const QString progId = versionLessProgId + dot + classMajorVersion;
++ QString key = slash + progId;
++ settings->setValue(key + QLatin1String("/."),
++ QString(className + QLatin1String(" Class")));
++ settings->setValue(key + QLatin1String("/CLSID/."), classId);
++ if (insertable)
++ settings->setValue(key + QLatin1String("/Insertable/."), QVariant(QLatin1String("")));
++
++ key = slash + module + dot + className;
++ settings->setValue(key + QLatin1String("/."),
++ QString(className + QLatin1String(" Class")));
++ settings->setValue(key + QLatin1String("/CLSID/."), classId);
++ settings->setValue(key + QLatin1String("/CurVer/."), progId);
++
++ key = QLatin1String("/CLSID/") + classId;
++ settings->setValue(key + QLatin1String("/."),
++ QString(className + QLatin1String(" Class")));
++ settings->setValue(key + QLatin1String("/AppID"), appId);
++ if (control)
++ settings->setValue(key + QLatin1String("/Control/."), QVariant(QLatin1String("")));
++ if (insertable)
++ settings->setValue(key + QLatin1String("/Insertable/."), QVariant(QLatin1String("")));
++ if (file.endsWith(QLatin1String("dll"), Qt::CaseInsensitive))
++ settings->setValue(key + QLatin1String("/InProcServer32/."), file);
++ else
++ settings->setValue(key + QLatin1String("/LocalServer32/."),
++ QString(QLatin1Char('\"') + file + QLatin1String("\" -activex")));
++ settings->setValue(key + QLatin1String("/MiscStatus/."), control ? QLatin1String("1") : QLatin1String("0"));
++ settings->setValue(key + QLatin1String("/MiscStatus/1/."), QString::number(olemisc));
++ settings->setValue(key + QLatin1String("/Programmable/."), QVariant(QLatin1String("")));
++ settings->setValue(key + QLatin1String("/ToolboxBitmap32/."),
++ QString(QLatin1Char('\"') + file + QLatin1String("\", 101")));
++ settings->setValue(key + QLatin1String("/TypeLib/."), libId);
++ settings->setValue(key + QLatin1String("/Version/."), classVersion);
++ settings->setValue(key + QLatin1String("/VersionIndependentProgID/."), versionLessProgId);
++ settings->setValue(key + QLatin1String("/ProgID/."), progId);
++
++ QString mime = QLatin1String(mo->classInfo(mo->indexOfClassInfo("MIME")).value());
++ if (!mime.isEmpty()) {
++ QStringList mimeTypes = mime.split(QLatin1Char(';'));
++ for (qsizetype m = 0; m < mimeTypes.size(); ++m) {
++ mime = mimeTypes.at(m);
++ if (mime.isEmpty())
++ continue;
++ QString extension;
++ while (mime.contains(QLatin1Char(':'))) {
++ extension = mime.mid(mime.lastIndexOf(QLatin1Char(':')) + 1);
++ mime.chop(extension.length() + 1);
++ // Prepend '.' before extension, if required.
++ extension = extension.trimmed();
++ if (!extension.startsWith(dot))
++ extension.prepend(dot);
++ }
++
++ if (!extension.isEmpty()) {
++ key = slash + extension;
++ settings->setValue(key + QLatin1String("/."),
++ QString(module + dot + className));
++ settings->setValue(key + QLatin1String("/Content Type"), mime);
++
++ mime.replace(slash, QLatin1Char('\\'));
++ key = QLatin1String("/MIME/Database/Content Type/") + mime;
++ settings->setValue(key + QLatin1String("/CLSID"), classId);
++ settings->setValue(key + QLatin1String("/Extension"), extension);
++ }
++ }
++ }
++
++ delete object;
++ qCDebug(lcAxRegistration).nospace().noquote() << "Registered \"" << progId
++ << "\"/" << classId << ", \"" << file << "\" at \"" << keyPath
++ << "\", insertable=" << insertable << ", control=" << control
++ << ", olemisc=" << Qt::hex << Qt::showbase << olemisc
++ << ", mime=" << mime;
++ }
++
++ qAxFactory()->registerClass(classNameIn, settings.data());
++ }
++ } else {
++ settings->remove(QLatin1String("/AppID/") + appId + QLatin1String("/."));
++ if (qAxOutProcServer)
++ settings->remove(QLatin1String("/AppID/") + module + QLatin1String(".EXE"));
++ else
++ settings->remove(QLatin1String("/AppID/") + appId + QLatin1String("/DllSurrogate"));
++
++ const QStringList keys = qAxFactory()->featureList();
++ for (const QString &classNameIn : keys) {
++ const QMetaObject *mo = qAxFactory()->metaObject(classNameIn);
++ const QString classId = qAxFactory()->classID(classNameIn).toString().toUpper();
++ const QString className = qax_clean_type(classNameIn, mo);
++
++ QString classVersion = mo ? QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("Version")).value()) : QString();
++ if (classVersion.isNull())
++ classVersion = QLatin1String("1.0");
++ const QString classMajorVersion = classVersion.left(classVersion.indexOf(dot));
++
++ qAxFactory()->unregisterClass(classNameIn, settings.data());
++ const QString progId = module + dot + className + dot + classMajorVersion;
++ QString key = slash + progId;
++ settings->remove(key + QLatin1String("/CLSID/."));
++ settings->remove(key + QLatin1String("/Insertable/."));
++ settings->remove(key + QLatin1String("/."));
++ settings->remove(key);
++
++ key = slash + module + dot + className;
++ settings->remove(key + QLatin1String("/CLSID/."));
++ settings->remove(key + QLatin1String("/CurVer/."));
++ settings->remove(key + QLatin1String("/."));
++ settings->remove(key);
++
++ key = QLatin1String("/CLSID/") + classId;
++ settings->remove(key + QLatin1String("/AppID"));
++ settings->remove(key + QLatin1String("/Control/."));
++ settings->remove(key + QLatin1String("/Insertable/."));
++ settings->remove(key + QLatin1String("/InProcServer32/."));
++ settings->remove(key + QLatin1String("/LocalServer32/."));
++ settings->remove(key + QLatin1String("/MiscStatus/1/."));
++ settings->remove(key + QLatin1String("/MiscStatus/."));
++ settings->remove(key + QLatin1String("/Programmable/."));
++ settings->remove(key + QLatin1String("/ToolboxBitmap32/."));
++ settings->remove(key + QLatin1String("/TypeLib/."));
++ settings->remove(key + QLatin1String("/Version/."));
++ settings->remove(key + QLatin1String("/VersionIndependentProgID/."));
++ settings->remove(key + QLatin1String("/ProgID/."));
++ settings->remove(key + QLatin1String("/."));
++ settings->remove(key);
++
++ QString mime = QLatin1String(mo->classInfo(mo->indexOfClassInfo("MIME")).value());
++ if (!mime.isEmpty()) {
++ QStringList mimeTypes = mime.split(QLatin1Char(';'));
++ for (qsizetype m = 0; m < mimeTypes.size(); ++m) {
++ mime = mimeTypes.at(m);
++ if (mime.isEmpty())
++ continue;
++ QString extension;
++ while (mime.contains(QLatin1Char(':'))) {
++ extension = mime.mid(mime.lastIndexOf(QLatin1Char(':')) + 1);
++ mime.chop(extension.length() + 1);
++ // Prepend '.' before extension, if required.
++ extension = extension.trimmed();
++ if (extension[0] != dot)
++ extension.prepend(dot);
++ }
++ if (!extension.isEmpty()) {
++ key = slash + extension;
++ settings->remove(key + QLatin1String("/Content Type"));
++ settings->remove(key + QLatin1String("/."));
++ settings->remove(key);
++ mime.replace(slash, QLatin1Char('\\'));
++ key = QLatin1String("/MIME/Database/Content Type/") + mime;
++ settings->remove(key + QLatin1String("/Extension"));
++ settings->remove(key + QLatin1String("/CLSID"));
++ settings->remove(key + QLatin1String("/."));
++ settings->remove(key);
++ }
++ }
++ }
++ qCDebug(lcAxRegistration).nospace().noquote() << "Unregistered \""
++ << progId << "\"/" << classId << ", \"" << file << "\" from \""
++ << keyPath << '"';
++ }
++ }
++ }
++
++ // (Un)Register the ActiveX server in the registry.
++ // The QAxFactory implementation provides the information.
++ HRESULT UpdateRegistry(bool bRegister, bool perUser)
++ {
++ qAxIsServer = false;
++ QString file = QString::fromWCharArray(qAxModuleFilename);
++ const QString module = QFileInfo(file).baseName();
++
++ QString libFile = qAxInit();
++ auto libFile_cleanup = qScopeGuard([] { qAxCleanup(); });
++
++ TLIBATTR *libAttr = nullptr;
++ if (qAxTypeLibrary)
++ qAxTypeLibrary->GetLibAttr(&libAttr);
++ if (!libAttr)
++ return SELFREG_E_TYPELIB;
++ auto libAttr_cleanup = qScopeGuard([libAttr] { qAxTypeLibrary->ReleaseTLibAttr(libAttr); });
++
++ if (bRegister) {
++ if (!perUser) {
++ HRESULT hr = RegisterTypeLib(qAxTypeLibrary.Get(), qaxQString2MutableOleChars(libFile), nullptr);
++ if (FAILED(hr)) {
++ qWarning("Failing to register %s due to insufficient permission.", qPrintable(module));
++ return hr;
++ }
++ } else {
++ #ifndef Q_CC_MINGW
++ // MinGW does not have RegisterTypeLibForUser() implemented so we cannot fallback in this case
++ RegisterTypeLibForUser(qAxTypeLibrary.Get(), qaxQString2MutableOleChars(libFile), nullptr);
++ #endif
++ }
++ } else {
++ if (!perUser) {
++ HRESULT hr = UnRegisterTypeLib(libAttr->guid, libAttr->wMajorVerNum, libAttr->wMinorVerNum, libAttr->lcid, libAttr->syskind);
++ if (FAILED(hr)) {
++ qWarning("Failing to register %s due to insufficient permission.", qPrintable(module));
++ return hr;
++ }
++ } else {
++ #ifndef Q_CC_MINGW
++ // MinGW does not have RegisterTypeLibForUser() implemented so we cannot fallback in this case
++ UnRegisterTypeLibForUser(libAttr->guid, libAttr->wMajorVerNum, libAttr->wMinorVerNum, libAttr->lcid, libAttr->syskind);
++ #endif
++ }
++ }
++
++ QString keyPath(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"));
++ if (perUser)
++ keyPath = QLatin1String("HKEY_CURRENT_USER\\Software\\Classes");
++ QScopedPointer<QSettings> settings(new QSettings(keyPath, QSettings::NativeFormat));
++
++ // we try to create the ActiveX widgets later on...
++ bool delete_qApp = false;
++ if (!qApp) {
++ (void)new QApplication(__argc, __argv);
++ delete_qApp = true;
++ }
++
++ UpdateRegistryKeys(bRegister, keyPath, settings);
++
++ if (delete_qApp)
++ delete qApp;
++
++ if (settings->status() == QSettings::NoError)
++ return S_OK;
++ qWarning() << module << ": Error writing to " << keyPath;
++ return SELFREG_E_CLASS;
++ }
++
++ /////////////////////////////////////////////////////////////////////////////
++ // IDL generator
++ /////////////////////////////////////////////////////////////////////////////
++
++ static QByteArrayList enums;
++ static QByteArrayList enumValues;
++ static QByteArrayList subtypes;
++
++ static const char* const type_map[][2] =
++ {
++ // QVariant/Qt Value data types
++ { "QString", "BSTR" },
++ { "QCString", "BSTR" },
++ { "bool", "VARIANT_BOOL" },
++ { "char", "char" },
++ { "unsigned char", "unsigned char" },
++ { "uchar", "unsigned char" },
++ { "short", "short" },
++ { "unsigned short", "unsigned short" },
++ { "ushort", "unsigned short" },
++ { "int", "int" },
++ { "void", "void" },
++ { "uint", "unsigned int" },
++ { "float", "float" },
++ { "double", "double" },
++ { "QColor", "OLE_COLOR" },
++ { "QDate", "DATE" },
++ { "QTime", "DATE" },
++ { "QDateTime", "DATE" },
++ { "QFont", "IFontDisp*" },
++ { "QPixmap", "IPictureDisp*" },
++ { "QVariant", "VARIANT" },
++ { "QVariantList", "SAFEARRAY(VARIANT)" },
++ { "QList<QVariant>", "SAFEARRAY(VARIANT)" },
++ { "quint64", "unsigned hyper" },
++ { "qint64", "hyper" },
++ { "qulonglong", "unsigned hyper" },
++ { "qlonglong", "hyper" },
++ { "QByteArray", "SAFEARRAY(BYTE)" },
++ { "QStringList", "SAFEARRAY(BSTR)" },
++ // Userdefined Qt datatypes - some not on Borland though
++ { "QCursor", "enum MousePointer" },
++ { "Qt::FocusPolicy", "enum FocusPolicy" },
++ #if __REQUIRED_RPCNDR_H_VERSION__ >= Q_REQUIRED_RPCNDR_H_VERSION
++ { "QRect", "struct QRect" },
++ { "QSize", "struct QSize" },
++ { "QPoint", "struct QPoint" },
++ #endif // __REQUIRED_RPCNDR_H_VERSION__ >= Q_REQUIRED_RPCNDR_H_VERSION
++ // And we support COM data types
++ { "BOOL", "BOOL" },
++ { "BSTR", "BSTR" },
++ { "OLE_COLOR", "OLE_COLOR" },
++ { "DATE", "DATE" },
++ { "VARIANT", "VARIANT" },
++ { "IDispatch", "IDispatch*" },
++ { "IUnknown", "IUnknown*" },
++ { "IDispatch*", "IDispatch*" },
++ { "IUnknown*", "IUnknown*" },
++ { nullptr, nullptr }
++ };
++
++ static QByteArray convertTypes(const QByteArray &qtype, bool *ok)
++ {
++ qRegisterMetaType<IDispatch *>("IDispatch*");
++ qRegisterMetaType<IUnknown *>("IUnknown*");
++
++ *ok = false;
++
++ int i = 0;
++ while (type_map[i][0]) {
++ if (qtype == type_map[i][0] && type_map[i][1]) {
++ *ok = true;
++ return type_map[i][1];
++ }
++ ++i;
++ }
++ if (enums.contains(qtype)) {
++ *ok = true;
++ return "enum " + qtype;
++ }
++ if (subtypes.contains(qtype)) {
++ *ok = true;
++ } else if (qtype.endsWith('*')) {
++ QByteArray cleanType = qtype.left(qtype.length() - 1);
++ const QMetaObject *mo = qAxFactory()->metaObject(QString::fromLatin1(cleanType.constData()));
++ if (mo) {
++ cleanType = qax_clean_type(QString::fromLatin1(cleanType), mo).toLatin1();
++ if (subtypes.contains(cleanType)) {
++ *ok = true;
++ return cleanType + '*';
++ }
++ }
++ }
++ return qtype;
++ }
++
++ static const char* const keyword_map[][2] =
++ {
++ { "aggregatable", "aggregating" },
++ { "allocate", "alloc" },
++ { "appobject", "appObject" },
++ { "arrays", "array" },
++ { "async", "asynchronous" },
++ { "bindable", "binding" },
++ { "Boolean", "boolval" },
++ { "boolean", "boolval" },
++ { "broadcast", "broadCast" },
++ { "callback", "callBack" },
++ { "decode", "deCode" },
++ { "default", "defaulted" },
++ { "defaultbind", "defaultBind" },
++ { "defaultvalue", "defaultValue" },
++ { "encode" "enCode" },
++ { "endpoint", "endPoint" },
++ { "hidden", "isHidden" },
++ { "ignore", "ignore_" },
++ { "local", "local_" },
++ { "notify", "notify_" },
++ { "object", "object_" },
++ { "optimize", "optimize_" },
++ { "optional", "optional_" },
++ { "out", "out_" },
++ { "pipe", "pipe_" },
++ { "proxy", "proxy_" },
++ { "ptr", "pointer" },
++ { "readonly", "readOnly" },
++ { "small", "small_" },
++ { "source", "source_" },
++ { "string", "string_" },
++ { "uuid", "uuid_" },
++ { nullptr, nullptr }
++ };
++
++ static QByteArray replaceKeyword(const QByteArray &name)
++ {
++ int i = 0;
++ while (keyword_map[i][0]) {
++ if (name == keyword_map[i][0] && keyword_map[i][1])
++ return keyword_map[i][1];
++ ++i;
++ }
++ return name;
++ }
++
++ static QMap<QByteArray, int> mapping;
++
++ static QByteArray renameOverloads(const QByteArray &name)
++ {
++ QByteArray newName = name;
++
++ if (mapping.contains(name)) {
++ int n = mapping.value(name);
++ newName = name + '_' + QByteArray::number(n);
++ mapping.insert(name, n+1);
++ } else {
++ mapping.insert(name, 1);
++ }
++
++ return newName;
++ }
++
++ // filter out some properties
++ static const char* const ignore_props[] =
++ {
++ "name",
++ "objectName",
++ "isTopLevel",
++ "isDialog",
++ "isModal",
++ "isPopup",
++ "isDesktop",
++ "geometry",
++ "pos",
++ "frameSize",
++ "frameGeometry",
++ "size",
++ "sizeHint",
++ "minimumSizeHint",
++ "microFocusHint",
++ "rect",
++ "childrenRect",
++ "childrenRegion",
++ "minimumSize",
++ "maximumSize",
++ "sizeIncrement",
++ "baseSize",
++ "ownPalette",
++ "ownFont",
++ "ownCursor",
++ "visibleRect",
++ "isActiveWindow",
++ "underMouse",
++ "visible",
++ "hidden",
++ "minimized",
++ "focus",
++ "focusEnabled",
++ "customWhatsThis",
++ "shown",
++ "windowOpacity",
++ nullptr
++ };
++
++ // filter out some slots
++ static const char* const ignore_slots[] =
++ {
++ "deleteLater",
++ "setMouseTracking",
++ "update",
++ "repaint",
++ "iconify",
++ "showMinimized",
++ "showMaximized",
++ "showFullScreen",
++ "showNormal",
++ "polish",
++ "constPolish",
++ "stackUnder",
++ "setShown",
++ "setHidden",
++ "move_1",
++ "resize_1",
++ "setGeometry_1",
++ nullptr
++ };
++
++ static bool ignore(const char *test, const char *const *table)
++ {
++ if (!test)
++ return true;
++ int i = 0;
++ while (table[i]) {
++ if (!strcmp(test, table[i]))
++ return true;
++ ++i;
++ }
++ return false;
++ }
++
++ bool ignoreSlots(const char *test)
++ {
++ return ignore(test, ignore_slots);
++ }
++
++ bool ignoreProps(const char *test)
++ {
++ return ignore(test, ignore_props);
++ }
++
++ static QString stripCurlyBraces(const QUuid &uuid)
++ {
++ if (uuid.isNull())
++ return QString();
++ QString result = uuid.toString().toUpper();
++ result.chop(1);
++ result.remove(0, 1);
++ return result;
++ }
++
++ static QByteArray prototype(const QByteArrayList ¶meterTypes, const QByteArrayList ¶meterNames, bool *ok)
++ {
++ QByteArray prototype;
++
++ for (qsizetype p = 0; p < parameterTypes.size() && *ok; ++p) {
++ bool out = false;
++ QByteArray type(parameterTypes.at(p));
++ const QByteArray &name = parameterNames.at(p);
++
++ if (type.endsWith('&')) {
++ out = true;
++ type.truncate(type.length() - 1);
++ } else if (type.endsWith("**")) {
++ out = true;
++ type.truncate(type.length() - 1);
++ } else if (type.endsWith('*') && !subtypes.contains(type)) {
++ type.truncate(type.length() - 1);
++ }
++ if (type.isEmpty()) {
++ *ok = false;
++ break;
++ }
++ type = convertTypes(type, ok);
++ if (!out)
++ prototype += "[in] " + type + ' ';
++ else
++ prototype += "[in,out] " + type + ' ';
++
++ if (out)
++ prototype += '*';
++ if (name.isEmpty())
++ prototype += 'p' + QByteArray::number(p);
++ else
++ prototype += "p_" + replaceKeyword(name);
++
++ if (p < parameterTypes.size() - 1)
++ prototype += ", ";
++ }
++
++ return prototype;
++ }
++
++ static QByteArray addDefaultArguments(const QByteArray &prototype, int numDefArgs)
++ {
++ // nothing to do, or unsupported anyway
++ if (!numDefArgs || prototype.contains("/**"))
++ return prototype;
++
++ QByteArray ptype(prototype);
++ int in = -1;
++ while (numDefArgs) {
++ in = ptype.lastIndexOf(']', in);
++ ptype.replace(in, 1, ",optional]");
++ in = ptype.indexOf(' ', in) + 1;
++ QByteArray type = ptype.mid(in, ptype.indexOf(' ', in) - in);
++ if (type == "enum")
++ type += ' ' + ptype.mid(in + 5, ptype.indexOf(' ', in + 5) - in - 5);
++ if (type == "struct")
++ type += ' ' + ptype.mid(in + 7, ptype.indexOf(' ', in + 7) - in - 7);
++ const QByteArray replacement = QByteArray("VARIANT /*was: ") + type + "*/";
++ ptype.replace(in, type.length(), replacement);
++ --numDefArgs;
++ }
++
++ return ptype;
++ }
++
++ static HRESULT classIDL(QObject *o, const QMetaObject *mo, const QString &className, bool isBindable, QTextStream &out)
++ {
++ int id = 1;
++ int i = 0;
++ if (!mo)
++ return 3;
++
++ QString topclass = qAxFactory()->exposeToSuperClass(className);
++ if (topclass.isEmpty())
++ topclass = QLatin1String("QObject");
++ bool hasStockEvents = qAxFactory()->hasStockEvents(className);
++
++ const QMetaObject *pmo = mo;
++ do {
++ pmo = pmo->superClass();
++ } while (pmo && topclass != QString::fromLatin1(pmo->className()));
++
++ int enumoff = pmo ? pmo->enumeratorOffset() : mo->enumeratorOffset();
++ int methodoff = pmo ? pmo->methodOffset() : mo->methodOffset();
++ int propoff = pmo ? pmo->propertyOffset() : mo->propertyOffset();
++
++ int qtProps = 0;
++ int qtSlots = 0;
++
++ bool control = false;
++
++ if (o && o->isWidgetType()) {
++ qtProps = QWidget::staticMetaObject.propertyCount();
++ qtSlots = QWidget::staticMetaObject.methodCount();
++ control = true;
++ }
++
++ const QString classID = stripCurlyBraces(qAxFactory()->classID(className));
++ if (classID.isEmpty())
++ return 4;
++ const QString interfaceID = stripCurlyBraces(qAxFactory()->interfaceID(className));
++ if (interfaceID.isEmpty())
++ return 5;
++ const QString eventsID = stripCurlyBraces(qAxFactory()->eventsID(className));
++ const bool hasEvents = !eventsID.isEmpty();
++
++ QString cleanClassName = qax_clean_type(className, mo);
++ QString defProp(QLatin1String(mo->classInfo(mo->indexOfClassInfo("DefaultProperty")).value()));
++ QString defSignal(QLatin1String(mo->classInfo(mo->indexOfClassInfo("DefaultSignal")).value()));
++
++ for (i = enumoff; i < mo->enumeratorCount(); ++i) {
++ const QMetaEnum enumerator = mo->enumerator(i);
++ if (enums.contains(enumerator.name()))
++ continue;
++
++ enums.append(enumerator.name());
++
++ out << "\tenum " << enumerator.name() << " {" << Qt::endl;
++
++ for (int j = 0; j < enumerator.keyCount(); ++j) {
++ QByteArray key(enumerator.key(j));
++ while (enumValues.contains(key)) {
++ key += '_';
++ }
++ enumValues.append(key);
++ const uint value = uint(enumerator.value(j));
++ key = key.leftJustified(20);
++ out << "\t\t" << key << "\t= ";
++ if (enumerator.isFlag())
++ out << "0x" << QByteArray::number(value, 16).rightJustified(8, '0');
++ else
++ out << value;
++ if (j < enumerator.keyCount()-1)
++ out << ", ";
++ out << Qt::endl;
++ }
++ out << "\t};" << Qt::endl << Qt::endl;
++ }
++
++ // mouse cursor enum for QCursor support
++ if (!enums.contains("MousePointer")) {
++ enums.append("MousePointer");
++ out << "\tenum MousePointer {" << Qt::endl;
++ out << "\t\tArrowCursor = " << Qt::ArrowCursor << ',' << Qt::endl;
++ out << "\t\tUpArrowCursor = " << Qt::UpArrowCursor << ',' << Qt::endl;
++ out << "\t\tCrossCursor = " << Qt::CrossCursor << ',' << Qt::endl;
++ out << "\t\tWaitCursor = " << Qt::WaitCursor << ',' << Qt::endl;
++ out << "\t\tIBeamCursor = " << Qt::IBeamCursor << ',' << Qt::endl;
++ out << "\t\tSizeVerCursor = " << Qt::SizeVerCursor << ',' << Qt::endl;
++ out << "\t\tSizeHorCursor = " << Qt::SizeHorCursor << ',' << Qt::endl;
++ out << "\t\tSizeBDiagCursor = " << Qt::SizeBDiagCursor << ',' << Qt::endl;
++ out << "\t\tSizeFDiagCursor = " << Qt::SizeFDiagCursor << ',' << Qt::endl;
++ out << "\t\tSizeAllCursor = " << Qt::SizeAllCursor << ',' << Qt::endl;
++ out << "\t\tBlankCursor = " << Qt::BlankCursor << ',' << Qt::endl;
++ out << "\t\tSplitVCursor = " << Qt::SplitVCursor << ',' << Qt::endl;
++ out << "\t\tSplitHCursor = " << Qt::SplitHCursor << ',' << Qt::endl;
++ out << "\t\tPointingHandCursor = " << Qt::PointingHandCursor << ',' << Qt::endl;
++ out << "\t\tForbiddenCursor = " << Qt::ForbiddenCursor << ',' << Qt::endl;
++ out << "\t\tWhatsThisCursor = " << Qt::WhatsThisCursor << ',' << Qt::endl;
++ out << "\t\tBusyCursor\t= " << Qt::BusyCursor << Qt::endl;
++ out << "\t};" << Qt::endl << Qt::endl;
++ }
++ if (!enums.contains("FocusPolicy")) {
++ enums.append("FocusPolicy");
++ out << "\tenum FocusPolicy {" << Qt::endl;
++ out << "\t\tNoFocus = " << Qt::NoFocus << ',' << Qt::endl;
++ out << "\t\tTabFocus = " << Qt::TabFocus << ',' << Qt::endl;
++ out << "\t\tClickFocus = " << Qt::ClickFocus << ',' << Qt::endl;
++ out << "\t\tStrongFocus = " << Qt::StrongFocus << ',' << Qt::endl;
++ out << "\t\tWheelFocus = " << Qt::WheelFocus << Qt::endl;
++ out << "\t};" << Qt::endl << Qt::endl;
++ }
++
++ out << Qt::endl;
++ out << "\t[" << Qt::endl;
++ out << "\t\tuuid(" << interfaceID << ")," << Qt::endl;
++ out << "\t\thelpstring(\"" << cleanClassName << " Interface\")" << Qt::endl;
++ out << "\t]" << Qt::endl;
++ out << "\tdispinterface I" << cleanClassName << Qt::endl;
++ out << "\t{" << Qt::endl;
++
++ out << "\tproperties:" << Qt::endl;
++ for (i = propoff; i < mo->propertyCount(); ++i) {
++ const QMetaProperty property = mo->property(i);
++ /* if (property.testFlags(QMetaProperty::Override))
++ continue;*/
++ if (i <= qtProps && ignoreProps(property.name()))
++ continue;
++ if (!property.name() || mo->indexOfProperty(property.name()) > i)
++ continue;
++
++ bool ok = true;
++ QByteArray type(convertTypes(property.typeName(), &ok));
++ QByteArray name(replaceKeyword(property.name()));
++
++ if (!ok)
++ out << "\t/****** Property is of unsupported datatype" << Qt::endl;
++
++ out << "\t\t[id(" << id << ')';
++ if (!property.isWritable())
++ out << ", readonly";
++ if (isBindable && property.isScriptable())
++ out << ", bindable";
++ if (!property.isDesignable())
++ out << ", nonbrowsable";
++ if (isBindable)
++ out << ", requestedit";
++ if (defProp == QLatin1String(name))
++ out << ", uidefault";
++ out << "] " << type << ' ' << name << ';' << Qt::endl;
++
++ if (!ok)
++ out << "\t******/" << Qt::endl;
++ ++id;
++ }
++ out << Qt::endl;
++ out << "\tmethods:" << Qt::endl;
++ int numDefArgs = 0;
++ QByteArray outBuffer;
++ for (i = methodoff; i < mo->methodCount(); ++i) {
++ const QMetaMethod slot = mo->method(i);
++ if (slot.access() != QMetaMethod::Public || slot.methodType() == QMetaMethod::Signal)
++ continue;
++
++ if (slot.attributes() & QMetaMethod::Cloned) {
++ ++numDefArgs;
++ continue;
++ }
++ if (!outBuffer.isEmpty()) {
++ outBuffer = addDefaultArguments(outBuffer, numDefArgs);
++ numDefArgs = 0;
++ out << outBuffer;
++ outBuffer = QByteArray();
++ }
++
++ QByteArray signature(slot.methodSignature());
++ QByteArray name(signature.left(signature.indexOf('(')));
++ if (i <= qtSlots && ignoreSlots(name))
++ continue;
++
++ signature.remove(0, name.length() + 1);
++ signature.truncate(signature.length() - 1);
++ name = renameOverloads(replaceKeyword(name));
++ if (ignoreSlots(name))
++ continue;
++
++ const auto parameterTypes = slot.parameterTypes();
++ const auto parameterNames = slot.parameterNames();
++
++ bool ok = true;
++ QByteArray type = slot.typeName();
++ if (type.isEmpty())
++ type = "void";
++ else
++ type = convertTypes(type, &ok);
++
++ QByteArray ptype(prototype(parameterTypes, parameterNames, &ok));
++ if (!ok)
++ outBuffer += "\t/****** Slot parameter uses unsupported datatype\n";
++
++ outBuffer += "\t\t[id(" + QByteArray::number(id) + ")] " + type + ' '
++ + name + '(' + ptype + ");\n";
++
++ if (!ok)
++ outBuffer += "\t******/\n";
++ ++id;
++ }
++ if (!outBuffer.isEmpty()) {
++ outBuffer = addDefaultArguments(outBuffer, numDefArgs);
++ numDefArgs = 0;
++ out << outBuffer;
++ outBuffer = QByteArray();
++ }
++ out << "\t};" << Qt::endl << Qt::endl;
++
++ mapping.clear();
++ id = 1;
++
++ if (hasEvents) {
++ out << "\t[" << Qt::endl;
++ out << "\t\tuuid(" << eventsID << ")," << Qt::endl;
++ out << "\t\thelpstring(\"" << cleanClassName << " Events Interface\")" << Qt::endl;
++ out << "\t]" << Qt::endl;
++ out << "\tdispinterface I" << cleanClassName << "Events" << Qt::endl;
++ out << "\t{" << Qt::endl;
++ out << "\tproperties:" << Qt::endl;
++ out << "\tmethods:" << Qt::endl;
++
++ if (hasStockEvents) {
++ out << "\t/****** Stock events ******/" << Qt::endl;
++ out << "\t\t[id(DISPID_CLICK)] void Click();" << Qt::endl;
++ out << "\t\t[id(DISPID_DBLCLICK)] void DblClick();" << Qt::endl;
++ out << "\t\t[id(DISPID_KEYDOWN)] void KeyDown(short* KeyCode, short Shift);" << Qt::endl;
++ out << "\t\t[id(DISPID_KEYPRESS)] void KeyPress(short* KeyAscii);" << Qt::endl;
++ out << "\t\t[id(DISPID_KEYUP)] void KeyUp(short* KeyCode, short Shift);" << Qt::endl;
++ out << "\t\t[id(DISPID_MOUSEDOWN)] void MouseDown(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << Qt::endl;
++ out << "\t\t[id(DISPID_MOUSEMOVE)] void MouseMove(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << Qt::endl;
++ out << "\t\t[id(DISPID_MOUSEUP)] void MouseUp(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << Qt::endl << Qt::endl;
++ }
++
++ for (i = methodoff; i < mo->methodCount(); ++i) {
++ const QMetaMethod signal = mo->method(i);
++ if (signal.methodType() != QMetaMethod::Signal)
++ continue;
++
++ QByteArray signature(signal.methodSignature());
++ QByteArray name(signature.left(signature.indexOf('(')));
++ signature.remove(0, name.length() + 1);
++ signature.truncate(signature.length() - 1);
++
++ const auto parameterTypes = signal.parameterTypes();
++ const auto parameterNames = signal.parameterNames();
++
++ bool isDefault = defSignal == QLatin1String(name);
++ name = renameOverloads(replaceKeyword(name));
++ bool ok = true;
++
++ QByteArray type = signal.typeName();
++ if (!type.isEmpty() && type != "void") // signals with return value not supported
++ continue;
++
++ QByteArray ptype(prototype(parameterTypes, parameterNames, &ok));
++ if (!ok)
++ out << "\t/****** Signal parameter uses unsupported datatype" << Qt::endl;
++
++ out << "\t\t[id(" << id << ')';
++ if (isDefault)
++ out << ", uidefault";
++ out << "] void " << name << '(' << ptype << ");" << Qt::endl;
++
++ if (!ok)
++ out << "\t******/" << Qt::endl;
++ ++id;
++ }
++ out << "\t};" << Qt::endl << Qt::endl;
++ }
++
++ out << "\t[" << Qt::endl;
++
++ if (qstricmp(mo->classInfo(mo->indexOfClassInfo("Aggregatable")).value(), "no"))
++ out << "\t\taggregatable," << Qt::endl;
++ if (!qstricmp(mo->classInfo(mo->indexOfClassInfo("RegisterObject")).value(), "yes"))
++ out << "\t\tappobject," << Qt::endl;
++ if (mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value())
++ out << "\t\tlicensed," << Qt::endl;
++ const char *helpString = mo->classInfo(mo->indexOfClassInfo("Description")).value();
++ if (helpString)
++ out << "\t\thelpstring(\"" << helpString << "\")," << Qt::endl;
++ else
++ out << "\t\thelpstring(\"" << cleanClassName << " Class\")," << Qt::endl;
++ const char *classVersion = mo->classInfo(mo->indexOfClassInfo("Version")).value();
++ if (classVersion)
++ out << "\t\tversion(" << classVersion << ")," << Qt::endl;
++ out << "\t\tuuid(" << classID << ')';
++ if (control) {
++ out << ", " << Qt::endl;
++ out << "\t\tcontrol";
++ } else if (!o) {
++ out << ", " << Qt::endl;
++ out << "\t\tnoncreatable";
++ }
++ out << Qt::endl;
++ out << "\t]" << Qt::endl;
++ out << "\tcoclass " << cleanClassName << Qt::endl;
++ out << "\t{" << Qt::endl;
++ out << "\t\t[default] dispinterface I" << cleanClassName << ';' << Qt::endl;
++ if (hasEvents)
++ out << "\t\t[default, source] dispinterface I" << cleanClassName << "Events;" << Qt::endl;
++ out << "\t};" << Qt::endl;
++
++ return S_OK;
++ }
++
++ extern "C" HRESULT __stdcall DumpIDL(const QString &outfile, const QString &ver)
++ {
++ qAxIsServer = false;
++ QTextStream out;
++ if (outfile.contains(QLatin1String("\\"))) {
++ QString outpath = outfile.left(outfile.lastIndexOf(QLatin1String("\\")));
++ QDir dir;
++ dir.mkpath(outpath);
++ }
++ QFile file(outfile);
++ file.remove();
++
++ QString filebase = QString::fromWCharArray(qAxModuleFilename);
++ filebase.truncate(filebase.lastIndexOf(QLatin1Char('.')));
++
++ const QString appID = stripCurlyBraces(qAxFactory()->appID());
++ if (appID.isEmpty())
++ return 1;
++ const QString typeLibID = stripCurlyBraces(qAxFactory()->typeLibID());
++ if (typeLibID.isEmpty())
++ return 2;
++ QString typelib = filebase.right(filebase.length() - filebase.lastIndexOf(QLatin1String("\\"))-1);
++
++ if (!file.open(QIODevice::WriteOnly))
++ return -1;
++
++ out.setDevice(&file);
++
++ QString version(ver.unicode(), ver.length());
++ // truncate "major.minor.patch.build" version string to "major.minor"
++ while (version.count(QLatin1Char('.')) > 1) {
++ int lastdot = version.lastIndexOf(QLatin1Char('.'));
++ version.truncate(lastdot);
++ }
++ if (version.isEmpty())
++ version = QLatin1String("1.0");
++
++ const QString idQRect = stripCurlyBraces(QUuid(CLSID_QRect));
++ const QString idQSize = stripCurlyBraces(QUuid(CLSID_QSize));
++ const QString idQPoint = stripCurlyBraces(QUuid(CLSID_QPoint));
++
++ out << "/****************************************************************************" << Qt::endl;
++ out << "** Interface definition generated for ActiveQt project" << Qt::endl;
++ out << "**" << Qt::endl;
++ out << "** '" << QString::fromWCharArray(qAxModuleFilename) << '\'' << Qt::endl;
++ out << "**" << Qt::endl;
++ out << "** Created: " << QDateTime::currentDateTime().toString() << Qt::endl;
++ out << "**" << Qt::endl;
++ out << "** WARNING! All changes made in this file will be lost!" << Qt::endl;
++ out << "****************************************************************************/" << Qt::endl << Qt::endl;
++
++ out << "import \"ocidl.idl\";" << Qt::endl;
++ out << "#include <olectl.h>" << Qt::endl << Qt::endl;
++
++ // dummy application to create widgets
++ bool delete_qApp = false;
++ if (!qApp) {
++ (void)new QApplication(__argc, __argv);
++ delete_qApp = true;
++ }
++
++ out << '[' << Qt::endl;
++ out << "\tuuid(" << typeLibID << ")," << Qt::endl;
++ out << "\tversion(" << version << ")," << Qt::endl;
++ out << "\thelpstring(\"" << typelib << ' ' << version << " Type Library\")" << Qt::endl;
++ out << ']' << Qt::endl;
++ out << "library " << typelib << "Lib" << Qt::endl;
++ out << '{' << Qt::endl;
++ out << "\timportlib(\"stdole32.tlb\");" << Qt::endl;
++ out << "\timportlib(\"stdole2.tlb\");" << Qt::endl << Qt::endl;
++
++ const QStringList keys = qAxFactory()->featureList();
++
++ out << "\t/************************************************************************" << Qt::endl;
++ out << "\t** If this causes a compile error in MIDL you need to upgrade the" << Qt::endl;
++ out << "\t** Platform SDK you are using. Download the SDK from msdn.microsoft.com" << Qt::endl;
++ out << "\t** and make sure that both the system and the Visual Studio environment" << Qt::endl;
++ out << "\t** use the correct files." << Qt::endl;
++ out << "\t**" << Qt::endl;
++
++ #if __REQUIRED_RPCNDR_H_VERSION__ < Q_REQUIRED_RPCNDR_H_VERSION
++ out << "\t** Required version of MIDL could not be verified. QRect, QSize and QPoint" << Qt::endl;
++ out << "\t** support needs an updated Platform SDK to be installed." << Qt::endl;
++ out << "\t*************************************************************************" << Qt::endl;
++ #else
++ out << "\t************************************************************************/" << Qt::endl;
++ #endif
++
++ out << Qt::endl;
++ out << "\t[uuid(" << idQRect << ")]" << Qt::endl;
++ out << "\tstruct QRect {" << Qt::endl;
++ out << "\t\tint left;" << Qt::endl;
++ out << "\t\tint top;" << Qt::endl;
++ out << "\t\tint right;" << Qt::endl;
++ out << "\t\tint bottom;" << Qt::endl;
++ out << "\t};" << Qt::endl << Qt::endl;
++
++ out << "\t[uuid(" << idQSize << ")]" << Qt::endl;
++ out << "\tstruct QSize {" << Qt::endl;
++ out << "\t\tint width;" << Qt::endl;
++ out << "\t\tint height;" << Qt::endl;
++ out << "\t};" << Qt::endl << Qt::endl;
++
++ out << "\t[uuid(" << idQPoint << ")]" << Qt::endl;
++ out << "\tstruct QPoint {" << Qt::endl;
++ out << "\t\tint x;" << Qt::endl;
++ out << "\t\tint y;" << Qt::endl;
++ out << "\t};" << Qt::endl;
++ #if __REQUIRED_RPCNDR_H_VERSION__ < Q_REQUIRED_RPCNDR_H_VERSION
++ out << "\t*/" << Qt::endl;
++ #endif
++ out << Qt::endl;
++
++ out << "\t/* Forward declaration of classes that might be used as parameters */" << Qt::endl << Qt::endl;
++
++ int res = S_OK;
++ for (const QString &className : keys) {
++ const QMetaObject *mo = qAxFactory()->metaObject(className);
++ // We have meta object information for this type. Forward declare it.
++ if (mo) {
++ QByteArray cleanType = qax_clean_type(className, mo).toLatin1();
++ out << "\tcoclass " << cleanType << ';' << Qt::endl;
++ subtypes.append(cleanType);
++ if (QMetaType::fromName(cleanType).id() == QMetaType::UnknownType)
++ qRegisterMetaType<void *>(cleanType);
++ cleanType += '*';
++ subtypes.append(cleanType);
++ if (QMetaType::fromName(cleanType).id() == QMetaType::UnknownType)
++ qRegisterMetaType<void *>(cleanType);
++ }
++ }
++ out << Qt::endl;
++
++ for (const QString &className : keys) {
++ const QMetaObject *mo = qAxFactory()->metaObject(className);
++ // We have meta object information for this type. Define it.
++ if (mo) {
++ QObject *o = qAxFactory()->createObject(className);
++ // It's not a control class, so it is actually a subtype. Define it.
++ if (!o)
++ res = classIDL(nullptr, mo, className, false, out);
++ delete o;
++ }
++ }
++
++ out << Qt::endl;
++ if (res != S_OK)
++ goto ErrorInClass;
++
++ for (const QString &className : keys) {
++ QObject *o = qAxFactory()->createObject(className);
++ if (!o)
++ continue;
++ const QMetaObject *mo = o->metaObject();
++ QAxBindable *bind = static_cast<QAxBindable *>(o->qt_metacast("QAxBindable"));
++ bool isBindable = bind != nullptr;
++
++ const QByteArray cleanType = qax_clean_type(className, mo).toLatin1();
++ subtypes.append(cleanType);
++ subtypes.append(cleanType + '*');
++ res = classIDL(o, mo, className, isBindable, out);
++ delete o;
++ if (res != S_OK)
++ break;
++ }
++
++ out << "};" << Qt::endl;
++ out.flush();
++
++ ErrorInClass:
++ if (delete_qApp)
++ delete qApp;
++
++ if (res != S_OK) {
++ file.close();
++ file.remove();
++ }
++
++ return res;
++ }
++
++ QT_END_NAMESPACE