| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** 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 Nokia Corporation 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 |
| |
| #define QT_CHECK_STATE |
| |
| #include "qaxobject.h" |
| |
| #ifndef QT_NO_WIN_ACTIVEQT |
| |
| #include <qfile.h> |
| #include <qwidget.h> |
| |
| #include <quuid.h> |
| #include <qhash.h> |
| #include <qset.h> |
| #include <qpair.h> |
| #include <qmetaobject.h> |
| #include <qsettings.h> |
| |
| #ifndef QT_NO_THREAD |
| # include <qmutex.h> |
| #endif |
| |
| #include <qt_windows.h> |
| #include <ocidl.h> |
| #include <ctype.h> |
| |
| #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<QUuid> connectionInterfaces; |
| // DISPID -> signal name |
| QMap< QUuid, QMap<DISPID, QByteArray> > sigs; |
| // DISPID -> property changed signal name |
| QMap< QUuid, QMap<DISPID, QByteArray> > propsigs; |
| // DISPID -> property name |
| QMap< QUuid, QMap<DISPID, QByteArray> > props; |
| |
| // Prototype -> member info |
| QHash<QByteArray, QList<QByteArray> > memberInfo; |
| QMap<QByteArray, QByteArray> realPrototype; |
| |
| // DISPID cache |
| QHash<QByteArray, DISPID> 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<QByteArray>()); |
| } else { |
| QList<QByteArray> 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<QByteArray> 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 = (wchar_t*)unicodeName.utf16(); |
| disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid); |
| if (dispid != DISPID_UNKNOWN) |
| dispIDs.insert(name, dispid); |
| } |
| return dispid; |
| } |
| |
| |
| static QHash<QString, QAxMetaObject*> mo_cache; |
| static QHash<QUuid, QMap<QByteArray, QList<QPair<QByteArray, int> > > > 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<int>", "QVariantList" }, |
| { "QList<uint>", "QVariantList" }, |
| { "QList<double>", "QVariantList" }, |
| { "QList<bool>", "QVariantList" }, |
| { "QList<QDateTime>", "QVariantList" }, |
| { "QList<qlonglong>", "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<DISPID, QByteArray> signalMap() const |
| { |
| return sigs; |
| } |
| QMap<DISPID, QByteArray> propertyMap() const |
| { |
| return props; |
| } |
| QMap<DISPID, QByteArray> 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<DISPID,QByteArray>::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<void*>(varp[p + 1].constData()); |
| if (ptype.endsWith('*')) { |
| argv_pointer[p + 1] = argv[p + 1]; |
| argv[p + 1] = argv_pointer + p + 1; |
| } |
| } |
| } else if (ptype == "QVariant") { |
| argv[p + 1] = varp + p + 1; |
| } else { |
| 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<DISPID, QByteArray> sigs; |
| QMap<DISPID, QByteArray> propsigs; |
| QMap<DISPID, QByteArray> 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*>("IUnknown*", &ptr); |
| qRegisterMetaType<IDispatch*>("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<QUuid, QAxEventSink*> 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<QByteArray, bool> propWritable; |
| |
| inline QAxMetaObject *metaObject() |
| { |
| if (!metaobj) |
| metaobj = new QAxMetaObject; |
| return metaobj; |
| } |
| |
| mutable QMap<QString, LONG> 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::fromWCharArray(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\<QVariant\> |
| \i const QList\<QVariant\>& |
| \i QList\<QVariant\>& |
| \row |
| \i SAFEARRAY(int), SAFEARRAY(double), SAFEARRAY(Date) |
| \i QList\<QVariant\> |
| \i const QList\<QVariant\>& |
| \i QList\<QVariant\>& |
| \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<QString,QVariant> 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 initializes 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_t*)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<QUuid, QAxEventSink*>::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::fromWCharArray(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::fromWCharArray(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::fromWCharArray(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<const wchar_t*>(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 |
| #if defined(Q_CC_GNU) && !defined(COAUTHIDENTITY) && !defined(__MINGW64_VERSION_MAJOR) |
| #define COAUTHIDENTITY AUTH_IDENTITY |
| #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_t*)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<QByteArray> &names, |
| QByteArray &type, QList<QByteArray> ¶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<QByteArray> paramList(const QByteArray &proto) |
| { |
| QByteArray prototype(proto); |
| QByteArray parameters = prototype.mid(prototype.indexOf('(') + 1); |
| parameters.truncate(parameters.length() - 1); |
| |
| QList<QByteArray> 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<QByteArray> 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<QByteArray, QByteArray> 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<QByteArray, Method> 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<QByteArray, Method> 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<QByteArray, Property> property_list; |
| void addProperty(const QByteArray &type, const QByteArray &name, uint flags) |
| { |
| QByteArray propertyType(type); |
| if (propertyType.endsWith('&')) |
| propertyType.chop(1); |
| |
| Property &prop = property_list[name]; |
| if (!propertyType.isEmpty() && propertyType != "HRESULT") { |
| prop.type = replaceType(propertyType); |
| if (prop.type != propertyType) |
| prop.realType = propertyType; |
| } |
| if (flags & Writable) |
| flags |= Stored; |
| prop.typeId |= flags; |
| QVariant::Type vartype = QVariant::nameToType(prop.type); |
| switch(vartype) { |
| case QVariant::Invalid: |
| case QVariant::UserType: |
| if (prop.type == "QVariant") { |
| prop.typeId |= 0xff << 24; |
| break; |
| } |
| 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<QByteArray, QList<QPair<QByteArray, int> > > enum_list; |
| inline void addEnumValue(const QByteArray &enumname, const QByteArray &key, int value) |
| { |
| enum_list[enumname].append(QPair<QByteArray, int>(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::fromWCharArray(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::fromWCharArray(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::fromWCharArray(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::fromWCharArray(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<QByteArray> 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::fromWCharArray(typelibname).toLatin1(); |
| SysFreeString(typelibname); |
| |
| // get type name |
| BSTR usertypename = 0; |
| usertypelib->GetDocumentation(index, &usertypename, 0, 0, 0); |
| QByteArray userTypeName = QString::fromWCharArray(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(QLatin1Char('/') + 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<QString> 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::fromWCharArray(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::fromWCharArray(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<QByteArray> &names, |
| QByteArray &type, QList<QByteArray> ¶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.endsWith(',')) { |
| 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<QByteArray> parameters; |
| |
| // parse function description |
| BSTR bstrNames[256]; |
| UINT maxNames = 255; |
| UINT maxNamesOut; |
| typeinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); |
| QList<QByteArray> names; |
| int p; |
| for (p = 0; p < (int)maxNamesOut; ++p) { |
| names << QString::fromWCharArray(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) { |
| // remove the typename guessed for property setters |
| // its done only for setter's with more than one parameter. |
| if (funcdesc->cParams - funcdesc->cParamsOpt > 1) { |
| type.clear(); |
| } |
| 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::fromWCharArray(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::fromWCharArray(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::fromWCharArray(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<QByteArray> parameters; |
| |
| // parse event function description |
| BSTR bstrNames[256]; |
| UINT maxNames = 255; |
| UINT maxNamesOut; |
| eventinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); |
| QList<QByteArray> names; |
| int p; |
| for (p = 0; p < (int)maxNamesOut; ++p) { |
| names << QString::fromWCharArray(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::fromWCharArray(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<QUuid> 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<QUuid>::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::fromWCharArray(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<QByteArray, QList<QPair<QByteArray, int> > >::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<QByteArray, QByteArray>::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<QByteArray, Method>::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<QByteArray, Method>::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<QByteArray, Property>::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<QByteArray, QList<QPair<QByteArray, int> > >::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<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { |
| for (QList<QPair<QByteArray,int> >::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<QUuid, QAxEventSink*>::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::fromWCharArray(exc->bstrSource); |
| desc = QString::fromWCharArray(exc->bstrDescription); |
| help = QString::fromWCharArray(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<void*>(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; |
| default: |
| 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; |
| default: |
| 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<QVariant> &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<QVariant> 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<QVariant> &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<QVariant> 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<QVariant> &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::fromWCharArray(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::fromWCharArray(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<QObject*>(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 |