mirror of https://github.com/qt/qtactiveqt.git
1169 lines
46 KiB
C++
1169 lines
46 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "moc.h"
|
|
|
|
#include <QAxObject>
|
|
#include <QAxBaseWidget>
|
|
#include <QFile>
|
|
#include <QMetaObject>
|
|
#include <QMetaEnum>
|
|
#include <QDebug>
|
|
#include <QTextStream>
|
|
#include <QSettings>
|
|
#include <QStringList>
|
|
#include <QTemporaryFile>
|
|
#include <QUuid>
|
|
#include <QCoreApplication>
|
|
#include <QCommandLineOption>
|
|
#include <QCommandLineParser>
|
|
#include <QFileInfo>
|
|
#include <qt_windows.h>
|
|
#include <ocidl.h>
|
|
#include <private/qmetaobject_p.h>
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
static ITypeInfo *currentTypeInfo = nullptr;
|
|
|
|
enum ProgramMode {
|
|
GenerateMode,
|
|
TypeLibID
|
|
};
|
|
|
|
enum ObjectCategory
|
|
{
|
|
DefaultObject = 0x00,
|
|
SubObject = 0x001,
|
|
ActiveX = 0x002,
|
|
NoMetaObject = 0x004,
|
|
NoImplementation = 0x008,
|
|
NoDeclaration = 0x010,
|
|
NoInlines = 0x020,
|
|
OnlyInlines = 0x040,
|
|
Licensed = 0x100,
|
|
};
|
|
|
|
Q_DECLARE_FLAGS(ObjectCategories, ObjectCategory)
|
|
Q_DECLARE_OPERATORS_FOR_FLAGS(ObjectCategories)
|
|
|
|
extern QMetaObject *qax_readEnumInfo(ITypeLib *typeLib, const QMetaObject *parentObject);
|
|
extern QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject);
|
|
extern QMetaObject *qax_readInterfaceInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject);
|
|
extern QByteArrayList qax_qualified_usertypes;
|
|
extern QHash<QByteArray, QByteArray> qax_enum_values;
|
|
extern QString qax_docuFromName(ITypeInfo *typeInfo, const QString &name);
|
|
extern bool qax_dispatchEqualsIDispatch;
|
|
extern void qax_deleteMetaObject(QMetaObject *mo);
|
|
|
|
static QMap<QByteArray, QByteArray> namespaceForType;
|
|
static QList<QByteArray> strings;
|
|
static QHash<QByteArray, int> stringIndex; // Optimization, speeds up generation
|
|
static QByteArrayList vTableOnlyStubs;
|
|
|
|
void writeEnums(QTextStream &out, const QMetaObject *mo)
|
|
{
|
|
// enums
|
|
for (int ienum = mo->enumeratorOffset(); ienum < mo->enumeratorCount(); ++ienum) {
|
|
formatCppEnum(out, mo->enumerator(ienum));
|
|
out << '\n';
|
|
}
|
|
}
|
|
|
|
void writeHeader(QTextStream &out, const QString &nameSpace, const QString &outFileName)
|
|
{
|
|
out << "#ifndef QAX_DUMPCPP_" << outFileName.toUpper() << "_H" << Qt::endl;
|
|
out << "#define QAX_DUMPCPP_" << outFileName.toUpper() << "_H" << Qt::endl;
|
|
out << Qt::endl;
|
|
out << "// Define this symbol to __declspec(dllexport) or __declspec(dllimport)" << Qt::endl;
|
|
out << "#ifndef " << nameSpace.toUpper() << "_EXPORT" << Qt::endl;
|
|
out << "#define " << nameSpace.toUpper() << "_EXPORT" << Qt::endl;
|
|
out << "#endif" << Qt::endl;
|
|
out << Qt::endl;
|
|
out << "#include <qaxobject.h>" << Qt::endl;
|
|
out << "#include <qaxwidget.h>" << Qt::endl;
|
|
out << "#include <qdatetime.h>" << Qt::endl;
|
|
out << "#include <qpixmap.h>" << Qt::endl;
|
|
out << "#include <qpoint.h>" << Qt::endl;
|
|
out << "#include <qrect.h>" << Qt::endl;
|
|
out << "#include <qsize.h>" << Qt::endl;
|
|
out << Qt::endl;
|
|
out << "struct IDispatch;" << Qt::endl;
|
|
out << Qt::endl;
|
|
}
|
|
|
|
void generateNameSpace(QTextStream &out, const QMetaObject *mo, const QByteArray &nameSpace)
|
|
{
|
|
out << "namespace " << nameSpace << " {" << Qt::endl;
|
|
out << Qt::endl;
|
|
writeEnums(out, mo);
|
|
|
|
// don't close on purpose
|
|
}
|
|
|
|
static QByteArray joinParameterNames(const QByteArrayList ¶meterNames)
|
|
{
|
|
QByteArray slotParameters;
|
|
for (qsizetype p = 0; p < parameterNames.size(); ++p) {
|
|
slotParameters += parameterNames.at(p);
|
|
if (p < parameterNames.size() - 1)
|
|
slotParameters += ',';
|
|
}
|
|
|
|
return slotParameters;
|
|
}
|
|
|
|
QByteArray constRefify(const QByteArray &type)
|
|
{
|
|
QByteArray ctype(type);
|
|
if (type == "QString" || type == "QPixmap"
|
|
|| type == "QVariant" || type == "QDateTime"
|
|
|| type == "QColor" || type == "QFont"
|
|
|| type == "QByteArray" || type == "QValueList<QVariant>"
|
|
|| type == "QStringList")
|
|
ctype = "const " + ctype + '&';
|
|
|
|
return ctype;
|
|
}
|
|
|
|
static void formatConstructorSignature(QTextStream &out, ObjectCategories category,
|
|
bool declaration)
|
|
{
|
|
out << '(';
|
|
if (category & Licensed) {
|
|
out << "const QString &licenseKey, ";
|
|
if (declaration)
|
|
out << " = QString()";
|
|
out << ", ";
|
|
}
|
|
if (category & ActiveX) {
|
|
out << "QWidget *parent";
|
|
if (declaration)
|
|
out << " = nullptr";
|
|
out << ", Qt::WindowFlags f";
|
|
if (declaration)
|
|
out << " = {}";
|
|
} else if (category & SubObject) {
|
|
out << "IDispatch *subobject";
|
|
if (declaration)
|
|
out << " = nullptr";
|
|
out << ", QAxObject *parent";
|
|
if (declaration)
|
|
out << " = nullptr";
|
|
} else {
|
|
out << "QObject *parent";
|
|
if (declaration)
|
|
out << " = nullptr";
|
|
}
|
|
out << ')';
|
|
}
|
|
|
|
static void formatMetaFunctions(QTextStream &out, const QByteArray &nameSpace,
|
|
const QByteArray &className, ObjectCategories category)
|
|
{
|
|
// add a replacement qt_metacall()
|
|
QStringView parentClass = (category & ActiveX) ? u"QAxWidget" : u"QAxObject";
|
|
out << "int ";
|
|
if (!nameSpace.isEmpty())
|
|
out << nameSpace << "::";
|
|
out << className << "::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n"
|
|
"{\n"
|
|
" return " << parentClass << "::qt_metacall(_c, _id, _a);\n"
|
|
"}\n\n";
|
|
}
|
|
|
|
static void formatConstructorBody(QTextStream &out, const QByteArray &nameSpace,
|
|
const QByteArray &className,
|
|
const QString &controlID, ObjectCategories category, bool useControlName)
|
|
{
|
|
QString controlName;
|
|
if (useControlName) {
|
|
if (!nameSpace.isEmpty())
|
|
controlName = QString::fromUtf8(nameSpace) + QStringLiteral(".");
|
|
controlName += QString::fromUtf8(className);
|
|
} else {
|
|
controlName = controlID;
|
|
}
|
|
if (!nameSpace.isEmpty())
|
|
out << nameSpace << "::";
|
|
out << className << "::" << className;
|
|
formatConstructorSignature(out, category, false);
|
|
out << " :" << Qt::endl << " ";
|
|
if (category & ActiveX)
|
|
out << "QAxWidget(parent, f";
|
|
else if (category & SubObject)
|
|
out << "QAxObject(subobject, parent";
|
|
else
|
|
out << "QAxObject(parent";
|
|
out << ')' << Qt::endl << '{' << Qt::endl;
|
|
if (category & SubObject) {
|
|
out << " internalRelease();" << Qt::endl;
|
|
} else if (category & Licensed) {
|
|
out << " if (licenseKey.isEmpty())" << Qt::endl;
|
|
out << " setControl(QStringLiteral(\"" << controlName << "\"));" << Qt::endl;
|
|
out << " else" << Qt::endl;
|
|
out << " setControl(QStringLiteral(\"" << controlName << ":\") + licenseKey);" << Qt::endl;
|
|
} else {
|
|
out << " setControl(QStringLiteral(\"" << controlName << "\"));" << Qt::endl;
|
|
}
|
|
out << '}' << Qt::endl << Qt::endl;
|
|
}
|
|
|
|
// Hash of C# only types.
|
|
static const QSet<QByteArray> cSharpTypes = {
|
|
"ICloneable", "ICollection", "IDisposable", "IEnumerable",
|
|
"IList", "ISerializable", "_Attribute"
|
|
};
|
|
|
|
// Return true if the passed string is one of the Qt type names that we re-defined in IDL files.
|
|
static bool isQtTypeName(QByteArrayView s)
|
|
{
|
|
return s == "struct QPoint"
|
|
|| s == "struct QRect"
|
|
|| s == "struct QSize";
|
|
}
|
|
|
|
void generateClassDecl(QTextStream &out, const QMetaObject *mo,
|
|
const QByteArray &className, const QByteArray &nameSpace,
|
|
ObjectCategories category)
|
|
{
|
|
QByteArrayList functions;
|
|
|
|
QByteArray indent;
|
|
if (!(category & OnlyInlines))
|
|
indent = " ";
|
|
|
|
if (!(category & OnlyInlines)) {
|
|
// constructors
|
|
out << "class " << nameSpace.toUpper() << "_EXPORT " << className << " : public ";
|
|
if (category & ActiveX)
|
|
out << "QAxWidget";
|
|
else
|
|
out << "QAxObject";
|
|
out << Qt::endl;
|
|
|
|
out << '{' << Qt::endl;
|
|
out << "public:" << Qt::endl << " explicit " << className;
|
|
formatConstructorSignature(out, category, true);
|
|
out << ';' << Qt::endl;
|
|
}
|
|
|
|
functions << className;
|
|
|
|
// enums
|
|
if (nameSpace.isEmpty() && !(category & OnlyInlines))
|
|
writeEnums(out, mo);
|
|
// QAxBase public virtual functions.
|
|
QByteArrayList axBase_vfuncs;
|
|
axBase_vfuncs.append("metaObject");
|
|
axBase_vfuncs.append("qObject");
|
|
axBase_vfuncs.append("className");
|
|
axBase_vfuncs.append("propertyWritable");
|
|
axBase_vfuncs.append("setPropertyWritable");
|
|
|
|
// properties
|
|
for (int iprop = mo->propertyOffset(); iprop < mo->propertyCount(); ++iprop) {
|
|
QMetaProperty property = mo->property(iprop);
|
|
if (!property.isReadable())
|
|
continue;
|
|
|
|
QByteArray propertyName(property.name());
|
|
if (propertyName == className)
|
|
continue;
|
|
|
|
if (!(category & OnlyInlines)) {
|
|
out << indent << "/*" << Qt::endl << indent << "Property " << propertyName << Qt::endl;
|
|
QString documentation = qax_docuFromName(currentTypeInfo, QString::fromLatin1(propertyName.constData()));
|
|
if (!documentation.isEmpty()) {
|
|
out << Qt::endl;
|
|
out << indent << documentation << Qt::endl;
|
|
}
|
|
out << indent << "*/" << Qt::endl;
|
|
}
|
|
|
|
// Check whether the new function conflicts with any of QAxBase public virtual functions.
|
|
// If so, prepend the function name with '<classname>_'. Since all internal metaobject magic
|
|
// remains the same, we have to use the original name when used with QObject::connect or QMetaObject
|
|
QByteArray propertyFunctionName(propertyName);
|
|
if (axBase_vfuncs.contains(propertyFunctionName)) {
|
|
propertyFunctionName = className + '_' + propertyName;
|
|
qWarning("property conflits with QAXBase: %s changed to %s", propertyName.constData(), propertyFunctionName.constData());
|
|
}
|
|
|
|
QByteArray propertyType(property.typeName());
|
|
|
|
QByteArray simplePropType = propertyType;
|
|
simplePropType.replace('*', "");
|
|
|
|
out << indent << "inline ";
|
|
bool foreignNamespace = true;
|
|
if (!propertyType.contains("::") &&
|
|
(qax_qualified_usertypes.contains(simplePropType) || qax_qualified_usertypes.contains("enum "+ simplePropType))
|
|
) {
|
|
propertyType.prepend(nameSpace + "::");
|
|
foreignNamespace = false;
|
|
}
|
|
|
|
out << propertyType << ' ';
|
|
|
|
if (category & OnlyInlines)
|
|
out << className << "::";
|
|
out << propertyFunctionName << "() const";
|
|
|
|
if (!(category & NoInlines)) {
|
|
out << Qt::endl << indent << '{' << Qt::endl;
|
|
if (qax_qualified_usertypes.contains(simplePropType)) {
|
|
if (foreignNamespace)
|
|
out << "#ifdef QAX_DUMPCPP_" << propertyType.left(propertyType.indexOf("::")).toUpper() << "_H" << Qt::endl;
|
|
QByteArray simplePropTypeWithNamespace = propertyType;
|
|
simplePropTypeWithNamespace.replace('*', "");
|
|
out << indent << " qRegisterMetaType<" << propertyType << ">(\"" << property.typeName() << "\");" << Qt::endl;
|
|
out << indent << " qRegisterMetaType<" << simplePropTypeWithNamespace << ">(\"" << simplePropType << "\");" << Qt::endl;
|
|
}
|
|
out << indent << " QVariant qax_result = property(\"" << propertyName << "\");" << Qt::endl;
|
|
if (propertyType.length() && propertyType.at(propertyType.length()-1) == '*')
|
|
out << indent << " if (qax_result.constData() == nullptr)\n"
|
|
<< indent << " return nullptr;\n"
|
|
<< indent << " Q_ASSERT(qax_result.isValid());" << Qt::endl;
|
|
if (qax_qualified_usertypes.contains(simplePropType)) {
|
|
simplePropType = propertyType;
|
|
simplePropType.replace('*', "");
|
|
out << indent << " return *reinterpret_cast<" << propertyType << "*>(qax_result.data());\n";
|
|
if (foreignNamespace) {
|
|
out << "#else" << Qt::endl;
|
|
out << indent << " return nullptr; // foreign namespace not included" << Qt::endl;
|
|
out << "#endif" << Qt::endl;
|
|
}
|
|
|
|
} else {
|
|
out << indent << " return *reinterpret_cast<" << propertyType << "*>(qax_result.data());\n";
|
|
}
|
|
out << indent << '}' << Qt::endl;
|
|
} else {
|
|
out << "; //Returns the value of " << propertyName << Qt::endl;
|
|
}
|
|
|
|
functions << propertyName;
|
|
|
|
if (property.isWritable()) {
|
|
const QByteArray setter = setterName(propertyName);
|
|
|
|
out << indent << "inline " << "void ";
|
|
if (category & OnlyInlines)
|
|
out << className << "::";
|
|
out << setter << '(' << constRefify(propertyType) << " value)";
|
|
|
|
if (!(category & NoInlines)) {
|
|
if (propertyType.endsWith('*')) {
|
|
out << '{' << Qt::endl;
|
|
out << " int typeId = qRegisterMetaType<" << propertyType << ">(\"" << propertyType << "\");" << Qt::endl;
|
|
out << " setProperty(\"" << propertyName << "\", QVariant(QMetaType(typeId), &value));" << Qt::endl;
|
|
out << '}' << Qt::endl;
|
|
} else {
|
|
out << "{ setProperty(\"" << propertyName << "\", QVariant(value)); }" << Qt::endl;
|
|
}
|
|
} else {
|
|
out << "; //Sets the value of the " << propertyName << " property" << Qt::endl;
|
|
}
|
|
|
|
functions << setter;
|
|
}
|
|
|
|
out << Qt::endl;
|
|
}
|
|
|
|
// slots - but not property setters
|
|
int defaultArguments = 0;
|
|
for (int islot = mo->methodOffset(); islot < mo->methodCount(); ++islot) {
|
|
const QMetaMethod slot(mo->method(islot));
|
|
if (slot.methodType() != QMetaMethod::Slot)
|
|
continue;
|
|
|
|
#if 0
|
|
// makes not sense really to respect default arguments...
|
|
if (slot.attributes() & Cloned) {
|
|
++defaultArguments;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
QByteArray slotSignature(slot.methodSignature());
|
|
QByteArray slotName = slotSignature.left(slotSignature.indexOf('('));
|
|
if (functions.contains(slotName))
|
|
continue;
|
|
|
|
if (!(category & OnlyInlines)) {
|
|
out << indent << "/*" << Qt::endl << indent << "Method " << slotName << Qt::endl;
|
|
QString documentation = qax_docuFromName(currentTypeInfo, QString::fromLatin1(slotName.constData()));
|
|
if (!documentation.isEmpty()) {
|
|
out << Qt::endl;
|
|
out << indent << documentation << Qt::endl;
|
|
}
|
|
out << indent << "*/" << Qt::endl;
|
|
}
|
|
|
|
QByteArray slotParameters(joinParameterNames(slot.parameterNames()));
|
|
QByteArray slotTag(slot.tag());
|
|
QByteArray slotType(slot.typeName());
|
|
|
|
QByteArray simpleSlotType = slotType;
|
|
simpleSlotType.replace('*', "");
|
|
if (!slotType.contains("::") && qax_qualified_usertypes.contains(simpleSlotType))
|
|
slotType.prepend(nameSpace + "::");
|
|
|
|
|
|
QByteArray slotNamedSignature;
|
|
if (slotSignature.endsWith("()")) { // no parameters - no names
|
|
slotNamedSignature = slotSignature;
|
|
} else {
|
|
slotNamedSignature = slotSignature.left(slotSignature.indexOf('(') + 1);
|
|
QByteArray slotSignatureTruncated(slotSignature.mid(slotNamedSignature.length()));
|
|
slotSignatureTruncated.truncate(slotSignatureTruncated.length() - 1);
|
|
|
|
const auto signatureSplit = slotSignatureTruncated.split(',');
|
|
QByteArrayList parameterSplit;
|
|
if (slotParameters.isEmpty()) { // generate parameter names
|
|
for (qsizetype i = 0; i < signatureSplit.size(); ++i)
|
|
parameterSplit << QByteArray("p") + QByteArray::number(i);
|
|
} else {
|
|
parameterSplit = slotParameters.split(',');
|
|
}
|
|
|
|
for (qsizetype i = 0; i < signatureSplit.count(); ++i) {
|
|
QByteArray parameterType = signatureSplit.at(i);
|
|
if (!parameterType.contains("::") && namespaceForType.contains(parameterType))
|
|
parameterType.prepend(namespaceForType.value(parameterType) + "::");
|
|
|
|
QByteArray arraySpec; // transform array method signature "foo(int[4])" ->"foo(int p[4])"
|
|
const qsizetype arrayPos = parameterType.lastIndexOf('[');
|
|
if (arrayPos != -1) {
|
|
arraySpec = parameterType.right(parameterType.size() - arrayPos);
|
|
parameterType.truncate(arrayPos);
|
|
}
|
|
slotNamedSignature += constRefify(parameterType);
|
|
slotNamedSignature += ' ';
|
|
slotNamedSignature += parameterSplit.at(i);
|
|
slotNamedSignature += arraySpec;
|
|
if (defaultArguments >= signatureSplit.size() - i) {
|
|
slotNamedSignature += " = ";
|
|
slotNamedSignature += parameterType + "()";
|
|
}
|
|
if (i + 1 < signatureSplit.size())
|
|
slotNamedSignature += ", ";
|
|
}
|
|
slotNamedSignature += ')';
|
|
}
|
|
|
|
out << indent << "inline ";
|
|
|
|
if (!slotTag.isEmpty())
|
|
out << slotTag << ' ';
|
|
else
|
|
out << slotType << ' ';
|
|
if (category & OnlyInlines)
|
|
out << className << "::";
|
|
|
|
// Update function name in case of conflicts with QAxBase public virtual functions.
|
|
int parnIdx = slotNamedSignature.indexOf('(');
|
|
QByteArray slotOriginalName = slotNamedSignature.left(parnIdx);
|
|
if (axBase_vfuncs.contains(slotOriginalName)) {
|
|
QByteArray newSignature = className + '_' + slotOriginalName;
|
|
newSignature += slotNamedSignature.mid(parnIdx);
|
|
qWarning("function name conflits with QAXBase %s changed to %s", slotNamedSignature.constData(), newSignature.constData());
|
|
slotNamedSignature = newSignature;
|
|
}
|
|
|
|
out << slotNamedSignature;
|
|
|
|
if (category & NoInlines) {
|
|
out << ';' << Qt::endl;
|
|
} else {
|
|
out << Qt::endl;
|
|
out << indent << '{' << Qt::endl;
|
|
|
|
if (slotType != QByteArrayLiteral("void")) {
|
|
out << indent << " " << slotType << " qax_result";
|
|
if (slotType.endsWith('*'))
|
|
out << " = 0";
|
|
out << ';' << Qt::endl;
|
|
if (qax_qualified_usertypes.contains(simpleSlotType)) {
|
|
bool foreignNamespace = simpleSlotType.contains("::");
|
|
if (foreignNamespace)
|
|
out << "#ifdef QAX_DUMPCPP_" << simpleSlotType.left(simpleSlotType.indexOf(':')).toUpper() << "_H" << Qt::endl;
|
|
QByteArray simpleSlotTypeWithNamespace = slotType;
|
|
simpleSlotTypeWithNamespace.replace('*', "");
|
|
out << indent << " qRegisterMetaType<" << simpleSlotTypeWithNamespace << "*>(\"" << simpleSlotType << "*\");" << Qt::endl;
|
|
if (!vTableOnlyStubs.contains(simpleSlotTypeWithNamespace))
|
|
out << indent << " qRegisterMetaType<" << simpleSlotTypeWithNamespace << ">(\"" << simpleSlotType << "\");" << Qt::endl;
|
|
if (foreignNamespace)
|
|
out << "#endif" << Qt::endl;
|
|
}
|
|
}
|
|
out << indent << " void *_a[] = {";
|
|
if (slotType != QByteArrayLiteral("void"))
|
|
out << "(void*)&qax_result";
|
|
else
|
|
out << '0';
|
|
if (!slotParameters.isEmpty()) {
|
|
out << ", (void*)&";
|
|
out << slotParameters.replace(",", ", (void*)&");
|
|
}
|
|
out << "};" << Qt::endl;
|
|
|
|
out << indent << " qt_metacall(QMetaObject::InvokeMetaMethod, " << islot << ", _a);" << Qt::endl;
|
|
if (slotType != QByteArrayLiteral("void"))
|
|
out << indent << " return qax_result;" << Qt::endl;
|
|
out << indent << '}' << Qt::endl;
|
|
}
|
|
|
|
out << Qt::endl;
|
|
defaultArguments = 0;
|
|
}
|
|
|
|
if (!(category & OnlyInlines)) {
|
|
if (!(category & NoMetaObject)) {
|
|
out << "// meta object functions" << Qt::endl;
|
|
out << " Q_OBJECT_FAKE" << Qt::endl;
|
|
}
|
|
|
|
out << "};" << Qt::endl;
|
|
}
|
|
}
|
|
|
|
bool generateClassImpl(QTextStream &out, const QMetaObject *mo, const QByteArray &className,
|
|
const QString &controlID,
|
|
const QByteArray &nameSpace, ObjectCategories category,
|
|
bool useControlName,
|
|
QString *errorString)
|
|
{
|
|
Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 13, "dumpcpp should generate the same version as moc");
|
|
|
|
QByteArray qualifiedClassName;
|
|
if (!nameSpace.isEmpty())
|
|
qualifiedClassName = nameSpace + "::";
|
|
qualifiedClassName += className;
|
|
const QByteArray nestedQualifier = className + "::";
|
|
|
|
QString moCode = mocCode(mo, QLatin1String(qualifiedClassName), errorString);
|
|
if (moCode.isEmpty()) {
|
|
out << "#error moc error\n";
|
|
return false;
|
|
}
|
|
out << moCode << "\n\n";
|
|
|
|
formatConstructorBody(out, nameSpace, className, controlID, category, useControlName);
|
|
formatMetaFunctions(out, nameSpace, className, category);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void formatCommentBlockFooter(const QString &typeLibFile, QTextStream &str)
|
|
{
|
|
str << " generated by dumpcpp v" << QT_VERSION_STR << " using\n**";
|
|
const QStringList arguments = QCoreApplication::arguments();
|
|
for (const QString &arg : arguments)
|
|
str << ' ' << arg;
|
|
str << "\n** from the type library " << typeLibFile << "\n**\n"
|
|
<< "****************************************************************************/\n\n";
|
|
}
|
|
|
|
static QByteArray classNameFromTypeInfo(ITypeInfo *typeinfo)
|
|
{
|
|
BSTR bstr;
|
|
QByteArray result;
|
|
if (SUCCEEDED(typeinfo->GetDocumentation(-1, &bstr, nullptr, nullptr, nullptr))) {
|
|
result = QString::fromWCharArray(bstr).toLatin1();
|
|
SysFreeString(bstr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Extract a list of VTable only stubs from a ITypeLib.
|
|
static QByteArrayList vTableOnlyStubsFromTypeLib(ITypeLib *typelib, const QString &nameSpace)
|
|
{
|
|
QByteArrayList result;
|
|
const QByteArray nameSpacePrefix = nameSpace.toLatin1() + "::";
|
|
for (UINT i = 0, typeCount = typelib->GetTypeInfoCount(); i < typeCount; ++i) {
|
|
TYPEKIND typekind;
|
|
if (SUCCEEDED(typelib->GetTypeInfoType(i, &typekind)) && typekind == TKIND_INTERFACE) {
|
|
ITypeInfo *typeinfo = nullptr;
|
|
if (SUCCEEDED(typelib->GetTypeInfo(i, &typeinfo) && typeinfo)) {
|
|
result.append(nameSpacePrefix + classNameFromTypeInfo(typeinfo));
|
|
typeinfo->Release();
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const QMetaObject *baseMetaObject(ObjectCategories c)
|
|
{
|
|
return c.testFlag(ActiveX)
|
|
? &QAxBaseWidget::staticMetaObject
|
|
: &QAxBaseObject::staticMetaObject;
|
|
}
|
|
|
|
bool generateTypeLibrary(QString typeLibFile, QString outname,
|
|
const QString &nameSpace, ObjectCategories category, bool useControlName)
|
|
{
|
|
typeLibFile.replace(QLatin1Char('/'), QLatin1Char('\\'));
|
|
|
|
ITypeLib *typelib;
|
|
LoadTypeLibEx(reinterpret_cast<const wchar_t *>(typeLibFile.utf16()), REGKIND_NONE, &typelib);
|
|
if (!typelib) {
|
|
qWarning("dumpcpp: loading '%s' as a type library failed", qPrintable(typeLibFile));
|
|
return false;
|
|
}
|
|
|
|
QString libName = nameSpace;
|
|
if (libName.isEmpty()) {
|
|
BSTR nameString = nullptr;
|
|
if (SUCCEEDED(typelib->GetDocumentation(-1, &nameString, nullptr, nullptr, nullptr))) {
|
|
libName = QString::fromWCharArray(nameString);
|
|
SysFreeString(nameString);
|
|
}
|
|
}
|
|
const QByteArray libNameBa = libName.toLatin1();
|
|
vTableOnlyStubs = vTableOnlyStubsFromTypeLib(typelib, libName);
|
|
|
|
QString libVersion(QLatin1String("1.0"));
|
|
|
|
TLIBATTR *tlibattr = nullptr;
|
|
typelib->GetLibAttr(&tlibattr);
|
|
if (tlibattr) {
|
|
libVersion = QString::fromLatin1("%1.%2").arg(tlibattr->wMajorVerNum).arg(tlibattr->wMinorVerNum);
|
|
typelib->ReleaseTLibAttr(tlibattr);
|
|
}
|
|
|
|
if (outname.isEmpty())
|
|
outname = libName.toLower();
|
|
|
|
if (outname.isEmpty()) {
|
|
qWarning("dumpcpp: no output filename provided, and cannot deduce output filename");
|
|
return false;
|
|
}
|
|
|
|
QMetaObject *namespaceObject = qax_readEnumInfo(typelib, nullptr);
|
|
|
|
QString classImpl;
|
|
QTextStream classImplOut(&classImpl);
|
|
QFile implFile(outname + QLatin1String(".cpp"));
|
|
QTextStream implOut(&implFile);
|
|
if (!(category & (NoMetaObject|NoImplementation))) {
|
|
if (!implFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
qWarning("dumpcpp: Could not open output file '%s'", qPrintable(implFile.fileName()));
|
|
return false;
|
|
}
|
|
|
|
implOut << "/****************************************************************************\n"
|
|
"**\n** Metadata for " << libName;
|
|
formatCommentBlockFooter(typeLibFile, implOut);
|
|
|
|
implOut << "#define QAX_DUMPCPP_" << libName.toUpper() << "_NOINLINES" << Qt::endl;
|
|
|
|
implOut << "#include \"" << outname << ".h\"" << Qt::endl;
|
|
implOut << "#include <OAIdl.h>" << Qt::endl; // For IDispatch
|
|
implOut << Qt::endl;
|
|
}
|
|
|
|
QFile declFile(outname + QLatin1String(".h"));
|
|
QTextStream declOut(&declFile);
|
|
QByteArray classes;
|
|
QTextStream classesOut(&classes, QIODevice::WriteOnly);
|
|
QByteArray inlines;
|
|
QTextStream inlinesOut(&inlines, QIODevice::WriteOnly);
|
|
|
|
QMap<QByteArray, QByteArrayList> namespaces;
|
|
|
|
if(!(category & NoDeclaration)) {
|
|
if (!declFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
qWarning("dumpcpp: Could not open output file '%s'", qPrintable(declFile.fileName()));
|
|
return false;
|
|
}
|
|
|
|
declOut << "/****************************************************************************\n"
|
|
"**\n** Namespace " << libName;
|
|
formatCommentBlockFooter(typeLibFile, declOut);
|
|
|
|
QFileInfo cppFileInfo(outname);
|
|
writeHeader(declOut, libName, cppFileInfo.fileName());
|
|
|
|
UINT typeCount = typelib->GetTypeInfoCount();
|
|
if (declFile.isOpen()) {
|
|
QByteArrayList opaquePointerTypes;
|
|
declOut << Qt::endl;
|
|
declOut << "// Referenced namespace" << Qt::endl;
|
|
for (UINT index = 0; index < typeCount; ++index) {
|
|
ITypeInfo *typeinfo = nullptr;
|
|
typelib->GetTypeInfo(index, &typeinfo);
|
|
if (!typeinfo)
|
|
continue;
|
|
|
|
TYPEATTR *typeattr;
|
|
typeinfo->GetTypeAttr(&typeattr);
|
|
if (!typeattr) {
|
|
typeinfo->Release();
|
|
continue;
|
|
}
|
|
|
|
TYPEKIND typekind;
|
|
typelib->GetTypeInfoType(index, &typekind);
|
|
|
|
QMetaObject *metaObject = nullptr;
|
|
|
|
// trigger meta object to collect references to other type libraries
|
|
switch (typekind) {
|
|
case TKIND_COCLASS:
|
|
metaObject = qax_readClassInfo(typelib, typeinfo, baseMetaObject(category));
|
|
break;
|
|
case TKIND_DISPATCH:
|
|
metaObject = qax_readInterfaceInfo(typelib, typeinfo, baseMetaObject(category));
|
|
break;
|
|
case TKIND_RECORD:
|
|
case TKIND_ENUM:
|
|
case TKIND_INTERFACE: // only for forward declarations
|
|
{
|
|
QByteArray className = classNameFromTypeInfo(typeinfo);
|
|
switch (typekind) {
|
|
case TKIND_RECORD:
|
|
className.prepend("struct ");
|
|
break;
|
|
case TKIND_ENUM:
|
|
className.prepend("enum ");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
namespaces[libNameBa].append(className);
|
|
if (!qax_qualified_usertypes.contains(className))
|
|
qax_qualified_usertypes << className;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
qax_deleteMetaObject(metaObject);
|
|
typeinfo->ReleaseTypeAttr(typeattr);
|
|
typeinfo->Release();
|
|
}
|
|
|
|
for (qsizetype i = 0; i < qax_qualified_usertypes.size(); ++i) {
|
|
QByteArray refType = qax_qualified_usertypes.at(i);
|
|
QByteArray refTypeLib;
|
|
if (refType.contains("::")) {
|
|
refTypeLib = refType;
|
|
refType.remove(0, refType.lastIndexOf("::") + 2);
|
|
if (refTypeLib.contains(' ')) {
|
|
refType = refTypeLib.left(refTypeLib.indexOf(' ')) + ' ' + refType;
|
|
}
|
|
refTypeLib.truncate(refTypeLib.indexOf("::"));
|
|
refTypeLib.remove(0, refTypeLib.lastIndexOf(' ') + 1);
|
|
namespaces[refTypeLib].append(refType);
|
|
} else {
|
|
namespaces[libNameBa].append(refType);
|
|
}
|
|
}
|
|
|
|
for (auto it = namespaces.cbegin(), end = namespaces.cend(); it != end; ++it) {
|
|
const QByteArray &nspace = it.key();
|
|
if (libName != QLatin1String(nspace)) {
|
|
declOut << "namespace " << nspace << " {" << Qt::endl;
|
|
for (const auto &className : it.value()) {
|
|
const auto spacePos = className.indexOf(' ');
|
|
if (spacePos != -1) {
|
|
const QByteArray name = className.mid(spacePos + 1);
|
|
if (className.startsWith("enum ")) {
|
|
declOut << " " << className << " {\n"
|
|
<< qax_enum_values.value(nspace + "::" + name) << " };\n";
|
|
} else {
|
|
declOut << " " << className << ";\n";
|
|
}
|
|
namespaceForType.insert(name, nspace);
|
|
} else {
|
|
declOut << " class " << className << ';' << Qt::endl;
|
|
opaquePointerTypes.append(nspace + "::" + className);
|
|
namespaceForType.insert(className, nspace);
|
|
namespaceForType.insert(className + '*', nspace);
|
|
namespaceForType.insert(className + "**", nspace);
|
|
}
|
|
}
|
|
declOut << '}' << Qt::endl << Qt::endl;
|
|
}
|
|
}
|
|
for (const QByteArray &opaquePointerType : std::as_const(opaquePointerTypes))
|
|
declOut << "Q_DECLARE_OPAQUE_POINTER(" << opaquePointerType << "*)" << Qt::endl;
|
|
declOut << Qt::endl;
|
|
}
|
|
generateNameSpace(declOut, namespaceObject, libNameBa);
|
|
|
|
auto nspIt = namespaces.constFind(libNameBa);
|
|
if (nspIt != namespaces.constEnd() && !nspIt.value().isEmpty()) {
|
|
declOut << "// forward declarations" << Qt::endl;
|
|
for (const auto &className : nspIt.value()) {
|
|
if (isQtTypeName(className))
|
|
continue;
|
|
if (className.contains(' ')) {
|
|
declOut << " " << className << ';' << Qt::endl;
|
|
namespaceForType.insert(className.mid(className.indexOf(' ') + 1), libNameBa);
|
|
} else {
|
|
declOut << " class " << className << ';' << Qt::endl;
|
|
namespaceForType.insert(className, libNameBa);
|
|
namespaceForType.insert(className + '*', libNameBa);
|
|
namespaceForType.insert(className + "**", libNameBa);
|
|
}
|
|
}
|
|
}
|
|
|
|
declOut << Qt::endl;
|
|
}
|
|
|
|
QByteArrayList subtypes;
|
|
|
|
UINT typeCount = typelib->GetTypeInfoCount();
|
|
for (UINT index = 0; index < typeCount; ++index) {
|
|
ITypeInfo *typeinfo = nullptr;
|
|
typelib->GetTypeInfo(index, &typeinfo);
|
|
if (!typeinfo)
|
|
continue;
|
|
|
|
TYPEATTR *typeattr;
|
|
typeinfo->GetTypeAttr(&typeattr);
|
|
if (!typeattr) {
|
|
typeinfo->Release();
|
|
continue;
|
|
}
|
|
|
|
TYPEKIND typekind;
|
|
typelib->GetTypeInfoType(index, &typekind);
|
|
|
|
ObjectCategories object_category = category;
|
|
if (!(typeattr->wTypeFlags & TYPEFLAG_FCANCREATE))
|
|
object_category |= SubObject;
|
|
else if (typeattr->wTypeFlags & TYPEFLAG_FCONTROL)
|
|
object_category |= ActiveX;
|
|
|
|
QMetaObject *metaObject = nullptr;
|
|
QUuid guid(typeattr->guid);
|
|
|
|
if (!(object_category & ActiveX)) {
|
|
QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\") + guid.toString(), QSettings::NativeFormat);
|
|
if (settings.childGroups().contains(QLatin1String("Control"))) {
|
|
object_category |= ActiveX;
|
|
object_category &= ~SubObject;
|
|
}
|
|
}
|
|
|
|
switch (typekind) {
|
|
case TKIND_COCLASS:
|
|
metaObject = qax_readClassInfo(typelib, typeinfo, baseMetaObject(object_category));
|
|
break;
|
|
case TKIND_DISPATCH:
|
|
metaObject = qax_readInterfaceInfo(typelib, typeinfo, baseMetaObject(object_category));
|
|
break;
|
|
case TKIND_INTERFACE: { // only stub: QTBUG-27792, explicitly disable copy in inherited
|
|
// class to make related error messages clearer
|
|
const QByteArray className = classNameFromTypeInfo(typeinfo);
|
|
declOut << "// stub for vtable-only interface\n"
|
|
<< "class " << className << " : public QAxObject { Q_DISABLE_COPY_MOVE("
|
|
<< className << ") };\n\n";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (metaObject) {
|
|
currentTypeInfo = typeinfo;
|
|
QByteArray className(metaObject->className());
|
|
if (!(typeattr->wTypeFlags & TYPEFLAG_FDUAL)
|
|
&& (metaObject->propertyCount() - metaObject->propertyOffset()) == 1
|
|
&& className.contains("Events")) {
|
|
declOut << "// skipping event interface " << className << Qt::endl << Qt::endl;
|
|
} else {
|
|
if (declFile.isOpen()) {
|
|
if (typeattr->wTypeFlags & TYPEFLAG_FLICENSED)
|
|
object_category |= Licensed;
|
|
if (typekind == TKIND_COCLASS) { // write those later...
|
|
generateClassDecl(classesOut, metaObject, className, libNameBa,
|
|
object_category | NoInlines);
|
|
classesOut << Qt::endl;
|
|
} else {
|
|
generateClassDecl(declOut, metaObject, className, libNameBa,
|
|
object_category | NoInlines);
|
|
declOut << Qt::endl;
|
|
}
|
|
subtypes << className;
|
|
generateClassDecl(inlinesOut, metaObject, className, libNameBa,
|
|
object_category | OnlyInlines);
|
|
inlinesOut << Qt::endl;
|
|
}
|
|
if (implFile.isOpen()) {
|
|
QString errorString;
|
|
if (!generateClassImpl(classImplOut, metaObject, className, guid.toString(), libNameBa,
|
|
object_category, useControlName, &errorString)) {
|
|
qWarning("%s", qPrintable(errorString));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
currentTypeInfo = nullptr;
|
|
}
|
|
|
|
qax_deleteMetaObject(metaObject);
|
|
|
|
typeinfo->ReleaseTypeAttr(typeattr);
|
|
typeinfo->Release();
|
|
}
|
|
|
|
// String table generation logic was ported from moc generator, with some modifications
|
|
// required to split large stringdata arrays.
|
|
if (implFile.isOpen()) {
|
|
classImplOut.flush();
|
|
implOut << classImpl << Qt::endl;
|
|
}
|
|
|
|
qax_deleteMetaObject(namespaceObject);
|
|
|
|
classesOut.flush();
|
|
inlinesOut.flush();
|
|
|
|
if (declFile.isOpen()) {
|
|
if (classes.size()) {
|
|
declOut << "// Actual coclasses" << Qt::endl;
|
|
declOut << classes;
|
|
}
|
|
if (inlines.size()) {
|
|
declOut << "// member function implementation" << Qt::endl;
|
|
declOut << "#ifndef QAX_DUMPCPP_" << libName.toUpper() << "_NOINLINES" << Qt::endl;
|
|
declOut << inlines << Qt::endl;
|
|
declOut << "#endif" << Qt::endl << Qt::endl;
|
|
}
|
|
// close namespace
|
|
declOut << '}' << Qt::endl;
|
|
declOut << Qt::endl;
|
|
|
|
declOut << "#endif" << Qt::endl;
|
|
declOut << Qt::endl;
|
|
}
|
|
|
|
typelib->Release();
|
|
return true;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
QT_USE_NAMESPACE
|
|
|
|
struct Options
|
|
{
|
|
Options() = default;
|
|
|
|
ProgramMode mode = GenerateMode;
|
|
ObjectCategories category = DefaultObject;
|
|
bool dispatchEqualsIDispatch = false;
|
|
bool useControlName = false;
|
|
|
|
QString outname;
|
|
QString typeLib;
|
|
QString nameSpace;
|
|
};
|
|
|
|
static void parseOptions(Options *options)
|
|
{
|
|
const char helpText[] =
|
|
"\nGenerate a C++ namespace from a type library.\n\n"
|
|
"Examples:\n"
|
|
" dumpcpp -o ieframe %WINDIR%\\system32\\ieframe.dll\n"
|
|
" dumpcpp -o outlook Outlook.Application\n"
|
|
" dumpcpp {3B756301-0075-4E40-8BE8-5A81DE2426B7}\n"
|
|
" dumpcpp -getfile {21D6D480-A88B-11D0-83DD-00AA003CCABD}\n";
|
|
|
|
const char outputOptionC[] = "-o";
|
|
const char nameSpaceOptionC[] = "-n";
|
|
const char getfileOptionC[] = "-getfile";
|
|
const char useControlNameOptionC[] = "-controlname";
|
|
|
|
QStringList args = QCoreApplication::arguments();
|
|
// Convert Windows-style '/option' into '-option'.
|
|
for (int i = 1; i < args.size(); ) {
|
|
QString &arg = args[i];
|
|
if (arg.startsWith(QLatin1Char('/')))
|
|
arg[0] = QLatin1Char('-');
|
|
const bool takesOptionValue = arg == QLatin1String(outputOptionC)
|
|
|| arg == QLatin1String(nameSpaceOptionC)
|
|
|| arg == QLatin1String(getfileOptionC);
|
|
i += takesOptionValue ? 2 : 1;
|
|
}
|
|
|
|
QCommandLineParser parser;
|
|
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
|
|
parser.setApplicationDescription(QLatin1String(helpText));
|
|
|
|
|
|
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
|
|
parser.addHelpOption();
|
|
parser.addVersionOption();
|
|
QCommandLineOption outputOption(QLatin1String(outputOptionC + 1),
|
|
QStringLiteral("Write output to file."),
|
|
QStringLiteral("file"));
|
|
parser.addOption(outputOption);
|
|
QCommandLineOption nameSpaceOption(QLatin1String(nameSpaceOptionC + 1),
|
|
QStringLiteral("The name of the generated C++ namespace."),
|
|
QStringLiteral("namespace"));
|
|
parser.addOption(nameSpaceOption);
|
|
QCommandLineOption noMetaObjectOption(QStringLiteral("nometaobject"),
|
|
QStringLiteral("Don't generate meta object information (no .cpp file). The meta object is then generated in runtime."));
|
|
parser.addOption(noMetaObjectOption);
|
|
QCommandLineOption noDeclarationOption(QStringLiteral("impl"),
|
|
QStringLiteral("Only generate the .cpp file."));
|
|
parser.addOption(noDeclarationOption);
|
|
QCommandLineOption noImplementationOption(QStringLiteral("decl"),
|
|
QStringLiteral("Only generate the .h file."));
|
|
parser.addOption(noImplementationOption);
|
|
QCommandLineOption compatOption(QStringLiteral("compat"),
|
|
QStringLiteral("Treat all coclass parameters as IDispatch."));
|
|
parser.addOption(compatOption);
|
|
QCommandLineOption getFileOption(QLatin1String(getfileOptionC + 1),
|
|
QStringLiteral("Print the filename for the type library it to standard output."),
|
|
QStringLiteral("id"));
|
|
parser.addOption(getFileOption);
|
|
parser.addPositionalArgument(QStringLiteral("input"),
|
|
QStringLiteral("A type library file, type library ID, ProgID or CLSID."));
|
|
QCommandLineOption useControlNameOption(QLatin1String(useControlNameOptionC + 1),
|
|
QStringLiteral("Use the control class name instead of the UUID for setControl()."));
|
|
parser.addOption(useControlNameOption);
|
|
parser.process(args);
|
|
|
|
if (parser.isSet(outputOption))
|
|
options->outname = parser.value(outputOption);
|
|
if (parser.isSet(nameSpaceOption))
|
|
options->nameSpace = parser.value(nameSpaceOption);
|
|
if (parser.isSet(noMetaObjectOption))
|
|
options->category |= NoMetaObject;
|
|
if (parser.isSet(noDeclarationOption))
|
|
options->category |= NoDeclaration;
|
|
if (parser.isSet(noImplementationOption))
|
|
options->category |= NoImplementation;
|
|
options->dispatchEqualsIDispatch = parser.isSet(compatOption);
|
|
if (parser.isSet(getFileOption)) {
|
|
options->typeLib = parser.value(getFileOption);
|
|
options->mode = TypeLibID;
|
|
}
|
|
if (parser.isSet(useControlNameOption))
|
|
options->useControlName = true;
|
|
if (!parser.positionalArguments().isEmpty())
|
|
options->typeLib = parser.positionalArguments().first();
|
|
|
|
if (options->mode == GenerateMode && options->typeLib.isEmpty()) {
|
|
qWarning("dumpcpp: No object class or type library name provided.\n");
|
|
parser.showHelp(1);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) {
|
|
qErrnoWarning("CoInitializeEx() failed.");
|
|
return -1;
|
|
}
|
|
QCoreApplication app(argc, argv);
|
|
|
|
Options options;
|
|
parseOptions(&options);
|
|
qax_dispatchEqualsIDispatch = options.dispatchEqualsIDispatch;
|
|
QString typeLib = options.typeLib;
|
|
|
|
if (options.mode == TypeLibID) {
|
|
QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\TypeLib\\") +
|
|
typeLib, QSettings::NativeFormat);
|
|
typeLib.clear();
|
|
QStringList codes = settings.childGroups();
|
|
for (qsizetype c = 0; c < codes.size(); ++c) {
|
|
const QString keyPrefix = QLatin1Char('/') + codes.at(c) + QLatin1String("/0/");
|
|
if (QT_POINTER_SIZE == 8) {
|
|
typeLib = settings.value(keyPrefix + QLatin1String("win64/.")).toString();
|
|
if (QFile::exists(typeLib))
|
|
break;
|
|
}
|
|
typeLib = settings.value(keyPrefix + QLatin1String("win32/.")).toString();
|
|
if (QFile::exists(typeLib))
|
|
break;
|
|
}
|
|
|
|
if (!typeLib.isEmpty())
|
|
fprintf(stdout, "\"%s\"\n", qPrintable(typeLib));
|
|
return 0;
|
|
}
|
|
|
|
if (typeLib.isEmpty()) {
|
|
qWarning("dumpcpp: No object class or type library name provided.\n"
|
|
" Use -h for help.");
|
|
return -1;
|
|
}
|
|
|
|
// not a file - search registry
|
|
if (!QFile::exists(typeLib)) {
|
|
bool isObject = false;
|
|
QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat);
|
|
|
|
// regular string and not a file - must be ProgID
|
|
if (typeLib.at(0) != QLatin1Char('{')) {
|
|
CLSID clsid;
|
|
if (CLSIDFromProgID(reinterpret_cast<const wchar_t *>(typeLib.utf16()), &clsid) != S_OK) {
|
|
qWarning("dumpcpp: '%s' is not a type library and not a registered ProgID",
|
|
qPrintable(typeLib));
|
|
return -2;
|
|
}
|
|
typeLib = QUuid(clsid).toString();
|
|
isObject = true;
|
|
}
|
|
|
|
// check if CLSID
|
|
if (!isObject) {
|
|
QVariant test = settings.value(QLatin1String("/CLSID/") + typeLib + QLatin1String("/."));
|
|
isObject = test.isValid();
|
|
}
|
|
|
|
// search typelib ID for CLSID
|
|
if (isObject)
|
|
typeLib = settings.value(QLatin1String("/CLSID/") + typeLib
|
|
+ QLatin1String("/Typelib/.")).toString();
|
|
|
|
// interpret input as type library ID
|
|
QString key = QLatin1String("/TypeLib/") + typeLib;
|
|
settings.beginGroup(key);
|
|
QStringList versions = settings.childGroups();
|
|
QStringList codes;
|
|
if (!versions.isEmpty()) {
|
|
settings.beginGroup(QLatin1Char('/') + versions.last());
|
|
codes = settings.childGroups();
|
|
key += QLatin1Char('/') + versions.last();
|
|
settings.endGroup();
|
|
}
|
|
settings.endGroup();
|
|
|
|
for (qsizetype c = 0; c < codes.size(); ++c) {
|
|
const QString keyPrefix = key + QLatin1Char('/') + codes.at(c) + QLatin1Char('/');
|
|
if (QT_POINTER_SIZE == 8) {
|
|
typeLib = settings.value(keyPrefix + QLatin1String("win64/.")).toString();
|
|
if (QFile::exists(typeLib))
|
|
break;
|
|
}
|
|
typeLib = settings.value(keyPrefix + QLatin1String("win32/.")).toString();
|
|
if (QFile::exists(typeLib))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!QFile::exists(typeLib)) {
|
|
qWarning("dumpcpp: type library '%s' not found", qPrintable(typeLib));
|
|
return -2;
|
|
}
|
|
|
|
if (!generateTypeLibrary(typeLib, options.outname, options.nameSpace, options.category, options.useControlName)) {
|
|
qWarning("dumpcpp: error processing type library '%s'", qPrintable(typeLib));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|