/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the ActiveQt framework of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //#define QAX_NO_CLASSINFO #ifndef UNICODE #define UNICODE #endif #define QT_CHECK_STATE #include "qaxobject.h" #ifndef QT_NO_WIN_ACTIVEQT #include #include #include #include #include #include #include #include #ifndef QT_NO_THREAD # include #endif #include #include #include #include "../shared/qaxtypes.h" QT_BEGIN_NAMESPACE /* \internal \class QAxMetaObject \brief The QAxMetaObject class stores extended information */ struct QAxMetaObject : public QMetaObject { QAxMetaObject() { d.data = 0; d.stringdata = 0; } ~QAxMetaObject() { delete [] (int*)d.data; delete [] (char*)d.stringdata; } int numParameter(const QByteArray &prototype); QByteArray paramType(const QByteArray &signature, int index, bool *out = 0); QByteArray propertyType(const QByteArray &propertyName); void parsePrototype(const QByteArray &prototype); DISPID dispIDofName(const QByteArray &name, IDispatch *disp); private: friend class MetaObjectGenerator; // save information about QAxEventSink connections, and connect when found in cache QList connectionInterfaces; // DISPID -> signal name QMap< QUuid, QMap > sigs; // DISPID -> property changed signal name QMap< QUuid, QMap > propsigs; // DISPID -> property name QMap< QUuid, QMap > props; // Prototype -> member info QHash > memberInfo; QMap realPrototype; // DISPID cache QHash dispIDs; }; void QAxMetaObject::parsePrototype(const QByteArray &prototype) { QByteArray realProto = realPrototype.value(prototype, prototype); QByteArray parameters = realProto.mid(realProto.indexOf('(') + 1); parameters.truncate(parameters.length() - 1); if (parameters.isEmpty()) { memberInfo.insert(prototype, QList()); } else { QList plist = parameters.split(','); memberInfo.insert(prototype, plist); } } inline QByteArray QAxMetaObject::propertyType(const QByteArray &propertyName) { return realPrototype.value(propertyName); } int QAxMetaObject::numParameter(const QByteArray &prototype) { if (!memberInfo.contains(prototype)) parsePrototype(prototype); return memberInfo.value(prototype).count(); } QByteArray QAxMetaObject::paramType(const QByteArray &prototype, int index, bool *out) { if (!memberInfo.contains(prototype)) parsePrototype(prototype); if (out) *out = false; QList plist = memberInfo.value(prototype); if (index > plist.count() - 1) return QByteArray(); QByteArray param(plist.at(index)); if (param.isEmpty()) return QByteArray(); bool byRef = param.endsWith('&') || param.endsWith("**"); if (byRef) { param.truncate(param.length() - 1); if (out) *out = true; } return param; } inline DISPID QAxMetaObject::dispIDofName(const QByteArray &name, IDispatch *disp) { DISPID dispid = dispIDs.value(name, DISPID_UNKNOWN); if (dispid == DISPID_UNKNOWN) { // get the Dispatch ID from the object QString unicodeName = QLatin1String(name); OLECHAR *names = (TCHAR*)unicodeName.utf16(); disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid); if (dispid != DISPID_UNKNOWN) dispIDs.insert(name, dispid); } return dispid; } static QHash mo_cache; static QHash > > > enum_cache; static int mo_cache_ref = 0; static QMutex cache_mutex; static const char *const type_conversion[][2] = { { "float", "double"}, { "short", "int"}, { "char", "int"}, { "QList", "QVariantList" }, { "QList", "QVariantList" }, { "QList", "QVariantList" }, { "QList", "QVariantList" }, { "QList", "QVariantList" }, { "QList", "QVariantList" }, { 0, 0 } }; /* \internal \class QAxEventSink \brief The QAxEventSink class implements the event sink for all IConnectionPoints implemented in the COM object. */ class QAxEventSink : public IDispatch, public IPropertyNotifySink { public: QAxEventSink(QAxBase *com) : cpoint(0), ciid(IID_NULL), combase(com), ref(1) {} virtual ~QAxEventSink() { Q_ASSERT(!cpoint); } QUuid connectionInterface() const { return ciid; } QMap signalMap() const { return sigs; } QMap propertyMap() const { return props; } QMap propSignalMap() const { return propsigs; } // add a connection void advise(IConnectionPoint *cp, IID iid) { cpoint = cp; cpoint->AddRef(); ciid = iid; cpoint->Advise((IUnknown*)(IDispatch*)this, &cookie); } // disconnect from all connection points void unadvise() { combase = 0; if (cpoint) { cpoint->Unadvise(cookie); cpoint->Release(); cpoint = 0; } } void addSignal(DISPID memid, const char *name) { QByteArray signalname = name; int pi = signalname.indexOf('('); int i = 0; while (type_conversion[i][0]) { int ti = pi; int len = int(strlen(type_conversion[i][0])); while ((ti = signalname.indexOf(type_conversion[i][0], ti)) != -1) signalname.replace(ti, len, type_conversion[i][1]); ++i; } sigs.insert(memid, signalname); QMap::ConstIterator it; DISPID id = -1; for (it = propsigs.constBegin(); it!= propsigs.constEnd(); ++it) { if (it.value() == signalname) { id = it.key(); break; } } if (id != -1) propsigs.remove(id); } void addProperty(DISPID propid, const char *name, const char *signal) { props.insert(propid, name); propsigs.insert(propid, signal); } // IUnknown unsigned long __stdcall AddRef() { return ref++; } unsigned long __stdcall Release() { if (!--ref) { delete this; return 0; } return ref; } HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject) { *ppvObject = 0; if (riid == IID_IUnknown) *ppvObject = (IUnknown*)(IDispatch*)this; else if (riid == IID_IDispatch) *ppvObject = (IDispatch*)this; else if (riid == IID_IPropertyNotifySink) *ppvObject = (IPropertyNotifySink*)this; else if (ciid == riid) *ppvObject = (IDispatch*)this; else return E_NOINTERFACE; AddRef(); return S_OK; } // IDispatch HRESULT __stdcall GetTypeInfoCount(unsigned int *) { return E_NOTIMPL; } HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_NOTIMPL; } HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *) { return E_NOTIMPL; } HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID, WORD wFlags, DISPPARAMS *pDispParams, VARIANT*, EXCEPINFO*, UINT*) { // verify input if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE; if (!(wFlags & DISPATCH_METHOD)) return DISP_E_MEMBERNOTFOUND; if (!combase) return E_UNEXPECTED; QByteArray signame = sigs.value(dispIdMember); if (signame.isEmpty()) return DISP_E_MEMBERNOTFOUND; QObject *qobject = combase->qObject(); if (qobject->signalsBlocked()) return S_OK; QAxMetaObject *axmeta = combase->internalMetaObject(); const QMetaObject *meta = combase->metaObject(); int index = -1; // emit the generic signal "as is" if (signalHasReceivers(qobject, "signal(QString,int,void*)")) { index = meta->indexOfSignal("signal(QString,int,void*)"); Q_ASSERT(index != -1); QString nameString = QLatin1String(signame); void *argv[] = {0, &nameString, &pDispParams->cArgs, &pDispParams->rgvarg}; combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); } HRESULT hres = S_OK; // get the signal information from the metaobject index = -1; if (signalHasReceivers(qobject, signame)) { index = meta->indexOfSignal(signame); Q_ASSERT(index != -1); const QMetaMethod signal = meta->method(index); Q_ASSERT(signal.methodType() == QMetaMethod::Signal); Q_ASSERT(signame == signal.signature()); // verify parameter count int pcount = axmeta->numParameter(signame); int argcount = pDispParams->cArgs; if (pcount > argcount) return DISP_E_PARAMNOTOPTIONAL; else if (pcount < argcount) return DISP_E_BADPARAMCOUNT; // setup parameters (no return values in signals) bool ok = true; void *static_argv[QAX_NUM_PARAMS + 1]; void *static_argv_pointer[QAX_NUM_PARAMS + 1]; QVariant static_varp[QAX_NUM_PARAMS + 1]; void **argv = 0; void **argv_pointer = 0; // in case we need an additional level of indirection QVariant *varp = 0; if (pcount) { if (pcount <= QAX_NUM_PARAMS) { argv = static_argv; argv_pointer = static_argv_pointer; varp = static_varp; } else { argv = new void*[pcount + 1]; argv_pointer = new void*[pcount + 1]; varp = new QVariant[pcount + 1]; } argv[0] = 0; argv_pointer[0] = 0; } int p; for (p = 0; p < pcount && ok; ++p) { // map the VARIANT to the void* QByteArray ptype = axmeta->paramType(signame, p); varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype); argv_pointer[p + 1] = 0; if (varp[p + 1].isValid()) { if (varp[p + 1].type() == QVariant::UserType) { argv[p + 1] = varp[p + 1].data(); } else if (ptype == "QVariant") { argv[p + 1] = varp + p + 1; } else { argv[p + 1] = const_cast(varp[p + 1].constData()); if (ptype.endsWith("*")) { argv_pointer[p + 1] = argv[p + 1]; argv[p + 1] = argv_pointer + p + 1; } } } else if (ptype == "QVariant") { argv[p + 1] = varp + p + 1; } else { ok = false; } } if (ok) { // emit the generated signal if everything went well combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); // update the VARIANT for references and free memory for (p = 0; p < pcount; ++p) { bool out; QByteArray ptype = axmeta->paramType(signame, p, &out); if (out) { if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out)) ok = false; } } } if (argv != static_argv) { delete [] argv; delete [] argv_pointer; delete [] varp; } hres = ok ? S_OK : (ok ? DISP_E_MEMBERNOTFOUND : DISP_E_TYPEMISMATCH); } return hres; } QByteArray findProperty(DISPID dispID); // IPropertyNotifySink HRESULT __stdcall OnChanged(DISPID dispID) { // verify input if (dispID == DISPID_UNKNOWN || !combase) return S_OK; const QMetaObject *meta = combase->metaObject(); if (!meta) return S_OK; QByteArray propname(findProperty(dispID)); if (propname.isEmpty()) return S_OK; QObject *qobject = combase->qObject(); if (qobject->signalsBlocked()) return S_OK; // emit the generic signal int index = meta->indexOfSignal("propertyChanged(QString)"); if (index != -1) { QString propnameString = QString::fromLatin1(propname); void *argv[] = {0, &propnameString}; combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); } QByteArray signame = propsigs.value(dispID); if (signame.isEmpty()) return S_OK; index = meta->indexOfSignal(signame); if (index == -1) // bindable but not marked as bindable in typelib return S_OK; // get the signal information from the metaobject if (signalHasReceivers(qobject, signame)) { index = meta->indexOfSignal(signame); Q_ASSERT(index != -1); // setup parameters QVariant var = qobject->property(propname); if (!var.isValid()) return S_OK; const QMetaProperty metaProp = meta->property(meta->indexOfProperty(propname)); void *argv[] = {0, var.data()}; if (metaProp.type() == QVariant::LastType) argv[1] = &var; // emit the "changed" signal combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); } return S_OK; } HRESULT __stdcall OnRequestEdit(DISPID dispID) { if (dispID == DISPID_UNKNOWN || !combase) return S_OK; QByteArray propname(findProperty(dispID)); if (propname.isEmpty()) return S_OK; return combase->propertyWritable(propname) ? S_OK : S_FALSE; } static bool signalHasReceivers(QObject *qobject, const char *signalName) { Q_ASSERT(qobject); return ((QAxObject*)qobject)->receivers(QByteArray::number(QSIGNAL_CODE) + signalName); } IConnectionPoint *cpoint; IID ciid; ULONG cookie; QMap sigs; QMap propsigs; QMap props; QAxBase *combase; long ref; }; /* \internal \class QAxBasePrivate */ class QAxBasePrivate { public: QAxBasePrivate() : useEventSink(true), useMetaObject(true), useClassInfo(true), cachedMetaObject(false), initialized(false), tryCache(false), ptr(0), disp(0), metaobj(0) { // protect initialization QMutexLocker locker(&cache_mutex); mo_cache_ref++; qRegisterMetaType("IUnknown*", &ptr); qRegisterMetaType("IDispatch*", &disp); } ~QAxBasePrivate() { Q_ASSERT(!ptr); Q_ASSERT(!disp); // protect cleanup QMutexLocker locker(&cache_mutex); if (!--mo_cache_ref) { qDeleteAll(mo_cache); mo_cache.clear(); } CoFreeUnusedLibraries(); } inline IDispatch *dispatch() const { if (disp) return disp; if (ptr) ptr->QueryInterface(IID_IDispatch, (void**)&disp); return disp; } QString ctrl; QHash eventSink; uint useEventSink :1; uint useMetaObject :1; uint useClassInfo :1; uint cachedMetaObject :1; uint initialized :1; uint tryCache :1; IUnknown *ptr; mutable IDispatch *disp; QMap propWritable; inline QAxMetaObject *metaObject() { if (!metaobj) metaobj = new QAxMetaObject; return metaobj; } mutable QMap verbs; QAxMetaObject *metaobj; }; QByteArray QAxEventSink::findProperty(DISPID dispID) { // look up in cache, and fall back to // type info for precompiled metaobjects QByteArray propname(props.value(dispID)); if (!propname.isEmpty()) return propname; IDispatch *dispatch = combase->d->dispatch(); ITypeInfo *typeinfo = 0; if (dispatch) dispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo); if (!typeinfo) return propname; BSTR names; UINT cNames; typeinfo->GetNames(dispID, &names, 1, &cNames); if (cNames) { propname = QString::fromUtf16((const ushort *)names).toLatin1(); SysFreeString(names); } typeinfo->Release(); QByteArray propsignal(propname + "Changed("); const QMetaObject *mo = combase->metaObject(); int index = mo->indexOfProperty(propname); const QMetaProperty prop = mo->property(index); propsignal += prop.typeName(); propsignal += ")"; addProperty(dispID, propname, propsignal); return propname; } /*! \class QAxBase \brief The QAxBase class is an abstract class that provides an API to initialize and access a COM object. \inmodule QAxContainer QAxBase is an abstract class that cannot be used directly, and is instantiated through the subclasses QAxObject and QAxWidget. This class provides the API to access the COM object directly through its IUnknown implementation. If the COM object implements the IDispatch interface, the properties and methods of that object become available as Qt properties and slots. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 0 Properties exposed by the object's IDispatch implementation can be read and written through the property system provided by the Qt Object Model (both subclasses are QObjects, so you can use QObject::setProperty() and QObject::property()). Properties with multiple parameters are not supported. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 1 Write-functions for properties and other methods exposed by the object's IDispatch implementation can be called directly using dynamicCall(), or indirectly as slots connected to a signal. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 2 Outgoing events supported by the COM object are emitted as standard Qt signals. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 3 QAxBase transparently converts between COM data types and the equivalent Qt data types. Some COM types have no equivalent Qt data structure. Supported COM datatypes are listed in the first column of following table. The second column is the Qt type that can be used with the QObject property functions. The third column is the Qt type that is used in the prototype of generated signals and slots for in-parameters, and the last column is the Qt type that is used in the prototype of signals and slots for out-parameters. \table \header \i COM type \i Qt property \i in-parameter \i out-parameter \row \i VARIANT_BOOL \i bool \i bool \i bool& \row \i BSTR \i QString \i const QString& \i QString& \row \i char, short, int, long \i int \i int \i int& \row \i uchar, ushort, uint, ulong \i uint \i uint \i uint& \row \i float, double \i double \i double \i double& \row \i DATE \i QDateTime \i const QDateTime& \i QDateTime& \row \i CY \i qlonglong \i qlonglong \i qlonglong& \row \i OLE_COLOR \i QColor \i const QColor& \i QColor& \row \i SAFEARRAY(VARIANT) \i QList\ \i const QList\& \i QList\& \row \i SAFEARRAY(int), SAFEARRAY(double), SAFEARRAY(Date) \i QList\ \i const QList\& \i QList\& \row \i SAFEARRAY(BYTE) \i QByteArray \i const QByteArray& \i QByteArray& \row \i SAFEARRAY(BSTR) \i QStringList \i const QStringList& \i QStringList& \row \i VARIANT \i type-dependent \i const QVariant& \i QVariant& \row \i IFontDisp* \i QFont \i const QFont& \i QFont& \row \i IPictureDisp* \i QPixmap \i const QPixmap& \i QPixmap& \row \i IDispatch* \i QAxObject* \i \c QAxBase::asVariant() \i QAxObject* (return value) \row \i IUnknown* \i QAxObject* \i \c QAxBase::asVariant() \i QAxObject* (return value) \row \i SCODE, DECIMAL \i \e unsupported \i \e unsupported \i \e unsupported \row \i VARIANT* (Since Qt 4.5) \i \e unsupported \i \e QVariant& \i \e QVariant& \endtable Supported are also enumerations, and typedefs to supported types. To call the methods of a COM interface described by the following IDL \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 4 use the QAxBase API like this: \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 5 Note that the QList the object should fill has to be provided as an element in the parameter list of \l{QVariant}s. If you need to access properties or pass parameters of unsupported datatypes you must access the COM object directly through its \c IDispatch implementation or other interfaces. Those interfaces can be retrieved through queryInterface(). \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 6 To get the definition of the COM interfaces you will have to use the header files provided with the component you want to use. Some compilers can also import type libraries using the #import compiler directive. See the component documentation to find out which type libraries you have to import, and how to use them. If you need to react to events that pass parameters of unsupported datatypes you can use the generic signal that delivers the event data as provided by the COM event. \sa QAxObject, QAxWidget, QAxScript, {ActiveQt Framework} */ /*! \typedef QAxBase::PropertyBag A QMap that can store properties as name:value pairs. */ /*! Creates a QAxBase object that wraps the COM object \a iface. If \a iface is 0 (the default), use setControl() to instantiate a COM object. */ QAxBase::QAxBase(IUnknown *iface) { d = new QAxBasePrivate(); d->ptr = iface; if (d->ptr) { d->ptr->AddRef(); d->initialized = true; } #if defined(Q_OS_WINCE) CoInitializeEx(0, COINIT_MULTITHREADED); #endif } /*! Shuts down the COM object and destroys the QAxBase object. \sa clear() */ QAxBase::~QAxBase() { #if defined(Q_OS_WINCE) CoUninitialize(); #endif clear(); delete d; d = 0; } /*! \internal Used by subclasses generated with dumpcpp to balance reference count. */ void QAxBase::internalRelease() { if (d->ptr) d->ptr->Release(); } /*! \internal Used by subclasses generated with dumpcpp to implement cast-operators. */ void QAxBase::initializeFrom(QAxBase *that) { if (d->ptr) return; d->ptr = that->d->ptr; if (d->ptr) { d->ptr->AddRef(); d->initialized = true; } } QAxMetaObject *QAxBase::internalMetaObject() const { return d->metaObject(); } /*! \property QAxBase::control \brief the name of the COM object wrapped by this QAxBase object. Setting this property initilializes the COM object. Any COM object previously set is shut down. The most efficient way to set this property is by using the registered component's UUID, e.g. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 7 The second fastest way is to use the registered control's class name (with or without version number), e.g. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 8 The slowest, but easiest way to use is to use the control's full name, e.g. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 9 It is also possible to initialize the object from a file, e.g. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 10 If the component's UUID is used the following patterns can be used to initialize the control on a remote machine, to initialize a licensed control or to connect to a running object: \list \i To initialize the control on a different machine use the following pattern: \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 11 \i To initialize a licensed control use the following pattern: \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 12 \i To connect to an already running object use the following pattern: \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 13 \endlist The first two patterns can be combined, e.g. to initialize a licensed control on a remote machine: \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 14 The control's read function always returns the control's UUID, if provided including the license key, and the name of the server, but not including the username, the domain or the password. */ bool QAxBase::setControl(const QString &c) { if (c.toLower() == d->ctrl.toLower()) return !d->ctrl.isEmpty(); QString search = c; // don't waste time for DCOM requests int dcomIDIndex = search.indexOf(QLatin1String("/{")); if ((dcomIDIndex == -1 || dcomIDIndex != search.length()-39) && !search.endsWith(QLatin1String("}&"))) { QUuid uuid(search); if (uuid.isNull()) { CLSID clsid; HRESULT res = CLSIDFromProgID((WCHAR*)c.utf16(), &clsid); if (res == S_OK) search = QUuid(clsid).toString(); else { QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\"), QSettings::NativeFormat); search = controls.value(c + QLatin1String("/CLSID/Default")).toString(); if (search.isEmpty()) { controls.beginGroup(QLatin1String("/CLSID")); QStringList clsids = controls.childGroups(); for (QStringList::Iterator it = clsids.begin(); it != clsids.end(); ++it) { QString clsid = *it; QString name = controls.value(clsid + QLatin1String("/Default")).toString(); if (name == c) { search = clsid; break; } } controls.endGroup(); } } } if (search.isEmpty()) search = c; } if (search.toLower() == d->ctrl.toLower()) return !d->ctrl.isEmpty(); clear(); d->ctrl = search; d->tryCache = true; if (!initialize(&d->ptr)) d->initialized = true; if (isNull()) { qWarning("QAxBase::setControl: requested control %s could not be instantiated", c.toLatin1().data()); clear(); return false; } return true; } QString QAxBase::control() const { return d->ctrl; } /*! Disables the event sink implementation for this ActiveX container. If you don't intend to listen to the ActiveX control's events use this function to speed up the meta object generation. Some ActiveX controls might be unstable when connected to an event sink. To get OLE events you must use standard COM methods to register your own event sink. Use queryInterface() to get access to the raw COM object. Note that this function should be called immediately after construction of the object. */ void QAxBase::disableEventSink() { d->useEventSink = false; } /*! Disables the meta object generation for this ActiveX container. This also disables the event sink and class info generation. If you don't intend to use the Qt meta object implementation call this function to speed up instantiation of the control. You will still be able to call the object through \l dynamicCall(), but signals, slots and properties will not be available with QObject APIs. Some ActiveX controls might be unstable when used with OLE automation. Use standard COM methods to use those controls through the COM interfaces provided by queryInterface(). Note that this function must be called immediately after construction of the object. */ void QAxBase::disableMetaObject() { d->useMetaObject = false; d->useEventSink = false; d->useClassInfo = false; } /*! Disables the class info generation for this ActiveX container. If you don't require any class information about the ActiveX control use this function to speed up the meta object generation. Note that this function must be called immediately after construction of the object */ void QAxBase::disableClassInfo() { d->useClassInfo = false; } /*! Disconnects and destroys the COM object. If you reimplement this function you must also reimplement the destructor to call clear(), and call this implementation at the end of your clear() function. */ void QAxBase::clear() { QHash::Iterator it = d->eventSink.begin(); while (it != d->eventSink.end()) { QAxEventSink *eventSink = it.value(); ++it; if (eventSink) { eventSink->unadvise(); eventSink->Release(); } } d->eventSink.clear(); if (d->disp) { d->disp->Release(); d->disp = 0; } if (d->ptr) { d->ptr->Release(); d->ptr = 0; d->initialized = false; } d->ctrl.clear(); if (!d->cachedMetaObject) delete d->metaobj; d->metaobj = 0; } /*! \since 4.1 Returns the list of verbs that the COM object can execute. If the object does not implement IOleObject, or does not support any verbs, then this function returns an empty stringlist. Note that the OLE default verbs (OLEIVERB_SHOW etc) are not included in the list. */ QStringList QAxBase::verbs() const { if (!d->ptr) return QStringList(); if (d->verbs.isEmpty()) { IOleObject *ole = 0; d->ptr->QueryInterface(IID_IOleObject, (void**)&ole); if (ole) { IEnumOLEVERB *enumVerbs = 0; ole->EnumVerbs(&enumVerbs); if (enumVerbs) { enumVerbs->Reset(); ULONG c; OLEVERB verb; while (enumVerbs->Next(1, &verb, &c) == S_OK) { if (!verb.lpszVerbName) continue; QString verbName = QString::fromUtf16((const ushort *)verb.lpszVerbName); if (!verbName.isEmpty()) d->verbs.insert(verbName, verb.lVerb); } enumVerbs->Release(); } ole->Release(); } } return d->verbs.keys(); } /*! \internal */ long QAxBase::indexOfVerb(const QString &verb) const { return d->verbs.value(verb); } /*! This virtual function is called by setControl() and creates the requested COM object. \a ptr is set to the object's IUnknown implementation. The function returns true if the object initialization succeeded; otherwise the function returns false. The default implementation interprets the string returned by control(), and calls initializeRemote(), initializeLicensed() or initializeActive() if the string matches the respective patterns. If control() is the name of an existing file, initializeFromFile() is called. If no pattern is matched, or if remote or licensed initialization fails, CoCreateInstance is used directly to create the object. See the \l control property documentation for details about supported patterns. The interface returned in \a ptr must be referenced exactly once when this function returns. The interface provided by e.g. CoCreateInstance is already referenced, and there is no need to reference it again. */ bool QAxBase::initialize(IUnknown **ptr) { if (*ptr || control().isEmpty()) return false; *ptr = 0; bool res = false; const QString ctrl(d->ctrl); if (ctrl.contains(QLatin1String("/{"))) // DCOM request res = initializeRemote(ptr); else if (ctrl.contains(QLatin1String("}:"))) // licensed control res = initializeLicensed(ptr); else if (ctrl.contains(QLatin1String("}&"))) // running object res = initializeActive(ptr); else if (QFile::exists(ctrl)) // existing file res = initializeFromFile(ptr); if (!res) { // standard HRESULT hres = CoCreateInstance(QUuid(ctrl), 0, CLSCTX_SERVER, IID_IUnknown, (void**)ptr); res = S_OK == hres; #ifndef QT_NO_DEBUG if (!res) qErrnoWarning(hres, "CoCreateInstance failure"); #endif } return *ptr != 0; } /*! Creates an instance of a licensed control, and returns the IUnknown interface to the object in \a ptr. This functions returns true if successful, otherwise returns false. This function is called by initialize() if the control string contains the substring "}:". The license key needs to follow this substring. \sa initialize() */ bool QAxBase::initializeLicensed(IUnknown** ptr) { int at = control().lastIndexOf(QLatin1String("}:")); QString clsid(control().left(at)); QString key(control().mid(at+2)); IClassFactory *factory = 0; CoGetClassObject(QUuid(clsid), CLSCTX_SERVER, 0, IID_IClassFactory, (void**)&factory); if (!factory) return false; initializeLicensedHelper(factory, key, ptr); factory->Release(); return *ptr != 0; } /* \internal Called by initializeLicensed and initializedRemote to create an object via IClassFactory2. */ bool QAxBase::initializeLicensedHelper(void *f, const QString &key, IUnknown **ptr) { IClassFactory *factory = (IClassFactory*)f; IClassFactory2 *factory2 = 0; factory->QueryInterface(IID_IClassFactory2, (void**)&factory2); if (factory2) { BSTR bkey = QStringToBSTR(key); HRESULT hres = factory2->CreateInstanceLic(0, 0, IID_IUnknown, bkey, (void**)ptr); SysFreeString(bkey); #ifdef QT_DEBUG LICINFO licinfo; licinfo.cbLicInfo = sizeof(LICINFO); factory2->GetLicInfo(&licinfo); if (hres != S_OK) { SetLastError(hres); qErrnoWarning("CreateInstanceLic failed"); if (!licinfo.fLicVerified) { qWarning("Wrong license key specified, and machine is not fully licensed."); } else if (licinfo.fRuntimeKeyAvail) { BSTR licenseKey; factory2->RequestLicKey(0, &licenseKey); QString qlicenseKey = QString::fromUtf16((const ushort *)licenseKey); SysFreeString(licenseKey); qWarning("Use license key is '%s' to create object on unlicensed machine.", qlicenseKey.toLatin1().constData()); } } else if (licinfo.fLicVerified) { qWarning("Machine is fully licensed for '%s'", control().toLatin1().constData()); if (licinfo.fRuntimeKeyAvail) { BSTR licenseKey; factory2->RequestLicKey(0, &licenseKey); QString qlicenseKey = QString::fromUtf16((const ushort *)licenseKey); SysFreeString(licenseKey); if (qlicenseKey != key) qWarning("Runtime license key is '%s'", qlicenseKey.toLatin1().constData()); } } #else Q_UNUSED(hres); #endif factory2->Release(); } else { // give it a shot without license factory->CreateInstance(0, IID_IUnknown, (void**)ptr); } return *ptr != 0; } /*! Connects to an active instance running on the current machine, and returns the IUnknown interface to the running object in \a ptr. This function returns true if successful, otherwise returns false. This function is called by initialize() if the control string contains the substring "}&". \sa initialize() */ bool QAxBase::initializeActive(IUnknown** ptr) { #if defined(Q_OS_WINCE) Q_UNUSED(ptr); return false; #else int at = control().lastIndexOf(QLatin1String("}&")); QString clsid(control().left(at)); GetActiveObject(QUuid(clsid), 0, ptr); return *ptr != 0; #endif } #ifdef Q_CC_GNU # ifndef OLEPENDER_NONE # define OLERENDER_NONE 0 # endif #endif /*! Creates the COM object handling the filename in the control property, and returns the IUnknown interface to the object in \a ptr. This function returns true if successful, otherwise returns false. This function is called by initialize() if the control string is the name of an existing file. \sa initialize() */ bool QAxBase::initializeFromFile(IUnknown** ptr) { #if defined(Q_OS_WINCE) Q_UNUSED(ptr); return false; #else IStorage *storage = 0; ILockBytes * bytes = 0; HRESULT hres = ::CreateILockBytesOnHGlobal(0, TRUE, &bytes); hres = ::StgCreateDocfileOnILockBytes(bytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage); hres = OleCreateFromFile(CLSID_NULL, reinterpret_cast(control().utf16()), IID_IUnknown, OLERENDER_NONE, 0, 0, storage, (void**)ptr); storage->Release(); bytes->Release(); return hres == S_OK; #endif } // There seams to be a naming problem in mingw headers #ifdef Q_CC_GNU #ifndef COAUTHIDENTITY #define COAUTHIDENTITY AUTH_IDENTITY #endif #endif /*! Creates the instance on a remote server, and returns the IUnknown interface to the object in \a ptr. This function returns true if successful, otherwise returns false. This function is called by initialize() if the control string contains the substring "/{". The information about the remote machine needs to be provided in front of the substring. \sa initialize() */ bool QAxBase::initializeRemote(IUnknown** ptr) { int at = control().lastIndexOf(QLatin1String("/{")); QString server(control().left(at)); QString clsid(control().mid(at+1)); QString user; QString domain; QString passwd; QString key; at = server.indexOf(QChar::fromLatin1('@')); if (at != -1) { user = server.left(at); server = server.mid(at+1); at = user.indexOf(QChar::fromLatin1(':')); if (at != -1) { passwd = user.mid(at+1); user = user.left(at); } at = user.indexOf(QChar::fromLatin1('/')); if (at != -1) { domain = user.left(at); user = user.mid(at+1); } } at = clsid.lastIndexOf(QLatin1String("}:")); if (at != -1) { key = clsid.mid(at+2); clsid = clsid.left(at); } d->ctrl = server + QChar::fromLatin1('/') + clsid; if (!key.isEmpty()) d->ctrl = d->ctrl + QChar::fromLatin1(':') + key; COAUTHIDENTITY authIdentity; authIdentity.UserLength = user.length(); authIdentity.User = authIdentity.UserLength ? (ushort*)user.utf16() : 0; authIdentity.DomainLength = domain.length(); authIdentity.Domain = authIdentity.DomainLength ? (ushort*)domain.utf16() : 0; authIdentity.PasswordLength = passwd.length(); authIdentity.Password = authIdentity.PasswordLength ? (ushort*)passwd.utf16() : 0; authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; COAUTHINFO authInfo; authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT; authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE; authInfo.pwszServerPrincName = 0; authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT; authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE; authInfo.pAuthIdentityData = &authIdentity; authInfo.dwCapabilities = 0; COSERVERINFO serverInfo; serverInfo.dwReserved1 = 0; serverInfo.dwReserved2 = 0; serverInfo.pAuthInfo = &authInfo; serverInfo.pwszName = (WCHAR*)server.utf16(); IClassFactory *factory = 0; HRESULT res = CoGetClassObject(QUuid(clsid), CLSCTX_REMOTE_SERVER, &serverInfo, IID_IClassFactory, (void**)&factory); if (factory) { if (!key.isEmpty()) initializeLicensedHelper(factory, key, ptr); else res = factory->CreateInstance(0, IID_IUnknown, (void**)ptr); factory->Release(); } #ifndef QT_NO_DEBUG if (res != S_OK) qErrnoWarning(res, "initializeRemote Failed"); #endif return res == S_OK; } /*! Requests the interface \a uuid from the COM object and sets the value of \a iface to the provided interface, or to 0 if the requested interface could not be provided. Returns the result of the QueryInterface implementation of the COM object. \sa control */ long QAxBase::queryInterface(const QUuid &uuid, void **iface) const { *iface = 0; if (!d->ptr) { ((QAxBase*)this)->initialize(&d->ptr); d->initialized = true; } if (d->ptr && !uuid.isNull()) return d->ptr->QueryInterface(uuid, iface); return E_NOTIMPL; } class MetaObjectGenerator { public: MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr); MetaObjectGenerator(ITypeLib *typelib, ITypeInfo *typeinfo); ~MetaObjectGenerator(); QMetaObject *metaObject(const QMetaObject *parentObject, const QByteArray &className = QByteArray()); void readClassInfo(); void readEnumInfo(); void readInterfaceInfo(); void readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs); void readVarsInfo(ITypeInfo *typeinfo, ushort nVars); void readEventInfo(); void readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint); inline void addClassInfo(const char *key, const char *value) { classinfo_list.insert(key, value); } private: void init(); QMetaObject *tryCache(); QByteArray createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList &names, QByteArray &type, QList ¶meters); QByteArray usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function); QByteArray guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function); // ### from qmetaobject.cpp enum ProperyFlags { Invalid = 0x00000000, Readable = 0x00000001, Writable = 0x00000002, Resettable = 0x00000004, EnumOrFlag = 0x00000008, StdCppSet = 0x00000100, // Override = 0x00000200, Designable = 0x00001000, ResolveDesignable = 0x00002000, Scriptable = 0x00004000, ResolveScriptable = 0x00008000, Stored = 0x00010000, ResolveStored = 0x00020000, Editable = 0x00040000, ResolveEditable = 0x00080000, User = 0x00100000, ResolveUser = 0x00200000, // And our own - don't use the upper byte, as it's used for the property type RequestingEdit = 0x00400000, Bindable = 0x00800000 }; enum MemberFlags { AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, MemberMethod = 0x00, MemberSignal = 0x04, MemberSlot = 0x08, MemberCompatibility = 0x10, MemberCloned = 0x20, MemberScriptable = 0x40, }; inline QList paramList(const QByteArray &proto) { QByteArray prototype(proto); QByteArray parameters = prototype.mid(prototype.indexOf('(') + 1); parameters.truncate(parameters.length() - 1); QList plist = parameters.split(','); return plist; } inline QByteArray replaceType(const QByteArray &type) { int i = 0; while (type_conversion[i][0]) { int len = int(strlen(type_conversion[i][0])); int ti; if ((ti = type.indexOf(type_conversion[i][0])) != -1) { QByteArray rtype(type); rtype.replace(ti, len, type_conversion[i][1]); return rtype; } ++i; } return type; } QByteArray replacePrototype(const QByteArray &prototype) { QByteArray proto(prototype); QList plist = paramList(prototype); for (int p = 0; p < plist.count(); ++p) { QByteArray param(plist.at(p)); if (param != replaceType(param)) { int type = 0; while (type_conversion[type][0]) { int paren = proto.indexOf('('); while ((paren = proto.indexOf(type_conversion[type][0])) != -1) { proto.replace(paren, qstrlen(type_conversion[type][0]), type_conversion[type][1]); } ++type; } break; } } return proto; } QMap classinfo_list; inline bool hasClassInfo(const char *key) { return classinfo_list.contains(key); } struct Method { Method() : flags(0) {} QByteArray type; QByteArray parameters; int flags; QByteArray realPrototype; }; QMap signal_list; inline void addSignal(const QByteArray &prototype, const QByteArray ¶meters) { QByteArray proto(replacePrototype(prototype)); Method &signal = signal_list[proto]; signal.type = 0; signal.parameters = parameters; signal.flags = QMetaMethod::Public | MemberSignal; if (proto != prototype) signal.realPrototype = prototype; } void addChangedSignal(const QByteArray &function, const QByteArray &type, int memid); inline bool hasSignal(const QByteArray &prototype) { return signal_list.contains(prototype); } QMap slot_list; inline void addSlot(const QByteArray &type, const QByteArray &prototype, const QByteArray ¶meters, int flags = QMetaMethod::Public) { QByteArray proto = replacePrototype(prototype); Method &slot = slot_list[proto]; slot.type = replaceType(type); slot.parameters = parameters; slot.flags = flags | MemberSlot; if (proto != prototype) slot.realPrototype = prototype; } void addSetterSlot(const QByteArray &property); inline bool hasSlot(const QByteArray &prototype) { return slot_list.contains(prototype); } struct Property { Property() : typeId(0) {} QByteArray type; uint typeId; QByteArray realType; }; QMap property_list; void addProperty(const QByteArray &type, const QByteArray &name, uint flags) { Property &prop = property_list[name]; if (!type.isEmpty() && type != "HRESULT") { prop.type = replaceType(type); if (prop.type != type) prop.realType = type; } if (flags & Writable) flags |= Stored; prop.typeId |= flags; QVariant::Type vartype = QVariant::nameToType(prop.type); switch(vartype) { case QVariant::Invalid: if (prop.type == "QVariant") { prop.typeId |= 0xff << 24; break; } // fall through case QVariant::UserType: if (QMetaType::type(prop.type) == -1) qWarning("QAxBase: Unsupported property type: %s", prop.type.data()); break; default: prop.typeId |= vartype << 24; break; } } inline bool hasProperty(const QByteArray &name) { return property_list.contains(name); } inline QByteArray propertyType(const QByteArray &name) { return property_list.value(name).type; } QMap > > enum_list; inline void addEnumValue(const QByteArray &enumname, const QByteArray &key, int value) { enum_list[enumname].append(QPair(key, value)); } inline bool hasEnum(const QByteArray &enumname) { return enum_list.contains(enumname); } QAxBase *that; QAxBasePrivate *d; IDispatch *disp; ITypeInfo *dispInfo; ITypeInfo *classInfo; ITypeLib *typelib; QByteArray current_typelib; QSettings iidnames; QString cacheKey; QByteArray debugInfo; QUuid iid_propNotifySink; friend QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject); }; QMetaObject *qax_readEnumInfo(ITypeLib *typeLib, const QMetaObject *parentObject) { MetaObjectGenerator generator(typeLib, 0); generator.readEnumInfo(); return generator.metaObject(parentObject, "EnumInfo"); } QMetaObject *qax_readInterfaceInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject) { MetaObjectGenerator generator(typeLib, typeInfo); QString className; BSTR bstr; if (S_OK != typeInfo->GetDocumentation(-1, &bstr, 0, 0, 0)) return 0; className = QString::fromUtf16((const ushort *)bstr); SysFreeString(bstr); generator.readEnumInfo(); generator.readFuncsInfo(typeInfo, 0); generator.readVarsInfo(typeInfo, 0); return generator.metaObject(parentObject, className.toLatin1()); } QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject) { MetaObjectGenerator generator(typeLib, 0); generator.addSignal("exception(int,QString,QString,QString)", "code,source,disc,help"); generator.addSignal("propertyChanged(QString)", "name"); QString className; BSTR bstr; if (S_OK != classInfo->GetDocumentation(-1, &bstr, 0, 0, 0)) return 0; className = QString::fromUtf16((const ushort *)bstr); SysFreeString(bstr); generator.readEnumInfo(); TYPEATTR *typeattr; classInfo->GetTypeAttr(&typeattr); if (typeattr) { int nInterfaces = typeattr->cImplTypes; classInfo->ReleaseTypeAttr(typeattr); for (int index = 0; index < nInterfaces; ++index) { HREFTYPE refType; if (S_OK != classInfo->GetRefTypeOfImplType(index, &refType)) continue; int flags = 0; classInfo->GetImplTypeFlags(index, &flags); if (flags & IMPLTYPEFLAG_FRESTRICTED) continue; ITypeInfo *interfaceInfo = 0; classInfo->GetRefTypeInfo(refType, &interfaceInfo); if (!interfaceInfo) continue; interfaceInfo->GetDocumentation(-1, &bstr, 0, 0, 0); QString interfaceName = QString::fromUtf16((const ushort *)bstr); SysFreeString(bstr); QByteArray key; TYPEATTR *typeattr = 0; interfaceInfo->GetTypeAttr(&typeattr); if (flags & IMPLTYPEFLAG_FSOURCE) { if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN)) key = "Event Interface " + QByteArray::number(index); generator.readEventInterface(interfaceInfo, 0); } else { if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN)) key = "Interface " + QByteArray::number(index); generator.readFuncsInfo(interfaceInfo, 0); generator.readVarsInfo(interfaceInfo, 0); } if (!key.isEmpty()) generator.addClassInfo(key.data(), interfaceName.toLatin1()); if (typeattr) interfaceInfo->ReleaseTypeAttr(typeattr); interfaceInfo->Release(); } } return generator.metaObject(parentObject, className.toLatin1()); } MetaObjectGenerator::MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr) : that(ax), d(dptr), disp(0), dispInfo(0), classInfo(0), typelib(0), iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat) { init(); } MetaObjectGenerator::MetaObjectGenerator(ITypeLib *tlib, ITypeInfo *tinfo) : that(0), d(0), disp(0), dispInfo(tinfo), classInfo(0), typelib(tlib), iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat) { init(); if (dispInfo) dispInfo->AddRef(); if (typelib) { typelib->AddRef(); BSTR bstr; typelib->GetDocumentation(-1, &bstr, 0, 0, 0); current_typelib = QString::fromUtf16((const ushort *)bstr).toLatin1(); SysFreeString(bstr); } readClassInfo(); } void MetaObjectGenerator::init() { if (d) disp = d->dispatch(); iid_propNotifySink = IID_IPropertyNotifySink; addSignal("signal(QString,int,void*)", "name,argc,argv"); addSignal("exception(int,QString,QString,QString)", "code,source,disc,help"); addSignal("propertyChanged(QString)", "name"); if (d || dispInfo) { addProperty("QString", "control", Readable|Writable|Designable|Scriptable|Stored|Editable|StdCppSet); } } MetaObjectGenerator::~MetaObjectGenerator() { if (dispInfo) dispInfo->Release(); if (classInfo) classInfo->Release(); if (typelib) typelib->Release(); } bool qax_dispatchEqualsIDispatch = true; QList qax_qualified_usertypes; QByteArray MetaObjectGenerator::usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function) { HREFTYPE usertype = tdesc.hreftype; if (tdesc.vt != VT_USERDEFINED) return 0; QByteArray typeName; ITypeInfo *usertypeinfo = 0; info->GetRefTypeInfo(usertype, &usertypeinfo); if (usertypeinfo) { ITypeLib *usertypelib = 0; UINT index; usertypeinfo->GetContainingTypeLib(&usertypelib, &index); if (usertypelib) { // get type library name BSTR typelibname = 0; usertypelib->GetDocumentation(-1, &typelibname, 0, 0, 0); QByteArray typeLibName = QString::fromUtf16((const ushort *)typelibname).toLatin1(); SysFreeString(typelibname); // get type name BSTR usertypename = 0; usertypelib->GetDocumentation(index, &usertypename, 0, 0, 0); QByteArray userTypeName = QString::fromUtf16((const ushort *)usertypename).toLatin1(); SysFreeString(usertypename); if (hasEnum(userTypeName)) // known enum? typeName = userTypeName; else if (userTypeName == "OLE_COLOR" || userTypeName == "VB_OLE_COLOR") typeName = "QColor"; else if (userTypeName == "IFontDisp" || userTypeName == "IFontDisp*" || userTypeName == "IFont" || userTypeName == "IFont*") typeName = "QFont"; else if (userTypeName == "Picture" || userTypeName == "Picture*") typeName = "QPixmap"; if (typeName.isEmpty()) { TYPEATTR *typeattr = 0; usertypeinfo->GetTypeAttr(&typeattr); if (typeattr) { switch(typeattr->typekind) { case TKIND_ALIAS: userTypeName = guessTypes(typeattr->tdescAlias, usertypeinfo, function); break; case TKIND_DISPATCH: case TKIND_COCLASS: if (qax_dispatchEqualsIDispatch) { userTypeName = "IDispatch"; } else { if (typeLibName != current_typelib) userTypeName = typeLibName + "::" + userTypeName; if (!qax_qualified_usertypes.contains(userTypeName)) qax_qualified_usertypes << userTypeName; } break; case TKIND_ENUM: if (typeLibName != current_typelib) userTypeName = typeLibName + "::" + userTypeName; if (!qax_qualified_usertypes.contains("enum " + userTypeName)) qax_qualified_usertypes << "enum " + userTypeName; break; case TKIND_INTERFACE: if (typeLibName != current_typelib) userTypeName = typeLibName + "::" + userTypeName; if (!qax_qualified_usertypes.contains(userTypeName)) qax_qualified_usertypes << userTypeName; break; case TKIND_RECORD: if (!qax_qualified_usertypes.contains("struct " + userTypeName)) qax_qualified_usertypes << "struct "+ userTypeName; break; default: break; } } usertypeinfo->ReleaseTypeAttr(typeattr); typeName = userTypeName; } usertypelib->Release(); } usertypeinfo->Release(); } return typeName; } #define VT_UNHANDLED(x) case VT_##x: qWarning("QAxBase: Unhandled type %s", #x); str = #x; break; QByteArray MetaObjectGenerator::guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function) { QByteArray str; switch (tdesc.vt) { case VT_EMPTY: case VT_VOID: break; case VT_LPWSTR: str = "wchar_t *"; break; case VT_BSTR: str = "QString"; break; case VT_BOOL: str = "bool"; break; case VT_I1: str = "char"; break; case VT_I2: str = "short"; break; case VT_I4: case VT_INT: str = "int"; break; case VT_I8: str = "qlonglong"; break; case VT_UI1: case VT_UI2: case VT_UI4: case VT_UINT: str = "uint"; break; case VT_UI8: str = "qulonglong"; break; case VT_CY: str = "qlonglong"; break; case VT_R4: str = "float"; break; case VT_R8: str = "double"; break; case VT_DATE: str = "QDateTime"; break; case VT_DISPATCH: str = "IDispatch*"; break; case VT_VARIANT: str = "QVariant"; break; case VT_UNKNOWN: str = "IUnknown*"; break; case VT_HRESULT: str = "HRESULT"; break; case VT_PTR: str = guessTypes(*tdesc.lptdesc, info, function); switch(tdesc.lptdesc->vt) { case VT_VOID: str = "void*"; break; case VT_VARIANT: case VT_BSTR: case VT_I1: case VT_I2: case VT_I4: case VT_I8: case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8: case VT_BOOL: case VT_R4: case VT_R8: case VT_INT: case VT_UINT: case VT_CY: str += "&"; break; case VT_PTR: if (str == "QFont" || str == "QPixmap") { str += "&"; break; } else if (str == "void*") { str = "void **"; break; } // FALLTHROUGH default: if (str == "QColor") str += "&"; else if (str == "QDateTime") str += "&"; else if (str == "QVariantList") str += "&"; else if (str == "QByteArray") str += "&"; else if (str == "QStringList") str += "&"; else if (!str.isEmpty() && hasEnum(str)) str += "&"; else if (!str.isEmpty() && str != "QFont" && str != "QPixmap" && str != "QVariant") str += "*"; } break; case VT_SAFEARRAY: switch(tdesc.lpadesc->tdescElem.vt) { // some shortcuts, and generic support for lists of QVariant-supported types case VT_UI1: str = "QByteArray"; break; case VT_BSTR: str = "QStringList"; break; case VT_VARIANT: str = "QVariantList"; break; default: str = guessTypes(tdesc.lpadesc->tdescElem, info, function); if (!str.isEmpty()) str = "QList<" + str + ">"; break; } break; case VT_CARRAY: str = guessTypes(tdesc.lpadesc->tdescElem, info, function); if (!str.isEmpty()) { for (int index = 0; index < tdesc.lpadesc->cDims; ++index) str += "[" + QByteArray::number((int)tdesc.lpadesc->rgbounds[index].cElements) + "]"; } break; case VT_USERDEFINED: str = usertypeToString(tdesc, info, function); break; VT_UNHANDLED(FILETIME); VT_UNHANDLED(BLOB); VT_UNHANDLED(ERROR); VT_UNHANDLED(DECIMAL); VT_UNHANDLED(LPSTR); default: break; } if (tdesc.vt & VT_BYREF) str += "&"; str.replace("&*", "**"); return str; } void MetaObjectGenerator::readClassInfo() { // Read class information IProvideClassInfo *provideClassInfo = 0; if (d) d->ptr->QueryInterface(IID_IProvideClassInfo, (void**)&provideClassInfo); if (provideClassInfo) { provideClassInfo->GetClassInfo(&classInfo); TYPEATTR *typeattr = 0; if (classInfo) classInfo->GetTypeAttr(&typeattr); QString coClassID; if (typeattr) { QUuid clsid(typeattr->guid); coClassID = clsid.toString().toUpper(); #ifndef QAX_NO_CLASSINFO // UUID if (d->useClassInfo && !hasClassInfo("CoClass")) { QString coClassIDstr = iidnames.value(QLatin1String("/CLSID/") + coClassID + QLatin1String("/Default"), coClassID).toString(); addClassInfo("CoClass", coClassIDstr.isEmpty() ? coClassID.toLatin1() : coClassIDstr.toLatin1()); QByteArray version = QByteArray::number(typeattr->wMajorVerNum) + "." + QByteArray::number(typeattr->wMinorVerNum); if (version != "0.0") addClassInfo("Version", version); } #endif classInfo->ReleaseTypeAttr(typeattr); } provideClassInfo->Release(); provideClassInfo = 0; if (d->tryCache && !coClassID.isEmpty()) cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(coClassID) .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch); } UINT index = 0; if (disp && !dispInfo) disp->GetTypeInfo(index, LOCALE_USER_DEFAULT, &dispInfo); if (dispInfo && !typelib) dispInfo->GetContainingTypeLib(&typelib, &index); if (!typelib) { QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software"), QSettings::NativeFormat); QString tlid = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/TypeLib/.")).toString(); QString tlfile; if (!tlid.isEmpty()) { controls.beginGroup(QLatin1String("/Classes/TypeLib/") + tlid); QStringList versions = controls.childGroups(); QStringList::Iterator vit = versions.begin(); while (tlfile.isEmpty() && vit != versions.end()) { QString version = *vit; ++vit; tlfile = controls.value(QLatin1String("/") + version + QLatin1String("/0/win32/.")).toString(); } controls.endGroup(); } else { tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/InprocServer32/.")).toString(); if (tlfile.isEmpty()) tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/LocalServer32/.")).toString(); } if (!tlfile.isEmpty()) { LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); if (!typelib) { tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".tlb"); LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); } if (!typelib) { tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".olb"); LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); } } } if (!classInfo && typelib && that) typelib->GetTypeInfoOfGuid(QUuid(that->control()), &classInfo); if (classInfo && !dispInfo) { TYPEATTR *classAttr; classInfo->GetTypeAttr(&classAttr); if (classAttr) { for (int i = 0; i < classAttr->cImplTypes; ++i) { int typeFlags = 0; classInfo->GetImplTypeFlags(i, &typeFlags); if (typeFlags & IMPLTYPEFLAG_FSOURCE) continue; HREFTYPE hrefType; if (S_OK == classInfo->GetRefTypeOfImplType(i, &hrefType)) classInfo->GetRefTypeInfo(hrefType, &dispInfo); if (dispInfo) { TYPEATTR *ifaceAttr; dispInfo->GetTypeAttr(&ifaceAttr); WORD typekind = ifaceAttr->typekind; dispInfo->ReleaseTypeAttr(ifaceAttr); if (typekind & TKIND_DISPATCH) { break; } else { dispInfo->Release(); dispInfo = 0; } } } classInfo->ReleaseTypeAttr(classAttr); } } if (!d || !dispInfo || !cacheKey.isEmpty() || !d->tryCache) return; TYPEATTR *typeattr = 0; dispInfo->GetTypeAttr(&typeattr); QString interfaceID; if (typeattr) { QUuid iid(typeattr->guid); interfaceID = iid.toString().toUpper(); dispInfo->ReleaseTypeAttr(typeattr); // ### event interfaces!! if (!interfaceID.isEmpty()) cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(interfaceID) .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch); } } void MetaObjectGenerator::readEnumInfo() { if (!typelib) return; QUuid libUuid; if (d && d->tryCache) { TLIBATTR *libAttr = 0; typelib->GetLibAttr(&libAttr); if (libAttr) { libUuid = QUuid(libAttr->guid); typelib->ReleaseTLibAttr(libAttr); enum_list = enum_cache.value(libUuid); if (!enum_list.isEmpty()) return; } } int valueindex = 0; QSet clashCheck; int clashIndex = 0; int enum_serial = 0; UINT index = typelib->GetTypeInfoCount(); for (UINT i = 0; i < index; ++i) { TYPEKIND typekind; typelib->GetTypeInfoType(i, &typekind); if (typekind == TKIND_ENUM) { // Get the type information for the enum ITypeInfo *enuminfo = 0; typelib->GetTypeInfo(i, &enuminfo); if (!enuminfo) continue; // Get the name of the enumeration BSTR enumname; QByteArray enumName; if (typelib->GetDocumentation(i, &enumname, 0, 0, 0) == S_OK) { enumName = QString::fromUtf16((const ushort *)enumname).toLatin1(); SysFreeString(enumname); } else { enumName = "enum" + QByteArray::number(++enum_serial); } // Get the attributes of the enum type TYPEATTR *typeattr = 0; enuminfo->GetTypeAttr(&typeattr); if (typeattr) { // Get all values of the enumeration for (UINT vd = 0; vd < (UINT)typeattr->cVars; ++vd) { VARDESC *vardesc = 0; enuminfo->GetVarDesc(vd, &vardesc); if (vardesc && vardesc->varkind == VAR_CONST) { int value = vardesc->lpvarValue->lVal; int memid = vardesc->memid; // Get the name of the value BSTR valuename; QByteArray valueName; UINT maxNamesOut; enuminfo->GetNames(memid, &valuename, 1, &maxNamesOut); if (maxNamesOut) { valueName = QString::fromUtf16((const ushort *)valuename).toLatin1(); SysFreeString(valuename); } else { valueName = "value" + QByteArray::number(valueindex++); } if (clashCheck.contains(QString::fromLatin1(valueName))) valueName += QByteArray::number(++clashIndex); clashCheck.insert(QString::fromLatin1(valueName)); addEnumValue(enumName, valueName, value); } enuminfo->ReleaseVarDesc(vardesc); } } enuminfo->ReleaseTypeAttr(typeattr); enuminfo->Release(); } } if (!libUuid.isNull()) enum_cache.insert(libUuid, enum_list); } void MetaObjectGenerator::addChangedSignal(const QByteArray &function, const QByteArray &type, int memid) { QAxEventSink *eventSink = 0; if (d) { eventSink = d->eventSink.value(iid_propNotifySink); if (!eventSink && d->useEventSink) { eventSink = new QAxEventSink(that); d->eventSink.insert(iid_propNotifySink, eventSink); } } // generate changed signal QByteArray signalName(function); signalName += "Changed"; QByteArray signalProto = signalName + "(" + replaceType(type) + ")"; if (!hasSignal(signalProto)) addSignal(signalProto, function); if (eventSink) eventSink->addProperty(memid, function, signalProto); } void MetaObjectGenerator::addSetterSlot(const QByteArray &property) { QByteArray set; QByteArray prototype(property); if (isupper(prototype.at(0))) { set = "Set"; } else { set = "set"; prototype[0] = toupper(prototype[0]); } prototype = set + prototype + "(" + propertyType(property) + ")"; if (!hasSlot(prototype)) addSlot(0, prototype, property); } QByteArray MetaObjectGenerator::createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList &names, QByteArray &type, QList ¶meters) { QByteArray prototype; QByteArray function(names.at(0)); const QByteArray hresult("HRESULT"); // get function prototype type = guessTypes(funcdesc->elemdescFunc.tdesc, typeinfo, function); if ((type.isEmpty() || type == hresult) && funcdesc->invkind == INVOKE_PROPERTYPUT && funcdesc->lprgelemdescParam) { type = guessTypes(funcdesc->lprgelemdescParam->tdesc, typeinfo, function); } prototype = function + "("; if (funcdesc->invkind == INVOKE_FUNC && type == hresult) type = 0; int p; for (p = 1; p < names.count(); ++p) { // parameter QByteArray paramName = names.at(p); bool optional = p > (funcdesc->cParams - funcdesc->cParamsOpt); TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc; PARAMDESC pdesc = funcdesc->lprgelemdescParam[p-1].paramdesc; QByteArray ptype = guessTypes(tdesc, typeinfo, function); if (pdesc.wParamFlags & PARAMFLAG_FRETVAL) { if (ptype.endsWith("&")) { ptype.truncate(ptype.length() - 1); } else if (ptype.endsWith("**")) { ptype.truncate(ptype.length() - 1); } type = ptype; } else { prototype += ptype; if (pdesc.wParamFlags & PARAMFLAG_FOUT && !ptype.endsWith("&") && !ptype.endsWith("**")) prototype += "&"; if (optional || pdesc.wParamFlags & PARAMFLAG_FOPT) paramName += "=0"; else if (pdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) { // ### get the value from pdesc.pparamdescex paramName += "=0"; } parameters << paramName; } if (p < funcdesc->cParams && !(pdesc.wParamFlags & PARAMFLAG_FRETVAL)) prototype += ","; } if (!prototype.isEmpty()) { if (prototype.right(1) == ",") { if (funcdesc->invkind == INVOKE_PROPERTYPUT && p == funcdesc->cParams) { TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc; QByteArray ptype = guessTypes(tdesc, typeinfo, function); prototype += ptype; prototype += ")"; parameters << "rhs"; } else { prototype[prototype.length()-1] = ')'; } } else { prototype += ")"; } } return prototype; } void MetaObjectGenerator::readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs) { if (!nFuncs) { TYPEATTR *typeattr = 0; typeinfo->GetTypeAttr(&typeattr); if (typeattr) { nFuncs = typeattr->cFuncs; typeinfo->ReleaseTypeAttr(typeattr); } } // get information about all functions for (ushort fd = 0; fd < nFuncs ; ++fd) { FUNCDESC *funcdesc = 0; typeinfo->GetFuncDesc(fd, &funcdesc); if (!funcdesc) break; QByteArray function; QByteArray type; QByteArray prototype; QList parameters; // parse function description BSTR bstrNames[256]; UINT maxNames = 255; UINT maxNamesOut; typeinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); QList names; int p; for (p = 0; p < (int)maxNamesOut; ++p) { names << QString::fromUtf16((const ushort *)bstrNames[p]).toLatin1(); SysFreeString(bstrNames[p]); } // function name function = names.at(0); if ((maxNamesOut == 3 && function == "QueryInterface") || (maxNamesOut == 1 && function == "AddRef") || (maxNamesOut == 1 && function == "Release") || (maxNamesOut == 9 && function == "Invoke") || (maxNamesOut == 6 && function == "GetIDsOfNames") || (maxNamesOut == 2 && function == "GetTypeInfoCount") || (maxNamesOut == 4 && function == "GetTypeInfo")) { typeinfo->ReleaseFuncDesc(funcdesc); continue; } prototype = createPrototype(/*in*/ funcdesc, typeinfo, names, /*out*/type, parameters); // get type of function switch(funcdesc->invkind) { case INVOKE_PROPERTYGET: // property case INVOKE_PROPERTYPUT: if (funcdesc->cParams - funcdesc->cParamsOpt <= 1) { bool dontBreak = false; // getter with non-default-parameters -> fall through to function handling if (funcdesc->invkind == INVOKE_PROPERTYGET && parameters.count() && funcdesc->cParams - funcdesc->cParamsOpt) { dontBreak = true; } else { uint flags = Readable; if (funcdesc->invkind != INVOKE_PROPERTYGET) flags |= Writable; if (!(funcdesc->wFuncFlags & (FUNCFLAG_FNONBROWSABLE | FUNCFLAG_FHIDDEN))) flags |= Designable; if (!(funcdesc->wFuncFlags & FUNCFLAG_FRESTRICTED)) flags |= Scriptable; if (funcdesc->wFuncFlags & FUNCFLAG_FREQUESTEDIT) flags |= RequestingEdit; if (hasEnum(type)) flags |= EnumOrFlag; if (funcdesc->wFuncFlags & FUNCFLAG_FBINDABLE && funcdesc->invkind == INVOKE_PROPERTYGET) { addChangedSignal(function, type, funcdesc->memid); flags |= Bindable; } // Don't generate code for properties without type if (type.isEmpty()) break; addProperty(type, function, flags); // more parameters -> function handling if (funcdesc->invkind == INVOKE_PROPERTYGET && funcdesc->cParams) dontBreak = true; } if (!funcdesc->cParams) { // don't generate slots for incomplete properties if (type.isEmpty()) break; // Done for getters if (funcdesc->invkind == INVOKE_PROPERTYGET) break; // generate setter slot if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) { addSetterSlot(function); break; } } else if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) { addSetterSlot(function); // more parameters -> function handling if (funcdesc->cParams > 1) dontBreak = true; } if (!dontBreak) break; } if (funcdesc->invkind == INVOKE_PROPERTYPUT) { QByteArray set; if (isupper(prototype.at(0))) { set = "Set"; } else { set = "set"; prototype[0] = toupper(prototype[0]); } prototype = set + prototype; } // FALL THROUGH to support multi-variat properties case INVOKE_FUNC: // method { bool cloned = false; bool defargs; do { QByteArray pnames; for (p = 0; p < parameters.count(); ++p) { pnames += parameters.at(p); if (p < parameters.count() - 1) pnames += ','; } defargs = pnames.contains("=0"); int flags = QMetaMethod::Public; if (cloned) flags |= QMetaMethod::Cloned << 4; cloned |= defargs; addSlot(type, prototype, pnames.replace("=0", ""), flags); if (defargs) { parameters.takeLast(); int lastParam = prototype.lastIndexOf(","); if (lastParam == -1) lastParam = prototype.indexOf("(") + 1; prototype.truncate(lastParam); prototype += ")"; } } while (defargs); } break; default: break; } #if 0 // documentation in metaobject would be cool? // get function documentation BSTR bstrDocu; info->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0); QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); SysFreeString(bstrDocu); if (!!strDocu) desc += "[" + strDocu + "]"; desc += "\n"; #endif typeinfo->ReleaseFuncDesc(funcdesc); } } void MetaObjectGenerator::readVarsInfo(ITypeInfo *typeinfo, ushort nVars) { if (!nVars) { TYPEATTR *typeattr = 0; typeinfo->GetTypeAttr(&typeattr); if (typeattr) { nVars = typeattr->cVars; typeinfo->ReleaseTypeAttr(typeattr); } } // get information about all variables for (ushort vd = 0; vd < nVars; ++vd) { VARDESC *vardesc; typeinfo->GetVarDesc(vd, &vardesc); if (!vardesc) break; // no use if it's not a dispatched variable if (vardesc->varkind != VAR_DISPATCH) { typeinfo->ReleaseVarDesc(vardesc); continue; } // get variable name BSTR bstrName; UINT maxNames = 1; UINT maxNamesOut; typeinfo->GetNames(vardesc->memid, &bstrName, maxNames, &maxNamesOut); if (maxNamesOut != 1 || !bstrName) { typeinfo->ReleaseVarDesc(vardesc); continue; } QByteArray variableType; QByteArray variableName; uint flags = 0; variableName = QString::fromUtf16((const ushort *)bstrName).toLatin1(); SysFreeString(bstrName); // get variable type TYPEDESC typedesc = vardesc->elemdescVar.tdesc; variableType = guessTypes(typedesc, typeinfo, variableName); // generate meta property if (!hasProperty(variableName)) { flags = Readable; if (!(vardesc->wVarFlags & VARFLAG_FREADONLY)) flags |= Writable; if (!(vardesc->wVarFlags & (VARFLAG_FNONBROWSABLE | VARFLAG_FHIDDEN))) flags |= Designable; if (!(vardesc->wVarFlags & VARFLAG_FRESTRICTED)) flags |= Scriptable; if (vardesc->wVarFlags & VARFLAG_FREQUESTEDIT) flags |= RequestingEdit; if (hasEnum(variableType)) flags |= EnumOrFlag; if (vardesc->wVarFlags & VARFLAG_FBINDABLE) { addChangedSignal(variableName, variableType, vardesc->memid); flags |= Bindable; } addProperty(variableType, variableName, flags); } // generate a set slot if (!(vardesc->wVarFlags & VARFLAG_FREADONLY)) addSetterSlot(variableName); #if 0 // documentation in metaobject would be cool? // get function documentation BSTR bstrDocu; info->GetDocumentation(vardesc->memid, 0, &bstrDocu, 0, 0); QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); SysFreeString(bstrDocu); if (!!strDocu) desc += "[" + strDocu + "]"; desc += "\n"; #endif typeinfo->ReleaseVarDesc(vardesc); } } void MetaObjectGenerator::readInterfaceInfo() { ITypeInfo *typeinfo = dispInfo; if (!typeinfo) return; typeinfo->AddRef(); int interface_serial = 0; while (typeinfo) { ushort nFuncs = 0; ushort nVars = 0; ushort nImpl = 0; // get information about type TYPEATTR *typeattr; typeinfo->GetTypeAttr(&typeattr); bool interesting = true; if (typeattr) { // get number of functions, variables, and implemented interfaces nFuncs = typeattr->cFuncs; nVars = typeattr->cVars; nImpl = typeattr->cImplTypes; if ((typeattr->typekind == TKIND_DISPATCH || typeattr->typekind == TKIND_INTERFACE) && (typeattr->guid != IID_IDispatch && typeattr->guid != IID_IUnknown)) { #ifndef QAX_NO_CLASSINFO if (d && d->useClassInfo) { // UUID QUuid uuid(typeattr->guid); QString uuidstr = uuid.toString().toUpper(); uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString(); addClassInfo("Interface " + QByteArray::number(++interface_serial), uuidstr.toLatin1()); } #endif typeinfo->ReleaseTypeAttr(typeattr); } else { interesting = false; typeinfo->ReleaseTypeAttr(typeattr); } } if (interesting) { readFuncsInfo(typeinfo, nFuncs); readVarsInfo(typeinfo, nVars); } if (!nImpl) { typeinfo->Release(); typeinfo = 0; break; } // go up one base class HREFTYPE pRefType; typeinfo->GetRefTypeOfImplType(0, &pRefType); ITypeInfo *baseInfo = 0; typeinfo->GetRefTypeInfo(pRefType, &baseInfo); typeinfo->Release(); if (typeinfo == baseInfo) { // IUnknown inherits IUnknown ??? baseInfo->Release(); typeinfo = 0; break; } typeinfo = baseInfo; } } void MetaObjectGenerator::readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint) { TYPEATTR *eventattr; eventinfo->GetTypeAttr(&eventattr); if (!eventattr) return; if (eventattr->typekind != TKIND_DISPATCH) { eventinfo->ReleaseTypeAttr(eventattr); return; } QAxEventSink *eventSink = 0; if (d) { IID conniid; cpoint->GetConnectionInterface(&conniid); eventSink = d->eventSink.value(QUuid(conniid)); if (!eventSink) { eventSink = new QAxEventSink(that); d->eventSink.insert(QUuid(conniid), eventSink); eventSink->advise(cpoint, conniid); } } // get information about all event functions for (UINT fd = 0; fd < (UINT)eventattr->cFuncs; ++fd) { FUNCDESC *funcdesc; eventinfo->GetFuncDesc(fd, &funcdesc); if (!funcdesc) break; if (funcdesc->invkind != INVOKE_FUNC || funcdesc->funckind != FUNC_DISPATCH) { eventinfo->ReleaseTypeAttr(eventattr); eventinfo->ReleaseFuncDesc(funcdesc); continue; } QByteArray function; QByteArray prototype; QList parameters; // parse event function description BSTR bstrNames[256]; UINT maxNames = 255; UINT maxNamesOut; eventinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); QList names; int p; for (p = 0; p < (int)maxNamesOut; ++p) { names << QString::fromUtf16((const ushort *)bstrNames[p]).toLatin1(); SysFreeString(bstrNames[p]); } // get event function prototype function = names.at(0); QByteArray type; // dummy - we don't care about return values for signals prototype = createPrototype(/*in*/ funcdesc, eventinfo, names, /*out*/type, parameters); if (!hasSignal(prototype)) { QByteArray pnames; for (p = 0; p < parameters.count(); ++p) { pnames += parameters.at(p); if (p < parameters.count() - 1) pnames += ','; } addSignal(prototype, pnames); } if (eventSink) eventSink->addSignal(funcdesc->memid, prototype); #if 0 // documentation in metaobject would be cool? // get function documentation BSTR bstrDocu; eventinfo->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0); QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); SysFreeString(bstrDocu); if (!!strDocu) desc += "[" + strDocu + "]"; desc += "\n"; #endif eventinfo->ReleaseFuncDesc(funcdesc); } eventinfo->ReleaseTypeAttr(eventattr); } void MetaObjectGenerator::readEventInfo() { int event_serial = 0; IConnectionPointContainer *cpoints = 0; if (d && d->useEventSink) d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); if (cpoints) { // Get connection point enumerator IEnumConnectionPoints *epoints = 0; cpoints->EnumConnectionPoints(&epoints); if (epoints) { ULONG c = 1; IConnectionPoint *cpoint = 0; epoints->Reset(); QList cpointlist; do { if (cpoint) cpoint->Release(); cpoint = 0; HRESULT hr = epoints->Next(c, &cpoint, &c); if (!c || hr != S_OK) break; IID conniid; cpoint->GetConnectionInterface(&conniid); // workaround for typelibrary bug of Word.Application QUuid connuuid(conniid); if (cpointlist.contains(connuuid)) break; #ifndef QAX_NO_CLASSINFO if (d->useClassInfo) { QString uuidstr = connuuid.toString().toUpper(); uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString(); addClassInfo("Event Interface " + QByteArray::number(++event_serial), uuidstr.toLatin1()); } #endif // get information about type if (conniid == IID_IPropertyNotifySink) { // test whether property notify sink has been created already, and advise on it QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink); if (eventSink) eventSink->advise(cpoint, conniid); continue; } ITypeInfo *eventinfo = 0; if (typelib) typelib->GetTypeInfoOfGuid(conniid, &eventinfo); if (eventinfo) { // avoid recursion (see workaround above) cpointlist.append(connuuid); readEventInterface(eventinfo, cpoint); eventinfo->Release(); } } while (c); if (cpoint) cpoint->Release(); epoints->Release(); } else if (classInfo) { // no enumeration - search source interfaces and ask for those TYPEATTR *typeattr = 0; classInfo->GetTypeAttr(&typeattr); if (typeattr) { for (int i = 0; i < typeattr->cImplTypes; ++i) { int flags = 0; classInfo->GetImplTypeFlags(i, &flags); if (!(flags & IMPLTYPEFLAG_FSOURCE)) continue; HREFTYPE reference; if (S_OK != classInfo->GetRefTypeOfImplType(i, &reference)) continue; ITypeInfo *eventInfo = 0; classInfo->GetRefTypeInfo(reference, &eventInfo); if (!eventInfo) continue; TYPEATTR *eventattr = 0; eventInfo->GetTypeAttr(&eventattr); if (eventattr) { IConnectionPoint *cpoint = 0; cpoints->FindConnectionPoint(eventattr->guid, &cpoint); if (cpoint) { if (eventattr->guid == IID_IPropertyNotifySink) { // test whether property notify sink has been created already, and advise on it QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink); if (eventSink) eventSink->advise(cpoint, eventattr->guid); continue; } readEventInterface(eventInfo, cpoint); cpoint->Release(); } eventInfo->ReleaseTypeAttr(eventattr); } eventInfo->Release(); } classInfo->ReleaseTypeAttr(typeattr); } } cpoints->Release(); } } QMetaObject *MetaObjectGenerator::tryCache() { if (!cacheKey.isEmpty()) { d->metaobj = mo_cache.value(cacheKey); if (d->metaobj) { d->cachedMetaObject = true; IConnectionPointContainer *cpoints = 0; d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); if (cpoints) { QList::ConstIterator it = d->metaobj->connectionInterfaces.begin(); while (it != d->metaobj->connectionInterfaces.end()) { QUuid iid = *it; ++it; IConnectionPoint *cpoint = 0; cpoints->FindConnectionPoint(iid, &cpoint); if (cpoint) { QAxEventSink *sink = new QAxEventSink(that); sink->advise(cpoint, iid); d->eventSink.insert(iid, sink); sink->sigs = d->metaobj->sigs.value(iid); sink->props = d->metaobj->props.value(iid); sink->propsigs = d->metaobj->propsigs.value(iid); cpoint->Release(); } } cpoints->Release(); } return d->metaobj; } } return 0; } QMetaObject *MetaObjectGenerator::metaObject(const QMetaObject *parentObject, const QByteArray &className) { if (that) { readClassInfo(); if (typelib) { BSTR bstr; typelib->GetDocumentation(-1, &bstr, 0, 0, 0); current_typelib = QString::fromUtf16((const ushort *)bstr).toLatin1(); SysFreeString(bstr); } if (d->tryCache && tryCache()) return d->metaobj; readEnumInfo(); readInterfaceInfo(); readEventInfo(); } current_typelib = QByteArray(); #ifndef QAX_NO_CLASSINFO if (!debugInfo.isEmpty() && d->useClassInfo) addClassInfo("debugInfo", debugInfo); #endif QAxMetaObject *metaobj = new QAxMetaObject; // revision + classname + table + zero terminator uint int_data_size = 1+1+2+2+2+2+1; int_data_size += classinfo_list.count() * 2; int_data_size += signal_list.count() * 5; int_data_size += slot_list.count() * 5; int_data_size += property_list.count() * 3; int_data_size += enum_list.count() * 4; for (QMap > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { int_data_size += (*it).count() * 2; } uint *int_data = new uint[int_data_size]; int_data[0] = 1; // revision number int_data[1] = 0; // classname index int_data[2] = classinfo_list.count(); // num_classinfo int_data[3] = 10; // idx_classinfo int_data[4] = signal_list.count() + slot_list.count(); // num_methods int_data[5] = int_data[3] + int_data[2] * 2; // idx_signals int_data[6] = property_list.count(); // num_properties int_data[7] = int_data[5] + int_data[4] * 5; // idx_properties int_data[8] = enum_list.count(); // num_enums int_data[9] = int_data[7] + int_data[6] * 3; // idx_enums int_data[int_data_size - 1] = 0; // eod; char null('\0'); // data + zero-terminator QByteArray stringdata = that ? QByteArray(that->className()) : className; stringdata += null; stringdata.reserve(8192); uint offset = int_data[3]; //idx_classinfo // each class info in form key\0value\0 for (QMap::ConstIterator it = classinfo_list.begin(); it != classinfo_list.end(); ++it) { QByteArray key(it.key()); QByteArray value(it.value()); int_data[offset++] = stringdata.length(); stringdata += key; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += value; stringdata += null; } Q_ASSERT(offset == int_data[5]); // each signal in form prototype\0parameters\0type\0tag\0 for (QMap::ConstIterator it = signal_list.begin(); it != signal_list.end(); ++it) { QByteArray prototype(QMetaObject::normalizedSignature(it.key())); QByteArray type(it.value().type); QByteArray parameters(it.value().parameters); if (!it.value().realPrototype.isEmpty()) metaobj->realPrototype.insert(prototype, it.value().realPrototype); QByteArray tag; int flags = it.value().flags; int_data[offset++] = stringdata.length(); stringdata += prototype; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += parameters; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += type; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += tag; stringdata += null; int_data[offset++] = flags; } // each slot in form prototype\0parameters\0type\0tag\0 for (QMap::ConstIterator it = slot_list.begin(); it != slot_list.end(); ++it) { QByteArray prototype(QMetaObject::normalizedSignature(it.key())); QByteArray type(it.value().type); QByteArray parameters(it.value().parameters); if (!it.value().realPrototype.isEmpty()) metaobj->realPrototype.insert(prototype, it.value().realPrototype); QByteArray tag; int flags = it.value().flags; int_data[offset++] = stringdata.length(); stringdata += prototype; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += parameters; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += type; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += tag; stringdata += null; int_data[offset++] = flags; } Q_ASSERT(offset == int_data[7]); // each property in form name\0type\0 for (QMap::ConstIterator it = property_list.begin(); it != property_list.end(); ++it) { QByteArray name(it.key()); QByteArray type(it.value().type); QByteArray realType(it.value().realType); if (!realType.isEmpty() && realType != type) metaobj->realPrototype.insert(name, realType); uint flags = it.value().typeId; int_data[offset++] = stringdata.length(); stringdata += name; stringdata += null; int_data[offset++] = stringdata.length(); stringdata += type; stringdata += null; int_data[offset++] = flags; } Q_ASSERT(offset == int_data[9]); int value_offset = offset + enum_list.count() * 4; // each enum in form name\0 for (QMap > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { QByteArray name(it.key()); int flags = 0x0; // 0x1 for flag? int count = it.value().count(); int_data[offset++] = stringdata.length(); stringdata += name; stringdata += null; int_data[offset++] = flags; int_data[offset++] = count; int_data[offset++] = value_offset; value_offset += count * 2; } Q_ASSERT(offset == int_data[9] + enum_list.count() * 4); // each enum value in form key\0 for (QMap > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { for (QList >::ConstIterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { QByteArray key((*it2).first); int value = (*it2).second; int_data[offset++] = stringdata.length(); stringdata += key; stringdata += null; int_data[offset++] = value; } } Q_ASSERT(offset == int_data_size-1); char *string_data = new char[stringdata.length()]; memset(string_data, 0, sizeof(string_data)); memcpy(string_data, stringdata, stringdata.length()); // put the metaobject together metaobj->d.data = int_data; metaobj->d.extradata = 0; metaobj->d.stringdata = string_data; metaobj->d.superdata = parentObject; if (d) d->metaobj = metaobj; if (!cacheKey.isEmpty()) { mo_cache.insert(cacheKey, d->metaobj); d->cachedMetaObject = true; for (QHash::Iterator it = d->eventSink.begin(); it != d->eventSink.end(); ++it) { QAxEventSink *sink = it.value(); if (sink) { QUuid ciid = sink->connectionInterface(); d->metaobj->connectionInterfaces.append(ciid); d->metaobj->sigs.insert(ciid, sink->signalMap()); d->metaobj->props.insert(ciid, sink->propertyMap()); d->metaobj->propsigs.insert(ciid, sink->propSignalMap()); } } } return metaobj; } static const uint qt_meta_data_QAxBase[] = { // content: 1, // revision 0, // classname 0, 0, // classinfo 3, 10, // methods 1, 25, // properties 0, 0, // enums/sets // signals: signature, parameters, type, tag, flags 24, 9, 8, 8, 0x05, 55, 50, 8, 8, 0x05, 102, 80, 8, 8, 0x05, // properties: name, type, flags 149, 141, 0x0a095103, 0 // eod }; static const char qt_meta_stringdata_QAxBase[] = { "QAxBase\0\0name,argc,argv\0signal(QString,int,void*)\0name\0" "propertyChanged(QString)\0code,source,desc,help\0" "exception(int,QString,QString,QString)\0QString\0control\0" }; static QMetaObject qaxobject_staticMetaObject = { &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, qt_meta_data_QAxBase, 0 }; static QMetaObject qaxwidget_staticMetaObject = { &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, qt_meta_data_QAxBase, 0 }; /*! \internal The metaobject is generated on the fly from the information provided by the IDispatch and ITypeInfo interface implementations in the COM object. */ const QMetaObject *QAxBase::metaObject() const { if (d->metaobj) return d->metaobj; const QMetaObject* parentObject = parentMetaObject(); if (!d->ptr && !d->initialized) { ((QAxBase*)this)->initialize(&d->ptr); d->initialized = true; } #ifndef QT_NO_THREAD // only one thread at a time can generate meta objects QMutexLocker locker(&cache_mutex); #endif // return the default meta object if not yet initialized if (!d->ptr || !d->useMetaObject) { if (qObject()->isWidgetType()) return &qaxwidget_staticMetaObject; return &qaxobject_staticMetaObject; } MetaObjectGenerator generator((QAxBase*)this, d); return generator.metaObject(parentObject); } /*! \internal Connects to all event interfaces of the object. Called by the subclasses' connectNotify() reimplementations, so at this point the connection as actually been created already. */ void QAxBase::connectNotify() { if (d->eventSink.count()) // already listening return; IEnumConnectionPoints *epoints = 0; if (d->ptr && d->useEventSink) { IConnectionPointContainer *cpoints = 0; d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); if (!cpoints) return; cpoints->EnumConnectionPoints(&epoints); cpoints->Release(); } if (!epoints) return; UINT index; IDispatch *disp = d->dispatch(); ITypeInfo *typeinfo = 0; ITypeLib *typelib = 0; if (disp) disp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo); if (typeinfo) typeinfo->GetContainingTypeLib(&typelib, &index); if (!typelib) { epoints->Release(); return; } MetaObjectGenerator generator(this, d); bool haveEnumInfo = false; ULONG c = 1; IConnectionPoint *cpoint = 0; epoints->Reset(); do { if (cpoint) cpoint->Release(); cpoint = 0; epoints->Next(c, &cpoint, &c); if (!c || !cpoint) break; IID conniid; cpoint->GetConnectionInterface(&conniid); // workaround for typelibrary bug of Word.Application QString connuuid(QUuid(conniid).toString()); if (d->eventSink.contains(connuuid)) break; // Get ITypeInfo for source-interface, and skip if not supporting IDispatch ITypeInfo *eventinfo = 0; typelib->GetTypeInfoOfGuid(conniid, &eventinfo); if (eventinfo) { TYPEATTR *eventAttr; eventinfo->GetTypeAttr(&eventAttr); if (!eventAttr) { eventinfo->Release(); break; } TYPEKIND eventKind = eventAttr->typekind; eventinfo->ReleaseTypeAttr(eventAttr); if (eventKind != TKIND_DISPATCH) { eventinfo->Release(); break; } } // always into the cache to avoid recoursion QAxEventSink *eventSink = eventinfo ? new QAxEventSink(this) : 0; d->eventSink.insert(connuuid, eventSink); if (!eventinfo) continue; // have to get type info to support signals with enum parameters if (!haveEnumInfo) { bool wasTryCache = d->tryCache; d->tryCache = true; generator.readClassInfo(); generator.readEnumInfo(); d->tryCache = wasTryCache; haveEnumInfo = true; } generator.readEventInterface(eventinfo, cpoint); eventSink->advise(cpoint, conniid); eventinfo->Release(); } while (c); if (cpoint) cpoint->Release(); epoints->Release(); typelib->Release(); // make sure we don't try again if (!d->eventSink.count()) d->eventSink.insert(QString(), 0); } /*! \fn QString QAxBase::generateDocumentation() Returns a rich text string with documentation for the wrapped COM object. Dump the string to an HTML-file, or use it in e.g. a QTextBrowser widget. */ static bool checkHRESULT(HRESULT hres, EXCEPINFO *exc, QAxBase *that, const QString &name, uint argerr) { switch(hres) { case S_OK: return true; case DISP_E_BADPARAMCOUNT: qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name.toLatin1().data()); return false; case DISP_E_BADVARTYPE: qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name.toLatin1().data()); return false; case DISP_E_EXCEPTION: { bool printWarning = true; unsigned short code = -1; QString source, desc, help; const QMetaObject *mo = that->metaObject(); int exceptionSignal = mo->indexOfSignal("exception(int,QString,QString,QString)"); if (exceptionSignal >= 0) { if (exc->pfnDeferredFillIn) exc->pfnDeferredFillIn(exc); code = exc->wCode ? exc->wCode : exc->scode; source = QString::fromUtf16((const ushort *)exc->bstrSource); desc = QString::fromUtf16((const ushort *)exc->bstrDescription); help = QString::fromUtf16((const ushort *)exc->bstrHelpFile); uint helpContext = exc->dwHelpContext; if (helpContext && !help.isEmpty()) help += QString::fromLatin1(" [%1]").arg(helpContext); if (QAxEventSink::signalHasReceivers(that->qObject(), "exception(int,QString,QString,QString)")) { void *argv[] = {0, &code, &source, &desc, &help}; that->qt_metacall(QMetaObject::InvokeMetaMethod, exceptionSignal, argv); printWarning = false; } } if (printWarning) { qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name.toLatin1().data()); qWarning(" Code : %d", code); qWarning(" Source : %s", source.toLatin1().data()); qWarning(" Description: %s", desc.toLatin1().data()); qWarning(" Help : %s", help.toLatin1().data()); qWarning(" Connect to the exception(int,QString,QString,QString) signal to catch this exception"); } } return false; case DISP_E_MEMBERNOTFOUND: qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name.toLatin1().data()); return false; case DISP_E_NONAMEDARGS: qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name.toLatin1().data()); return false; case DISP_E_OVERFLOW: qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name.toLatin1().data()); return false; case DISP_E_PARAMNOTFOUND: qWarning("QAxBase: Error calling IDispatch member %s: Parameter %d not found", name.toLatin1().data(), argerr); return false; case DISP_E_TYPEMISMATCH: qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch in parameter %d", name.toLatin1().data(), argerr); return false; case DISP_E_UNKNOWNINTERFACE: qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name.toLatin1().data()); return false; case DISP_E_UNKNOWNLCID: qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name.toLatin1().data()); return false; case DISP_E_PARAMNOTOPTIONAL: qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name.toLatin1().data()); return false; default: qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name.toLatin1().data()); return false; } } /*! \internal */ int QAxBase::internalProperty(QMetaObject::Call call, int index, void **v) { const QMetaObject *mo = metaObject(); const QMetaProperty prop = mo->property(index + mo->propertyOffset()); QByteArray propname = prop.name(); // hardcoded control property if (propname == "control") { switch(call) { case QMetaObject::ReadProperty: *(QString*)*v = control(); break; case QMetaObject::WriteProperty: setControl(*(QString*)*v); break; case QMetaObject::ResetProperty: clear(); break; default: break; } return index - mo->propertyCount(); } // get the IDispatch if (!d->ptr || !prop.isValid()) return index; IDispatch *disp = d->dispatch(); if (!disp) return index; DISPID dispid = d->metaObject()->dispIDofName(propname, disp); if (dispid == DISPID_UNKNOWN) return index; Q_ASSERT(d->metaobj); // property found, so everthing that goes wrong now should not bother the caller index -= mo->propertyCount(); VARIANTARG arg; VariantInit(&arg); DISPPARAMS params; EXCEPINFO excepinfo; memset(&excepinfo, 0, sizeof(excepinfo)); UINT argerr = 0; HRESULT hres = E_FAIL; QByteArray proptype(prop.typeName()); switch (call) { case QMetaObject::ReadProperty: { params.cArgs = 0; params.cNamedArgs = 0; params.rgdispidNamedArgs = 0; params.rgvarg = 0; hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &arg, &excepinfo, 0); // map result VARIANTARG to void* uint type = QVariant::Int; if (!prop.isEnumType()) type = prop.type(); QVariantToVoidStar(VARIANTToQVariant(arg, proptype, type), *v, proptype, type); if ((arg.vt != VT_DISPATCH && arg.vt != VT_UNKNOWN) || type == QVariant::Pixmap || type == QVariant::Font) clearVARIANT(&arg); } break; case QMetaObject::WriteProperty: { QVariant::Type t = (QVariant::Type)prop.type(); DISPID dispidNamed = DISPID_PROPERTYPUT; params.cArgs = 1; params.cNamedArgs = 1; params.rgdispidNamedArgs = &dispidNamed; params.rgvarg = &arg; arg.vt = VT_ERROR; arg.scode = DISP_E_TYPEMISMATCH; // map void* to VARIANTARG via QVariant QVariant qvar; if (prop.isEnumType()) { qvar = *(int*)v[0]; proptype = 0; } else { if (t == QVariant::LastType) { qvar = *(QVariant*)v[0]; proptype = 0; } else if (t == QVariant::UserType) { qvar = QVariant(qRegisterMetaType(prop.typeName()), (void**)v[0]); // qVariantSetValue(qvar, *(void**)v[0], prop.typeName()); } else { proptype = d->metaObject()->propertyType(propname); qvar = QVariant(t, v[0]); } } QVariantToVARIANT(qvar, arg, proptype); if (arg.vt == VT_EMPTY || arg.vt == VT_ERROR) { qWarning("QAxBase::setProperty: Unhandled property type %s", prop.typeName()); break; } } hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, 0, &excepinfo, &argerr); clearVARIANT(&arg); break; default: break; } checkHRESULT(hres, &excepinfo, this, QLatin1String(propname), argerr); return index; } int QAxBase::internalInvoke(QMetaObject::Call call, int index, void **v) { Q_ASSERT(call == QMetaObject::InvokeMetaMethod); Q_UNUSED(call); // get the IDispatch IDispatch *disp = d->dispatch(); if (!disp) return index; const QMetaObject *mo = metaObject(); // get the slot information const QMetaMethod slot = mo->method(index + mo->methodOffset()); Q_ASSERT(slot.methodType() == QMetaMethod::Slot); QByteArray signature(slot.signature()); QByteArray slotname(signature); slotname.truncate(slotname.indexOf('(')); // Get the Dispatch ID of the method to be called bool isProperty = false; DISPID dispid = d->metaObject()->dispIDofName(slotname, disp); Q_ASSERT(d->metaobj); if (dispid == DISPID_UNKNOWN && slotname.toLower().startsWith("set")) { // see if we are calling a property set function as a slot slotname = slotname.right(slotname.length() - 3); dispid = d->metaobj->dispIDofName(slotname, disp); isProperty = true; } if (dispid == DISPID_UNKNOWN) return index; // slot found, so everthing that goes wrong now should not bother the caller index -= mo->methodCount(); // setup the parameters DISPPARAMS params; DISPID dispidNamed = DISPID_PROPERTYPUT; params.cArgs = d->metaobj->numParameter(signature); params.cNamedArgs = isProperty ? 1 : 0; params.rgdispidNamedArgs = isProperty ? &dispidNamed : 0; params.rgvarg = 0; VARIANTARG static_rgvarg[QAX_NUM_PARAMS]; if (params.cArgs) { if (params.cArgs <= QAX_NUM_PARAMS) params.rgvarg = static_rgvarg; else params.rgvarg = new VARIANTARG[params.cArgs]; } int p; for (p = 0; p < (int)params.cArgs; ++p) { bool out; QByteArray type = d->metaobj->paramType(signature, p, &out); QVariant::Type vt = QVariant::nameToType(type); QVariant qvar; if (vt != QVariant::UserType) qvar = QVariant(vt, v[p + 1]); if (!qvar.isValid()) { if (type == "IDispatch*") qVariantSetValue(qvar, *(IDispatch**)v[p+1]); else if (type == "IUnknown*") qVariantSetValue(qvar, *(IUnknown**)v[p+1]); else if (type == "QVariant") qvar = *(QVariant*)v[p + 1]; else if (mo->indexOfEnumerator(type) != -1) qvar = *(int*)v[p + 1]; else qvar = QVariant(QMetaType::type(type), v[p + 1]); } QVariantToVARIANT(qvar, params.rgvarg[params.cArgs - p - 1], type, out); } // call the method VARIANT ret; VariantInit(&ret); UINT argerr = 0; HRESULT hres = E_FAIL; EXCEPINFO excepinfo; memset(&excepinfo, 0, sizeof(excepinfo)); WORD wFlags = isProperty ? DISPATCH_PROPERTYPUT : DISPATCH_METHOD | DISPATCH_PROPERTYGET; hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags, ¶ms, &ret, &excepinfo, &argerr); // get return value if (hres == S_OK && ret.vt != VT_EMPTY) QVariantToVoidStar(VARIANTToQVariant(ret, slot.typeName()), v[0], slot.typeName()); // update out parameters for (p = 0; p < (int)params.cArgs; ++p) { bool out; QByteArray ptype = d->metaobj->paramType(signature, p, &out); if (out) QVariantToVoidStar(VARIANTToQVariant(params.rgvarg[params.cArgs - p - 1], ptype), v[p+1], ptype); } // clean up for (p = 0; p < (int)params.cArgs; ++p) clearVARIANT(params.rgvarg+p); if (params.rgvarg != static_rgvarg) delete [] params.rgvarg; checkHRESULT(hres, &excepinfo, this, QString::fromLatin1(slotname), params.cArgs-argerr-1); return index; } /*! \internal */ int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) { const QMetaObject *mo = metaObject(); if (isNull() && mo->property(id + mo->propertyOffset()).name() != QByteArray("control")) { qWarning("QAxBase::qt_metacall: Object is not initialized, or initialization failed"); return id; } switch(call) { case QMetaObject::InvokeMetaMethod: switch (mo->method(id + mo->methodOffset()).methodType()) { case QMetaMethod::Signal: QMetaObject::activate(qObject(), mo, id, v); id -= mo->methodCount(); break; case QMetaMethod::Method: case QMetaMethod::Slot: id = internalInvoke(call, id, v); break; } break; case QMetaObject::ReadProperty: case QMetaObject::WriteProperty: case QMetaObject::ResetProperty: id = internalProperty(call, id, v); break; case QMetaObject::QueryPropertyScriptable: case QMetaObject::QueryPropertyDesignable: case QMetaObject::QueryPropertyStored: case QMetaObject::QueryPropertyEditable: case QMetaObject::QueryPropertyUser: id -= mo->propertyCount(); break; } Q_ASSERT(id < 0); return id; } #ifdef QT_CHECK_STATE static void qax_noSuchFunction(int disptype, const QByteArray &name, const QByteArray &function, const QAxBase *that) { const QMetaObject *metaObject = that->metaObject(); const char *coclass = metaObject->classInfo(metaObject->indexOfClassInfo("CoClass")).value(); if (disptype == DISPATCH_METHOD) { qWarning("QAxBase::dynamicCallHelper: %s: No such method in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown"); qWarning("\tCandidates are:"); for (int i = 0; i < metaObject->methodCount(); ++i) { const QMetaMethod slot(metaObject->method(i)); if (slot.methodType() != QMetaMethod::Slot) continue; QByteArray signature = slot.signature(); if (signature.toLower().startsWith(function.toLower())) qWarning("\t\t%s", signature.data()); } } else { qWarning("QAxBase::dynamicCallHelper: %s: No such property in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown"); if (!function.isEmpty()) { qWarning("\tCandidates are:"); char f0 = function.toLower().at(0); for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) { QByteArray signature(metaObject->property(i).name()); if (!signature.isEmpty() && signature.toLower().at(0) == f0) qWarning("\t\t%s", signature.data()); } } } } #endif /*! \internal \a name is already normalized? */ bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList &vars, QByteArray &type) { if (isNull()) { qWarning("QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed"); return false; } IDispatch *disp = d->dispatch(); if (!disp) { qWarning("QAxBase::dynamicCallHelper: Object does not support automation"); return false; } const QMetaObject *mo = metaObject(); d->metaObject(); Q_ASSERT(d->metaobj); int varc = vars.count(); QByteArray normFunction = QMetaObject::normalizedSignature(name); QByteArray function(normFunction); VARIANT staticarg[QAX_NUM_PARAMS]; VARIANT *arg = 0; VARIANTARG *res = (VARIANTARG*)inout; unsigned short disptype; int id = -1; bool parse = false; if (function.contains('(')) { disptype = DISPATCH_METHOD | DISPATCH_PROPERTYGET; if (d->useMetaObject) id = mo->indexOfSlot(function); if (id >= 0) { const QMetaMethod slot = mo->method(id); Q_ASSERT(slot.methodType() == QMetaMethod::Slot); function = slot.signature(); type = slot.typeName(); } function.truncate(function.indexOf('(')); parse = !varc && normFunction.length() > function.length() + 2; if (parse) { QString args = QLatin1String(normFunction); args = args.mid(function.length() + 1); // parse argument string int list of arguments QString curArg; const QChar *c = args.unicode(); int index = 0; bool inString = false; bool inEscape = false; while (index < (int)args.length()) { QChar cc = *c; ++c; ++index; switch(cc.toLatin1()) { case 'n': if (inEscape) cc = QLatin1Char('\n'); break; case 'r': if (inEscape) cc = QLatin1Char('\r'); break; case 't': if (inEscape) cc = QLatin1Char('\t'); break; case '\\': if (!inEscape && inString) { inEscape = true; continue; } break; case '"': if (!inEscape) { inString = !inString; curArg += cc; continue; } break; case ' ': if (!inString && curArg.isEmpty()) continue; break; case ',': case ')': if (inString) break; curArg = curArg.trimmed(); if (curArg.at(0) == QLatin1Char('\"') && curArg.at(curArg.length()-1) == QLatin1Char('\"')) { vars << curArg.mid(1, curArg.length() - 2); } else { bool isNumber = false; bool isDouble = false; int number = curArg.toInt(&isNumber); double dbl = curArg.toDouble(&isDouble); if (isNumber) { vars << number; } else if (isDouble) { vars << dbl; } else { bool isEnum = false; for (int enumIndex = 0; enumIndex < mo->enumeratorCount(); ++enumIndex) { QMetaEnum metaEnum =mo->enumerator(enumIndex); int value = metaEnum.keyToValue(curArg.toLatin1()); if (value != -1 && !QByteArray(metaEnum.valueToKey(value)).isEmpty()) { vars << value; isEnum = true; break; } } if (!isEnum) vars << curArg; } } curArg.clear(); continue; default: break; } inEscape = false; curArg += cc; } varc = vars.count(); } } else { if (d->useMetaObject) id = mo->indexOfProperty(normFunction); if (id >= 0) { const QMetaProperty prop =mo->property(id); type = prop.typeName(); } if (varc == 1) { res = 0; disptype = DISPATCH_PROPERTYPUT; } else { disptype = DISPATCH_PROPERTYGET; } } if (varc) { varc = qMin(varc, d->metaobj->numParameter(normFunction)); arg = varc <= QAX_NUM_PARAMS ? staticarg : new VARIANT[varc]; for (int i = 0; i < varc; ++i) { QVariant var(vars.at(i)); VariantInit(arg + (varc - i - 1)); bool out = false; QByteArray paramType; if (disptype == DISPATCH_PROPERTYPUT) paramType = type; else if (parse || disptype == DISPATCH_PROPERTYGET) paramType = 0; else paramType = d->metaobj->paramType(normFunction, i, &out); if (!parse && d->useMetaObject && var.type() == QVariant::String || var.type() == QVariant::ByteArray) { int enumIndex =mo->indexOfEnumerator(paramType); if (enumIndex != -1) { QMetaEnum metaEnum =mo->enumerator(enumIndex); QVariantToVARIANT(metaEnum.keyToValue(var.toByteArray()), arg[varc - i - 1], "int", out); } } if (arg[varc - i - 1].vt == VT_EMPTY) QVariantToVARIANT(var, arg[varc - i - 1], paramType, out); } } DISPID dispid = d->metaobj->dispIDofName(function, disp); if (dispid == DISPID_UNKNOWN && function.toLower().startsWith("set")) { function = function.mid(3); dispid = d->metaobj->dispIDofName(function, disp); disptype = DISPATCH_PROPERTYPUT; } if (dispid == DISPID_UNKNOWN) { #ifdef QT_CHECK_STATE qax_noSuchFunction(disptype, normFunction, function, this); #endif return false; } DISPPARAMS params; DISPID dispidNamed = DISPID_PROPERTYPUT; params.cArgs = varc; params.cNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? 1 : 0; params.rgdispidNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? &dispidNamed : 0; params.rgvarg = arg; EXCEPINFO excepinfo; memset(&excepinfo, 0, sizeof(excepinfo)); UINT argerr = 0; HRESULT hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, disptype, ¶ms, res, &excepinfo, &argerr); if (disptype == (DISPATCH_METHOD|DISPATCH_PROPERTYGET) && hres == S_OK && varc) { for (int i = 0; i < varc; ++i) if (arg[varc-i-1].vt & VT_BYREF) // update out-parameters vars[i] = VARIANTToQVariant(arg[varc-i-1], vars.at(i).typeName()); } // clean up for (int i = 0; i < varc; ++i) clearVARIANT(params.rgvarg+i); if (arg && arg != staticarg) delete[] arg; return checkHRESULT(hres, &excepinfo, this, QLatin1String(function), varc-argerr-1); } /*! Calls the COM object's method \a function, passing the parameters \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, \a var6, \a var7 and \a var8, and returns the value returned by the method, or an invalid QVariant if the method does not return a value or when the function call failed. If \a function is a method of the object the string must be provided as the full prototype, for example as it would be written in a QObject::connect() call. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 15 Alternatively a function can be called passing the parameters embedded in the string, e.g. above function can also be invoked using \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 16 All parameters are passed as strings; it depends on the control whether they are interpreted correctly, and is slower than using the prototype with correctly typed parameters. If \a function is a property the string has to be the name of the property. The property setter is called when \a var1 is a valid QVariant, otherwise the getter is called. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 17 Note that it is faster to get and set properties using QObject::property() and QObject::setProperty(). dynamicCall() can also be used to call objects with a \l{QAxBase::disableMetaObject()}{disabled metaobject} wrapper, which can improve performance significantely, esp. when calling many different objects of different types during an automation process. ActiveQt will then however not validate parameters. It is only possible to call functions through dynamicCall() that have parameters or return values of datatypes supported by QVariant. See the QAxBase class documentation for a list of supported and unsupported datatypes. If you want to call functions that have unsupported datatypes in the parameter list, use queryInterface() to retrieve the appropriate COM interface, and use the function directly. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 18 This is also more efficient. */ QVariant QAxBase::dynamicCall(const char *function, const QVariant &var1, const QVariant &var2, const QVariant &var3, const QVariant &var4, const QVariant &var5, const QVariant &var6, const QVariant &var7, const QVariant &var8) { QList vars; QVariant var = var1; int argc = 1; while(var.isValid()) { vars << var; switch(++argc) { case 2: var = var2; break; case 3: var = var3; break; case 4: var = var4; break; case 5: var = var5; break; case 6: var = var6; break; case 7: var = var7; break; case 8: var = var8; break; default:var = QVariant(); break; } } return dynamicCall(function, vars); } /*! \overload Calls the COM object's method \a function, passing the parameters in \a vars, and returns the value returned by the method. If the method does not return a value or when the function call failed this function returns an invalid QVariant object. The QVariant objects in \a vars are updated when the method has out-parameters. */ QVariant QAxBase::dynamicCall(const char *function, QList &vars) { VARIANTARG res; VariantInit(&res); QByteArray rettype; if (!dynamicCallHelper(function, &res, vars, rettype)) return QVariant(); QVariant qvar = VARIANTToQVariant(res, rettype); if ((res.vt != VT_DISPATCH && res.vt != VT_UNKNOWN) || qvar.type() == QVariant::Pixmap || qvar.type() == QVariant::Font) clearVARIANT(&res); return qvar; } /*! Returns a pointer to a QAxObject wrapping the COM object provided by the method or property \a name, passing passing the parameters \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, \a var6, \a var7 and \a var8. If \a name is provided by a method the string must include the full function prototype. If \a name is a property the string must be the name of the property, and \a var1, ... \a var8 are ignored. The returned QAxObject is a child of this object (which is either of type QAxObject or QAxWidget), and is deleted when this object is deleted. It is however safe to delete the returned object yourself, and you should do so when you iterate over lists of subobjects. COM enabled applications usually have an object model publishing certain elements of the application as dispatch interfaces. Use this method to navigate the hierarchy of the object model, e.g. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 19 */ QAxObject *QAxBase::querySubObject(const char *name, const QVariant &var1, const QVariant &var2, const QVariant &var3, const QVariant &var4, const QVariant &var5, const QVariant &var6, const QVariant &var7, const QVariant &var8) { QList vars; QVariant var = var1; int argc = 1; while(var.isValid()) { vars << var; switch(++argc) { case 2: var = var2; break; case 3: var = var3; break; case 4: var = var4; break; case 5: var = var5; break; case 6: var = var6; break; case 7: var = var7; break; case 8: var = var8; break; default:var = QVariant(); break; } } return querySubObject(name, vars); } /*! \overload The QVariant objects in \a vars are updated when the method has out-parameters. */ QAxObject *QAxBase::querySubObject(const char *name, QList &vars) { QAxObject *object = 0; VARIANTARG res; VariantInit(&res); QByteArray rettype; if (!dynamicCallHelper(name, &res, vars, rettype)) return 0; switch (res.vt) { case VT_DISPATCH: if (res.pdispVal) { if (rettype.isEmpty() || rettype == "IDispatch*" || rettype == "QVariant") { object = new QAxObject(res.pdispVal, qObject()); } else if (QMetaType::type(rettype)) { QVariant qvar = VARIANTToQVariant(res, rettype, 0); object = *(QAxObject**)qvar.constData(); // qVariantGet(qvar, object, rettype); res.pdispVal->AddRef(); } if (object) ((QAxBase*)object)->d->tryCache = true; } break; case VT_UNKNOWN: if (res.punkVal) { if (rettype.isEmpty() || rettype == "IUnknown*") { object = new QAxObject(res.punkVal, qObject()); } else if (QMetaType::type(rettype)) { QVariant qvar = VARIANTToQVariant(res, rettype, 0); object = *(QAxObject**)qvar.constData(); // qVariantGet(qvar, object, rettype); res.punkVal->AddRef(); } if (object) ((QAxBase*)object)->d->tryCache = true; } break; case VT_EMPTY: #ifdef QT_CHECK_STATE { const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value(); qWarning("QAxBase::querySubObject: %s: Error calling function or property in %s (%s)" , name, control().toLatin1().data(), coclass ? coclass: "unknown"); } #endif break; default: #ifdef QT_CHECK_STATE { const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value(); qWarning("QAxBase::querySubObject: %s: Method or property is not of interface type in %s (%s)" , name, control().toLatin1().data(), coclass ? coclass: "unknown"); } #endif break; } clearVARIANT(&res); return object; } class QtPropertyBag : public IPropertyBag { public: QtPropertyBag() :ref(0) {} virtual ~QtPropertyBag() {} HRESULT __stdcall QueryInterface(REFIID iid, LPVOID *iface) { *iface = 0; if (iid == IID_IUnknown) *iface = this; else if (iid == IID_IPropertyBag) *iface = this; else return E_NOINTERFACE; AddRef(); return S_OK; } unsigned long __stdcall AddRef() { return ++ref; } unsigned long __stdcall Release() { if (!--ref) { delete this; return 0; } return ref; } HRESULT __stdcall Read(LPCOLESTR name, VARIANT *var, IErrorLog *) { if (!var) return E_POINTER; QString property = QString::fromUtf16((const ushort *)name); QVariant qvar = map.value(property); QVariantToVARIANT(qvar, *var); return S_OK; } HRESULT __stdcall Write(LPCOLESTR name, VARIANT *var) { if (!var) return E_POINTER; QString property = QString::fromUtf16((const ushort *)name); QVariant qvar = VARIANTToQVariant(*var, 0); map[property] = qvar; return S_OK; } QAxBase::PropertyBag map; private: unsigned long ref; }; /*! Returns a name:value map of all the properties exposed by the COM object. This is more efficient than getting multiple properties individually if the COM object supports property bags. \warning It is not guaranteed that the property bag implementation of the COM object returns all properties, or that the properties returned are the same as those available through the IDispatch interface. */ QAxBase::PropertyBag QAxBase::propertyBag() const { PropertyBag result; if (!d->ptr && !d->initialized) { ((QAxBase*)this)->initialize(&d->ptr); d->initialized = true; } if (isNull()) return result; IPersistPropertyBag *persist = 0; d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist); if (persist) { QtPropertyBag *pbag = new QtPropertyBag(); pbag->AddRef(); persist->Save(pbag, false, true); result = pbag->map; pbag->Release(); persist->Release(); return result; } else { const QMetaObject *mo = metaObject(); for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) { const QMetaProperty property = mo->property(p); QVariant var = qObject()->property(property.name()); result.insert(QLatin1String(property.name()), var); } } return result; } /*! Sets the properties of the COM object to the corresponding values in \a bag. \warning You should only set property bags that have been returned by the propertyBag function, as it cannot be guaranteed that the property bag implementation of the COM object supports the same properties that are available through the IDispatch interface. \sa propertyBag() */ void QAxBase::setPropertyBag(const PropertyBag &bag) { if (!d->ptr && !d->initialized) { initialize(&d->ptr); d->initialized = true; } if (isNull()) return; IPersistPropertyBag *persist = 0; d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist); if (persist) { QtPropertyBag *pbag = new QtPropertyBag(); pbag->map = bag; pbag->AddRef(); persist->Load(pbag, 0); pbag->Release(); persist->Release(); } else { const QMetaObject *mo = metaObject(); for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) { const QMetaProperty property = mo->property(p); QVariant var = bag.value(QLatin1String(property.name())); qObject()->setProperty(property.name(), var); } } } /*! Returns true if the property \a prop is writable; otherwise returns false. By default, all properties are writable. \warning Depending on the control implementation this setting might be ignored for some properties. \sa setPropertyWritable(), propertyChanged() */ bool QAxBase::propertyWritable(const char *prop) const { return d->propWritable.value(prop, true); } /*! Sets the property \a prop to writable if \a ok is true, otherwise sets \a prop to be read-only. By default, all properties are writable. \warning Depending on the control implementation this setting might be ignored for some properties. \sa propertyWritable(), propertyChanged() */ void QAxBase::setPropertyWritable(const char *prop, bool ok) { d->propWritable[prop] = ok; } /*! Returns true if there is no COM object loaded by this wrapper; otherwise return false. \sa control */ bool QAxBase::isNull() const { return !d->ptr; } /*! Returns a QVariant that wraps the COM object. The variant can then be used as a parameter in e.g. dynamicCall(). */ QVariant QAxBase::asVariant() const { if (!d->ptr && !d->initialized) { ((QAxBase*)this)->initialize(&d->ptr); d->initialized = true; } QVariant qvar; QByteArray cn(className()); if (cn == "QAxObject" || cn == "QAxWidget" || cn == "QAxBase") { if (d->dispatch()) qVariantSetValue(qvar, d->dispatch()); else if (d->ptr) qVariantSetValue(qvar, d->ptr); } else { cn = cn.mid(cn.lastIndexOf(':') + 1); QObject *object = qObject(); if (QMetaType::type(cn)) qvar = QVariant(qRegisterMetaType(cn + "*"), &object); // qVariantSetValue(qvar, qObject(), cn + "*"); } return qvar; } // internal function that creates a QAxObject from an iface // used by type-conversion code (types.cpp) void *qax_createObjectWrapper(int metaType, IUnknown *iface) { if (!iface) return 0; QAxObject *object = (QAxObject*)QMetaType::construct(metaType, 0); QAxBasePrivate *d = object->d; d->ptr = iface; d->initialized = true; // no release, since no addref return object; } /*! \fn void QAxBase::signal(const QString &name, int argc, void *argv) This generic signal gets emitted when the COM object issues the event \a name. \a argc is the number of parameters provided by the event (DISPPARAMS.cArgs), and \a argv is the pointer to the parameter values (DISPPARAMS.rgvarg). Note that the order of parameter values is turned around, ie. the last element of the array is the first parameter in the function. \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 20 Use this signal if the event has parameters of unsupported data types. Otherwise, connect directly to the signal \a name. */ /*! \fn void QAxBase::propertyChanged(const QString &name) If the COM object supports property notification, this signal gets emitted when the property called \a name is changed. */ /*! \fn void QAxBase::exception(int code, const QString &source, const QString &desc, const QString &help) This signal is emitted when the COM object throws an exception while called using the OLE automation interface IDispatch. \a code, \a source, \a desc and \a help provide information about the exception as provided by the COM server and can be used to provide useful feedback to the end user. \a help includes the help file, and the help context ID in brackets, e.g. "filename [id]". */ /*! \fn QObject *QAxBase::qObject() const \internal */ /*! \fn const char *QAxBase::className() const \internal */ QT_END_NAMESPACE #endif //QT_NO_WIN_ACTIVEQT