diff options
Diffstat (limited to 'src/activeqt/control')
-rw-r--r-- | src/activeqt/control/control.pro | 43 | ||||
-rw-r--r-- | src/activeqt/control/qaxaggregated.h | 93 | ||||
-rw-r--r-- | src/activeqt/control/qaxbindable.cpp | 324 | ||||
-rw-r--r-- | src/activeqt/control/qaxbindable.h | 87 | ||||
-rw-r--r-- | src/activeqt/control/qaxfactory.cpp | 591 | ||||
-rw-r--r-- | src/activeqt/control/qaxfactory.h | 310 | ||||
-rw-r--r-- | src/activeqt/control/qaxmain.cpp | 54 | ||||
-rw-r--r-- | src/activeqt/control/qaxserver.cpp | 1251 | ||||
-rw-r--r-- | src/activeqt/control/qaxserver.def | 8 | ||||
-rw-r--r-- | src/activeqt/control/qaxserver.ico | bin | 0 -> 766 bytes | |||
-rw-r--r-- | src/activeqt/control/qaxserver.rc | 2 | ||||
-rw-r--r-- | src/activeqt/control/qaxserverbase.cpp | 4477 | ||||
-rw-r--r-- | src/activeqt/control/qaxserverdll.cpp | 138 | ||||
-rw-r--r-- | src/activeqt/control/qaxservermain.cpp | 279 |
14 files changed, 7657 insertions, 0 deletions
diff --git a/src/activeqt/control/control.pro b/src/activeqt/control/control.pro new file mode 100644 index 0000000..65b0251 --- /dev/null +++ b/src/activeqt/control/control.pro @@ -0,0 +1,43 @@ +TEMPLATE = lib + +TARGET = ActiveQt +CONFIG += qt_install_headers +SYNCQT.HEADER_FILES = qaxaggregated.h qaxbindable.h qaxfactory.h +SYNCQT.HEADER_CLASSES = ../../../include/ActiveQt/QAxAggregated ../../../include/ActiveQt/QAxBindable ../../../include/ActiveQt/QAxFactory ../../../include/ActiveQt/QAxClass +include(../../qt_install.pri) + +TARGET = QAxServer + +!debug_and_release|build_pass { + CONFIG(debug, debug|release) { + TARGET = $$member(TARGET, 0)d + } +} + +CONFIG += qt warn_off staticlib +QTDIR_build:DESTDIR = $$QT_BUILD_TREE\lib + +DEFINES += QAX_SERVER +win32-g++:DEFINES += QT_NEEDS_QMAIN +win32-borland:DEFINES += QT_NEEDS_QMAIN + +LIBS += -luser32 -lole32 -loleaut32 -lgdi32 +win32-g++:LIBS += -luuid + +contains(QT_EDITION, OpenSource|Console) { + message( "You are not licensed to use ActiveQt." ) +} else { + HEADERS = qaxaggregated.h \ + qaxbindable.h \ + qaxfactory.h \ + ../shared/qaxtypes.h + + SOURCES = qaxserver.cpp \ + qaxserverbase.cpp \ + qaxbindable.cpp \ + qaxfactory.cpp \ + qaxservermain.cpp \ + qaxserverdll.cpp \ + qaxmain.cpp \ + ../shared/qaxtypes.cpp +} diff --git a/src/activeqt/control/qaxaggregated.h b/src/activeqt/control/qaxaggregated.h new file mode 100644 index 0000000..3e6ce44 --- /dev/null +++ b/src/activeqt/control/qaxaggregated.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#ifndef QAXAGGREGATED_H +#define QAXAGGREGATED_H + +#include <QtCore/qobject.h> + +struct IUnknown; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +struct QUuid; + +class QAxAggregated +{ + friend class QAxServerBase; + friend class QAxClientSite; +public: + virtual long queryInterface(const QUuid &iid, void **iface) = 0; + +protected: + virtual ~QAxAggregated() + {} + + inline IUnknown *controllingUnknown() const + { return controlling_unknown; } + inline QWidget *widget() const + { + return qobject_cast<QWidget*>(the_object); + } + inline QObject *object() const { return the_object; } + +private: + IUnknown *controlling_unknown; + QObject *the_object; +}; + +#define QAXAGG_IUNKNOWN \ + HRESULT WINAPI QueryInterface(REFIID iid, LPVOID *iface) { \ + return controllingUnknown()->QueryInterface(iid, iface); } \ + ULONG WINAPI AddRef() {return controllingUnknown()->AddRef(); } \ + ULONG WINAPI Release() {return controllingUnknown()->Release(); } \ + +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAXAGGREGATED_H diff --git a/src/activeqt/control/qaxbindable.cpp b/src/activeqt/control/qaxbindable.cpp new file mode 100644 index 0000000..b2e1a49 --- /dev/null +++ b/src/activeqt/control/qaxbindable.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#include "qaxbindable.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qmetaobject.h> + +#include <qt_windows.h> // for IUnknown +#include "../shared/qaxtypes.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAxBindable + \brief The QAxBindable class provides an interface between a + QWidget and an ActiveX client. + + \inmodule QAxServer + + The functions provided by this class allow an ActiveX control to + communicate property changes to a client application. Inherit + your control class from both QWidget (directly or indirectly) and + this class to get access to this class's functions. The + \l{moc}{meta-object compiler} requires you to inherit from + QWidget first. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxbindable.cpp 0 + + When implementing the property write function, use + requestPropertyChange() to get permission from the ActiveX client + application to change this property. When the property changes, + call propertyChanged() to notify the ActiveX client application + about the change. If a fatal error occurs in the control, use the + static reportError() function to notify the client. + + Use the interface returned by clientSite() to call the ActiveX + client. To implement additional COM interfaces in your ActiveX + control, reimplement createAggregate() to return a new object of a + QAxAggregated subclass. + + The ActiveQt \l{activeqt/opengl}{OpenGL} example shows how to use + QAxBindable to implement additional COM interfaces. + + \sa QAxAggregated, QAxFactory, {ActiveQt Framework} +*/ + +/*! + Constructs an empty QAxBindable object. +*/ +QAxBindable::QAxBindable() +:activex(0) +{ +} + +/*! + Destroys the QAxBindable object. +*/ +QAxBindable::~QAxBindable() +{ +} + +/*! + Call this function to request permission to change the property + \a property from the client that is hosting this ActiveX control. + Returns true if the client allows the change; otherwise returns + false. + + This function is usually called first in the write function for \a + property, and writing is abandoned if the function returns false. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxbindable.cpp 1 + + \sa propertyChanged() +*/ +bool QAxBindable::requestPropertyChange(const char *property) +{ + if (!activex) + return true; + + return activex->emitRequestPropertyChange(property); +} + +/*! + Call this function to notify the client that is hosting this + ActiveX control that the property \a property has been changed. + + This function is usually called at the end of the property's write + function. + + \sa requestPropertyChange() +*/ +void QAxBindable::propertyChanged(const char *property) +{ + if (!activex) + return; + + activex->emitPropertyChanged(property); +} + +/*! + Returns a pointer to the client site interface for this ActiveX object, + or null if no client site has been set. + + Call \c QueryInterface() on the returned interface to get the + interface you want to call. +*/ +IUnknown *QAxBindable::clientSite() const +{ + if (!activex) + return 0; + + return activex->clientSite(); +} + +/*! + Reimplement this function when you want to implement additional + COM interfaces in the ActiveX control, or when you want to provide + alternative implementations of COM interfaces. Return a new object + of a QAxAggregated subclass. + + The default implementation returns the null pointer. +*/ +QAxAggregated *QAxBindable::createAggregate() +{ + return 0; +} + +/*! + Reports an error to the client application. \a code is a + control-defined error code. \a desc is a human-readable description + of the error intended for the application user. \a src is the name + of the source for the error, typically the ActiveX server name. \a + context can be the location of a help file with more information + about the error. If \a context ends with a number in brackets, + e.g. [12], this number will be interpreted as the context ID in + the help file. +*/ +void QAxBindable::reportError(int code, const QString &src, const QString &desc, const QString &context) +{ + if (!activex) + return; + + activex->reportError(code, src, desc, context); +} + +/*! + \since 4.1 + + If the COM object supports a MIME type then this function is called + to initialize the COM object from the data \a source in \a format. + You have to open \a source for reading before you can read from it. + + Returns true to indicate success. If the function returns false, + then ActiveQt will process the data by setting the properties + through the meta object system. + + If you reimplement this function you also have to implement + writeData(). The default implementation does nothing and returns + false. + + \warning ActiveX controls embedded in HTML can use either the + \c type and \c data attribute of the \c object tag to read data, + or use a list of \c param tags to initialize properties. If + \c param tags are used, then Internet Explorer will ignore the + \c data attribute, and readData will not be called. + + \sa writeData() +*/ +bool QAxBindable::readData(QIODevice *source, const QString &format) +{ + Q_UNUSED(source); + Q_UNUSED(format); + return false; +} + +/*! + \since 4.1 + + If the COM object supports a MIME type then this function is called + to store the COM object into \a sink. + You have to open \a sink for writing before you can write to it. + + Returns true to indicate success. If the function returns false, + then ActiveQt will serialize the object by storing the property + values. + + If you reimplement this function you also have to implement + readData(). The default implementation does nothing and returns + false. + + \sa readData() +*/ +bool QAxBindable::writeData(QIODevice *sink) +{ + Q_UNUSED(sink); + return false; +} + +/*! + \class QAxAggregated + \brief The QAxAggregated class is an abstract base class for implementations of + additional COM interfaces. + + \inmodule QAxServer + + Create a subclass of QAxAggregated and reimplement + queryInterface() to support additional COM interfaces. Use + multiple inheritance from those COM interfaces. Implement the + IUnknown interface of those COM interfaces by delegating the + calls to \c QueryInterface(), \c AddRef() and \c Release() to the + interface provided by controllingUnknown(). + + Use the widget() method if you need to make calls to the QWidget + implementing the ActiveX control. You must not store that pointer + in your subclass (unless you use QPointer), as the QWidget can + be destroyed by the ActiveQt framework at any time. + + \sa QAxBindable, QAxFactory, {ActiveQt Framework} +*/ + +/*! + \fn QAxAggregated::~QAxAggregated() + + The destructor is called internally by Qt. +*/ + +/*! + \fn long QAxAggregated::queryInterface(const QUuid &iid, void **iface) + + Reimplement this pure virtual function to support additional COM + interfaces. Set the value of \a iface to point to this object to + support the interface \a iid. Note that you must cast the \c + this pointer to the appropriate superclass. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxbindable.cpp 2 + + Return the standard COM results \c S_OK (interface is supported) + or \c E_NOINTERFACE (requested interface is not supported). + + \warning + Even though you must implement the \c IUnknown interface if you + implement any COM interface you must not support the \c IUnknown + interface in your queryInterface() implementation. +*/ + +/*! + \fn IUnknown *QAxAggregated::controllingUnknown() const + + Returns the \c IUnknown interface of the ActiveX control. Implement + the \c IUnknown interface in your QAxAggregated subclass to + delegate calls to \c QueryInterface(), \c AddRef(), and \c + Release() to the interface provided by this function. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxbindable.cpp 3 + + Instead of declaring and implementing these three functions + manually, you can use the \c QAXAGG_IUNKNOWN macro in the class + declaration of your subclass. +*/ + +/*! + \fn QObject *QAxAggregated::object() const + + Returns a pointer to the QObject subclass implementing the COM object. + This function might return 0. + + \warning + You must not store the returned pointer, unless you use a + QPointer, since the QObject can be destroyed by ActiveQt at any + time. +*/ + +/*! + \fn QWidget *QAxAggregated::widget() const + + Returns a pointer to the QWidget subclass implementing the ActiveX control. + This function might return 0. + + \warning + You must not store the returned pointer, unless you use a + QPointer, since the QWidget can be destroyed by ActiveQt at any + time. +*/ + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxbindable.h b/src/activeqt/control/qaxbindable.h new file mode 100644 index 0000000..d3fd07a --- /dev/null +++ b/src/activeqt/control/qaxbindable.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#ifndef QAXBINDABLE_H +#define QAXBINDABLE_H + +#include <QtGui/qwidget.h> + +struct IUnknown; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QAxAggregated; +class QIODevice; +struct IAxServerBase; + +class QAxBindable +{ + friend class QAxServerBase; +public: + QAxBindable(); + virtual ~QAxBindable(); + + virtual QAxAggregated *createAggregate(); + void reportError(int code, const QString &src, const QString &desc, const QString &help = QString()); + + virtual bool readData(QIODevice *source, const QString &format); + virtual bool writeData(QIODevice *sink); + +protected: + bool requestPropertyChange(const char *property); + void propertyChanged(const char *property); + + IUnknown *clientSite() const; + +private: + IAxServerBase *activex; +}; + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXBINDABLE_H diff --git a/src/activeqt/control/qaxfactory.cpp b/src/activeqt/control/qaxfactory.cpp new file mode 100644 index 0000000..ec72c830 --- /dev/null +++ b/src/activeqt/control/qaxfactory.cpp @@ -0,0 +1,591 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#include "qaxfactory.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qfile.h> +#include <qfileinfo.h> +#include <qmetaobject.h> +#include <qsettings.h> +#include <qwidget.h> +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +extern char qAxModuleFilename[MAX_PATH]; + +/*! + \class QAxFactory + \brief The QAxFactory class defines a factory for the creation of COM components. + + \inmodule QAxServer + + Implement this factory once in your COM server to provide information + about the components the server can create. Subclass QAxFactory and implement + the pure virtual functions in any implementation file (e.g. main.cpp), and export + the factory using the \c QAXFACTORY_EXPORT() macro. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 0 + + If you use the \c Q_CLASSINFO() macro to provide the unique + identifiers or other attributes for your class you can use the \c + QAXFACTORY_BEGIN(), \c QAXCLASS() and \c QAXFACTORY_END() macros to + expose one or more classes as COM objects. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 1 + + + If your server supports just a single COM object, you can use + a default factory implementation through the \c QAXFACTORY_DEFAULT() macro. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 2 + + Only one QAxFactory implementation may be instantiated and + exported by an ActiveX server application. This instance is accessible + through the global qAxFactory() function. + + A factory can also reimplement the registerClass() and + unregisterClass() functions to set additional flags for an ActiveX + control in the registry. To limit the number of methods or + properties a widget class exposes from its parent classes + reimplement exposeToSuperClass(). + + \sa QAxAggregated, QAxBindable, {ActiveQt Framework} +*/ + +/*! + Constructs a QAxFactory object that returns \a libid and \a appid + in the implementation of the respective interface functions. +*/ + +QAxFactory::QAxFactory(const QUuid &libid, const QUuid &appid) +: typelib(libid), app(appid) +{ +} + +/*! + Destroys the QAxFactory object. +*/ +QAxFactory::~QAxFactory() +{ +} + +/*! + \fn QUuid QAxFactory::typeLibID() const + + Reimplement this function to return the ActiveX server's type + library identifier. +*/ +QUuid QAxFactory::typeLibID() const +{ + return typelib; +} + +/*! + \fn QUuid QAxFactory::appID() const + + Reimplement this function to return the ActiveX server's + application identifier. +*/ +QUuid QAxFactory::appID() const +{ + return app; +} + +/*! + \fn QStringList QAxFactory::featureList() const + + Reimplement this function to return a list of the widgets (class + names) supported by this factory. +*/ + +/*! + \fn QObject *QAxFactory::createObject(const QString &key) + + Reimplement this function to return a new object for \a key, or 0 if + this factory doesn't support the value of \a key. + + If the object returned is a QWidget it will be exposed as an ActiveX + control, otherwise the returned object will be exposed as a simple COM + object. +*/ + +/*! + \fn const QMetaObject *QAxFactory::metaObject(const QString &key) const + + Reimplement this function to return the QMetaObject corresponding to + \a key, or 0 if this factory doesn't support the value of \a key. +*/ + +/*! + \fn bool QAxFactory::createObjectWrapper(QObject *object, IDispatch **wrapper) + + Reimplement this function to provide the COM object for \a object + in \a wrapper. Return true if the function was successful; otherwise + return false. + + The default implementation creates a generic automation wrapper based + on the meta object information of \a object. +*/ +// implementation in qaxserverbase.cpp + +/*! + Reimplement this function to return the class identifier for each + \a key returned by the featureList() implementation, or an empty + QUuid if this factory doesn't support the value of \a key. + + The default implementation interprets \a key as the class name, + and returns the value of the Q_CLASSINFO() entry "ClassID". +*/ +QUuid QAxFactory::classID(const QString &key) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return QUuid(); + QString id = QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("ClassID")).value()); + + return QUuid(id); +} + +/*! + Reimplement this function to return the interface identifier for + each \a key returned by the featureList() implementation, or an + empty QUuid if this factory doesn't support the value of \a key. + + The default implementation interprets \a key as the class name, + and returns the value of the Q_CLASSINFO() entry "InterfaceID". +*/ +QUuid QAxFactory::interfaceID(const QString &key) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return QUuid(); + QString id = QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("InterfaceID")).value()); + + return QUuid(id); +} + +/*! + Reimplement this function to return the identifier of the event + interface for each \a key returned by the featureList() + implementation, or an empty QUuid if this factory doesn't support + the value of \a key. + + The default implementation interprets \a key as the class name, + and returns the value of the Q_CLASSINFO() entry "EventsID". +*/ +QUuid QAxFactory::eventsID(const QString &key) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return QUuid(); + QString id = QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("EventsID")).value()); + + return QUuid(id); +} + +/*! + Registers additional values for the class \a key in the system + registry using the \a settings object. The standard values have + already been registered by the framework, but additional values, + e.g. implemented categories, can be added in an implementation of + this function. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 3 + + If you reimplement this function you must also reimplement + unregisterClass() to remove the additional registry values. + + \sa QSettings +*/ +void QAxFactory::registerClass(const QString &key, QSettings *settings) const +{ + Q_UNUSED(key); + Q_UNUSED(settings) +} + +/*! + Unregisters any additional values for the class \a key from the + system registry using the \a settings object. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 4 + + \sa registerClass(), QSettings +*/ +void QAxFactory::unregisterClass(const QString &key, QSettings *settings) const +{ + Q_UNUSED(key); + Q_UNUSED(settings) +} + +/*! + Reimplement this function to return true if \a licenseKey is a valid + license for the class \a key, or if the current machine is licensed. + + The default implementation returns true if the class \a key is + not licensed (ie. no \c Q_CLASSINFO() attribute "LicenseKey"), or + if \a licenseKey matches the value of the "LicenseKey" + attribute, or if the machine is licensed through a .LIC file with + the same filename as this COM server. +*/ +bool QAxFactory::validateLicenseKey(const QString &key, const QString &licenseKey) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return true; + + QString classKey = QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value()); + if (classKey.isEmpty()) + return true; + + if (licenseKey.isEmpty()) { + QString licFile(QFile::decodeName(qAxModuleFilename)); + int lastDot = licFile.lastIndexOf(QLatin1Char('.')); + licFile = licFile.left(lastDot) + QLatin1String(".lic"); + if (QFile::exists(licFile)) + return true; + return false; + } + return licenseKey == classKey; +} + +/*! + Reimplement this function to return the name of the super class of + \a key up to which methods and properties should be exposed by the + ActiveX control. + + The default implementation interprets \a key as the class name, + and returns the value of the \c Q_CLASSINFO() entry + "ToSuperClass". If no such value is set the null-string is + returned, and the functions and properties of all the super + classes including QWidget will be exposed. + + To only expose the functions and properties of the class itself, + reimplement this function to return \a key. +*/ +QString QAxFactory::exposeToSuperClass(const QString &key) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return QString(); + return QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("ToSuperClass")).value()); +} + +/*! + Reimplement this function to return true if the ActiveX control \a key + should be a top level window, e.g. a dialog. The default implementation + returns false. +*/ +bool QAxFactory::stayTopLevel(const QString &key) const +{ + return false; +} + +/*! + Reimplement this function to return true if the ActiveX control + \a key should support the standard ActiveX events + \list + \i Click + \i DblClick + \i KeyDown + \i KeyPress + \i KeyUp + \i MouseDown + \i MouseUp + \i MouseMove + \endlist + + The default implementation interprets \a key as the class name, + and returns true if the value of the \c Q_CLASSINFO() entry + "StockEvents" is "yes". Otherwise this function returns false. +*/ +bool QAxFactory::hasStockEvents(const QString &key) const +{ + const QMetaObject *mo = metaObject(key); + if (!mo) + return false; + return QString::fromLatin1(mo->classInfo(mo->indexOfClassInfo("StockEvents")).value()) == QLatin1String("yes"); +} + + +extern bool qAxIsServer; + +/*! + Returns true if the application has been started (by COM) as an ActiveX + server, otherwise returns false. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 5 +*/ + +bool QAxFactory::isServer() +{ + return qAxIsServer; +} + +extern char qAxModuleFilename[MAX_PATH]; + +/*! + Returns the directory that contains the server binary. + + For out-of-process servers this is the same as + QApplication::applicationDirPath(). For in-process servers + that function returns the directory that contains the hosting + application. +*/ +QString QAxFactory::serverDirPath() +{ + return QFileInfo(QString::fromLocal8Bit(qAxModuleFilename)).absolutePath(); +} + +/*! + Returns the file path of the server binary. + + For out-of-process servers this is the same as + QApplication::applicationFilePath(). For in-process servers + that function returns the file path of the hosting application. +*/ +QString QAxFactory::serverFilePath() +{ + return QString::fromLocal8Bit(qAxModuleFilename); +} + +/*! + Reimplement this function to return true if the server is + running as a persistent service (e.g. an NT service) and should + not terminate even when all objects provided have been released. + + The default implementation returns false. +*/ +bool QAxFactory::isService() const +{ + return false; +} + +/*! + \enum QAxFactory::ServerType + + This enum specifies the different types of servers that can be + started with startServer. + + \value SingleInstance The server process can create only one instance of each + exported class. COM starts a new process for each request. This is typically + used in servers that export only one creatable class. + \value MultipleInstances The server can create multiple instances of + each exported class. This is the default. All instances will live in the same + thread, and will share static resources. +*/ + +/*! + \fn bool QAxFactory::startServer(ServerType type); + + Starts the COM server with \a type and returns true if successful, + otherwise returns false. + + Calling this function if the server is already running (or for an + in-process server) does nothing and returns true. + + The server is started automatically with \a type set to \c MultipleInstances + if the server executable has been started with the \c -activex + command line parameter. To switch to SingleInstance, call + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 6 + + in your own main() entry point function. +*/ + +/*! + \fn bool QAxFactory::stopServer(); + + Stops the COM server and returns true if successful, otherwise + returns false. + + Calling this function if the server is not running (or for an + in-process server) does nothing and returns true. + + Stopping the server will not invalidate existing objects, but no + new objects can be created from the existing server process. Usually + COM will start a new server process if additional objects are requested. + + The server is stopped automatically when the main() function returns. +*/ + +class ActiveObject : public QObject +{ +public: + ActiveObject(QObject *parent, QAxFactory *factory); + ~ActiveObject(); + + IDispatch *wrapper; + DWORD cookie; +}; + +ActiveObject::ActiveObject(QObject *parent, QAxFactory *factory) +: QObject(parent), wrapper(0), cookie(0) +{ + QLatin1String key(parent->metaObject()->className()); + + factory->createObjectWrapper(parent, &wrapper); + if (wrapper) + RegisterActiveObject(wrapper, QUuid(factory->classID(key)), ACTIVEOBJECT_STRONG, &cookie); +} + +ActiveObject::~ActiveObject() +{ + if (cookie) + RevokeActiveObject(cookie, 0); + if (wrapper) + wrapper->Release(); +} + +/*! + Registers the QObject \a object with COM as a running object, and returns true if + the registration succeeded, otherwise returns false. The object is unregistered + automatically when it is destroyed. + + This function should only be called if the application has been started by the user + (i.e. not by COM to respond to a request), and only for one object, usually the + toplevel object of the application's object hierarchy. + + This function does nothing and returns false if the object's class info for + "RegisterObject" is not set to "yes", or if the server is an in-process server. +*/ +bool QAxFactory::registerActiveObject(QObject *object) +{ + if (qstricmp(object->metaObject()->classInfo(object->metaObject()->indexOfClassInfo("RegisterObject")).value(), "yes")) + return false; + + if (!QString::fromLocal8Bit(qAxModuleFilename).toLower().endsWith(QLatin1String(".exe"))) + return false; + + ActiveObject *active = new ActiveObject(object, qAxFactory()); + if (!active->wrapper || !active->cookie) { + delete active; + return false; + } + return true; +} + +/*! + \macro QAXFACTORY_DEFAULT(Class, ClassID, InterfaceID, EventID, LibID, AppID) + \relates QAxFactory + + This macro can be used to export a single QObject subclass \a Class a this + COM server through an implicitly declared QAxFactory implementation. + + This macro exports the class \a Class as a COM coclass with the CLSID \a ClassID. + The properties and slots will be declared through a COM interface with the IID + \a InterfaceID, and signals will be declared through a COM event interface with + the IID \a EventID. All declarations will be in a type library with the id \a LibID, + and if the server is an executable server then it will have the application id + \a AppID. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 7 + + \sa QAXFACTORY_EXPORT(), QAXFACTORY_BEGIN() +*/ + +/*! + \macro QAXFACTORY_EXPORT(Class, LibID, AppID) + \relates QAxFactory + + This macro can be used to export a QAxFactory implementation \a Class from + a COM server. All declarations will be in a type library with the id \a LibID, + and if the server is an executable server then it will have the application id + \a AppID. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 8 + + \sa QAXFACTORY_BEGIN() +*/ + +/*! + \macro QAXFACTORY_BEGIN(IDTypeLib, IDApp) + \relates QAxFactory + + This macro can be used to export multiple QObject classes through an + implicitly declared QAxFactory implementation. All QObject classes have to + declare the ClassID, InterfaceID and EventsID (if applicable) through the + Q_CLASSINFO() macro. All declarations will be in a type library with the id + \a IDTypeLib, and if the server is an executable server then it will have the + application id \a IDApp. + + This macro needs to be used together with the QAXCLASS(), QAXTYPE() + and QAXFACTORY_END() macros. + + \snippet doc/src/snippets/code/src_activeqt_control_qaxfactory.cpp 9 +*/ + +/*! + \macro QAXCLASS(Class) + \relates QAxFactory + + This macro adds a creatable COM class \a Class to the QAxFactory declared + with the QAXFACTORY_BEGIN() macro. + + \sa QAXFACTORY_BEGIN(), QAXTYPE(), QAXFACTORY_END(), Q_CLASSINFO() +*/ + +/*! + \macro QAXTYPE(Class) + \relates QAxFactory + + This macro adds a non-creatable COM class \a Class to the QAxFactory + declared with the QAXFACTORY_BEGIN(). The class \a Class can be used + in APIs of other COM classes exported through QAXTYPE() or QAXCLASS(). + + Instances of type \a Class can only be retrieved using APIs of already + instantiated objects. + + \sa QAXFACTORY_BEGIN(), QAXCLASS(), QAXFACTORY_END(), Q_CLASSINFO() +*/ + +/*! + \macro QAXFACTORY_END() + \relates QAxFactory + + Completes the QAxFactory declaration started with the QAXFACTORY_BEGIN() + macro. + + \sa QAXFACTORY_BEGIN(), QAXCLASS(), QAXTYPE() +*/ + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxfactory.h b/src/activeqt/control/qaxfactory.h new file mode 100644 index 0000000..e5bb9c1 --- /dev/null +++ b/src/activeqt/control/qaxfactory.h @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#ifndef QAXFACTORY_H +#define QAXFACTORY_H + +#include <QtCore/qhash.h> +#include <QtCore/quuid.h> +#include <QtCore/qfactoryinterface.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> + +struct IUnknown; +struct IDispatch; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QWidget; +class QSettings; + +class QAxFactory : public QObject +{ +public: + QAxFactory(const QUuid &libId, const QUuid &appId); + virtual ~QAxFactory(); + + virtual QStringList featureList() const = 0; + + virtual QObject *createObject(const QString &key) = 0; + virtual const QMetaObject *metaObject(const QString &key) const = 0; + virtual bool createObjectWrapper(QObject *object, IDispatch **wrapper); + + virtual QUuid classID(const QString &key) const; + virtual QUuid interfaceID(const QString &key) const; + virtual QUuid eventsID(const QString &key) const; + + virtual QUuid typeLibID() const; + virtual QUuid appID() const; + + virtual void registerClass(const QString &key, QSettings *) const; + virtual void unregisterClass(const QString &key, QSettings *) const; + + virtual bool validateLicenseKey(const QString &key, const QString &licenseKey) const; + + virtual QString exposeToSuperClass(const QString &key) const; + virtual bool stayTopLevel(const QString &key) const; + virtual bool hasStockEvents(const QString &key) const; + virtual bool isService() const; + + enum ServerType { + SingleInstance, + MultipleInstances + }; + + static bool isServer(); + static QString serverDirPath(); + static QString serverFilePath(); + static bool startServer(ServerType type = MultipleInstances); + static bool stopServer(); + + static bool registerActiveObject(QObject *object); + +private: + QUuid typelib; + QUuid app; +}; + +extern QAxFactory *qAxFactory(); + +extern bool qax_startServer(QAxFactory::ServerType); + +inline bool QAxFactory::startServer(ServerType type) +{ + // implementation in qaxservermain.cpp + return qax_startServer(type); +} + +extern bool qax_stopServer(); + +inline bool QAxFactory::stopServer() +{ + // implementation in qaxservermain.cpp + return qax_stopServer(); +} + +#define QAXFACTORY_EXPORT(IMPL, TYPELIB, APPID) \ + QT_BEGIN_NAMESPACE \ + QAxFactory *qax_instantiate() \ + { \ + IMPL *impl = new IMPL(QUuid(TYPELIB), QUuid(APPID)); \ + return impl; \ + } \ + QT_END_NAMESPACE + +#define QAXFACTORY_DEFAULT(Class, IIDClass, IIDInterface, IIDEvents, IIDTypeLib, IIDApp) \ + QT_BEGIN_NAMESPACE \ + class QAxDefaultFactory : public QAxFactory \ + { \ + public: \ + QAxDefaultFactory(const QUuid &app, const QUuid &lib) \ + : QAxFactory(app, lib), className(QLatin1String(#Class)) {} \ + QStringList featureList() const \ + { \ + QStringList list; \ + list << className; \ + return list; \ + } \ + const QMetaObject *metaObject(const QString &key) const \ + { \ + if (key == className) \ + return &Class::staticMetaObject; \ + return 0; \ + } \ + QObject *createObject(const QString &key) \ + { \ + if (key == className) \ + return new Class(0); \ + return 0; \ + } \ + QUuid classID(const QString &key) const \ + { \ + if (key == className) \ + return QUuid(IIDClass); \ + return QUuid(); \ + } \ + QUuid interfaceID(const QString &key) const \ + { \ + if (key == className) \ + return QUuid(IIDInterface); \ + return QUuid(); \ + } \ + QUuid eventsID(const QString &key) const \ + { \ + if (key == className) \ + return QUuid(IIDEvents); \ + return QUuid(); \ + } \ + private: \ + QString className; \ + }; \ + QT_END_NAMESPACE \ + QAXFACTORY_EXPORT(QAxDefaultFactory, IIDTypeLib, IIDApp) \ + +template<class T> +class QAxClass : public QAxFactory +{ +public: + QAxClass(const QString &libId, const QString &appId) + : QAxFactory(libId, appId) + {} + + const QMetaObject *metaObject(const QString &) const { return &T::staticMetaObject; } + QStringList featureList() const { return QStringList(QString(T::staticMetaObject.className())); } + QObject *createObject(const QString &key) + { + const QMetaObject &mo = T::staticMetaObject; + if (key != QLatin1String(mo.className())) + return 0; + if (!qstrcmp(mo.classInfo(mo.indexOfClassInfo("Creatable")).value(), "no")) + return 0; + return new T(0); + } +}; + +#define QAXFACTORY_BEGIN(IDTypeLib, IDApp) \ + QT_BEGIN_NAMESPACE \ + class QAxFactoryList : public QAxFactory \ + { \ + QStringList factoryKeys; \ + QHash<QString, QAxFactory*> factories; \ + QHash<QString, bool> creatable; \ + public: \ + QAxFactoryList() \ + : QAxFactory(IDTypeLib, IDApp) \ + { \ + QAxFactory *factory = 0; \ + QStringList keys; \ + QStringList::Iterator it; \ + +#define QAXCLASS(Class) \ + factory = new QAxClass<Class>(typeLibID(), appID()); \ + qRegisterMetaType<Class*>(#Class"*"); \ + keys = factory->featureList(); \ + for (it = keys.begin(); it != keys.end(); ++it) { \ + factoryKeys += *it; \ + factories.insert(*it, factory); \ + creatable.insert(*it, true); \ + }\ + +#define QAXTYPE(Class) \ + factory = new QAxClass<Class>(typeLibID(), appID()); \ + qRegisterMetaType<Class*>(#Class"*"); \ + keys = factory->featureList(); \ + for (it = keys.begin(); it != keys.end(); ++it) { \ + factoryKeys += *it; \ + factories.insert(*it, factory); \ + creatable.insert(*it, false); \ + }\ + +#define QAXFACTORY_END() \ + } \ + ~QAxFactoryList() { qDeleteAll(factories); } \ + QStringList featureList() const { return factoryKeys; } \ + const QMetaObject *metaObject(const QString&key) const { \ + QAxFactory *f = factories[key]; \ + return f ? f->metaObject(key) : 0; \ + } \ + QObject *createObject(const QString &key) { \ + if (!creatable.value(key)) \ + return 0; \ + QAxFactory *f = factories[key]; \ + return f ? f->createObject(key) : 0; \ + } \ + QUuid classID(const QString &key) { \ + QAxFactory *f = factories.value(key); \ + return f ? f->classID(key) : QUuid(); \ + } \ + QUuid interfaceID(const QString &key) { \ + QAxFactory *f = factories.value(key); \ + return f ? f->interfaceID(key) : QUuid(); \ + } \ + QUuid eventsID(const QString &key) { \ + QAxFactory *f = factories.value(key); \ + return f ? f->eventsID(key) : QUuid(); \ + } \ + void registerClass(const QString &key, QSettings *s) const { \ + QAxFactory *f = factories.value(key); \ + if (f) f->registerClass(key, s); \ + } \ + void unregisterClass(const QString &key, QSettings *s) const { \ + QAxFactory *f = factories.value(key); \ + if (f) f->unregisterClass(key, s); \ + } \ + QString exposeToSuperClass(const QString &key) const { \ + QAxFactory *f = factories.value(key); \ + return f ? f->exposeToSuperClass(key) : QString(); \ + } \ + bool stayTopLevel(const QString &key) const { \ + QAxFactory *f = factories.value(key); \ + return f ? f->stayTopLevel(key) : false; \ + } \ + bool hasStockEvents(const QString &key) const { \ + QAxFactory *f = factories.value(key); \ + return f ? f->hasStockEvents(key) : false; \ + } \ + }; \ + QAxFactory *qax_instantiate() \ + { \ + QAxFactoryList *impl = new QAxFactoryList(); \ + return impl; \ + } \ + QT_END_NAMESPACE + +QT_END_NAMESPACE + +#ifndef Q_COM_METATYPE_DECLARED +#define Q_COM_METATYPE_DECLARED + +Q_DECLARE_METATYPE(IUnknown*) +Q_DECLARE_METATYPE(IDispatch*) + +#endif + +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXFACTORY_H diff --git a/src/activeqt/control/qaxmain.cpp b/src/activeqt/control/qaxmain.cpp new file mode 100644 index 0000000..27ab052 --- /dev/null +++ b/src/activeqt/control/qaxmain.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#include <qapplication.h> +#include <qaxfactory.h> + +#ifndef QT_NO_WIN_ACTIVEQT + +int main(int argc, char **argv) +{ + QT_USE_NAMESPACE + QAxFactory::startServer(); + QApplication app(argc, argv); + app.setQuitOnLastWindowClosed(false); + + return app.exec(); +} +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxserver.cpp b/src/activeqt/control/qaxserver.cpp new file mode 100644 index 0000000..d919382 --- /dev/null +++ b/src/activeqt/control/qaxserver.cpp @@ -0,0 +1,1251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#include "qaxbindable.h" +#include "qaxfactory.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qapplication.h> +#include <qdatetime.h> +#include <qdir.h> +#include <qmap.h> +#include <qmenubar.h> +#include <qmetaobject.h> +#include <qsettings.h> +#include <qvariant.h> +#include <qtextstream.h> + +#include <qt_windows.h> +#include <olectl.h> + +QT_BEGIN_NAMESPACE + +#define Q_REQUIRED_RPCNDR_H_VERSION 475 + +// Some global variables to store module information +bool qAxIsServer = false; +HANDLE qAxInstance = 0; +ITypeLib *qAxTypeLibrary = 0; +char qAxModuleFilename[MAX_PATH]; +bool qAxOutProcServer = false; + +// The QAxFactory instance +static QAxFactory* qax_factory = 0; +extern CLSID CLSID_QRect; +extern CLSID CLSID_QSize; +extern CLSID CLSID_QPoint; +extern void qax_shutDown(); +extern bool qax_ownQApp; + + +extern QAxFactory *qax_instantiate(); + +QAxFactory *qAxFactory() +{ + if (!qax_factory) { + bool hadQApp = qApp != 0; + qax_factory = qax_instantiate(); + // QAxFactory created a QApplication + if (!hadQApp && qApp) + qax_ownQApp = true; + + // register all types with metatype system as pointers + QStringList keys(qax_factory->featureList()); + for (int i = 0; i < keys.count(); ++i) { + QString key(keys.at(i)); + qRegisterMetaType((key + QLatin1String("*")).toLatin1(), (void**)0); + } + } + return qax_factory; +} + +// Some local variables to handle module lifetime +static unsigned long qAxModuleRef = 0; +static CRITICAL_SECTION qAxModuleSection; + + +///////////////////////////////////////////////////////////////////////////// +// Server control +///////////////////////////////////////////////////////////////////////////// + +static int initCount = 0; + +QString qAxInit() +{ + static QString libFile; + + if (initCount++) + return libFile; + + InitializeCriticalSection(&qAxModuleSection); + + libFile = QString::fromLocal8Bit(qAxModuleFilename); + libFile = libFile.toLower(); + if (LoadTypeLibEx((TCHAR*)libFile.utf16(), REGKIND_NONE, &qAxTypeLibrary) == S_OK) + return libFile; + + int lastDot = libFile.lastIndexOf(QLatin1Char('.')); + libFile = libFile.left(lastDot) + QLatin1String(".tlb"); + if (LoadTypeLibEx((TCHAR*)libFile.utf16(), REGKIND_NONE, &qAxTypeLibrary) == S_OK) + return libFile; + + lastDot = libFile.lastIndexOf(QLatin1Char('.')); + libFile = libFile.left(lastDot) + QLatin1String(".olb"); + if (LoadTypeLibEx((TCHAR*)libFile.utf16(), REGKIND_NONE, &qAxTypeLibrary) == S_OK) + return libFile; + + libFile = QString(); + return libFile; +} + +void qAxCleanup() +{ + if (!initCount) + qWarning("qAxInit/qAxCleanup mismatch"); + + if (--initCount) + return; + + delete qax_factory; + qax_factory = 0; + + if (qAxTypeLibrary) { + qAxTypeLibrary->Release(); + qAxTypeLibrary = 0; + } + + DeleteCriticalSection(&qAxModuleSection); +} + +unsigned long qAxLock() +{ + EnterCriticalSection(&qAxModuleSection); + unsigned long ref = ++qAxModuleRef; + LeaveCriticalSection(&qAxModuleSection); + return ref; +} + +unsigned long qAxUnlock() +{ + if (!initCount) // cleaned up already + return 0; + + EnterCriticalSection(&qAxModuleSection); + unsigned long ref = --qAxModuleRef; + LeaveCriticalSection(&qAxModuleSection); + + if (!ref) + qax_shutDown(); + return ref; +} + +unsigned long qAxLockCount() +{ + return qAxModuleRef; +} + +///////////////////////////////////////////////////////////////////////////// +// Registry +///////////////////////////////////////////////////////////////////////////// + +extern bool qax_disable_inplaceframe; + +QString qax_clean_type(const QString &type, const QMetaObject *mo) +{ + if (mo) { + int classInfoIdx = mo->indexOfClassInfo("CoClassAlias"); + if (classInfoIdx != -1) { + const QMetaClassInfo classInfo = mo->classInfo(classInfoIdx); + return QLatin1String(classInfo.value()); + } + } + + QString alias(type); + alias.remove(QLatin1String("::")); + return alias; +} + +// (Un)Register the ActiveX server in the registry. +// The QAxFactory implementation provides the information. +HRESULT UpdateRegistry(BOOL bRegister) +{ + qAxIsServer = false; + QString file = QString::fromLocal8Bit(qAxModuleFilename); + QString path = file.left(file.lastIndexOf(QLatin1String("\\"))+1); + QString module = file.right(file.length() - path.length()); + module = module.left(module.lastIndexOf(QLatin1String("."))); + + const QString appId = qAxFactory()->appID().toString().toUpper(); + const QString libId = qAxFactory()->typeLibID().toString().toUpper(); + + QString libFile = qAxInit(); + QString typeLibVersion; + + TLIBATTR *libAttr = 0; + if (qAxTypeLibrary) + qAxTypeLibrary->GetLibAttr(&libAttr); + if (!libAttr) + return SELFREG_E_TYPELIB; + + DWORD major = libAttr->wMajorVerNum; + DWORD minor = libAttr->wMinorVerNum; + typeLibVersion = QString::number((uint)major) + QLatin1String(".") + QString::number((uint)minor); + + if (bRegister) + RegisterTypeLib(qAxTypeLibrary, (TCHAR*)libFile.utf16(), 0); + else + UnRegisterTypeLib(libAttr->guid, libAttr->wMajorVerNum, libAttr->wMinorVerNum, libAttr->lcid, libAttr->syskind); + + qAxTypeLibrary->ReleaseTLibAttr(libAttr); + + if (typeLibVersion.isEmpty()) + typeLibVersion = QLatin1String("1.0"); + + // check whether the user has permission to write to HKLM\Software\Classes + // if not, use HKCU\Software\Classes + QString keyPath(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes")); + QSettings test(keyPath, QSettings::NativeFormat); + if (!test.isWritable()) + keyPath = QLatin1String("HKEY_CURRENT_USER\\Software\\Classes"); + + QSettings settings(keyPath, QSettings::NativeFormat); + + // we try to create the ActiveX widgets later on... + bool delete_qApp = false; + if (!qApp) { + int argc = 0; + (void)new QApplication(argc, 0); + delete_qApp = true; + } + + if (bRegister) { + if (qAxOutProcServer) { + settings.setValue(QLatin1String("/AppID/") + appId + QLatin1String("/."), module); + settings.setValue(QLatin1String("/AppID/") + module + QLatin1String(".EXE/AppID"), appId); + } + + QStringList keys = qAxFactory()->featureList(); + for (QStringList::Iterator key = keys.begin(); key != keys.end(); ++key) { + QString className = *key; + QObject *object = qAxFactory()->createObject(className); + const QMetaObject *mo = qAxFactory()->metaObject(className); + const QString classId = qAxFactory()->classID(className).toString().toUpper(); + + className = qax_clean_type(className, mo); + + if (object) { // don't register subobject classes + QString classVersion = mo ? QString(QLatin1String(mo->classInfo(mo->indexOfClassInfo("Version")).value())) : QString(); + if (classVersion.isNull()) + classVersion = QLatin1String("1.0"); + bool insertable = mo && !qstricmp(mo->classInfo(mo->indexOfClassInfo("Insertable")).value(), "yes"); + bool control = object->isWidgetType(); + const QString classMajorVersion = classVersion.left(classVersion.indexOf(QLatin1String("."))); + uint olemisc = OLEMISC_SETCLIENTSITEFIRST + |OLEMISC_ACTIVATEWHENVISIBLE + |OLEMISC_INSIDEOUT + |OLEMISC_CANTLINKINSIDE + |OLEMISC_RECOMPOSEONRESIZE; + if (!control) + olemisc |= OLEMISC_INVISIBLEATRUNTIME; + else if (qFindChild<QMenuBar*>(object) && !qax_disable_inplaceframe) + olemisc |= OLEMISC_WANTSTOMENUMERGE; + + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/."), className + QLatin1String(" Class")); + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/CLSID/."), classId); + if (insertable) + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/Insertable/."), QVariant(QLatin1String(""))); + + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/."), className + QLatin1String(" Class")); + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/CLSID/."), classId); + settings.setValue(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/CurVer/."), module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion); + + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/."), className + QLatin1String(" Class")); + if (file.right(3).toLower() == QLatin1String("exe")) + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/AppID"), appId); + if (control) + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/Control/."), QVariant(QLatin1String(""))); + if (insertable) + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/Insertable/."), QVariant(QLatin1String(""))); + if (file.right(3).toLower() == QLatin1String("dll")) + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/InProcServer32/."), file); + else + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/LocalServer32/."), + QLatin1String("\"") + file + QLatin1String("\" -activex")); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/MiscStatus/."), control ? QLatin1String("1") : QLatin1String("0")); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/MiscStatus/1/."), QString::number(olemisc)); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/Programmable/."), QVariant(QLatin1String(""))); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/ToolboxBitmap32/."), QLatin1String("\"") + + file + QLatin1String("\", 101")); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/TypeLib/."), libId); settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/Version/."), classVersion); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/VersionIndependentProgID/."), module + QLatin1String(".") + className); + settings.setValue(QLatin1String("/CLSID/") + classId + QLatin1String("/ProgID/."), module + QLatin1String(".") + className + QLatin1String(".") + classVersion.left(classVersion.indexOf(QLatin1Char('.')))); + + QString mime = QLatin1String(mo->classInfo(mo->indexOfClassInfo("MIME")).value()); + if (!mime.isEmpty()) { + QStringList mimeTypes = mime.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + mime = mimeTypes.at(m); + if (mime.isEmpty()) + continue; + QString extension; + while (mime.contains(QLatin1Char(':'))) { + extension = mime.mid(mime.lastIndexOf(QLatin1Char(':')) + 1); + mime = mime.left(mime.length() - extension.length() - 1); + // Prepend '.' before extension, if required. + extension = extension.trimmed(); + if (extension[0] != QChar(QLatin1Char('.'))) + extension = QLatin1String(".") + extension; + } + + if (!extension.isEmpty()) { + settings.setValue(QLatin1String("/") + extension + QLatin1String("/."), module + QLatin1String(".") + className); + settings.setValue(QLatin1String("/") + extension + QLatin1String("/Content Type"), mime); + + mime = mime.replace(QLatin1String("/"), QLatin1String("\\")); + settings.setValue(QLatin1String("/MIME/Database/Content Type/") + mime + QLatin1String("/CLSID"), classId); + settings.setValue(QLatin1String("/MIME/Database/Content Type/") + mime + QLatin1String("/Extension"), extension); + } + } + } + + delete object; + } + + qAxFactory()->registerClass(*key, &settings); + } + } else { + QStringList keys = qAxFactory()->featureList(); + for (QStringList::Iterator key = keys.begin(); key != keys.end(); ++key) { + QString className = *key; + const QMetaObject *mo = qAxFactory()->metaObject(className); + const QString classId = qAxFactory()->classID(className).toString().toUpper(); + className = qax_clean_type(className, mo); + + QString classVersion = mo ? QString(QLatin1String(mo->classInfo(mo->indexOfClassInfo("Version")).value())) : QString(); + if (classVersion.isNull()) + classVersion = QLatin1String("1.0"); + const QString classMajorVersion = classVersion.left(classVersion.indexOf(QLatin1String("."))); + + qAxFactory()->unregisterClass(*key, &settings); + + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/CLSID/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/Insertable/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion + QLatin1String("/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String(".") + classMajorVersion); + + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/CLSID/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/CurVer/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className + QLatin1String("/.")); + settings.remove(QLatin1String("/") + module + QLatin1String(".") + className); + + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/AppID")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/Control/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/Insertable/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/InProcServer32/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/LocalServer32/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/MiscStatus/1/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/MiscStatus/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/Programmable/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/ToolboxBitmap32/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/TypeLib/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/Version/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/VersionIndependentProgID/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/ProgID/.")); + settings.remove(QLatin1String("/CLSID/") + classId + QLatin1String("/.")); + settings.remove(QLatin1String("/CLSID/") + classId); + + QString mime = QLatin1String(mo->classInfo(mo->indexOfClassInfo("MIME")).value()); + if (!mime.isEmpty()) { + QStringList mimeTypes = mime.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + mime = mimeTypes.at(m); + if (mime.isEmpty()) + continue; + QString extension; + while (mime.contains(QLatin1Char(':'))) { + extension = mime.mid(mime.lastIndexOf(QLatin1Char(':')) + 1); + mime = mime.left(mime.length() - extension.length() - 1); + // Prepend '.' before extension, if required. + extension = extension.trimmed(); + if (extension[0] != QChar(QLatin1Char('.'))) + extension = QLatin1String(".") + extension; + } + if (!extension.isEmpty()) { + settings.remove(QLatin1String("/") + extension + QLatin1String("/Content Type")); + settings.remove(QLatin1String("/") + extension + QLatin1String("/.")); + settings.remove(QLatin1String("/") + extension); + mime = mime.replace(QLatin1String("/"), QLatin1String("\\")); + settings.remove(QLatin1String("/MIME/Database/Content Type/") + mime + QLatin1String("/Extension")); + settings.remove(QLatin1String("/MIME/Database/Content Type/") + mime + QLatin1String("/CLSID")); + settings.remove(QLatin1String("/MIME/Database/Content Type/") + mime + QLatin1String("/.")); + settings.remove(QLatin1String("/MIME/Database/Content Type/") + mime); + } + } + } + } + } + + if (delete_qApp) + delete qApp; + + qAxCleanup(); + if (settings.status() == QSettings::NoError) + return S_OK; + return SELFREG_E_CLASS; +} + +///////////////////////////////////////////////////////////////////////////// +// IDL generator +///////////////////////////////////////////////////////////////////////////// + +static QList<QByteArray> enums; +static QList<QByteArray> enumValues; +static QList<QByteArray> subtypes; + +static const char* const type_map[][2] = +{ + // QVariant/Qt Value data types + { "QString", "BSTR" }, + { "QCString", "BSTR" }, + { "bool", "VARIANT_BOOL" }, + { "int", "int" }, + { "uint", "unsigned int" }, + { "double", "double" }, + { "QColor", "OLE_COLOR" }, + { "QDate", "DATE" }, + { "QTime", "DATE" }, + { "QDateTime", "DATE" }, + { "QFont", "IFontDisp*" }, + { "QPixmap", "IPictureDisp*" }, + { "QVariant", "VARIANT" }, + { "QVariantList", "SAFEARRAY(VARIANT)" }, + { "QList<QVariant>","SAFEARRAY(VARIANT)" }, + { "quint64", "CY" }, + { "qint64", "CY" }, + { "qulonglong", "CY" }, + { "qlonglong", "CY" }, + { "QByteArray", "SAFEARRAY(BYTE)" }, + { "QStringList", "SAFEARRAY(BSTR)" }, + // Userdefined Qt datatypes - some not on Borland though + { "QCursor", "enum MousePointer" }, + { "Qt::FocusPolicy","enum FocusPolicy" }, +#ifndef Q_CC_BOR +# if __REQUIRED_RPCNDR_H_VERSION__ >= Q_REQUIRED_RPCNDR_H_VERSION + { "QRect", "struct QRect" }, + { "QSize", "struct QSize" }, + { "QPoint", "struct QPoint" }, +# endif +#endif + // And we support COM data types + { "BOOL", "BOOL" }, + { "BSTR", "BSTR" }, + { "OLE_COLOR", "OLE_COLOR" }, + { "DATE", "DATE" }, + { "VARIANT", "VARIANT" }, + { "IDispatch", "IDispatch*" }, + { "IUnknown", "IUnknown*" }, + { "IDispatch*", "IDispatch*" }, + { "IUnknown*", "IUnknown*" }, + { 0, 0 } +}; + +static QByteArray convertTypes(const QByteArray &qtype, bool *ok) +{ + qRegisterMetaType("IDispatch*", (void**)0); + qRegisterMetaType("IUnknown*", (void**)0); + + *ok = false; + + int i = 0; + while (type_map[i][0]) { + if (qtype == type_map[i][0] && type_map[i][1]) { + *ok = true; + return type_map[i][1]; + } + ++i; + } + if (enums.contains(qtype)) { + *ok = true; + return "enum " + qtype; + } + if (subtypes.contains(qtype)) { + *ok = true; + } else if (qtype.endsWith('*')) { + QByteArray cleanType = qtype.left(qtype.length() - 1); + const QMetaObject *mo = qAxFactory()->metaObject(QString::fromLatin1(cleanType.constData())); + if (mo) { + cleanType = qax_clean_type(QString::fromLatin1(cleanType), mo).toLatin1(); + if (subtypes.contains(cleanType)) { + *ok = true; + return cleanType + '*'; + } + } + } + return qtype; +} + +static const char* const keyword_map[][2] = +{ + { "aggregatable", "aggregating" }, + { "allocate", "alloc" }, + { "appobject", "appObject" }, + { "arrays", "array" }, + { "async", "asynchronous" }, + { "bindable", "binding" }, + { "Boolean", "boolval" }, + { "boolean", "boolval" }, + { "broadcast", "broadCast" }, + { "callback", "callBack" }, + { "decode", "deCode" }, + { "default", "defaulted" }, + { "defaultbind", "defaultBind" }, + { "defaultvalue", "defaultValue" }, + { "encode" "enCode" }, + { "endpoint", "endPoint" }, + { "hidden", "isHidden" }, + { "ignore", "ignore_" }, + { "local", "local_" }, + { "notify", "notify_" }, + { "object", "object_" }, + { "optimize", "optimize_" }, + { "optional", "optional_" }, + { "out", "out_" }, + { "pipe", "pipe_" }, + { "proxy", "proxy_" }, + { "ptr", "pointer" }, + { "readonly", "readOnly" }, + { "small", "small_" }, + { "source", "source_" }, + { "string", "string_" }, + { "uuid", "uuid_" }, + { 0, 0 } +}; + +static QByteArray replaceKeyword(const QByteArray &name) +{ + int i = 0; + while (keyword_map[i][0]) { + if (name == keyword_map[i][0] && keyword_map[i][1]) + return keyword_map[i][1]; + ++i; + } + return name; +} + +static QMap<QByteArray, int> mapping; + +static QByteArray renameOverloads(const QByteArray &name) +{ + QByteArray newName = name; + + int n = mapping.value(name); + if (mapping.contains(name)) { + int n = mapping.value(name); + newName = name + "_" + QByteArray::number(n); + mapping.insert(name, n+1); + } else { + mapping.insert(name, 1); + } + + return newName; +} + +// filter out some properties +static const char* const ignore_props[] = +{ + "name", + "objectName", + "isTopLevel", + "isDialog", + "isModal", + "isPopup", + "isDesktop", + "geometry", + "pos", + "frameSize", + "frameGeometry", + "size", + "sizeHint", + "minimumSizeHint", + "microFocusHint", + "rect", + "childrenRect", + "childrenRegion", + "minimumSize", + "maximumSize", + "sizeIncrement", + "baseSize", + "ownPalette", + "ownFont", + "ownCursor", + "visibleRect", + "isActiveWindow", + "underMouse", + "visible", + "hidden", + "minimized", + "focus", + "focusEnabled", + "customWhatsThis", + "shown", + "windowOpacity", + 0 +}; + +// filter out some slots +static const char* const ignore_slots[] = +{ + "deleteLater", + "setMouseTracking", + "update", + "repaint", + "iconify", + "showMinimized", + "showMaximized", + "showFullScreen", + "showNormal", + "polish", + "constPolish", + "stackUnder", + "setShown", + "setHidden", + "move_1", + "resize_1", + "setGeometry_1", + 0 +}; + +static bool ignore(const char *test, const char *const *table) +{ + if (!test) + return true; + int i = 0; + while (table[i]) { + if (!strcmp(test, table[i])) + return true; + ++i; + } + return false; +} + +bool ignoreSlots(const char *test) +{ + return ignore(test, ignore_slots); +} + +bool ignoreProps(const char *test) +{ + return ignore(test, ignore_props); +} + +#define STRIPCB(x) x = x.mid(1, x.length()-2) + +static QByteArray prototype(const QList<QByteArray> ¶meterTypes, const QList<QByteArray> ¶meterNames, bool *ok) +{ + QByteArray prototype; + + for (int p = 0; p < parameterTypes.count() && *ok; ++p) { + bool out = false; + QByteArray type(parameterTypes.at(p)); + QByteArray name(parameterNames.at(p)); + + if (type.endsWith("&")) { + out = true; + type.truncate(type.length() - 1); + } else if (type.endsWith("**")) { + out = true; + type.truncate(type.length() - 1); + } else if (type.endsWith("*") && !subtypes.contains(type)) { + type.truncate(type.length() - 1); + } + if (type.isEmpty()) { + *ok = false; + break; + } + type = convertTypes(type, ok); + if (!out) + prototype += "[in] " + type + " "; + else + prototype += "[in,out] " + type + " "; + + if (out) + prototype += "*"; + if (name.isEmpty()) + prototype += "p" + QByteArray::number(p); + else + prototype += "p_" + replaceKeyword(name); + + if (p < parameterTypes.count() - 1) + prototype += ", "; + } + + return prototype; +} + +static QByteArray addDefaultArguments(const QByteArray &prototype, int numDefArgs) +{ + // nothing to do, or unsupported anyway + if (!numDefArgs || prototype.contains("/**")) + return prototype; + + QByteArray ptype(prototype); + int in = -1; + while (numDefArgs) { + in = ptype.lastIndexOf("]", in); + ptype.replace(in, 1, ",optional]"); + in = ptype.indexOf(' ', in) + 1; + QByteArray type = ptype.mid(in, ptype.indexOf(' ', in) - in); + if (type == "enum") + type += " " + ptype.mid(in + 5, ptype.indexOf(' ', in + 5) - in - 5); + ptype.replace(in, type.length(), QByteArray("VARIANT /*was: ") + type + "*/"); + --numDefArgs; + } + + return ptype; +} + +static HRESULT classIDL(QObject *o, const QMetaObject *mo, const QString &className, bool isBindable, QTextStream &out) +{ + int id = 1; + int i = 0; + if (!mo) + return 3; + + QString topclass = qAxFactory()->exposeToSuperClass(className); + if (topclass.isEmpty()) + topclass = QLatin1String("QObject"); + bool hasStockEvents = qAxFactory()->hasStockEvents(className); + + const QMetaObject *pmo = mo; + do { + pmo = pmo->superClass(); + } while (pmo && topclass != QString::fromLatin1(pmo->className())); + + int enumoff = pmo ? pmo->enumeratorOffset() : mo->enumeratorOffset(); + int methodoff = pmo ? pmo->methodOffset() : mo->methodOffset(); + int propoff = pmo ? pmo->propertyOffset() : mo->propertyOffset(); + + int qtProps = 0; + int qtSlots = 0; + + bool control = false; + + if (o && o->isWidgetType()) { + qtProps = QWidget::staticMetaObject.propertyCount(); + qtSlots = QWidget::staticMetaObject.methodCount(); + control = true; + } + + QString classID = qAxFactory()->classID(className).toString().toUpper(); + if (QUuid(classID).isNull()) + return 4; + STRIPCB(classID); + QString interfaceID = qAxFactory()->interfaceID(className).toString().toUpper(); + if (QUuid(interfaceID).isNull()) + return 5; + STRIPCB(interfaceID); + QString eventsID = qAxFactory()->eventsID(className).toString().toUpper(); + bool hasEvents = !QUuid(eventsID).isNull(); + STRIPCB(eventsID); + + QString cleanClassName = qax_clean_type(className, mo); + QString defProp(QLatin1String(mo->classInfo(mo->indexOfClassInfo("DefaultProperty")).value())); + QString defSignal(QLatin1String(mo->classInfo(mo->indexOfClassInfo("DefaultSignal")).value())); + + for (i = enumoff; i < mo->enumeratorCount(); ++i) { + const QMetaEnum enumerator = mo->enumerator(i); + if (enums.contains(enumerator.name())) + continue; + + enums.append(enumerator.name()); + + out << "\tenum " << enumerator.name() << " {" << endl; + + for (int j = 0; j < enumerator.keyCount(); ++j) { + QByteArray key(enumerator.key(j)); + while (enumValues.contains(key)) { + key += "_"; + } + enumValues.append(key); + uint value = (uint)enumerator.value(j); + key = key.leftJustified(20); + out << "\t\t" << key << "\t= "; + if (enumerator.isFlag()) + out << "0x" << QByteArray::number(value, 16).rightJustified(8, '0'); + else + out << value; + if (j < enumerator.keyCount()-1) + out << ", "; + out << endl; + } + out << "\t};" << endl << endl; + } + + // mouse cursor enum for QCursor support + if (!enums.contains("MousePointer")) { + enums.append("MousePointer"); + out << "\tenum MousePointer {" << endl; + out << "\t\tArrowCursor = " << Qt::ArrowCursor << "," << endl; + out << "\t\tUpArrowCursor = " << Qt::UpArrowCursor << "," << endl; + out << "\t\tCrossCursor = " << Qt::CrossCursor << "," << endl; + out << "\t\tWaitCursor = " << Qt::WaitCursor << "," << endl; + out << "\t\tIBeamCursor = " << Qt::IBeamCursor << "," << endl; + out << "\t\tSizeVerCursor = " << Qt::SizeVerCursor << "," << endl; + out << "\t\tSizeHorCursor = " << Qt::SizeHorCursor << "," << endl; + out << "\t\tSizeBDiagCursor = " << Qt::SizeBDiagCursor << "," << endl; + out << "\t\tSizeFDiagCursor = " << Qt::SizeFDiagCursor << "," << endl; + out << "\t\tSizeAllCursor = " << Qt::SizeAllCursor << "," << endl; + out << "\t\tBlankCursor = " << Qt::BlankCursor << "," << endl; + out << "\t\tSplitVCursor = " << Qt::SplitVCursor << "," << endl; + out << "\t\tSplitHCursor = " << Qt::SplitHCursor << "," << endl; + out << "\t\tPointingHandCursor = " << Qt::PointingHandCursor << "," << endl; + out << "\t\tForbiddenCursor = " << Qt::ForbiddenCursor << "," << endl; + out << "\t\tWhatsThisCursor = " << Qt::WhatsThisCursor << "," << endl; + out << "\t\tBusyCursor\t= " << Qt::BusyCursor << endl; + out << "\t};" << endl << endl; + } + if (!enums.contains("FocusPolicy")) { + enums.append("FocusPolicy"); + out << "\tenum FocusPolicy {" << endl; + out << "\t\tNoFocus = " << Qt::NoFocus << "," << endl; + out << "\t\tTabFocus = " << Qt::TabFocus << "," << endl; + out << "\t\tClickFocus = " << Qt::ClickFocus << "," << endl; + out << "\t\tStrongFocus = " << Qt::StrongFocus << "," << endl; + out << "\t\tWheelFocus = " << Qt::WheelFocus << endl; + out << "\t};" << endl << endl; + } + + out << endl; + out << "\t[" << endl; + out << "\t\tuuid(" << interfaceID << ")," << endl; + out << "\t\thelpstring(\"" << cleanClassName << " Interface\")" << endl; + out << "\t]" << endl; + out << "\tdispinterface I" << cleanClassName << endl; + out << "\t{" << endl; + + out << "\tproperties:" << endl; + for (i = propoff; i < mo->propertyCount(); ++i) { + const QMetaProperty property = mo->property(i); + /* if (property.testFlags(QMetaProperty::Override)) + continue;*/ + if (i <= qtProps && ignoreProps(property.name())) + continue; + if (!property.name() || mo->indexOfProperty(property.name()) > i) + continue; + + bool ok = true; + QByteArray type(convertTypes(property.typeName(), &ok)); + QByteArray name(replaceKeyword(property.name())); + + if (!ok) + out << "\t/****** Property is of unsupported datatype" << endl; + + out << "\t\t[id(" << id << ")"; + if (!property.isWritable()) + out << ", readonly"; + if (isBindable && property.isScriptable(o)) + out << ", bindable"; + if (!property.isDesignable(o)) + out << ", nonbrowsable"; + if (isBindable) + out << ", requestedit"; + if (defProp == QString::fromLatin1(name.constData())) + out << ", uidefault"; + out << "] " << type << " " << name << ";" << endl; + + if (!ok) + out << "\t******/" << endl; + ++id; + } + out << endl; + out << "\tmethods:" << endl; + int numDefArgs = 0; + QByteArray outBuffer; + for (i = methodoff; i < mo->methodCount(); ++i) { + const QMetaMethod slot = mo->method(i); + if (slot.access() != QMetaMethod::Public || slot.methodType() == QMetaMethod::Signal) + continue; + + if (slot.attributes() & QMetaMethod::Cloned) { + ++numDefArgs; + continue; + } + if (!outBuffer.isEmpty()) { + outBuffer = addDefaultArguments(outBuffer, numDefArgs); + numDefArgs = 0; + out << outBuffer; + outBuffer = QByteArray(); + } + + QByteArray signature(slot.signature()); + QByteArray name(signature.left(signature.indexOf('('))); + if (i <= qtSlots && ignoreSlots(name)) + continue; + + signature = signature.mid(name.length() + 1); + signature.truncate(signature.length() - 1); + name = renameOverloads(replaceKeyword(name)); + if (ignoreSlots(name)) + continue; + + QList<QByteArray> parameterTypes(slot.parameterTypes()); + QList<QByteArray> parameterNames(slot.parameterNames()); + + bool ok = true; + QByteArray type = slot.typeName(); + if (type.isEmpty()) + type = "void"; + else + type = convertTypes(type, &ok); + + QByteArray ptype(prototype(parameterTypes, parameterNames, &ok)); + if (!ok) + outBuffer += "\t/****** Slot parameter uses unsupported datatype\n"; + + outBuffer += "\t\t[id(" + QString::number(id).toLatin1() + ")] " + type + " " + name + "(" + ptype + ");\n"; + + if (!ok) + outBuffer += "\t******/\n"; + ++id; + } + if (!outBuffer.isEmpty()) { + outBuffer = addDefaultArguments(outBuffer, numDefArgs); + numDefArgs = 0; + out << outBuffer; + outBuffer = QByteArray(); + } + out << "\t};" << endl << endl; + + mapping.clear(); + id = 1; + + if (hasEvents) { + out << "\t[" << endl; + out << "\t\tuuid(" << eventsID << ")," << endl; + out << "\t\thelpstring(\"" << cleanClassName << " Events Interface\")" << endl; + out << "\t]" << endl; + out << "\tdispinterface I" << cleanClassName << "Events" << endl; + out << "\t{" << endl; + out << "\tproperties:" << endl; + out << "\tmethods:" << endl; + + if (hasStockEvents) { + out << "\t/****** Stock events ******/" << endl; + out << "\t\t[id(DISPID_CLICK)] void Click();" << endl; + out << "\t\t[id(DISPID_DBLCLICK)] void DblClick();" << endl; + out << "\t\t[id(DISPID_KEYDOWN)] void KeyDown(short* KeyCode, short Shift);" << endl; + out << "\t\t[id(DISPID_KEYPRESS)] void KeyPress(short* KeyAscii);" << endl; + out << "\t\t[id(DISPID_KEYUP)] void KeyUp(short* KeyCode, short Shift);" << endl; + out << "\t\t[id(DISPID_MOUSEDOWN)] void MouseDown(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << endl; + out << "\t\t[id(DISPID_MOUSEMOVE)] void MouseMove(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << endl; + out << "\t\t[id(DISPID_MOUSEUP)] void MouseUp(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);" << endl << endl; + } + + for (i = methodoff; i < mo->methodCount(); ++i) { + const QMetaMethod signal = mo->method(i); + if (signal.methodType() != QMetaMethod::Signal) + continue; + + QByteArray signature(signal.signature()); + QByteArray name(signature.left(signature.indexOf('('))); + signature = signature.mid(name.length() + 1); + signature.truncate(signature.length() - 1); + + QList<QByteArray> parameterTypes(signal.parameterTypes()); + QList<QByteArray> parameterNames(signal.parameterNames()); + + bool isDefault = defSignal == QString::fromLatin1(name.constData()); + name = renameOverloads(replaceKeyword(name)); + bool ok = true; + + QByteArray type = signal.typeName(); + if (!type.isEmpty()) // signals with return value not supported + continue; + + QByteArray ptype(prototype(parameterTypes, parameterNames, &ok)); + if (!ok) + out << "\t/****** Signal parameter uses unsupported datatype" << endl; + + out << "\t\t[id(" << id << ")"; + if (isDefault) + out << ", uidefault"; + out << "] void " << name << "(" << ptype << ");" << endl; + + if (!ok) + out << "\t******/" << endl; + ++id; + } + out << "\t};" << endl << endl; + } + + out << "\t[" << endl; + + if (qstricmp(mo->classInfo(mo->indexOfClassInfo("Aggregatable")).value(), "no")) + out << "\t\taggregatable," << endl; + if (!qstricmp(mo->classInfo(mo->indexOfClassInfo("RegisterObject")).value(), "yes")) + out << "\t\tappobject," << endl; + if (mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value()) + out << "\t\tlicensed," << endl; + const char *helpString = mo->classInfo(mo->indexOfClassInfo("Description")).value(); + if (helpString) + out << "\t\thelpstring(\"" << helpString << "\")," << endl; + else + out << "\t\thelpstring(\"" << cleanClassName << " Class\")," << endl; + const char *classVersion = mo->classInfo(mo->indexOfClassInfo("Version")).value(); + if (classVersion) + out << "\t\tversion(" << classVersion << ")," << endl; + out << "\t\tuuid(" << classID << ")"; + if (control) { + out << ", " << endl; + out << "\t\tcontrol"; + } else if (!o) { + out << ", " << endl; + out << "\t\tnoncreatable"; + } + out << endl; + out << "\t]" << endl; + out << "\tcoclass " << cleanClassName << endl; + out << "\t{" << endl; + out << "\t\t[default] dispinterface I" << cleanClassName << ";" << endl; + if (hasEvents) + out << "\t\t[default, source] dispinterface I" << cleanClassName << "Events;" << endl; + out << "\t};" << endl; + + return S_OK; +} + +#if defined(Q_CC_BOR) +extern "C" __stdcall HRESULT DumpIDL(const QString &outfile, const QString &ver) +#else +extern "C" HRESULT __stdcall DumpIDL(const QString &outfile, const QString &ver) +#endif +{ + qAxIsServer = false; + QTextStream out; + if (outfile.contains(QLatin1String("\\"))) { + QString outpath = outfile.left(outfile.lastIndexOf(QLatin1String("\\"))); + QDir dir; + dir.mkpath(outpath); + } + QFile file(outfile); + file.remove(); + + QString filebase = QString::fromLocal8Bit(qAxModuleFilename); + filebase = filebase.left(filebase.lastIndexOf(QLatin1String("."))); + + QString appID = qAxFactory()->appID().toString().toUpper(); + if (QUuid(appID).isNull()) + return 1; + STRIPCB(appID); + QString typeLibID = qAxFactory()->typeLibID().toString().toUpper(); + if (QUuid(typeLibID).isNull()) + return 2; + STRIPCB(typeLibID); + QString typelib = filebase.right(filebase.length() - filebase.lastIndexOf(QLatin1String("\\"))-1); + + if (!file.open(QIODevice::WriteOnly)) + return -1; + + out.setDevice(&file); + + QString version(ver.unicode(), ver.length()); + while (version.count(QLatin1Char('.')) > 1) { + int lastdot = version.lastIndexOf(QLatin1Char('.')); + version = version.left(lastdot) + version.right(version.length() - lastdot - 1); + } + if (version.isEmpty()) + version = QLatin1String("1.0"); + + QString idQRect(QUuid(CLSID_QRect).toString()); + STRIPCB(idQRect); + QString idQSize(QUuid(CLSID_QSize).toString()); + STRIPCB(idQSize); + QString idQPoint(QUuid(CLSID_QPoint).toString()); + STRIPCB(idQPoint); + + out << "/****************************************************************************" << endl; + out << "** Interface definition generated for ActiveQt project" << endl; + out << "**" << endl; + out << "** '" << qAxModuleFilename << "'" << endl; + out << "**" << endl; + out << "** Created: " << QDateTime::currentDateTime().toString() << endl; + out << "**" << endl; + out << "** WARNING! All changes made in this file will be lost!" << endl; + out << "****************************************************************************/" << endl << endl; + + out << "import \"ocidl.idl\";" << endl; + out << "#include <olectl.h>" << endl << endl; + + // dummy application to create widgets + bool delete_qApp = false; + if (!qApp) { + int argc; + (void)new QApplication(argc, 0); + delete_qApp = true; + } + + out << "[" << endl; + out << "\tuuid(" << typeLibID << ")," << endl; + out << "\tversion(" << version << ")," << endl; + out << "\thelpstring(\"" << typelib << " " << version << " Type Library\")" << endl; + out << "]" << endl; + out << "library " << typelib << "Lib" << endl; + out << "{" << endl; + out << "\timportlib(\"stdole32.tlb\");" << endl; + out << "\timportlib(\"stdole2.tlb\");" << endl << endl; + + QStringList keys = qAxFactory()->featureList(); + QStringList::ConstIterator key; + + out << "\t/************************************************************************" << endl; + out << "\t** If this causes a compile error in MIDL you need to upgrade the" << endl; + out << "\t** Platform SDK you are using. Download the SDK from msdn.microsoft.com" << endl; + out << "\t** and make sure that both the system and the Visual Studio environment" << endl; + out << "\t** use the correct files." << endl; + out << "\t**" << endl; + +#ifndef Q_CC_BOR +#if __REQUIRED_RPCNDR_H_VERSION__ < Q_REQUIRED_RPCNDR_H_VERSION + out << "\t** Required version of MIDL could not be verified. QRect, QSize and QPoint" << endl; + out << "\t** support needs an updated Platform SDK to be installed." << endl; + out << "\t*************************************************************************" << endl; +#else + out << "\t************************************************************************/" << endl; +#endif + + out << endl; + out << "\t[uuid(" << idQRect << ")]" << endl; + out << "\tstruct QRect {" << endl; + out << "\t\tint left;" << endl; + out << "\t\tint top;" << endl; + out << "\t\tint right;" << endl; + out << "\t\tint bottom;" << endl; + out << "\t};" << endl << endl; + + out << "\t[uuid(" << idQSize << ")]" << endl; + out << "\tstruct QSize {" << endl; + out << "\t\tint width;" << endl; + out << "\t\tint height;" << endl; + out << "\t};" << endl << endl; + + out << "\t[uuid(" << idQPoint << ")]" << endl; + out << "\tstruct QPoint {" << endl; + out << "\t\tint x;" << endl; + out << "\t\tint y;" << endl; + out << "\t};" << endl; +#if __REQUIRED_RPCNDR_H_VERSION__ < Q_REQUIRED_RPCNDR_H_VERSION + out << "\t*/" << endl; +#endif +#else + out << "\t** Custom data types not supported with Borland." << endl; + out << "\t*************************************************************************" << endl; +#endif + out << endl; + + out << "\t/* Forward declaration of classes that might be used as parameters */" << endl << endl; + + int res = S_OK; + for (key = keys.begin(); key != keys.end(); ++key) { + QByteArray className = (*key).toLatin1(); + const QMetaObject *mo = qAxFactory()->metaObject(QString::fromLatin1(className.constData())); + // We have meta object information for this type. Forward declare it. + if (mo) { + QByteArray cleanType = qax_clean_type(*key, mo).toLatin1(); + out << "\tcoclass " << cleanType << ";" << endl; + subtypes.append(cleanType); + subtypes.append(cleanType + "*"); + qRegisterMetaType(cleanType, (void**)0); + qRegisterMetaType(cleanType + "*", (void**)0); + } + } + out << endl; + + for (key = keys.begin(); key != keys.end(); ++key) { + QByteArray className = (*key).toLatin1(); + const QMetaObject *mo = qAxFactory()->metaObject(QString::fromLatin1(className.constData())); + // We have meta object information for this type. Define it. + if (mo) { + QObject *o = qAxFactory()->createObject(QString::fromLatin1(className.constData())); + // It's not a control class, so it is actually a subtype. Define it. + if (!o) + res = classIDL(0, mo, QString::fromLatin1(className), false, out); + delete o; + } + } + + out << endl; + if (res != S_OK) + goto ErrorInClass; + + for (key = keys.begin(); key != keys.end(); ++key) { + QByteArray className = (*key).toLatin1(); + QObject *o = qAxFactory()->createObject(QString::fromLatin1(className.constData())); + if (!o) + continue; + const QMetaObject *mo = o->metaObject(); + QAxBindable *bind = (QAxBindable*)o->qt_metacast("QAxBindable"); + bool isBindable = bind != 0; + + QByteArray cleanType = qax_clean_type(*key, mo).toLatin1(); + subtypes.append(cleanType); + subtypes.append(cleanType + "*"); + res = classIDL(o, mo, QString::fromLatin1(className.constData()), isBindable, out); + delete o; + if (res != S_OK) + break; + } + + out << "};" << endl; + out.flush(); + +ErrorInClass: + if (delete_qApp) + delete qApp; + + if (res != S_OK) { + file.close(); + file.remove(); + } + + return res; +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxserver.def b/src/activeqt/control/qaxserver.def new file mode 100644 index 0000000..a00638d --- /dev/null +++ b/src/activeqt/control/qaxserver.def @@ -0,0 +1,8 @@ +; mfc_test.def : Declares the module parameters. + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DumpIDL PRIVATE diff --git a/src/activeqt/control/qaxserver.ico b/src/activeqt/control/qaxserver.ico Binary files differnew file mode 100644 index 0000000..c80d36a --- /dev/null +++ b/src/activeqt/control/qaxserver.ico diff --git a/src/activeqt/control/qaxserver.rc b/src/activeqt/control/qaxserver.rc new file mode 100644 index 0000000..390e481 --- /dev/null +++ b/src/activeqt/control/qaxserver.rc @@ -0,0 +1,2 @@ +1 TYPELIB "qaxserver.rc" +1 ICON DISCARDABLE "qaxserver.ico" diff --git a/src/activeqt/control/qaxserverbase.cpp b/src/activeqt/control/qaxserverbase.cpp new file mode 100644 index 0000000..b9d73a1 --- /dev/null +++ b/src/activeqt/control/qaxserverbase.cpp @@ -0,0 +1,4477 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 QT_NO_CAST_TO_ASCII + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qabstracteventdispatcher.h> +#include <qapplication.h> +#include <qbuffer.h> +#include <qdatastream.h> +#include <qdebug.h> +#include <qevent.h> +#include <qeventloop.h> +#include <qfile.h> +#include <qpointer.h> +#include <qhash.h> +#include <qmap.h> +#include <qmenubar.h> +#include <qmenu.h> +#include <qmetaobject.h> +#include <qpixmap.h> +#include <qstatusbar.h> +#include <qwhatsthis.h> +#include <ocidl.h> +#include <olectl.h> +#include <private/qcoreapplication_p.h> + +#include "qaxfactory.h" +#include "qaxbindable.h" +#include "qaxaggregated.h" + +#include "../shared/qaxtypes.h" + +#if defined Q_CC_GNU +# include <w32api.h> +#endif + +#ifndef Q_OS_WIN64 +#define ULONG_PTR DWORD +#endif + +QT_BEGIN_NAMESPACE + +extern HHOOK qax_hhook; + +// in qaxserver.cpp +extern ITypeLib *qAxTypeLibrary; +extern QAxFactory *qAxFactory(); +extern unsigned long qAxLock(); +extern unsigned long qAxUnlock(); +extern HANDLE qAxInstance; +extern bool qAxOutProcServer; + +static int invokeCount = 0; + +#ifdef QT_DEBUG +unsigned long qaxserverbase_instance_count = 0; +#endif + +// in qaxserverdll.cpp +extern bool qax_ownQApp; + +struct QAxExceptInfo +{ + QAxExceptInfo(int c, const QString &s, const QString &d, const QString &x) + : code(c), src(s), desc(d), context(x) + { + } + int code; + QString src; + QString desc; + QString context; +}; + + +bool qt_sendSpontaneousEvent(QObject*, QEvent*); + +/* + \class QAxServerBase + \brief The QAxServerBase class is an ActiveX control hosting a QWidget. + + \internal +*/ +class QAxServerBase : + public QObject, + public IAxServerBase, + public IDispatch, + public IOleObject, + public IOleControl, +#if defined Q_CC_GNU +# if (__W32API_MAJOR_VERSION < 2 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION < 5)) + public IViewObject, // this should not be needed as IViewObject2 is meant to inherit from this, + // untill the mingw headers are fixed this will need to stay. +# endif +#endif + public IViewObject2, + public IOleInPlaceObject, + public IOleInPlaceActiveObject, + public IProvideClassInfo2, + public IConnectionPointContainer, + public IPersistStream, + public IPersistStreamInit, + public IPersistStorage, + public IPersistPropertyBag, + public IPersistFile, + public IDataObject +{ +public: + typedef QMap<QUuid,IConnectionPoint*> ConnectionPoints; + typedef QMap<QUuid,IConnectionPoint*>::Iterator ConnectionPointsIterator; + + QAxServerBase(const QString &classname, IUnknown *outerUnknown); + QAxServerBase(QObject *o); + + void init(); + + ~QAxServerBase(); + +// Window creation + HWND create(HWND hWndParent, RECT& rcPos); + HMENU createPopup(QMenu *popup, HMENU oldMenu = 0); + void createMenu(QMenuBar *menuBar); + void removeMenu(); + + static LRESULT CALLBACK ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +// Object registration with OLE + void registerActiveObject(IUnknown *object); + void revokeActiveObject(); + +// IUnknown + unsigned long WINAPI AddRef() + { + if (m_outerUnknown) + return m_outerUnknown->AddRef(); + + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + + return r; + } + unsigned long WINAPI Release() + { + if (m_outerUnknown) + return m_outerUnknown->Release(); + + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, void **iface); + HRESULT InternalQueryInterface(REFIID iid, void **iface); + +// IAxServerBase + IUnknown *clientSite() const + { + return m_spClientSite; + } + + void emitPropertyChanged(const char*); + bool emitRequestPropertyChange(const char*); + QObject *qObject() const + { + return theObject; + } + void ensureMetaData(); + bool isPropertyExposed(int index); + + void reportError(int code, const QString &src, const QString &desc, const QString &context) + { + if (exception) + delete exception; + exception = new QAxExceptInfo(code, src, desc, context); + } + +// IDispatch + STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); + STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); + STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); + STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, UINT* puArgErr); + +// IProvideClassInfo + STDMETHOD(GetClassInfo)(ITypeInfo** pptinfo); + +// IProvideClassInfo2 + STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID); + +// IOleObject + STDMETHOD(Advise)(IAdviseSink* pAdvSink, DWORD* pdwConnection); + STDMETHOD(Close)(DWORD dwSaveOption); + STDMETHOD(DoVerb)(LONG iVerb, LPMSG lpmsg, IOleClientSite* pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect); + STDMETHOD(EnumAdvise)(IEnumSTATDATA** ppenumAdvise); + STDMETHOD(EnumVerbs)(IEnumOLEVERB** ppEnumOleVerb); + STDMETHOD(GetClientSite)(IOleClientSite** ppClientSite); + STDMETHOD(GetClipboardData)(DWORD dwReserved, IDataObject** ppDataObject); + STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL* psizel); + STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus); + STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk); + STDMETHOD(GetUserClassID)(CLSID* pClsid); + STDMETHOD(GetUserType)(DWORD dwFormOfType, LPOLESTR *pszUserType); + STDMETHOD(InitFromData)(IDataObject* pDataObject, BOOL fCreation, DWORD dwReserved); + STDMETHOD(IsUpToDate)(); + STDMETHOD(SetClientSite)(IOleClientSite* pClientSite); + STDMETHOD(SetColorScheme)(LOGPALETTE* pLogPal); + STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL* psizel); + STDMETHOD(SetHostNames)(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj); + STDMETHOD(SetMoniker)(DWORD dwWhichMoniker, IMoniker* ppmk); + STDMETHOD(Unadvise)(DWORD dwConnection); + STDMETHOD(Update)(); + +// IViewObject + STDMETHOD(Draw)(DWORD dwAspect, LONG lIndex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDevice, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, + BOOL(__stdcall*pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue); + STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, LOGPALETTE **ppColorSet); + STDMETHOD(Freeze)(DWORD dwAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze); + STDMETHOD(Unfreeze)(DWORD dwFreeze); + STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink); + STDMETHOD(GetAdvise)(DWORD *aspects, DWORD *advf, IAdviseSink **pAdvSink); + +// IViewObject2 + STDMETHOD(GetExtent)(DWORD dwAspect, LONG lindex, DVTARGETDEVICE *ptd, LPSIZEL lpsizel); + +// IOleControl + STDMETHOD(FreezeEvents)(BOOL); + STDMETHOD(GetControlInfo)(LPCONTROLINFO); + STDMETHOD(OnAmbientPropertyChange)(DISPID); + STDMETHOD(OnMnemonic)(LPMSG); + +// IOleWindow + STDMETHOD(GetWindow)(HWND *pHwnd); + STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode); + +// IOleInPlaceObject + STDMETHOD(InPlaceDeactivate)(); + STDMETHOD(UIDeactivate)(); + STDMETHOD(SetObjectRects)(LPCRECT lprcPosRect, LPCRECT lprcClipRect); + STDMETHOD(ReactivateAndUndo)(); + +// IOleInPlaceActiveObject + STDMETHOD(TranslateAcceleratorW)(MSG *pMsg); + STDMETHOD(TranslateAcceleratorA)(MSG *pMsg); + STDMETHOD(OnFrameWindowActivate)(BOOL); + STDMETHOD(OnDocWindowActivate)(BOOL fActivate); + STDMETHOD(ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow); + STDMETHOD(EnableModeless)(BOOL); + +// IConnectionPointContainer + STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints**); + STDMETHOD(FindConnectionPoint)(REFIID, IConnectionPoint**); + +// IPersist + STDMETHOD(GetClassID)(GUID*clsid) + { + *clsid = qAxFactory()->classID(class_name); + return S_OK; + } + +// IPersistStreamInit + STDMETHOD(InitNew)(VOID); + STDMETHOD(IsDirty)(); + STDMETHOD(Load)(IStream *pStm); + STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty); + STDMETHOD(GetSizeMax)(ULARGE_INTEGER *pcbSize); + +// IPersistPropertyBag + STDMETHOD(Load)(IPropertyBag *, IErrorLog *); + STDMETHOD(Save)(IPropertyBag *, BOOL, BOOL); + +// IPersistStorage + STDMETHOD(InitNew)(IStorage *pStg); + STDMETHOD(Load)(IStorage *pStg); + STDMETHOD(Save)(IStorage *pStg, BOOL fSameAsLoad); + STDMETHOD(SaveCompleted)(IStorage *pStgNew); + STDMETHOD(HandsOffStorage)(); + +// IPersistFile + STDMETHOD(SaveCompleted)(LPCOLESTR fileName); + STDMETHOD(GetCurFile)(LPOLESTR *currentFile); + STDMETHOD(Load)(LPCOLESTR fileName, DWORD mode); + STDMETHOD(Save)(LPCOLESTR fileName, BOOL fRemember); + +// IDataObject + STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium); + STDMETHOD(GetDataHere)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */); + STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */); + STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */); + STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */); + STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */); + STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection); + STDMETHOD(DUnadvise)(DWORD dwConnection); + STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise); + +// QObject + int qt_metacall(QMetaObject::Call, int index, void **argv); + + bool eventFilter(QObject *o, QEvent *e); +private: + void update(); + void resize(const QSize &newSize); + void updateGeometry(); + void updateMask(); + bool internalCreate(); + void internalBind(); + void internalConnect(); + HRESULT internalActivate(); + + friend class QAxBindable; + friend class QAxPropertyPage; + + QAxAggregated *aggregatedObject; + ConnectionPoints points; + + union { + QWidget *widget; + QObject *object; + } qt; + QPointer<QObject> theObject; + unsigned isWidget :1; + unsigned ownObject :1; + unsigned initNewCalled :1; + unsigned dirtyflag :1; + unsigned hasStockEvents :1; + unsigned stayTopLevel :1; + unsigned isInPlaceActive :1; + unsigned isUIActive :1; + unsigned wasUIActive :1; + unsigned inDesignMode :1; + unsigned canTakeFocus :1; + short freezeEvents; + + HWND m_hWnd; + + HMENU hmenuShared; + HOLEMENU holemenu; + HWND hwndMenuOwner; + QMap<HMENU, QMenu*> menuMap; + QMap<UINT, QAction*> actionMap; + QPointer<QMenuBar> menuBar; + QPointer<QStatusBar> statusBar; + QPointer<QMenu> currentPopup; + QAxExceptInfo *exception; + + CRITICAL_SECTION refCountSection; + CRITICAL_SECTION createWindowSection; + + unsigned long ref; + unsigned long ole_ref; + + QString class_name; + QString currentFileName; + + QHash<long, int> indexCache; + QHash<int,DISPID> signalCache; + + IUnknown *m_outerUnknown; + IAdviseSink *m_spAdviseSink; + QList<STATDATA> adviseSinks; + IOleClientSite *m_spClientSite; + IOleInPlaceSiteWindowless *m_spInPlaceSite; + IOleInPlaceFrame *m_spInPlaceFrame; + ITypeInfo *m_spTypeInfo; + IStorage *m_spStorage; + QSize m_currentExtent; +}; + +class QAxServerAggregate : public IUnknown +{ +public: + QAxServerAggregate(const QString &className, IUnknown *outerUnknown) + : m_outerUnknown(outerUnknown), ref(0) + { + object = new QAxServerBase(className, outerUnknown); + object->registerActiveObject(this); + + InitializeCriticalSection(&refCountSection); + InitializeCriticalSection(&createWindowSection); + } + ~QAxServerAggregate() + { + DeleteCriticalSection(&refCountSection); + DeleteCriticalSection(&createWindowSection); + + delete object; + } + +// IUnknown + unsigned long WINAPI AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + + return r; + } + unsigned long WINAPI Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, void **iface) + { + *iface = 0; + + HRESULT res = E_NOINTERFACE; + if (iid == IID_IUnknown) { + *iface = (IUnknown*)this; + AddRef(); + return S_OK; + } + return object->InternalQueryInterface(iid, iface); + } + +private: + QAxServerBase *object; + IUnknown *m_outerUnknown; + unsigned long ref; + + CRITICAL_SECTION refCountSection; + CRITICAL_SECTION createWindowSection; +}; + +bool QAxFactory::createObjectWrapper(QObject *object, IDispatch **wrapper) +{ + *wrapper = 0; + QAxServerBase *obj = new QAxServerBase(object); + obj->QueryInterface(IID_IDispatch, (void**)wrapper); + if (*wrapper) + return true; + + delete obj; + return false; +} + + +/* + Helper class to enumerate all supported event interfaces. +*/ +class QAxSignalVec : public IEnumConnectionPoints +{ +public: + QAxSignalVec(const QAxServerBase::ConnectionPoints &points) + : cpoints(points), ref(0) + { + InitializeCriticalSection(&refCountSection); + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->AddRef(); + } + QAxSignalVec(const QAxSignalVec &old) + { + InitializeCriticalSection(&refCountSection); + ref = 0; + cpoints = old.cpoints; + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->AddRef(); + it = old.it; + } + ~QAxSignalVec() + { + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->Release(); + + DeleteCriticalSection(&refCountSection); + } + + unsigned long __stdcall AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return ++r; + } + unsigned long __stdcall Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + STDMETHOD(QueryInterface)(REFIID iid, void **iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = this; + else if (iid == IID_IEnumConnectionPoints) + *iface = this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + STDMETHOD(Next)(ULONG cConnections, IConnectionPoint **cpoint, ULONG *pcFetched) + { + unsigned long i; + for (i = 0; i < cConnections; i++) { + if (it == cpoints.end()) + break; + IConnectionPoint *cp = *it; + cp->AddRef(); + cpoint[i] = cp; + ++it; + } + *pcFetched = i; + return i == cConnections ? S_OK : S_FALSE; + } + STDMETHOD(Skip)(ULONG cConnections) + { + while (cConnections) { + ++it; + --cConnections; + if (it == cpoints.end()) + return S_FALSE; + } + return S_OK; + } + STDMETHOD(Reset)() + { + it = cpoints.begin(); + + return S_OK; + } + STDMETHOD(Clone)(IEnumConnectionPoints **ppEnum) + { + *ppEnum = new QAxSignalVec(*this); + (*ppEnum)->AddRef(); + + return S_OK; + } + + QAxServerBase::ConnectionPoints cpoints; + QAxServerBase::ConnectionPointsIterator it; + +private: + CRITICAL_SECTION refCountSection; + + unsigned long ref; +}; + +/* + Helper class to store and enumerate all connected event listeners. +*/ +class QAxConnection : public IConnectionPoint, + public IEnumConnections +{ +public: + typedef QList<CONNECTDATA> Connections; + typedef QList<CONNECTDATA>::Iterator Iterator; + + QAxConnection(QAxServerBase *parent, const QUuid &uuid) + : that(parent), iid(uuid), ref(1) + { + InitializeCriticalSection(&refCountSection); + } + QAxConnection(const QAxConnection &old) + { + InitializeCriticalSection(&refCountSection); + ref = 0; + connections = old.connections; + it = old.it; + that = old.that; + iid = old.iid; + QList<CONNECTDATA>::Iterator it = connections.begin(); + while (it != connections.end()) { + CONNECTDATA connection = *it; + ++it; + connection.pUnk->AddRef(); + } + } + ~QAxConnection() + { + DeleteCriticalSection(&refCountSection); + } + + unsigned long __stdcall AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return r; + } + unsigned long __stdcall Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + STDMETHOD(QueryInterface)(REFIID iid, void **iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = (IConnectionPoint*)this; + else if (iid == IID_IConnectionPoint) + *iface = this; + else if (iid == IID_IEnumConnections) + *iface = this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + STDMETHOD(GetConnectionInterface)(IID *pIID) + { + *pIID = iid; + return S_OK; + } + STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC) + { + return that->QueryInterface(IID_IConnectionPointContainer, (void**)ppCPC); + } + STDMETHOD(Advise)(IUnknown*pUnk, DWORD *pdwCookie) + { + { + IDispatch *checkImpl = 0; + pUnk->QueryInterface(iid, (void**)&checkImpl); + if (!checkImpl) + return CONNECT_E_CANNOTCONNECT; + checkImpl->Release(); + } + + CONNECTDATA cd; + cd.dwCookie = connections.count()+1; + cd.pUnk = pUnk; + cd.pUnk->AddRef(); + connections.append(cd); + + *pdwCookie = cd.dwCookie; + return S_OK; + } + STDMETHOD(Unadvise)(DWORD dwCookie) + { + QList<CONNECTDATA>::Iterator it = connections.begin(); + while (it != connections.end()) { + CONNECTDATA cd = *it; + if (cd.dwCookie == dwCookie) { + cd.pUnk->Release(); + connections.erase(it); + return S_OK; + } + ++it; + } + return CONNECT_E_NOCONNECTION; + } + STDMETHOD(EnumConnections)(IEnumConnections **ppEnum) + { + *ppEnum = this; + AddRef(); + + return S_OK; + } + STDMETHOD(Next)(ULONG cConnections, CONNECTDATA *cd, ULONG *pcFetched) + { + unsigned long i; + for (i = 0; i < cConnections; i++) { + if (it == connections.end()) + break; + cd[i] = *it; + cd[i].pUnk->AddRef(); + ++it; + } + if (pcFetched) + *pcFetched = i; + return i == cConnections ? S_OK : S_FALSE; + } + STDMETHOD(Skip)(ULONG cConnections) + { + while (cConnections) { + ++it; + --cConnections; + if (it == connections.end()) + return S_FALSE; + } + return S_OK; + } + STDMETHOD(Reset)() + { + it = connections.begin(); + + return S_OK; + } + STDMETHOD(Clone)(IEnumConnections **ppEnum) + { + *ppEnum = new QAxConnection(*this); + (*ppEnum)->AddRef(); + + return S_OK; + } + +private: + QAxServerBase *that; + QUuid iid; + Connections connections; + Iterator it; + + CRITICAL_SECTION refCountSection; + unsigned long ref; +}; + +// callback for DLL server to hook into non-Qt eventloop +LRESULT CALLBACK axs_FilterProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (qApp && !invokeCount) + qApp->sendPostedEvents(); + + return CallNextHookEx(qax_hhook, nCode, wParam, lParam); +} + +// filter for executable case to hook into Qt eventloop +// for DLLs the client calls TranslateAccelerator +bool qax_winEventFilter(void *message) +{ + MSG *pMsg = (MSG*)message; + if (pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) + return false; + + bool ret = false; + QWidget *aqt = QWidget::find(pMsg->hwnd); + if (!aqt) + return ret; + + HWND baseHwnd = ::GetParent(aqt->winId()); + QAxServerBase *axbase = 0; + while (!axbase && baseHwnd) { +#ifdef GWLP_USERDATA + QT_WA({ + axbase = (QAxServerBase*)GetWindowLongPtrW(baseHwnd, GWLP_USERDATA); + }, { + axbase = (QAxServerBase*)GetWindowLongPtrA(baseHwnd, GWLP_USERDATA); + }); +#else + QT_WA({ + axbase = (QAxServerBase*)GetWindowLongW(baseHwnd, GWL_USERDATA); + }, { + axbase = (QAxServerBase*)GetWindowLongA(baseHwnd, GWL_USERDATA); + }); +#endif + + baseHwnd = ::GetParent(baseHwnd); + } + if (!axbase) + return ret; + + HRESULT hres = axbase->TranslateAcceleratorW(pMsg); + return hres == S_OK; +} + +extern void qWinMsgHandler(QtMsgType t, const char* str); + +// COM Factory class, mapping COM requests to ActiveQt requests. +// One instance of this class for each ActiveX the server can provide. +class QClassFactory : public IClassFactory2 +{ +public: + QClassFactory(CLSID clsid) + : ref(0), licensed(false) + { + InitializeCriticalSection(&refCountSection); + + // COM only knows the CLSID, but QAxFactory is class name based... + QStringList keys = qAxFactory()->featureList(); + for (QStringList::Iterator key = keys.begin(); key != keys.end(); ++key) { + if (qAxFactory()->classID(*key) == clsid) { + className = *key; + break; + } + } + + const QMetaObject *mo = qAxFactory()->metaObject(className); + if (mo) { + classKey = QLatin1String(mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value()); + licensed = !classKey.isEmpty(); + } + } + + ~QClassFactory() + { + DeleteCriticalSection(&refCountSection); + } + + // IUnknown + unsigned long WINAPI AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return ++r; + } + unsigned long WINAPI Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, LPVOID *iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = (IUnknown*)this; + else if (iid == IID_IClassFactory) + *iface = (IClassFactory*)this; + else if (iid == IID_IClassFactory2 && licensed) + *iface = (IClassFactory2*)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + + HRESULT WINAPI CreateInstanceHelper(IUnknown *pUnkOuter, REFIID iid, void **ppObject) + { + if (pUnkOuter) { + if (iid != IID_IUnknown) + return CLASS_E_NOAGGREGATION; + const QMetaObject *mo = qAxFactory()->metaObject(className); + if (mo && !qstricmp(mo->classInfo(mo->indexOfClassInfo("Aggregatable")).value(), "no")) + return CLASS_E_NOAGGREGATION; + } + + // Make sure a QApplication instance is present (inprocess case) + if (!qApp) { + qInstallMsgHandler(qWinMsgHandler); + qax_ownQApp = true; + int argc = 0; + QApplication *app = new QApplication(argc, 0); + } + qApp->setQuitOnLastWindowClosed(false); + + if (qAxOutProcServer) + QAbstractEventDispatcher::instance()->setEventFilter(qax_winEventFilter); + else + QApplication::instance()->d_func()->in_exec = true; + + // hook into eventloop; this allows a server to create his own QApplication object + if (!qax_hhook && qax_ownQApp) { + QT_WA({ + qax_hhook = SetWindowsHookExW(WH_GETMESSAGE, axs_FilterProc, 0, GetCurrentThreadId()); + }, { + qax_hhook = SetWindowsHookExA(WH_GETMESSAGE, axs_FilterProc, 0, GetCurrentThreadId()); + }); + } + + HRESULT res; + // Create the ActiveX wrapper - aggregate if requested + if (pUnkOuter) { + QAxServerAggregate *aggregate = new QAxServerAggregate(className, pUnkOuter); + res = aggregate->QueryInterface(iid, ppObject); + if (FAILED(res)) + delete aggregate; + } else { + QAxServerBase *activeqt = new QAxServerBase(className, pUnkOuter); + res = activeqt->QueryInterface(iid, ppObject); + if (FAILED(res)) + delete activeqt; + else + activeqt->registerActiveObject((IUnknown*)(IDispatch*)activeqt); + } + return res; + } + + // IClassFactory + HRESULT WINAPI CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppObject) + { + // class is licensed + if (licensed && !qAxFactory()->validateLicenseKey(className, QString())) + return CLASS_E_NOTLICENSED; + + return CreateInstanceHelper(pUnkOuter, iid, ppObject); + } + HRESULT WINAPI LockServer(BOOL fLock) + { + if (fLock) + qAxLock(); + else + qAxUnlock(); + + return S_OK; + } + + // IClassFactory2 + HRESULT WINAPI RequestLicKey(DWORD, BSTR *pKey) + { + if (!pKey) + return E_POINTER; + *pKey = 0; + + // This of course works only on fully licensed machines + if (!qAxFactory()->validateLicenseKey(className, QString())) + return CLASS_E_NOTLICENSED; + + *pKey = QStringToBSTR(classKey); + return S_OK; + } + + HRESULT WINAPI GetLicInfo(LICINFO *pLicInfo) + { + if (!pLicInfo) + return E_POINTER; + pLicInfo->cbLicInfo = sizeof(LICINFO); + + // class specific license key? + const QMetaObject *mo = qAxFactory()->metaObject(className); + const char *key = mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value(); + pLicInfo->fRuntimeKeyAvail = key && key[0]; + + // machine fully licensed? + pLicInfo->fLicVerified = qAxFactory()->validateLicenseKey(className, QString()); + + return S_OK; + } + + HRESULT WINAPI CreateInstanceLic(IUnknown *pUnkOuter, IUnknown *pUnkReserved, REFIID iid, BSTR bKey, PVOID *ppObject) + { + QString licenseKey = QString::fromUtf16((const ushort *)bKey); + if (!qAxFactory()->validateLicenseKey(className, licenseKey)) + return CLASS_E_NOTLICENSED; + return CreateInstanceHelper(pUnkOuter, iid, ppObject); + } + + QString className; + +protected: + CRITICAL_SECTION refCountSection; + unsigned long ref; + bool licensed; + QString classKey; +}; + +// Create a QClassFactory object for class \a iid +HRESULT GetClassObject(REFIID clsid, REFIID iid, void **ppUnk) +{ + QClassFactory *factory = new QClassFactory(clsid); + if (!factory) + return E_OUTOFMEMORY; + if (factory->className.isEmpty()) { + delete factory; + return E_NOINTERFACE; + } + HRESULT res = factory->QueryInterface(iid, ppUnk); + if (res != S_OK) + delete factory; + return res; +} + + +/*! + Constructs a QAxServerBase object wrapping the QWidget \a + classname into an ActiveX control. + + The constructor is called by the QClassFactory object provided by + the COM server for the respective CLSID. +*/ +QAxServerBase::QAxServerBase(const QString &classname, IUnknown *outerUnknown) +: aggregatedObject(0), ref(0), ole_ref(0), class_name(classname), + m_hWnd(0), hmenuShared(0), hwndMenuOwner(0), + m_outerUnknown(outerUnknown) +{ + init(); + + internalCreate(); +} + +/*! + Constructs a QAxServerBase object wrapping \a o. +*/ +QAxServerBase::QAxServerBase(QObject *o) +: aggregatedObject(0), ref(0), ole_ref(0), + m_hWnd(0), hmenuShared(0), hwndMenuOwner(0), + m_outerUnknown(0) +{ + init(); + + qt.object = o; + if (o) { + theObject = o; + isWidget = false; + class_name = QLatin1String(o->metaObject()->className()); + } + internalBind(); + internalConnect(); +} + +/*! + Initializes data members. +*/ +void QAxServerBase::init() +{ + qt.object = 0; + isWidget = false; + ownObject = false; + initNewCalled = false; + dirtyflag = false; + hasStockEvents = false; + stayTopLevel = false; + isInPlaceActive = false; + isUIActive = false; + wasUIActive = false; + inDesignMode = false; + canTakeFocus = false; + freezeEvents = 0; + exception = 0; + + m_spAdviseSink = 0; + m_spClientSite = 0; + m_spInPlaceSite = 0; + m_spInPlaceFrame = 0; + m_spTypeInfo = 0; + m_spStorage = 0; + + InitializeCriticalSection(&refCountSection); + InitializeCriticalSection(&createWindowSection); + +#ifdef QT_DEBUG + EnterCriticalSection(&refCountSection); + ++qaxserverbase_instance_count; + LeaveCriticalSection(&refCountSection); +#endif + + qAxLock(); + + points[IID_IPropertyNotifySink] = new QAxConnection(this, IID_IPropertyNotifySink); +} + +/*! + Destroys the QAxServerBase object, releasing all allocated + resources and interfaces. +*/ +QAxServerBase::~QAxServerBase() +{ +#ifdef QT_DEBUG + EnterCriticalSection(&refCountSection); + --qaxserverbase_instance_count; + LeaveCriticalSection(&refCountSection); +#endif + + revokeActiveObject(); + + for (QAxServerBase::ConnectionPointsIterator it = points.begin(); it != points.end(); ++it) { + if (it.value()) + (*it)->Release(); + } + delete aggregatedObject; + aggregatedObject = 0; + if (theObject) { + qt.object->disconnect(this); + QObject *aqt = qt.object; + qt.object = 0; + if (ownObject) + delete aqt; + } + + if (m_spAdviseSink) m_spAdviseSink->Release(); + m_spAdviseSink = 0; + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->Release(); + } + if (m_spClientSite) m_spClientSite->Release(); + m_spClientSite = 0; + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + if (m_spTypeInfo) m_spTypeInfo->Release(); + m_spTypeInfo = 0; + if (m_spStorage) m_spStorage->Release(); + m_spStorage = 0; + + DeleteCriticalSection(&refCountSection); + DeleteCriticalSection(&createWindowSection); + + qAxUnlock(); +} + +/* + Registering with OLE +*/ +void QAxServerBase::registerActiveObject(IUnknown *object) +{ + if (ole_ref || !qt.object || !qAxOutProcServer) + return; + + const QMetaObject *mo = qt.object->metaObject(); + if (!qstricmp(mo->classInfo(mo->indexOfClassInfo("RegisterObject")).value(), "yes")) + RegisterActiveObject(object, qAxFactory()->classID(class_name), ACTIVEOBJECT_WEAK, &ole_ref); +} + +void QAxServerBase::revokeActiveObject() +{ + if (!ole_ref) + return; + + RevokeActiveObject(ole_ref, 0); + ole_ref = 0; +} + +/* + QueryInterface implementation. +*/ +HRESULT WINAPI QAxServerBase::QueryInterface(REFIID iid, void **iface) +{ + if (m_outerUnknown) + return m_outerUnknown->QueryInterface(iid, iface); + + return InternalQueryInterface(iid, iface); +} + +HRESULT QAxServerBase::InternalQueryInterface(REFIID iid, void **iface) +{ + *iface = 0; + + if (iid == IID_IUnknown) { + *iface = (IUnknown*)(IDispatch*)this; + } else { + HRESULT res = S_OK; + if (aggregatedObject) + res = aggregatedObject->queryInterface(iid, iface); + if (*iface) + return res; + } + + if (!(*iface)) { + if (iid == qAxFactory()->interfaceID(class_name)) + *iface = (IDispatch*)this; + if (iid == IID_IDispatch) + *iface = (IDispatch*)this; + else if (iid == IID_IAxServerBase) + *iface = (IAxServerBase*)this; + else if (iid == IID_IOleObject) + *iface = (IOleObject*)this; + else if (iid == IID_IConnectionPointContainer) + *iface = (IConnectionPointContainer*)this; + else if (iid == IID_IProvideClassInfo) + *iface = (IProvideClassInfo*)this; + else if (iid == IID_IProvideClassInfo2) + *iface = (IProvideClassInfo2*)this; + else if (iid == IID_IPersist) + *iface = (IPersist*)(IPersistStream*)this; + else if (iid == IID_IPersistStream) + *iface = (IPersistStream*)this; + else if (iid == IID_IPersistStreamInit) + *iface = (IPersistStreamInit*)this; + else if (iid == IID_IPersistStorage) + *iface = (IPersistStorage*)this; + else if (iid == IID_IPersistPropertyBag) + *iface = (IPersistPropertyBag*)this; + else if (iid == IID_IPersistFile && + qAxFactory()->metaObject(class_name)->indexOfClassInfo("MIME") != -1) + *iface = (IPersistFile*)this; + else if (iid == IID_IViewObject) + *iface = (IViewObject*)this; + else if (iid == IID_IViewObject2) + *iface = (IViewObject2*)this; + else if (isWidget) { + if (iid == IID_IOleControl) + *iface = (IOleControl*)this; + else if (iid == IID_IOleWindow) + *iface = (IOleWindow*)(IOleInPlaceObject*)this; + else if (iid == IID_IOleInPlaceObject) + *iface = (IOleInPlaceObject*)this; + else if (iid == IID_IOleInPlaceActiveObject) + *iface = (IOleInPlaceActiveObject*)this; + else if (iid == IID_IDataObject) + *iface = (IDataObject*)this; + } + } + if (!*iface) + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +/*! + Detects and initilaizes implementation of QAxBindable in objects. +*/ +void QAxServerBase::internalBind() +{ + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (axb) { + // no addref; this is aggregated + axb->activex = this; + if (!aggregatedObject) + aggregatedObject = axb->createAggregate(); + if (aggregatedObject) { + aggregatedObject->controlling_unknown = (IUnknown*)(IDispatch*)this; + aggregatedObject->the_object = qt.object; + } + } +} + +/*! + Connects object signals to event dispatcher. +*/ +void QAxServerBase::internalConnect() +{ + QUuid eventsID = qAxFactory()->eventsID(class_name); + if (!eventsID.isNull()) { + if (!points[eventsID]) + points[eventsID] = new QAxConnection(this, eventsID); + + // connect the generic slot to all signals of qt.object + const QMetaObject *mo = qt.object->metaObject(); + for (int isignal = mo->methodCount()-1; isignal >= 0; --isignal) { + if (mo->method(isignal).methodType() == QMetaMethod::Signal) + QMetaObject::connect(qt.object, isignal, this, isignal); + } + } +} + +/*! + Creates the QWidget for the classname passed to the c'tor. + + All signals of the widget class are connected to the internal event mapper. + If the widget implements QAxBindable, stock events are also connected. +*/ +bool QAxServerBase::internalCreate() +{ + if (qt.object) + return true; + + qt.object = qAxFactory()->createObject(class_name); + Q_ASSERT(qt.object); + if (!qt.object) + return false; + + theObject = qt.object; + ownObject = true; + isWidget = qt.object->isWidgetType(); + hasStockEvents = qAxFactory()->hasStockEvents(class_name); + stayTopLevel = qAxFactory()->stayTopLevel(class_name); + + internalBind(); + if (isWidget) { + if (!stayTopLevel) { + QEvent e(QEvent::EmbeddingControl); + QApplication::sendEvent(qt.widget, &e); + QT_WA({ + ::SetWindowLongW(qt.widget->winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }, { + ::SetWindowLongA(qt.widget->winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }); + } + qt.widget->setAttribute(Qt::WA_QuitOnClose, false); + qt.widget->move(0, 0); + + // initialize to sizeHint, but don't set resized flag so that container has a chance to override + bool wasResized = qt.widget->testAttribute(Qt::WA_Resized); + updateGeometry(); + if (!wasResized && qt.widget->testAttribute(Qt::WA_Resized) + && qt.widget->sizePolicy() != QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)) { + qt.widget->setAttribute(Qt::WA_Resized, false); + } + } + + internalConnect(); + // install an event filter for stock events + if (isWidget) { + qt.object->installEventFilter(this); + const QList<QWidget*> children = qFindChildren<QWidget*>(qt.object); + QList<QWidget*>::ConstIterator it = children.constBegin(); + while (it != children.constEnd()) { + (*it)->installEventFilter(this); + ++it; + } + } + return true; +} + +/* +class HackMenuData : public QMenuData +{ + friend class QAxServerBase; +}; +*/ + +class HackWidget : public QWidget +{ + friend class QAxServerBase; +}; +/* + Message handler. \a hWnd is always the ActiveX widget hosting the Qt widget. + \a uMsg is handled as follows + \list + \i WM_CREATE The ActiveX control is created + \i WM_DESTROY The QWidget is destroyed + \i WM_SHOWWINDOW The QWidget is parented into the ActiveX window + \i WM_PAINT The QWidget is updated + \i WM_SIZE The QWidget is resized to the new size + \i WM_SETFOCUS and + \i WM_KILLFOCUS The client site is notified about the focus transfer + \i WM_MOUSEACTIVATE The ActiveX is activated + \endlist + + The semantics of \a wParam and \a lParam depend on the value of \a uMsg. +*/ +LRESULT CALLBACK QAxServerBase::ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_CREATE) { + QAxServerBase *that; + QT_WA({ + CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam; + that = (QAxServerBase*)cs->lpCreateParams; + }, { + CREATESTRUCTA *cs = (CREATESTRUCTA*)lParam; + that = (QAxServerBase*)cs->lpCreateParams; + }); + +#ifdef GWLP_USERDATA + QT_WA({ + SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)that); + }, { + SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)that); + }); +#else + QT_WA({ + SetWindowLongW(hWnd, GWL_USERDATA, (LONG)that); + }, { + SetWindowLongA(hWnd, GWL_USERDATA, (LONG)that); + }); +#endif + + that->m_hWnd = hWnd; + + QT_WA({ + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + }, { + return ::DefWindowProcA(hWnd, uMsg, wParam, lParam); + }); + } + + QAxServerBase *that = 0; + +#ifdef GWLP_USERDATA + QT_WA({ + that = (QAxServerBase*)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + }, { + that = (QAxServerBase*)GetWindowLongPtrA(hWnd, GWLP_USERDATA); + }); +#else + QT_WA({ + that = (QAxServerBase*)GetWindowLongW(hWnd, GWL_USERDATA); + }, { + that = (QAxServerBase*)GetWindowLongA(hWnd, GWL_USERDATA); + }); +#endif + + if (that) { + int width = that->qt.widget ? that->qt.widget->width() : 0; + int height = that->qt.widget ? that->qt.widget->height() : 0; + RECT rcPos = {0, 0, width + 1, height + 1}; + + switch (uMsg) { + case WM_NCDESTROY: + that->m_hWnd = 0; + break; + + case WM_QUERYENDSESSION: + case WM_DESTROY: + // save the window handle + if (that->qt.widget) { + that->qt.widget->hide(); + ::SetParent(that->qt.widget->winId(), 0); + } + break; + + case WM_SHOWWINDOW: + if(wParam) { + that->internalCreate(); + if (!that->stayTopLevel) { + ::SetParent(that->qt.widget->winId(), that->m_hWnd); + that->qt.widget->raise(); + that->qt.widget->move(0, 0); + } + that->qt.widget->show(); + } else if (that->qt.widget) { + that->qt.widget->hide(); + } + break; + + case WM_ERASEBKGND: + that->updateMask(); + break; + + case WM_SIZE: + that->resize(QSize(LOWORD(lParam), HIWORD(lParam))); + break; + + case WM_SETFOCUS: + if (that->isInPlaceActive && that->m_spClientSite && !that->inDesignMode && that->canTakeFocus) { + that->DoVerb(OLEIVERB_UIACTIVATE, NULL, that->m_spClientSite, 0, that->m_hWnd, &rcPos); + if (that->isUIActive) { + IOleControlSite *spSite = 0; + that->m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&spSite); + if (spSite) { + spSite->OnFocus(true); + spSite->Release(); + } + QWidget *candidate = that->qt.widget; + while (!(candidate->focusPolicy() & Qt::TabFocus)) { + candidate = candidate->nextInFocusChain(); + if (candidate == that->qt.widget) { + candidate = 0; + break; + } + } + if (candidate) { + candidate->setFocus(); + HackWidget *widget = (HackWidget*)that->qt.widget; + if (::GetKeyState(VK_SHIFT) < 0) + widget->focusNextPrevChild(false); + } + } + } + break; + + case WM_KILLFOCUS: + if (that->isInPlaceActive && that->isUIActive && that->m_spClientSite) { + IOleControlSite *spSite = 0; + that->m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&spSite); + if (spSite) { + if (!::IsChild(that->m_hWnd, ::GetFocus())) + spSite->OnFocus(false); + spSite->Release(); + } + } + break; + + case WM_MOUSEACTIVATE: + that->DoVerb(OLEIVERB_UIACTIVATE, NULL, that->m_spClientSite, 0, that->m_hWnd, &rcPos); + break; + + case WM_INITMENUPOPUP: + if (that->qt.widget) { + that->currentPopup = that->menuMap[(HMENU)wParam]; + if (!that->currentPopup) + break; + const QMetaObject *mo = that->currentPopup->metaObject(); + int index = mo->indexOfSignal("aboutToShow()"); + if (index < 0) + break; + + that->currentPopup->qt_metacall(QMetaObject::InvokeMetaMethod, index, 0); + that->createPopup(that->currentPopup, (HMENU)wParam); + return 0; + } + break; + + case WM_MENUSELECT: + case WM_COMMAND: + if (that->qt.widget) { + QMenuBar *menuBar = that->menuBar; + if (!menuBar) + break; + + QObject *menuObject = 0; + bool menuClosed = false; + + if (uMsg == WM_COMMAND) { + menuObject = that->actionMap.value(wParam); + } else if (!lParam) { + menuClosed = true; + menuObject = that->currentPopup; + } else { + menuObject = that->actionMap.value(LOWORD(wParam)); + } + + if (menuObject) { + const QMetaObject *mo = menuObject->metaObject(); + int index = -1; + + if (uMsg == WM_COMMAND) + index = mo->indexOfSignal("activated()"); + else if (menuClosed) + index = mo->indexOfSignal("aboutToHide()"); + else + index = mo->indexOfSignal("hovered()"); + + if (index < 0) + break; + + menuObject->qt_metacall(QMetaObject::InvokeMetaMethod, index, 0); + if (menuClosed || uMsg == WM_COMMAND) + that->currentPopup = 0; + return 0; + } + } + break; + + default: + break; + } + } + + QT_WA({ + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + }, { + return ::DefWindowProcA(hWnd, uMsg, wParam, lParam); + }); +} + +/*! + Creates the window hosting the QWidget. +*/ +HWND QAxServerBase::create(HWND hWndParent, RECT& rcPos) +{ + Q_ASSERT(isWidget && qt.widget); + + static ATOM atom = 0; + HINSTANCE hInst = (HINSTANCE)qAxInstance; + EnterCriticalSection(&createWindowSection); + QString cn(QLatin1String("QAxControl")); + cn += QString::number((int)ActiveXProc); + if (!atom) { + QT_WA({ + WNDCLASSW wcTemp; + wcTemp.style = CS_DBLCLKS; + wcTemp.cbClsExtra = 0; + wcTemp.cbWndExtra = 0; + wcTemp.hbrBackground = 0; + wcTemp.hCursor = 0; + wcTemp.hIcon = 0; + wcTemp.hInstance = hInst; + wcTemp.lpszClassName = (wchar_t*)cn.utf16(); + wcTemp.lpszMenuName = 0; + wcTemp.lpfnWndProc = ActiveXProc; + + atom = RegisterClassW(&wcTemp); + }, { + QByteArray cna = cn.toLatin1(); + WNDCLASSA wcTemp; + wcTemp.style = CS_DBLCLKS; + wcTemp.cbClsExtra = 0; + wcTemp.cbWndExtra = 0; + wcTemp.hbrBackground = 0; + wcTemp.hCursor = 0; + wcTemp.hIcon = 0; + wcTemp.hInstance = hInst; + wcTemp.lpszClassName = cna.data(); + wcTemp.lpszMenuName = 0; + wcTemp.lpfnWndProc = ActiveXProc; + + atom = RegisterClassA(&wcTemp); + }); + } + LeaveCriticalSection(&createWindowSection); + if (!atom && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + return 0; + + Q_ASSERT(!m_hWnd); + HWND hWnd = 0; + QT_WA({ + hWnd = ::CreateWindowW((wchar_t*)cn.utf16(), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + rcPos.left, rcPos.top, rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, hWndParent, 0, hInst, this); + }, { + hWnd = ::CreateWindowA(cn.toLatin1().data(), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + rcPos.left, rcPos.top, rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, hWndParent, 0, hInst, this); + }); + + Q_ASSERT(m_hWnd == hWnd); + + updateMask(); + EnableWindow(m_hWnd, qt.widget->isEnabled()); + + return hWnd; +} + +/* + Recoursively creates Win32 submenus. +*/ +HMENU QAxServerBase::createPopup(QMenu *popup, HMENU oldMenu) +{ + HMENU popupMenu = oldMenu ? oldMenu : CreatePopupMenu(); + menuMap.insert(popupMenu, popup); + + if (oldMenu) while (GetMenuItemCount(oldMenu)) { + DeleteMenu(oldMenu, 0, MF_BYPOSITION); + } + + const QList<QAction*> actions = popup->actions(); + for (int i = 0; i < actions.count(); ++i) { + QAction *action = actions.at(i); + + uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED; + if (action->isSeparator()) + flags |= MF_SEPARATOR; + else if (action->menu()) + flags |= MF_POPUP; + else + flags |= MF_STRING; + if (action->isChecked()) + flags |= MF_CHECKED; + + ushort itemId; + if (flags & MF_POPUP) { + itemId = static_cast<ushort>( + reinterpret_cast<ulong>(createPopup(action->menu())) + ); + } else { + itemId = static_cast<ushort>(reinterpret_cast<ulong>(action)); + actionMap.remove(itemId); + actionMap.insert(itemId, action); + } + QT_WA({ + AppendMenuW(popupMenu, flags, itemId, (TCHAR*)action->text().utf16()); + }, { + AppendMenuA(popupMenu, flags, itemId, action->text().toLocal8Bit()); + }); + } + if (oldMenu) + DrawMenuBar(hwndMenuOwner); + return popupMenu; +} + +/*! + Creates a Win32 menubar. +*/ +void QAxServerBase::createMenu(QMenuBar *menuBar) +{ + hmenuShared = ::CreateMenu(); + + int edit = 0; + int object = 0; + int help = 0; + + const QList<QAction*> actions = menuBar->actions(); + for (int i = 0; i < actions.count(); ++i) { + QAction *action = actions.at(i); + + uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED; + if (action->isSeparator()) + flags |= MF_SEPARATOR; + else if (action->menu()) + flags |= MF_POPUP; + else + flags |= MF_STRING; + + if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Edit")) + edit++; + else if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Help")) + help++; + else + object++; + + ushort itemId; + if (flags & MF_POPUP) { + itemId = static_cast<ushort>( + reinterpret_cast<ulong>(createPopup(action->menu())) + ); + } else { + itemId = static_cast<ushort>(reinterpret_cast<ulong>(action)); + actionMap.insert(itemId, action); + } + QT_WA({ + AppendMenuW(hmenuShared, flags, itemId, (TCHAR*)action->text().utf16()); + } , { + AppendMenuA(hmenuShared, flags, itemId, action->text().toLocal8Bit()); + }); + } + + OLEMENUGROUPWIDTHS menuWidths = {0,edit,0,object,0,help}; + HRESULT hres = m_spInPlaceFrame->InsertMenus(hmenuShared, &menuWidths); + if (FAILED(hres)) { + ::DestroyMenu(hmenuShared); + hmenuShared = 0; + return; + } + + m_spInPlaceFrame->GetWindow(&hwndMenuOwner); + + holemenu = OleCreateMenuDescriptor(hmenuShared, &menuWidths); + hres = m_spInPlaceFrame->SetMenu(hmenuShared, holemenu, m_hWnd); + if (FAILED(hres)) { + ::DestroyMenu(hmenuShared); + hmenuShared = 0; + OleDestroyMenuDescriptor(holemenu); + } +} + +/*! + Remove the Win32 menubar. +*/ +void QAxServerBase::removeMenu() +{ + if (hmenuShared) + m_spInPlaceFrame->RemoveMenus(hmenuShared); + holemenu = 0; + m_spInPlaceFrame->SetMenu(0, 0, m_hWnd); + if (hmenuShared) { + DestroyMenu(hmenuShared); + hmenuShared = 0; + menuMap.clear(); + } + hwndMenuOwner = 0; +} + +extern bool ignoreSlots(const char *test); +extern bool ignoreProps(const char *test); + +/*! + Makes sure the type info is loaded +*/ +void QAxServerBase::ensureMetaData() +{ + if (!m_spTypeInfo) { + qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->interfaceID(class_name), &m_spTypeInfo); + m_spTypeInfo->AddRef(); + } +} + +/*! + \internal + Returns true if the property \a index is exposed to COM and should + be saved/loaded. +*/ +bool QAxServerBase::isPropertyExposed(int index) +{ + if (!theObject) + return false; + + bool result = false; + const QMetaObject *mo = theObject->metaObject(); + + int qtProps = 0; + if (theObject->isWidgetType()) + qtProps = QWidget::staticMetaObject.propertyCount(); + QMetaProperty property = mo->property(index); + if (index <= qtProps && ignoreProps(property.name())) + return result; + + BSTR bstrNames = QStringToBSTR(QLatin1String(property.name())); + DISPID dispId; + GetIDsOfNames(IID_NULL, (BSTR*)&bstrNames, 1, LOCALE_USER_DEFAULT, &dispId); + result = dispId != DISPID_UNKNOWN; + SysFreeString(bstrNames); + + return result; +} + + +/*! + \internal + Updates the view, or asks the client site to do so. +*/ +void QAxServerBase::update() +{ + if (isInPlaceActive) { + if (m_hWnd) + ::InvalidateRect(m_hWnd, 0, true); + else if (m_spInPlaceSite) + m_spInPlaceSite->InvalidateRect(NULL, true); + } else if (m_spAdviseSink) { + m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1); + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnViewChange(DVASPECT_CONTENT, -1); + } + } +} + +/*! + Resizes the control, faking a QResizeEvent if required +*/ +void QAxServerBase::resize(const QSize &size) +{ + if (!isWidget || !qt.widget || !size.isValid() || size == QSize(0, 0)) + return; + + QSize oldSize = qt.widget->size(); + qt.widget->resize(size); + QSize newSize = qt.widget->size(); + // make sure we get a resize event even if not embedded as a control + if (!m_hWnd && !qt.widget->isVisible() && newSize != oldSize) { + QResizeEvent resizeEvent(newSize, oldSize); +#ifndef QT_DLL // import from static library + extern bool qt_sendSpontaneousEvent(QObject*,QEvent*); +#endif + qt_sendSpontaneousEvent(qt.widget, &resizeEvent); + } + m_currentExtent = qt.widget->size(); +} + +/*! + \internal + + Updates the internal size values. +*/ +void QAxServerBase::updateGeometry() +{ + if (!isWidget || !qt.widget) + return; + + const QSize sizeHint = qt.widget->sizeHint(); + const QSize size = qt.widget->size(); + if (sizeHint.isValid()) { // if provided, adjust to sizeHint + QSize newSize = size; + if (!qt.widget->testAttribute(Qt::WA_Resized)) { + newSize = sizeHint; + } else { // according to sizePolicy rules if already resized + QSizePolicy sizePolicy = qt.widget->sizePolicy(); + if (sizeHint.width() > size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)) + newSize.setWidth(sizeHint.width()); + if (sizeHint.width() < size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag)) + newSize.setWidth(sizeHint.width()); + if (sizeHint.height() > size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag)) + newSize.setHeight(sizeHint.height()); + if (sizeHint.height() < size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag)) + newSize.setHeight(sizeHint.height()); + } + resize(newSize); + + // set an initial size suitable for embedded controls + } else if (!qt.widget->testAttribute(Qt::WA_Resized)) { + resize(QSize(100, 100)); + qt.widget->setAttribute(Qt::WA_Resized, false); + } +} + +/*! + \internal + + Updates the mask of the widget parent. +*/ +void QAxServerBase::updateMask() +{ + if (!isWidget || !qt.widget || qt.widget->mask().isEmpty()) + return; + + QRegion rgn = qt.widget->mask(); + HRGN hrgn = rgn.handle(); + + // Since SetWindowRegion takes ownership + HRGN wr = CreateRectRgn(0,0,0,0); + CombineRgn(wr, hrgn, 0, RGN_COPY); + SetWindowRgn(m_hWnd, wr, true); +} + +static bool checkHRESULT(HRESULT hres) +{ + const char *name = 0; + switch(hres) { + case S_OK: + return true; + case DISP_E_BADPARAMCOUNT: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name); +#endif + return false; + case DISP_E_BADVARTYPE: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name); +#endif + return false; + case DISP_E_EXCEPTION: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name); +#endif + return false; + case DISP_E_MEMBERNOTFOUND: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name); +#endif + return false; + case DISP_E_NONAMEDARGS: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name); +#endif + return false; + case DISP_E_OVERFLOW: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name); +#endif + return false; + case DISP_E_PARAMNOTFOUND: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Parameter not found", name); +#endif + return false; + case DISP_E_TYPEMISMATCH: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch", name); +#endif + return false; + case DISP_E_UNKNOWNINTERFACE: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name); +#endif + return false; + case DISP_E_UNKNOWNLCID: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name); +#endif + return false; + case DISP_E_PARAMNOTOPTIONAL: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name); +#endif + return false; + default: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name); +#endif + return false; + } +} + +static inline QByteArray paramType(const QByteArray &ptype, bool *out) +{ + *out = ptype.endsWith('&') || ptype.endsWith("**"); + if (*out) { + QByteArray res(ptype); + res.truncate(res.length() - 1); + return res; + } + + return ptype; +} + +/*! + Catches all signals emitted by the Qt widget and fires the respective COM event. + + \a isignal is the Qt Meta Object index of the received signal, and \a _o the + signal parameters. +*/ +int QAxServerBase::qt_metacall(QMetaObject::Call call, int index, void **argv) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + + if (index == -1) { + if (sender() && m_spInPlaceFrame) { + if (qobject_cast<QStatusBar*>(sender()) != statusBar) + return true; + + if (statusBar->isHidden()) { + QString message = *(QString*)argv[1]; + m_spInPlaceFrame->SetStatusText(QStringToBSTR(message)); + } + } + return true; + } + + if (freezeEvents || inDesignMode) + return true; + + ensureMetaData(); + + // get the signal information. + const QMetaObject *mo = qt.object->metaObject(); + QMetaMethod signal; + DISPID eventId = index; + int pcount = 0; + QByteArray type; + QList<QByteArray> ptypes; + + switch(index) { + case DISPID_KEYDOWN: + case DISPID_KEYUP: + pcount = 2; + ptypes << "int&" << "int"; + break; + case DISPID_KEYPRESS: + pcount = 1; + ptypes << "int&"; + break; + case DISPID_MOUSEDOWN: + case DISPID_MOUSEMOVE: + case DISPID_MOUSEUP: + pcount = 4; + ptypes << "int" << "int" << "int" << "int"; + break; + case DISPID_CLICK: + pcount = 0; + break; + case DISPID_DBLCLICK: + pcount = 0; + break; + default: + { + signal = mo->method(index); + Q_ASSERT(signal.methodType() == QMetaMethod::Signal); + type = signal.typeName(); + QByteArray signature(signal.signature()); + QByteArray name(signature); + name.truncate(name.indexOf('(')); + + eventId = signalCache.value(index, -1); + if (eventId == -1) { + ITypeInfo *eventInfo = 0; + qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->eventsID(class_name), &eventInfo); + if (eventInfo) { + QString uni_name = QLatin1String(name); + const OLECHAR *olename = reinterpret_cast<const OLECHAR *>(uni_name.utf16()); + eventInfo->GetIDsOfNames((OLECHAR**)&olename, 1, &eventId); + eventInfo->Release(); + } + } + + signature = signature.mid(name.length() + 1); + signature.truncate(signature.length() - 1); + + if (!signature.isEmpty()) + ptypes = signature.split(','); + + pcount = ptypes.count(); + } + break; + } + if (pcount && !argv) { + qWarning("QAxServerBase::qt_metacall: Missing %d arguments", pcount); + return false; + } + if (eventId == -1) + return false; + + // For all connected event sinks... + IConnectionPoint *cpoint = 0; + GUID IID_QAxEvents = qAxFactory()->eventsID(class_name); + FindConnectionPoint(IID_QAxEvents, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + // setup parameters + unsigned int argErr = 0; + DISPPARAMS dispParams; + dispParams.cArgs = pcount; + dispParams.cNamedArgs = 0; + dispParams.rgdispidNamedArgs = 0; + dispParams.rgvarg = 0; + + if (pcount) // Use malloc/free for eval package compatibility + dispParams.rgvarg = (VARIANTARG*)malloc(pcount * sizeof(VARIANTARG)); + int p = 0; + for (p = 0; p < pcount; ++p) { + VARIANT *arg = dispParams.rgvarg + (pcount - p - 1); + VariantInit(arg); + + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + QVariant variant; + if (mo->indexOfEnumerator(ptype) != -1) { + // convert enum values to int + variant = QVariant(*reinterpret_cast<int *>(argv[p+1])); + } else { + QVariant::Type vt = QVariant::nameToType(ptype); + if (vt == QVariant::UserType) { + if (ptype.endsWith('*')) { + variant = QVariant(QMetaType::type(ptype), (void**)argv[p+1]); + // qVariantSetValue(variant, *(void**)(argv[p + 1]), ptype); + } else { + variant = QVariant(QMetaType::type(ptype), argv[p+1]); + // qVariantSetValue(variant, argv[p + 1], ptype); + } + } else { + variant = QVariant(vt, argv[p + 1]); + } + } + + QVariantToVARIANT(variant, *arg, type, out); + } + + VARIANT retval; + VariantInit(&retval); + VARIANT *pretval = 0; + if (!type.isEmpty()) + pretval = &retval; + + // call listeners (through IDispatch) + while (cc) { + if (c->pUnk) { + IDispatch *disp = 0; + c->pUnk->QueryInterface(IID_QAxEvents, (void**)&disp); + if (disp) { + disp->Invoke(eventId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispParams, pretval, 0, &argErr); + + // update out-parameters and return value + if (index > 0) { + for (p = 0; p < pcount; ++p) { + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + if (out) + QVariantToVoidStar(VARIANTToQVariant(dispParams.rgvarg[pcount - p - 1], ptype), argv[p+1], ptype); + } + if (pretval) + QVariantToVoidStar(VARIANTToQVariant(retval, type), argv[0], type); + } + disp->Release(); + } + c->pUnk->Release(); // AddRef'ed by clist->Next implementation + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + + // clean up + for (p = 0; p < pcount; ++p) + clearVARIANT(dispParams.rgvarg+p); + free(dispParams.rgvarg); + } + clist->Release(); + } + cpoint->Release(); + } + + return true; +} + +/*! + Call IPropertyNotifySink of connected clients. + \a dispId specifies the ID of the property that changed. +*/ +bool QAxServerBase::emitRequestPropertyChange(const char *property) +{ + long dispId = -1; + + IConnectionPoint *cpoint = 0; + FindConnectionPoint(IID_IPropertyNotifySink, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + if (dispId == -1) { + BSTR bstr = QStringToBSTR(QLatin1String(property)); + GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId); + SysFreeString(bstr); + } + if (dispId != -1) while (cc) { + if (c->pUnk) { + IPropertyNotifySink *sink = 0; + c->pUnk->QueryInterface(IID_IPropertyNotifySink, (void**)&sink); + bool disallows = sink && sink->OnRequestEdit(dispId) == S_FALSE; + sink->Release(); + c->pUnk->Release(); + if (disallows) { // a client disallows the property to change + clist->Release(); + cpoint->Release(); + return false; + } + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + } + clist->Release(); + } + cpoint->Release(); + } + dirtyflag = true; + return true; +} + +/*! + Call IPropertyNotifySink of connected clients. + \a dispId specifies the ID of the property that changed. +*/ +void QAxServerBase::emitPropertyChanged(const char *property) +{ + long dispId = -1; + + IConnectionPoint *cpoint = 0; + FindConnectionPoint(IID_IPropertyNotifySink, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + if (dispId == -1) { + BSTR bstr = QStringToBSTR(QLatin1String(property)); + GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId); + SysFreeString(bstr); + } + if (dispId != -1) while (cc) { + if (c->pUnk) { + IPropertyNotifySink *sink = 0; + c->pUnk->QueryInterface(IID_IPropertyNotifySink, (void**)&sink); + if (sink) { + sink->OnChanged(dispId); + sink->Release(); + } + c->pUnk->Release(); + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + } + clist->Release(); + } + cpoint->Release(); + } + dirtyflag = true; +} + +//**** IProvideClassInfo +/* + Provide the ITypeInfo implementation for the COM class. +*/ +HRESULT WINAPI QAxServerBase::GetClassInfo(ITypeInfo** pptinfo) +{ + if (!pptinfo) + return E_POINTER; + + *pptinfo = 0; + if (!qAxTypeLibrary) + return DISP_E_BADINDEX; + + return qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->classID(class_name), pptinfo); +} + +//**** IProvideClassInfo2 +/* + Provide the ID of the event interface. +*/ +HRESULT WINAPI QAxServerBase::GetGUID(DWORD dwGuidKind, GUID* pGUID) +{ + if (!pGUID) + return E_POINTER; + + if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) { + *pGUID = qAxFactory()->eventsID(class_name); + return S_OK; + } + *pGUID = GUID_NULL; + return E_FAIL; +} + +//**** IDispatch +/* + Returns the number of class infos for this IDispatch. +*/ +HRESULT WINAPI QAxServerBase::GetTypeInfoCount(UINT* pctinfo) +{ + if (!pctinfo) + return E_POINTER; + + *pctinfo = qAxTypeLibrary ? 1 : 0; + return S_OK; +} + +/* + Provides the ITypeInfo for this IDispatch implementation. +*/ +HRESULT WINAPI QAxServerBase::GetTypeInfo(UINT itinfo, LCID /*lcid*/, ITypeInfo** pptinfo) +{ + if (!pptinfo) + return E_POINTER; + + if (!qAxTypeLibrary) + return DISP_E_BADINDEX; + + ensureMetaData(); + + *pptinfo = m_spTypeInfo; + (*pptinfo)->AddRef(); + + return S_OK; +} + +/* + Provides the names of the methods implemented in this IDispatch implementation. +*/ +HRESULT WINAPI QAxServerBase::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, + LCID /*lcid*/, DISPID* rgdispid) +{ + if (!rgszNames || !rgdispid) + return E_POINTER; + + if (!qAxTypeLibrary) + return DISP_E_UNKNOWNNAME; + + ensureMetaData(); + if (!m_spTypeInfo) + return DISP_E_UNKNOWNNAME; + + return m_spTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid); +} + +/* + Map the COM call to the Qt slot/property for \a dispidMember. +*/ +HRESULT WINAPI QAxServerBase::Invoke(DISPID dispidMember, REFIID riid, + LCID /*lcid*/, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, UINT* puArgErr) +{ + if (riid != IID_NULL) + return DISP_E_UNKNOWNINTERFACE; + if (!theObject) + return E_UNEXPECTED; + + HRESULT res = DISP_E_MEMBERNOTFOUND; + + bool uniqueIndex = wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_METHOD; + + int index = uniqueIndex ? indexCache.value(dispidMember, -1) : -1; + QByteArray name; + if (index == -1) { + ensureMetaData(); + + // This property or method is invoked when an ActiveX client specifies + // the object name without a property or method. We only support property. + if (dispidMember == DISPID_VALUE && (wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT)) { + const QMetaObject *mo = qt.object->metaObject(); + index = mo->indexOfClassInfo("DefaultProperty"); + if (index != -1) { + name = mo->classInfo(index).value(); + index = mo->indexOfProperty(name); + } + } else { + BSTR bname; + UINT cname = 0; + if (m_spTypeInfo) + m_spTypeInfo->GetNames(dispidMember, &bname, 1, &cname); + if (!cname) + return res; + + name = QString::fromUtf16((const ushort *)bname).toLatin1(); + SysFreeString(bname); + } + } + + const QMetaObject *mo = qt.object->metaObject(); + QSize oldSizeHint; + if (isWidget) + oldSizeHint = qt.widget->sizeHint(); + + switch (wFlags) { + case DISPATCH_PROPERTYGET|DISPATCH_METHOD: + case DISPATCH_PROPERTYGET: + { + if (index == -1) { + index = mo->indexOfProperty(name); + if (index == -1 && wFlags == DISPATCH_PROPERTYGET) + return res; + } + + QMetaProperty property; + if (index < mo->propertyCount()) + property = mo->property(index); + + if (property.isReadable()) { + if (!pvarResult) + return DISP_E_PARAMNOTOPTIONAL; + if (pDispParams->cArgs || + pDispParams->cNamedArgs) + return DISP_E_BADPARAMCOUNT; + + QVariant var = qt.object->property(property.name()); + if (!var.isValid()) + res = DISP_E_MEMBERNOTFOUND; + else if (!QVariantToVARIANT(var, *pvarResult)) + res = DISP_E_TYPEMISMATCH; + else + res = S_OK; + break; + } else if (wFlags == DISPATCH_PROPERTYGET) { + break; + } + } + // FALLTHROUGH if wFlags == DISPATCH_PROPERTYGET|DISPATCH_METHOD AND not a property. + case DISPATCH_METHOD: + { + int nameLength = 0; + if (index == -1) { + nameLength = name.length(); + name += "("; + // no parameter - shortcut + if (!pDispParams->cArgs) + index = mo->indexOfSlot((name + ")")); + // search + if (index == -1) { + for (int i = 0; i < mo->methodCount(); ++i) { + const QMetaMethod slot(mo->method(i)); + if (slot.methodType() == QMetaMethod::Slot && QByteArray(slot.signature()).startsWith(name)) { + index = i; + break; + } + } + // resolve overloads + if (index == -1) { + QRegExp regexp(QLatin1String("_([0-9])\\(")); + if (regexp.lastIndexIn(QString::fromLatin1(name.constData())) != -1) { + name = name.left(name.length() - regexp.cap(0).length()) + "("; + int overload = regexp.cap(1).toInt() + 1; + + for (int s = 0; s < qt.object->metaObject()->methodCount(); ++s) { + QMetaMethod slot = qt.object->metaObject()->method(s); + if (slot.methodType() == QMetaMethod::Slot && QByteArray(slot.signature()).startsWith(name)) { + if (!--overload) { + index = s; + break; + } + } + } + } + } + if (index == -1) + return res; + } + } + + int lookupIndex = index; + + // get slot info + QMetaMethod slot(mo->method(index)); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + QByteArray type = slot.typeName(); + name = slot.signature(); + nameLength = name.indexOf('('); + QByteArray prototype = name.mid(nameLength + 1); + prototype.truncate(prototype.length() - 1); + QList<QByteArray> ptypes; + if (!prototype.isEmpty()) + ptypes = prototype.split(','); + int pcount = ptypes.count(); + + // verify parameter count + if (pcount > pDispParams->cArgs) { + // count cloned slots immediately following the real thing + int defArgs = 0; + while (index < mo->methodCount()) { + ++index; + slot = mo->method(index); + if (!(slot.attributes() & QMetaMethod::Cloned)) + break; + --pcount; + // found a matching overload. ptypes still valid + if (pcount <= pDispParams->cArgs) + break; + } + // still wrong :( + if (pcount > pDispParams->cArgs) + return DISP_E_PARAMNOTOPTIONAL; + } else if (pcount < pDispParams->cArgs) { + return DISP_E_BADPARAMCOUNT; + } + + // setup parameters (pcount + return) + bool ok = true; + void *static_argv[QAX_NUM_PARAMS + 1]; + QVariant static_varp[QAX_NUM_PARAMS + 1]; + void *static_argv_pointer[QAX_NUM_PARAMS + 1]; + + int totalParam = pcount; + if (!type.isEmpty()) + ++totalParam; + + void **argv = 0; // the actual array passed into qt_metacall + void **argv_pointer = 0; // in case we need an additional level of indirection + QVariant *varp = 0; // QVariants to hold the temporary Qt data object for us + + if (totalParam) { + if (totalParam <= 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_pointer[0] = 0; + } + + for (int p = 0; p < pcount; ++p) { + // map the VARIANT to the void* + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + 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 { + if (puArgErr) + *puArgErr = pcount-p-1; + ok = false; + } + } + + // return value + if (!type.isEmpty()) { + QVariant::Type vt = QVariant::nameToType(type); + if (vt == QVariant::UserType) + vt = QVariant::Invalid; + varp[0] = QVariant(vt); + if (varp[0].type() == QVariant::Invalid && mo->indexOfEnumerator(slot.typeName()) != -1) + varp[0] = QVariant(QVariant::Int); + + if (varp[0].type() == QVariant::Invalid) { + if (type == "QVariant") + argv[0] = varp; + else + argv[0] = 0; + } else { + argv[0] = const_cast<void*>(varp[0].constData()); + } + if (type.endsWith("*")) { + argv_pointer[0] = argv[0]; + argv[0] = argv_pointer; + } + } + + // call the slot if everthing went fine. + if (ok) { + ++invokeCount; + qt.object->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + if (--invokeCount < 0) + invokeCount = 0; + + // update reference parameters and return value + for (int p = 0; p < pcount; ++p) { + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + if (out) { + if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out)) + ok = false; + } + } + if (!type.isEmpty() && pvarResult) { + if (!varp[0].isValid() && type != "QVariant") + varp[0] = QVariant(QMetaType::type(type), argv_pointer); +// qVariantSetValue(varp[0], argv_pointer[0], type); + ok = QVariantToVARIANT(varp[0], *pvarResult, type); + } + } + if (argv && argv != static_argv) { + delete []argv; + delete []argv_pointer; + delete []varp; + } + + res = ok ? S_OK : DISP_E_TYPEMISMATCH; + + // reset in case index changed for default-arg handling + index = lookupIndex; + } + break; + case DISPATCH_PROPERTYPUT: + case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF: + { + if (index == -1) { + index = mo->indexOfProperty(name); + if (index == -1) + return res; + } + + QMetaProperty property; + if (index < mo->propertyCount()) + property = mo->property(index); + if (!property.isWritable()) + return DISP_E_MEMBERNOTFOUND; + if (!pDispParams->cArgs) + return DISP_E_PARAMNOTOPTIONAL; + if (pDispParams->cArgs != 1 || + pDispParams->cNamedArgs != 1 || + *pDispParams->rgdispidNamedArgs != DISPID_PROPERTYPUT) + return DISP_E_BADPARAMCOUNT; + + QVariant var = VARIANTToQVariant(*pDispParams->rgvarg, property.typeName(), property.type()); + if (!var.isValid()) { + if (puArgErr) + *puArgErr = 0; + return DISP_E_BADVARTYPE; + } + if (!qt.object->setProperty(property.name(), var)) { + if (puArgErr) + *puArgErr = 0; + return DISP_E_TYPEMISMATCH; + } + + res = S_OK; + } + break; + + default: + break; + } + + // maybe calling a setter? Notify client about changes + switch(wFlags) { + case DISPATCH_METHOD: + case DISPATCH_PROPERTYPUT: + case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF: + if (m_spAdviseSink || adviseSinks.count()) { + FORMATETC fmt; + fmt.cfFormat = 0; + fmt.ptd = 0; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_NULL; + + STGMEDIUM stg; + stg.tymed = TYMED_NULL; + stg.pUnkForRelease = 0; + stg.hBitmap = 0; // initializes the whole union + + if (m_spAdviseSink) { + m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1); + m_spAdviseSink->OnDataChange(&fmt, &stg); + } + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnDataChange(&fmt, &stg); + } + } + + dirtyflag = true; + break; + default: + break; + } + + if (index != -1 && uniqueIndex) + indexCache.insert(dispidMember, index); + + if (exception) { + if (pexcepinfo) { + memset(pexcepinfo, 0, sizeof(EXCEPINFO)); + + pexcepinfo->wCode = exception->code; + if (!exception->src.isNull()) + pexcepinfo->bstrSource = QStringToBSTR(exception->src); + if (!exception->desc.isNull()) + pexcepinfo->bstrDescription = QStringToBSTR(exception->desc); + if (!exception->context.isNull()) { + QString context = exception->context; + int contextID = 0; + int br = context.indexOf(QLatin1Char('[')); + if (br != -1) { + context = context.mid(br+1); + context = context.left(context.length() - 1); + contextID = context.toInt(); + + context = exception->context; + context = context.left(br-1); + } + pexcepinfo->bstrHelpFile = QStringToBSTR(context); + pexcepinfo->dwHelpContext = contextID; + } + } + delete exception; + exception = 0; + return DISP_E_EXCEPTION; + } else if (isWidget) { + QSize sizeHint = qt.widget->sizeHint(); + if (oldSizeHint != sizeHint) { + updateGeometry(); + if (m_spInPlaceSite) { + RECT rect = {0, 0, sizeHint.width(), sizeHint.height()}; + m_spInPlaceSite->OnPosRectChange(&rect); + } + } + updateMask(); + } + + return res; +} + +//**** IConnectionPointContainer +/* + Provide the IEnumConnectionPoints implemented in the QAxSignalVec class. +*/ +HRESULT WINAPI QAxServerBase::EnumConnectionPoints(IEnumConnectionPoints **epoints) +{ + if (!epoints) + return E_POINTER; + *epoints = new QAxSignalVec(points); + (*epoints)->AddRef(); + return S_OK; +} + +/* + Provide the IConnectionPoint implemented in the QAxConnection for \a iid. +*/ +HRESULT WINAPI QAxServerBase::FindConnectionPoint(REFIID iid, IConnectionPoint **cpoint) +{ + if (!cpoint) + return E_POINTER; + + IConnectionPoint *cp = points[iid]; + *cpoint = cp; + if (cp) { + cp->AddRef(); + return S_OK; + } + return CONNECT_E_NOCONNECTION; +} + +//**** IPersistStream +/* + \reimp + + See documentation of IPersistStorage::IsDirty. +*/ +HRESULT WINAPI QAxServerBase::IsDirty() +{ + return dirtyflag ? S_OK : S_FALSE; +} + +HRESULT WINAPI QAxServerBase::Load(IStream *pStm) +{ + STATSTG stat; + HRESULT hres = pStm->Stat(&stat, STATFLAG_DEFAULT); + bool openAsText = false; + QByteArray qtarray; + if (hres == S_OK) { + QString streamName = QString::fromUtf16((const ushort *)stat.pwcsName); + CoTaskMemFree(stat.pwcsName); + openAsText = streamName == QLatin1String("SomeStreamName"); + if (stat.cbSize.HighPart) // more than 4GB - too large! + return S_FALSE; + + qtarray.resize(stat.cbSize.LowPart); + ULONG read; + pStm->Read(qtarray.data(), stat.cbSize.LowPart, &read); + } + const QMetaObject *mo = qt.object->metaObject(); + + QBuffer qtbuffer(&qtarray); + QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value(); + if (!mimeType.isEmpty()) { + mimeType = mimeType.left(mimeType.indexOf(':')); // first type + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (axb && axb->readData(&qtbuffer, QString::fromLatin1(mimeType))) + return S_OK; + } + + qtbuffer.close(); // resets + qtbuffer.open(openAsText ? (QIODevice::ReadOnly | QIODevice::Text) : QIODevice::ReadOnly); + + QDataStream qtstream(&qtbuffer); + int version; + qtstream >> version; + qtstream.setVersion(version); + int more = 0; + qtstream >> more; + + while (!qtbuffer.atEnd() && more) { + QString propname; + QVariant value; + qtstream >> propname; + if (propname.isEmpty()) + break; + qtstream >> value; + qtstream >> more; + + int idx = mo->indexOfProperty(propname.toLatin1()); + QMetaProperty property = mo->property(idx); + if (property.isWritable()) + qt.object->setProperty(propname.toLatin1(), value); + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Save(IStream *pStm, BOOL clearDirty) +{ + const QMetaObject *mo = qt.object->metaObject(); + + QBuffer qtbuffer; + bool saved = false; + QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value(); + if (!mimeType.isEmpty()) { + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + saved = axb && axb->writeData(&qtbuffer); + qtbuffer.close(); + } + + if (!saved) { + qtbuffer.open(QIODevice::WriteOnly); + QDataStream qtstream(&qtbuffer); + qtstream << qtstream.version(); + + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty metaprop = mo->property(prop); + if (QByteArray(metaprop.typeName()).endsWith('*')) + continue; + QString property = QLatin1String(metaprop.name()); + QVariant qvar = qt.object->property(metaprop.name()); + if (qvar.isValid()) { + qtstream << int(1); + qtstream << property; + qtstream << qvar; + } + } + + qtstream << int(0); + qtbuffer.close(); + } + + QByteArray qtarray = qtbuffer.buffer(); + ULONG written = 0; + const char *data = qtarray.constData(); + ULARGE_INTEGER newsize; + newsize.HighPart = 0; + newsize.LowPart = qtarray.size(); + pStm->SetSize(newsize); + pStm->Write(data, qtarray.size(), &written); + pStm->Commit(STGC_ONLYIFCURRENT); + + if (clearDirty) + dirtyflag = false; + return S_OK; +} + +HRESULT WINAPI QAxServerBase::GetSizeMax(ULARGE_INTEGER *pcbSize) +{ + const QMetaObject *mo = qt.object->metaObject(); + + int np = mo->propertyCount(); + pcbSize->HighPart = 0; + pcbSize->LowPart = np * 50; + + return S_OK; +} + +//**** IPersistStorage + +HRESULT WINAPI QAxServerBase::InitNew(IStorage *pStg) +{ + if (initNewCalled) + return CO_E_ALREADYINITIALIZED; + + dirtyflag = false; + initNewCalled = true; + + m_spStorage = pStg; + if (m_spStorage) + m_spStorage->AddRef(); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Load(IStorage *pStg) +{ + if (InitNew(pStg) != S_OK) + return CO_E_ALREADYINITIALIZED; + + IStream *spStream = 0; + QString streamName = QLatin1String(qt.object->metaObject()->className()); + streamName.replace(QLatin1Char(':'), QLatin1Char('.')); + /* Also invalid, but not relevant + streamName.replace(QLatin1Char('/'), QLatin1Char('_')); + streamName.replace(QLatin1Char('\\'), QLatin1Char('_')); + */ + streamName += QLatin1String("_Stream4.2"); + + pStg->OpenStream((const WCHAR *)streamName.utf16(), 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream); + if (!spStream) // support for streams saved with 4.1 and earlier + pStg->OpenStream(L"SomeStreamName", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream); + if (!spStream) + return E_FAIL; + + Load(spStream); + spStream->Release(); + + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Save(IStorage *pStg, BOOL fSameAsLoad) +{ + IStream *spStream = 0; + QString streamName = QLatin1String(qt.object->metaObject()->className()); + streamName.replace(QLatin1Char(':'), QLatin1Char('.')); + /* Also invalid, but not relevant + streamName.replace(QLatin1Char('/'), QLatin1Char('_')); + streamName.replace(QLatin1Char('\\'), QLatin1Char('_')); + */ + streamName += QLatin1String("_Stream4.2"); + + pStg->CreateStream((const WCHAR *)streamName.utf16(), STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &spStream); + if (!spStream) + return E_FAIL; + + Save(spStream, true); + + spStream->Release(); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::SaveCompleted(IStorage *pStgNew) +{ + if (pStgNew) { + if (m_spStorage) + m_spStorage->Release(); + m_spStorage = pStgNew; + m_spStorage->AddRef(); + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::HandsOffStorage() +{ + if (m_spStorage) m_spStorage->Release(); + m_spStorage = 0; + + return S_OK; +} + +//**** IPersistPropertyBag +/* + Initialize the properties of the Qt widget. +*/ +HRESULT WINAPI QAxServerBase::InitNew() +{ + if (initNewCalled) + return CO_E_ALREADYINITIALIZED; + + dirtyflag = false; + initNewCalled = true; + return S_OK; +} + +/* + Set the properties of the Qt widget to the values provided in the \a bag. +*/ +HRESULT WINAPI QAxServerBase::Load(IPropertyBag *bag, IErrorLog * /*log*/) +{ + if (!bag) + return E_POINTER; + + if (InitNew() != S_OK) + return E_UNEXPECTED; + + bool error = false; + const QMetaObject *mo = qt.object->metaObject(); + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty property = mo->property(prop); + const char* pname = property.name(); + BSTR bstr = QStringToBSTR(QLatin1String(pname)); + VARIANT var; + var.vt = VT_EMPTY; + HRESULT res = bag->Read(bstr, &var, 0); + if (property.isWritable() && var.vt != VT_EMPTY) { + if (res != S_OK || !qt.object->setProperty(pname, VARIANTToQVariant(var, property.typeName(), property.type()))) + error = true; + } + SysFreeString(bstr); + } + + updateGeometry(); + + return /*error ? E_FAIL :*/ S_OK; +} + +/* + Save the properties of the Qt widget into the \a bag. +*/ +HRESULT WINAPI QAxServerBase::Save(IPropertyBag *bag, BOOL clearDirty, BOOL /*saveAll*/) +{ + if (!bag) + return E_POINTER; + + if (clearDirty) + dirtyflag = false; + bool error = false; + const QMetaObject *mo = qt.object->metaObject(); + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty property = mo->property(prop); + if (QByteArray(property.typeName()).endsWith('*')) + continue; + + BSTR bstr = QStringToBSTR(QLatin1String(property.name())); + QVariant qvar = qt.object->property(property.name()); + if (!qvar.isValid()) + error = true; + VARIANT var; + QVariantToVARIANT(qvar, var); + bag->Write(bstr, &var); + SysFreeString(bstr); + } + return /*error ? E_FAIL :*/ S_OK; +} + +//**** IPersistFile +/* +*/ +HRESULT WINAPI QAxServerBase::SaveCompleted(LPCOLESTR fileName) +{ + if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1) + return E_NOTIMPL; + + currentFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::GetCurFile(LPOLESTR *currentFile) +{ + if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1) + return E_NOTIMPL; + + if (currentFileName.isEmpty()) { + *currentFile = 0; + return S_FALSE; + } + IMalloc *malloc = 0; + CoGetMalloc(1, &malloc); + if (!malloc) + return E_OUTOFMEMORY; + + *currentFile = static_cast<WCHAR *>(malloc->Alloc(currentFileName.length() * 2)); + malloc->Release(); + memcpy(*currentFile, currentFileName.unicode(), currentFileName.length() * 2); + + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Load(LPCOLESTR fileName, DWORD mode) +{ + const QMetaObject *mo = qt.object->metaObject(); + int mimeIndex = mo->indexOfClassInfo("MIME"); + if (mimeIndex == -1) + return E_NOTIMPL; + + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (!axb) { + qWarning() << class_name << ": No QAxBindable implementation for mime-type handling"; + return E_NOTIMPL; + } + + QString loadFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + QString fileExtension = loadFileName.mid(loadFileName.lastIndexOf(QLatin1Char('.')) + 1); + QFile file(loadFileName); + + QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value()); + QStringList mimeTypes = mimeType.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + QString mime = mimeTypes.at(m); + if (mime.count(QLatin1Char(':')) != 2) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + + mimeType = mime.left(mimeType.indexOf(QLatin1Char(':'))); // first type + if (mimeType.isEmpty()) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + QString mimeExtension = mime.mid(mimeType.length() + 1); + mimeExtension = mimeExtension.left(mimeExtension.indexOf(QLatin1Char(':'))); + if (mimeExtension != fileExtension) + continue; + + if (axb->readData(&file, mimeType)) { + currentFileName = loadFileName; + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT WINAPI QAxServerBase::Save(LPCOLESTR fileName, BOOL fRemember) +{ + const QMetaObject *mo = qt.object->metaObject(); + int mimeIndex = mo->indexOfClassInfo("MIME"); + if (mimeIndex == -1) + return E_NOTIMPL; + + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (!axb) { + qWarning() << class_name << ": No QAxBindable implementation for mime-type handling"; + return E_NOTIMPL; + } + + QString saveFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + QString fileExtension = saveFileName.mid(saveFileName.lastIndexOf(QLatin1Char('.')) + 1); + QFile file(saveFileName); + + QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value()); + QStringList mimeTypes = mimeType.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + QString mime = mimeTypes.at(m); + if (mime.count(QLatin1Char(':')) != 2) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + mimeType = mime.left(mimeType.indexOf(QLatin1Char(':'))); // first type + if (mimeType.isEmpty()) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + QString mimeExtension = mime.mid(mimeType.length() + 1); + mimeExtension = mimeExtension.left(mimeExtension.indexOf(QLatin1Char(':'))); + if (mimeExtension != fileExtension) + continue; + if (axb->writeData(&file)) { + if (fRemember) + currentFileName = saveFileName; + return S_OK; + } + } + return E_FAIL; +} + +//**** IViewObject +/* + Draws the widget into the provided device context. +*/ +HRESULT WINAPI QAxServerBase::Draw(DWORD dwAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL /*lprcWBounds*/, + BOOL(__stdcall* /*pfnContinue*/)(ULONG_PTR), ULONG_PTR /*dwContinue*/) +{ + if (!lprcBounds) + return E_INVALIDARG; + + internalCreate(); + if (!isWidget || !qt.widget) + return OLE_E_BLANK; + + switch (dwAspect) { + case DVASPECT_CONTENT: + case DVASPECT_OPAQUE: + case DVASPECT_TRANSPARENT: + break; + default: + return DV_E_DVASPECT; + } + if (!ptd) + hicTargetDev = 0; + + bool bDeleteDC = false; + if (!hicTargetDev) { + hicTargetDev = ::CreateDCA("DISPLAY", NULL, NULL, NULL); + bDeleteDC = (hicTargetDev != hdcDraw); + } + + RECTL rc = *lprcBounds; + bool bMetaFile = GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE; + if (!bMetaFile) + ::LPtoDP(hicTargetDev, (LPPOINT)&rc, 2); + + QPixmap pm = QPixmap::grabWidget(qt.widget); + HBITMAP hbm = pm.toWinHBITMAP(); + HDC hdc = CreateCompatibleDC(0); + SelectObject(hdc, hbm); + ::StretchBlt(hdcDraw, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdc, 0, 0,pm.width(), pm.height(), SRCCOPY); + DeleteDC(hdc); + DeleteObject(hbm); + + if (bDeleteDC) + DeleteDC(hicTargetDev); + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetColorSet(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, LOGPALETTE **ppColorSet) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Freeze(DWORD dwAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Unfreeze(DWORD dwFreeze) +{ + return E_NOTIMPL; +} + +/* + Stores the provided advise sink. +*/ +HRESULT WINAPI QAxServerBase::SetAdvise(DWORD /*aspects*/, DWORD /*advf*/, IAdviseSink *pAdvSink) +{ + if (m_spAdviseSink) m_spAdviseSink->Release(); + + m_spAdviseSink = pAdvSink; + if (m_spAdviseSink) m_spAdviseSink->AddRef(); + return S_OK; +} + +/* + Returns the advise sink. +*/ +HRESULT WINAPI QAxServerBase::GetAdvise(DWORD* /*aspects*/, DWORD* /*advf*/, IAdviseSink **ppAdvSink) +{ + if (!ppAdvSink) + return E_POINTER; + + *ppAdvSink = m_spAdviseSink; + if (*ppAdvSink) + (*ppAdvSink)->AddRef(); + return S_OK; +} + +//**** IViewObject2 +/* + Returns the current size ONLY if the widget has already been sized. +*/ +HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwAspect, LONG /*lindex*/, DVTARGETDEVICE* /*ptd*/, LPSIZEL lpsizel) +{ + if (!isWidget || !qt.widget || !qt.widget->testAttribute(Qt::WA_Resized)) + return OLE_E_BLANK; + + return GetExtent(dwAspect, lpsizel); +} + +//**** IOleControl +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetControlInfo(LPCONTROLINFO) +{ + return E_NOTIMPL; +} + +/* + Turns event firing on and off. +*/ +HRESULT WINAPI QAxServerBase::FreezeEvents(BOOL bFreeze) +{ + // member of CComControl + if (bFreeze) + freezeEvents++; + else + freezeEvents--; + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::OnMnemonic(LPMSG) +{ + return E_NOTIMPL; +} + +/* + Update the ambient properties of the Qt widget. +*/ +HRESULT WINAPI QAxServerBase::OnAmbientPropertyChange(DISPID dispID) +{ + if (!m_spClientSite || !theObject) + return S_OK; + + IDispatch *disp = 0; + m_spClientSite->QueryInterface(IID_IDispatch, (void**)&disp); + if (!disp) + return S_OK; + + VARIANT var; + VariantInit(&var); + DISPPARAMS params = { 0, 0, 0, 0 }; + disp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &var, 0, 0); + disp->Release(); + disp = 0; + + switch(dispID) { + case DISPID_AMBIENT_APPEARANCE: + break; + case DISPID_AMBIENT_AUTOCLIP: + break; + case DISPID_AMBIENT_BACKCOLOR: + case DISPID_AMBIENT_FORECOLOR: + if (isWidget) { + long rgb; + if (var.vt == VT_UI4) + rgb = var.ulVal; + else if (var.vt == VT_I4) + rgb = var.lVal; + else + break; + QPalette pal = qt.widget->palette(); + pal.setColor(dispID == DISPID_AMBIENT_BACKCOLOR ? QPalette::Window : QPalette::WindowText, + OLEColorToQColor(rgb)); + qt.widget->setPalette(pal); + } + break; + case DISPID_AMBIENT_DISPLAYASDEFAULT: + break; + case DISPID_AMBIENT_DISPLAYNAME: + if (var.vt != VT_BSTR || !isWidget) + break; + qt.widget->setWindowTitle(QString::fromUtf16((const ushort *)var.bstrVal)); + break; + case DISPID_AMBIENT_FONT: + if (var.vt != VT_DISPATCH || !isWidget) + break; + { + QVariant qvar = VARIANTToQVariant(var, "QFont", QVariant::Font); + QFont qfont = qVariantValue<QFont>(qvar); + qt.widget->setFont(qfont); + } + break; + case DISPID_AMBIENT_LOCALEID: + break; + case DISPID_AMBIENT_MESSAGEREFLECT: + if (var.vt != VT_BOOL) + break; + if (var.boolVal) + qt.widget->installEventFilter(this); + else + qt.widget->removeEventFilter(this); + break; + case DISPID_AMBIENT_PALETTE: + break; + case DISPID_AMBIENT_SCALEUNITS: + break; + case DISPID_AMBIENT_SHOWGRABHANDLES: + break; + case DISPID_AMBIENT_SHOWHATCHING: + break; + case DISPID_AMBIENT_SUPPORTSMNEMONICS: + break; + case DISPID_AMBIENT_TEXTALIGN: + break; + case DISPID_AMBIENT_UIDEAD: + if (var.vt != VT_BOOL || !isWidget) + break; + qt.widget->setEnabled(!var.boolVal); + break; + case DISPID_AMBIENT_USERMODE: + if (var.vt != VT_BOOL) + break; + inDesignMode = !var.boolVal; + break; + case DISPID_AMBIENT_RIGHTTOLEFT: + if (var.vt != VT_BOOL) + break; + qApp->setLayoutDirection(var.boolVal?Qt::RightToLeft:Qt::LeftToRight); + break; + } + + return S_OK; +} + +//**** IOleWindow +/* + Returns the HWND of the control. +*/ +HRESULT WINAPI QAxServerBase::GetWindow(HWND *pHwnd) +{ + if (!pHwnd) + return E_POINTER; + *pHwnd = m_hWnd; + return S_OK; +} + +/* + Enters What's This mode. +*/ +HRESULT WINAPI QAxServerBase::ContextSensitiveHelp(BOOL fEnterMode) +{ + if (fEnterMode) + QWhatsThis::enterWhatsThisMode(); + else + QWhatsThis::leaveWhatsThisMode(); + return S_OK; +} + +//**** IOleInPlaceObject +/* + Deactivates the control in place. +*/ +HRESULT WINAPI QAxServerBase::InPlaceDeactivate() +{ + if (!isInPlaceActive) + return S_OK; + UIDeactivate(); + + isInPlaceActive = false; + + // if we have a window, tell it to go away. + if (m_hWnd) { + if (::IsWindow(m_hWnd)) + ::DestroyWindow(m_hWnd); + m_hWnd = 0; + } + + if (m_spInPlaceSite) + m_spInPlaceSite->OnInPlaceDeactivate(); + + return S_OK; +} + +/* + Deactivates the control's user interface. +*/ +HRESULT WINAPI QAxServerBase::UIDeactivate() +{ + // if we're not UIActive, not much to do. + if (!isUIActive || !m_spInPlaceSite) + return S_OK; + + isUIActive = false; + + // notify frame windows, if appropriate, that we're no longer ui-active. + HWND hwndParent; + if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) { + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + + m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (spInPlaceUIWindow) { + spInPlaceUIWindow->SetActiveObject(0, 0); + spInPlaceUIWindow->Release(); + } + if (m_spInPlaceFrame) { + removeMenu(); + if (menuBar) { + menuBar->removeEventFilter(this); + menuBar = 0; + } + if (statusBar) { + statusBar->removeEventFilter(this); + const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)"); + QMetaObject::disconnect(statusBar, index, this, -1); + statusBar = 0; + } + m_spInPlaceFrame->SetActiveObject(0, 0); + m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + } + } + // we don't need to explicitly release the focus here since somebody + // else grabbing the focus is usually why we are getting called at all + m_spInPlaceSite->OnUIDeactivate(false); + + return S_OK; +} + +/* + Positions the control, and applies requested clipping. +*/ +HRESULT WINAPI QAxServerBase::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip) +{ + if (prcPos == 0 || prcClip == 0) + return E_POINTER; + + if (m_hWnd) { + // the container wants us to clip, so figure out if we really need to + RECT rcIXect; + BOOL b = IntersectRect(&rcIXect, prcPos, prcClip); + HRGN tempRgn = 0; + if (b && !EqualRect(&rcIXect, prcPos)) { + OffsetRect(&rcIXect, -(prcPos->left), -(prcPos->top)); + tempRgn = CreateRectRgnIndirect(&rcIXect); + } + + ::SetWindowRgn(m_hWnd, tempRgn, true); + ::SetWindowPos(m_hWnd, 0, prcPos->left, prcPos->top, + prcPos->right - prcPos->left, prcPos->bottom - prcPos->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + + //Save the new extent. + m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), int(prcPos->right - prcPos->left), qt.widget->maximumWidth()); + m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), int(prcPos->bottom - prcPos->top), qt.widget->maximumHeight()); + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::ReactivateAndUndo() +{ + return E_NOTIMPL; +} + +//**** IOleInPlaceActiveObject + +Q_GUI_EXPORT int qt_translateKeyCode(int); + +HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg) +{ + if (pMsg->message != WM_KEYDOWN || !isWidget) + return S_FALSE; + + DWORD dwKeyMod = 0; + if (::GetKeyState(VK_SHIFT) < 0) + dwKeyMod |= 1; // KEYMOD_SHIFT + if (::GetKeyState(VK_CONTROL) < 0) + dwKeyMod |= 2; // KEYMOD_CONTROL + if (::GetKeyState(VK_MENU) < 0) + dwKeyMod |= 4; // KEYMOD_ALT + + switch (LOWORD(pMsg->wParam)) { + case VK_TAB: + if (isUIActive) { + bool shift = ::GetKeyState(VK_SHIFT) < 0; + bool giveUp = true; + QWidget *curFocus = qt.widget->focusWidget(); + if (curFocus) { + if (shift) { + if (!curFocus->isWindow()) { + QWidget *nextFocus = curFocus->nextInFocusChain(); + QWidget *prevFocus = 0; + QWidget *topLevel = 0; + while (nextFocus != curFocus) { + if (nextFocus->focusPolicy() & Qt::TabFocus) { + prevFocus = nextFocus; + topLevel = 0; + } else if (nextFocus->isWindow()) { + topLevel = nextFocus; + } + nextFocus = nextFocus->nextInFocusChain(); + } + + if (!topLevel) { + giveUp = false; + ((HackWidget*)curFocus)->focusNextPrevChild(false); + } + } + } else { + QWidget *nextFocus = curFocus; + while (1) { + nextFocus = nextFocus->nextInFocusChain(); + if (nextFocus->isWindow()) + break; + if (nextFocus->focusPolicy() & Qt::TabFocus) { + giveUp = false; + ((HackWidget*)curFocus)->focusNextPrevChild(true); + break; + } + } + } + } + if (giveUp) { + HWND hwnd = ::GetParent(m_hWnd); + ::SetFocus(hwnd); + } else { + return S_OK; + } + + } + break; + + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + if (isUIActive) + return S_FALSE; + break; + + default: + if (isUIActive && qt.widget->focusWidget()) { + int state = Qt::NoButton; + if (dwKeyMod & 1) + state |= Qt::ShiftModifier; + if (dwKeyMod & 2) + state |= Qt::ControlModifier; + if (dwKeyMod & 4) + state |= Qt::AltModifier; + + int key = pMsg->wParam; + if (!(key >= 'A' && key <= 'Z') && !(key >= '0' && key <= '9')) + key = qt_translateKeyCode(pMsg->wParam); + + QKeyEvent override(QEvent::ShortcutOverride, key, (Qt::KeyboardModifiers)state); + override.ignore(); + QApplication::sendEvent(qt.widget->focusWidget(), &override); + if (override.isAccepted()) + return S_FALSE; + } + break; + } + + if (!m_spClientSite) + return S_FALSE; + + IOleControlSite *controlSite = 0; + m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&controlSite); + if (!controlSite) + return S_FALSE; + + HRESULT hres = controlSite->TranslateAcceleratorW(pMsg, dwKeyMod); + + controlSite->Release(); + + return hres; +} + +HRESULT WINAPI QAxServerBase::TranslateAcceleratorA(MSG *pMsg) +{ + return TranslateAcceleratorW(pMsg); +} + +HRESULT WINAPI QAxServerBase::OnFrameWindowActivate(BOOL fActivate) +{ + if (fActivate) { + if (wasUIActive) + ::SetFocus(m_hWnd); + } else { + wasUIActive = isUIActive; + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::OnDocWindowActivate(BOOL fActivate) +{ + return S_OK; +} + +HRESULT WINAPI QAxServerBase::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow) +{ + return S_OK; +} + +HRESULT WINAPI QAxServerBase::EnableModeless(BOOL fEnable) +{ + if (!isWidget) + return S_OK; + + EnableWindow(qt.widget->winId(), fEnable); + return S_OK; +} + +//**** IOleObject + +static inline LPOLESTR QStringToOLESTR(const QString &qstring) +{ + LPOLESTR olestr = (wchar_t*)CoTaskMemAlloc(qstring.length()*2+2); + memcpy(olestr, (ushort*)qstring.unicode(), qstring.length()*2); + olestr[qstring.length()] = 0; + return olestr; +} + +/* + \reimp + + See documentation of IOleObject::GetUserType. +*/ +HRESULT WINAPI QAxServerBase::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType) +{ + if (!pszUserType) + return E_POINTER; + + switch (dwFormOfType) { + case USERCLASSTYPE_FULL: + *pszUserType = QStringToOLESTR(class_name); + break; + case USERCLASSTYPE_SHORT: + if (!qt.widget || !isWidget || qt.widget->windowTitle().isEmpty()) + *pszUserType = QStringToOLESTR(class_name); + else + *pszUserType = QStringToOLESTR(qt.widget->windowTitle()); + break; + case USERCLASSTYPE_APPNAME: + *pszUserType = QStringToOLESTR(qApp->objectName()); + break; + } + + return S_OK; +} + +/* + Returns the status flags registered for this control. +*/ +HRESULT WINAPI QAxServerBase::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus) +{ + return OleRegGetMiscStatus(qAxFactory()->classID(class_name), dwAspect, pdwStatus); +} + +/* + Stores the provided advise sink. +*/ +HRESULT WINAPI QAxServerBase::Advise(IAdviseSink* pAdvSink, DWORD* pdwConnection) +{ + *pdwConnection = adviseSinks.count() + 1; + STATDATA data = { {0, 0, DVASPECT_CONTENT, -1, TYMED_NULL} , 0, pAdvSink, *pdwConnection }; + adviseSinks.append(data); + pAdvSink->AddRef(); + return S_OK; +} + +/* + Closes the control. +*/ +HRESULT WINAPI QAxServerBase::Close(DWORD dwSaveOption) +{ + if (dwSaveOption != OLECLOSE_NOSAVE && m_spClientSite) + m_spClientSite->SaveObject(); + if (isInPlaceActive) { + HRESULT hr = InPlaceDeactivate(); + if (FAILED(hr)) + return hr; + } + if (m_hWnd) { + if (IsWindow(m_hWnd)) + DestroyWindow(m_hWnd); + m_hWnd = 0; + if (m_spClientSite) + m_spClientSite->OnShowWindow(false); + } + + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + + if (m_spAdviseSink) + m_spAdviseSink->OnClose(); + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnClose(); + } + + return S_OK; +} + +bool qax_disable_inplaceframe = true; + +/* + Executes the steps to activate the control. +*/ +HRESULT QAxServerBase::internalActivate() +{ + if (!m_spClientSite) + return S_OK; + if (!m_spInPlaceSite) + m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (void**)&m_spInPlaceSite); + if (!m_spInPlaceSite) + return E_FAIL; + + HRESULT hr = E_FAIL; + if (!isInPlaceActive) { + BOOL bNoRedraw = false; + hr = m_spInPlaceSite->CanInPlaceActivate(); + if (FAILED(hr)) + return hr; + if (hr != S_OK) + return E_FAIL; + m_spInPlaceSite->OnInPlaceActivate(); + } + + isInPlaceActive = true; + OnAmbientPropertyChange(DISPID_AMBIENT_USERMODE); + + if (isWidget) { + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + HWND hwndParent; + if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) { + // get location in the parent window, as well as some information about the parent + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (m_hWnd) { + ::ShowWindow(m_hWnd, SW_SHOW); + if (!::IsChild(m_hWnd, ::GetFocus()) && qt.widget->focusPolicy() != Qt::NoFocus) + ::SetFocus(m_hWnd); + } else { + create(hwndParent, rcPos); + } + } + + // Gone active by now, take care of UIACTIVATE + canTakeFocus = qt.widget->focusPolicy() != Qt::NoFocus && !inDesignMode; + if (!canTakeFocus && !inDesignMode) { + QList<QWidget*> widgets = qFindChildren<QWidget*>(qt.widget); + for (int w = 0; w < widgets.count(); ++w) { + QWidget *widget = widgets[w]; + canTakeFocus = widget->focusPolicy() != Qt::NoFocus; + if (canTakeFocus) + break; + } + } + if (!isUIActive && canTakeFocus) { + isUIActive = true; + hr = m_spInPlaceSite->OnUIActivate(); + if (FAILED(hr)) { + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); + return hr; + } + + if (isInPlaceActive) { + if (!::IsChild(m_hWnd, ::GetFocus())) + ::SetFocus(m_hWnd); + } + + if (m_spInPlaceFrame) { + hr = m_spInPlaceFrame->SetActiveObject(this, QStringToBSTR(class_name)); + if (!FAILED(hr)) { + menuBar = (qt.widget && !qax_disable_inplaceframe) ? qFindChild<QMenuBar*>(qt.widget) : 0; + if (menuBar && !menuBar->isVisible()) { + createMenu(menuBar); + menuBar->hide(); + menuBar->installEventFilter(this); + } + statusBar = qt.widget ? qFindChild<QStatusBar*>(qt.widget) : 0; + if (statusBar && !statusBar->isVisible()) { + const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)"); + QMetaObject::connect(statusBar, index, this, -1); + statusBar->hide(); + statusBar->installEventFilter(this); + } + } + } + if (spInPlaceUIWindow) { + spInPlaceUIWindow->SetActiveObject(this, QStringToBSTR(class_name)); + spInPlaceUIWindow->SetBorderSpace(0); + } + } + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); + ShowWindow(m_hWnd, SW_NORMAL); + } + + m_spClientSite->ShowObject(); + + return S_OK; +} + +/* + Executes the "verb" \a iVerb. +*/ +HRESULT WINAPI QAxServerBase::DoVerb(LONG iVerb, LPMSG /*lpmsg*/, IOleClientSite* /*pActiveSite*/, LONG /*lindex*/, + HWND /*hwndParent*/, LPCRECT /*prcPosRect*/) +{ + HRESULT hr = E_NOTIMPL; + switch (iVerb) + { + case OLEIVERB_SHOW: + hr = internalActivate(); + if (SUCCEEDED(hr)) + hr = S_OK; + break; + + case OLEIVERB_PRIMARY: + case OLEIVERB_INPLACEACTIVATE: + hr = internalActivate(); + if (SUCCEEDED(hr)) { + hr = S_OK; + update(); + } + break; + + case OLEIVERB_UIACTIVATE: + if (!isUIActive) { + hr = internalActivate(); + if (SUCCEEDED(hr)) + hr = S_OK; + } + break; + + case OLEIVERB_HIDE: + UIDeactivate(); + if (m_hWnd) + ::ShowWindow(m_hWnd, SW_HIDE); + hr = S_OK; + return hr; + + default: + break; + } + return hr; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumAdvise(IEnumSTATDATA** /*ppenumAdvise*/) +{ + return E_NOTIMPL; +} + +/* + Returns an enumerator for the verbs registered for this class. +*/ +HRESULT WINAPI QAxServerBase::EnumVerbs(IEnumOLEVERB** ppEnumOleVerb) +{ + if (!ppEnumOleVerb) + return E_POINTER; + return OleRegEnumVerbs(qAxFactory()->classID(class_name), ppEnumOleVerb); +} + +/* + Returns the current client site.. +*/ +HRESULT WINAPI QAxServerBase::GetClientSite(IOleClientSite** ppClientSite) +{ + if (!ppClientSite) + return E_POINTER; + *ppClientSite = m_spClientSite; + if (*ppClientSite) + (*ppClientSite)->AddRef(); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetClipboardData(DWORD, IDataObject**) +{ + return E_NOTIMPL; +} + +/* + Returns the current extent. +*/ +HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwDrawAspect, SIZEL* psizel) +{ + if (dwDrawAspect != DVASPECT_CONTENT || !isWidget || !qt.widget) + return E_FAIL; + if (!psizel) + return E_POINTER; + + psizel->cx = MAP_PIX_TO_LOGHIM(m_currentExtent.width(), qt.widget->logicalDpiX()); + psizel->cy = MAP_PIX_TO_LOGHIM(m_currentExtent.height(), qt.widget->logicalDpiY()); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetMoniker(DWORD, DWORD, IMoniker** ) +{ + return E_NOTIMPL; +} + +/* + Returns the CLSID of this class. +*/ +HRESULT WINAPI QAxServerBase::GetUserClassID(CLSID* pClsid) +{ + if (!pClsid) + return E_POINTER; + *pClsid = qAxFactory()->classID(class_name); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::InitFromData(IDataObject*, BOOL, DWORD) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::IsUpToDate() +{ + return S_OK; +} + +/* + Stores the client site. +*/ +HRESULT WINAPI QAxServerBase::SetClientSite(IOleClientSite* pClientSite) +{ + // release all client site interfaces + if (m_spClientSite) m_spClientSite->Release(); + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + + m_spClientSite = pClientSite; + if (m_spClientSite) { + m_spClientSite->AddRef(); + m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (void **)&m_spInPlaceSite); + } + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetColorScheme(LOGPALETTE*) +{ + return E_NOTIMPL; +} + + +#ifdef QT_DLL // avoid conflict with symbol in static lib +bool qt_sendSpontaneousEvent(QObject *o, QEvent *e) +{ + return QCoreApplication::sendSpontaneousEvent(o, e); +} +#endif + +/* + Tries to set the size of the control. +*/ +HRESULT WINAPI QAxServerBase::SetExtent(DWORD dwDrawAspect, SIZEL* psizel) +{ + if (dwDrawAspect != DVASPECT_CONTENT) + return DV_E_DVASPECT; + if (!psizel) + return E_POINTER; + + if (!isWidget || !qt.widget) // nothing to do + return S_OK; + + QSize proposedSize(MAP_LOGHIM_TO_PIX(psizel->cx, qt.widget->logicalDpiX()), + MAP_LOGHIM_TO_PIX(psizel->cy, qt.widget->logicalDpiY())); + + // can the widget be resized at all? + if (qt.widget->minimumSize() == qt.widget->maximumSize() && qt.widget->minimumSize() != proposedSize) + return E_FAIL; + //Save the extent, bound to the widget restrictions. + m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), proposedSize.width(), qt.widget->maximumWidth()); + m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), proposedSize.height(), qt.widget->maximumHeight()); + + resize(proposedSize); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetHostNames(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj) +{ + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetMoniker(DWORD, IMoniker*) +{ + return E_NOTIMPL; +} + +/* + Disconnects an advise sink. +*/ +HRESULT WINAPI QAxServerBase::Unadvise(DWORD dwConnection) +{ + for (int i = 0; i < adviseSinks.count(); ++i) { + STATDATA entry = adviseSinks.at(i); + if (entry.dwConnection == dwConnection) { + entry.pAdvSink->Release(); + adviseSinks.removeAt(i); + return S_OK; + } + } + return OLE_E_NOCONNECTION; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Update() +{ + return S_OK; +} + +//**** IDataObject +/* + Calls IViewObject::Draw after setting up the parameters. +*/ +HRESULT WINAPI QAxServerBase::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) +{ + if (!pmedium) + return E_POINTER; + if ((pformatetcIn->tymed & TYMED_MFPICT) == 0) + return DATA_E_FORMATETC; + + internalCreate(); + if (!isWidget || !qt.widget) + return E_UNEXPECTED; + + // Container wants to draw, but the size is not defined yet - ask container + if (m_spInPlaceSite && !qt.widget->testAttribute(Qt::WA_Resized)) { + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + + HRESULT hres = m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (hres == S_OK) { + QSize size(rcPos.right - rcPos.left, rcPos.bottom - rcPos.top); + resize(size); + } else { + qt.widget->adjustSize(); + } + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); // no need for it + } + + int width = qt.widget->width(); + int height = qt.widget->height(); + RECTL rectl = {0, 0, width, height}; + + HDC hdc = CreateMetaFile(0); + SaveDC(hdc); + SetWindowOrgEx(hdc, 0, 0, 0); + SetWindowExtEx(hdc, rectl.right, rectl.bottom, 0); + + Draw(pformatetcIn->dwAspect, pformatetcIn->lindex, 0, pformatetcIn->ptd, 0, hdc, &rectl, &rectl, 0, 0); + + RestoreDC(hdc, -1); + HMETAFILE hMF = CloseMetaFile(hdc); + if (!hMF) + return E_UNEXPECTED; + + HGLOBAL hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, sizeof(METAFILEPICT)); + if (!hMem) { + DeleteMetaFile(hMF); + return ResultFromScode(STG_E_MEDIUMFULL); + } + + LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMem); + pMF->hMF = hMF; + pMF->mm = MM_ANISOTROPIC; + pMF->xExt = MAP_PIX_TO_LOGHIM(width, qt.widget->logicalDpiX()); + pMF->yExt = MAP_PIX_TO_LOGHIM(height, qt.widget->logicalDpiY()); + GlobalUnlock(hMem); + + memset(pmedium, 0, sizeof(STGMEDIUM)); + pmedium->tymed = TYMED_MFPICT; + pmedium->hGlobal = hMem; + pmedium->pUnkForRelease = 0; + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::DAdvise(FORMATETC *pformatetc, DWORD advf, + IAdviseSink *pAdvSink, DWORD *pdwConnection) +{ + if (pformatetc->dwAspect != DVASPECT_CONTENT) + return E_FAIL; + + *pdwConnection = adviseSinks.count() + 1; + STATDATA data = { + {pformatetc->cfFormat,pformatetc->ptd,pformatetc->dwAspect,pformatetc->lindex,pformatetc->tymed}, + advf, pAdvSink, *pdwConnection + }; + adviseSinks.append(data); + pAdvSink->AddRef(); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::DUnadvise(DWORD dwConnection) +{ + return Unadvise(dwConnection); +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumDAdvise(IEnumSTATDATA ** /*ppenumAdvise*/) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetDataHere(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::QueryGetData(FORMATETC* /* pformatetc */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetCanonicalFormatEtc(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetData(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumFormatEtc(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */) +{ + return E_NOTIMPL; +} + + + +static int mapModifiers(int state) +{ + int ole = 0; + if (state & Qt::ShiftModifier) + ole |= 1; + if (state & Qt::ControlModifier) + ole |= 2; + if (state & Qt::AltModifier) + ole |= 4; + + return ole; +} + +/* + \reimp +*/ +bool QAxServerBase::eventFilter(QObject *o, QEvent *e) +{ + if (!theObject) + return QObject::eventFilter(o, e); + + if ((e->type() == QEvent::Show || e->type() == QEvent::Hide) && (o == statusBar || o == menuBar)) { + if (o == menuBar) { + if (e->type() == QEvent::Hide) { + createMenu(menuBar); + } else if (e->type() == QEvent::Show) { + removeMenu(); + } + } else if (statusBar) { + statusBar->setSizeGripEnabled(false); + } + updateGeometry(); + if (m_spInPlaceSite && qt.widget->sizeHint().isValid()) { + RECT rect = {0, 0, qt.widget->sizeHint().width(), qt.widget->sizeHint().height()}; + m_spInPlaceSite->OnPosRectChange(&rect); + } + } + switch (e->type()) { + case QEvent::ChildAdded: + static_cast<QChildEvent*>(e)->child()->installEventFilter(this); + break; + case QEvent::ChildRemoved: + static_cast<QChildEvent*>(e)->child()->removeEventFilter(this); + break; + case QEvent::KeyPress: + if (o == qt.object && hasStockEvents) { + QKeyEvent *ke = (QKeyEvent*)e; + int key = ke->key(); + int state = ke->modifiers(); + void *argv[] = { + 0, + &key, + &state + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYDOWN, argv); + if (!ke->text().isEmpty()) + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYPRESS, argv); + } + break; + case QEvent::KeyRelease: + if (o == qt.object && hasStockEvents) { + QKeyEvent *ke = (QKeyEvent*)e; + int key = ke->key(); + int state = ke->modifiers(); + void *argv[] = { + 0, + &key, + &state + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYUP, argv); + } + break; + case QEvent::MouseMove: + if (o == qt.object && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->buttons() & Qt::MouseButtonMask; + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEMOVE, argv); + } + break; + case QEvent::MouseButtonRelease: + if (o == qt.object && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->button(); + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEUP, argv); + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_CLICK, 0); + } + break; + case QEvent::MouseButtonDblClick: + if (o == qt.object && hasStockEvents) { + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_DBLCLICK, 0); + } + break; + case QEvent::MouseButtonPress: + if (m_spInPlaceSite && !isUIActive) { + internalActivate(); + } + if (o == qt.widget && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->button(); + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEDOWN, argv); + } + break; + case QEvent::Show: + if (m_hWnd && o == qt.widget) + ShowWindow(m_hWnd, SW_SHOW); + updateMask(); + break; + case QEvent::Hide: + if (m_hWnd && o == qt.widget) + ShowWindow(m_hWnd, SW_HIDE); + break; + + case QEvent::EnabledChange: + if (m_hWnd && o == qt.widget) + EnableWindow(m_hWnd, qt.widget->isEnabled()); + // Fall Through + case QEvent::FontChange: + case QEvent::ActivationChange: + case QEvent::StyleChange: + case QEvent::IconTextChange: + case QEvent::ModifiedChange: + case QEvent::Resize: + updateMask(); + break; + case QEvent::WindowBlocked: + if (!m_spInPlaceFrame) + break; + m_spInPlaceFrame->EnableModeless(FALSE); + break; + case QEvent::WindowUnblocked: + if (!m_spInPlaceFrame) + break; + m_spInPlaceFrame->EnableModeless(TRUE); + break; + default: + break; + } + return QObject::eventFilter(o, e); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxserverdll.cpp b/src/activeqt/control/qaxserverdll.cpp new file mode 100644 index 0000000..1263863 --- /dev/null +++ b/src/activeqt/control/qaxserverdll.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qapplication.h> +#include <qwidget.h> + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +bool qax_ownQApp = false; +HHOOK qax_hhook = 0; + +// in qaxserver.cpp +extern char qAxModuleFilename[MAX_PATH]; +extern bool qAxIsServer; +extern ITypeLib *qAxTypeLibrary; +extern unsigned long qAxLockCount(); +extern QString qAxInit(); +extern void qAxCleanup(); +extern HANDLE qAxInstance; +static uint qAxThreadId = 0; + +extern HRESULT UpdateRegistry(int bRegister); +extern HRESULT GetClassObject(const GUID &clsid, const GUID &iid, void **ppUnk); + +STDAPI DllRegisterServer() +{ + return UpdateRegistry(true); +} + +STDAPI DllUnregisterServer() +{ + return UpdateRegistry(false); +} + +STDAPI DllGetClassObject(const GUID &clsid, const GUID &iid, void** ppv) +{ + if (!qAxThreadId) + qAxThreadId = GetCurrentThreadId(); + else if (GetCurrentThreadId() != qAxThreadId) + return E_FAIL; + + GetClassObject(clsid, iid, ppv); + if (!*ppv) + return CLASS_E_CLASSNOTAVAILABLE; + return S_OK; +} + +STDAPI DllCanUnloadNow() +{ + if (GetCurrentThreadId() != qAxThreadId) + return S_FALSE; + if (qAxLockCount()) + return S_FALSE; + if (!qax_ownQApp) + return S_OK; + + // check if qApp still runs widgets (in other DLLs) + QWidgetList widgets = qApp->allWidgets(); + int count = widgets.count(); + for (int w = 0; w < widgets.count(); ++w) { + // remove all Qt generated widgets + QWidget *widget = widgets.at(w); + if (widget->windowType() == Qt::Desktop || widget->objectName() == QLatin1String("Qt internal tablet widget")) + count--; + } + if (count) + return S_FALSE; + + // no widgets left - destroy qApp + if (qax_hhook) + UnhookWindowsHookEx(qax_hhook); + + delete qApp; + qax_ownQApp = false; + + // never allow unloading - safety net for Internet Explorer + return S_FALSE; +} + + +EXTERN_C BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) +{ + GetModuleFileNameA(hInstance, qAxModuleFilename, MAX_PATH-1); + qAxInstance = hInstance; + qAxIsServer = true; + + if (dwReason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls(hInstance); + qAxInit(); + } else if (dwReason == DLL_PROCESS_DETACH) { + qAxCleanup(); + } + + return true; +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/control/qaxservermain.cpp b/src/activeqt/control/qaxservermain.cpp new file mode 100644 index 0000000..a50fef0 --- /dev/null +++ b/src/activeqt/control/qaxservermain.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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$ +** +****************************************************************************/ + +#include <qstringlist.h> +#include <qvector.h> + +#include "qaxfactory.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +static DWORD *classRegistration = 0; +static DWORD dwThreadID; +static bool qAxActivity = false; +static HANDLE hEventShutdown; + +#ifdef QT_DEBUG +QT_STATIC_CONST DWORD dwTimeOut = 1000; +QT_STATIC_CONST DWORD dwPause = 500; +#else +QT_STATIC_CONST DWORD dwTimeOut = 5000; // time for EXE to be idle before shutting down +QT_STATIC_CONST DWORD dwPause = 1000; // time to wait for threads to finish up +#endif + +extern HANDLE hEventShutdown; +extern bool qAxActivity; +extern HANDLE qAxInstance; +extern bool qAxIsServer; +extern bool qAxOutProcServer; +extern char qAxModuleFilename[MAX_PATH]; +extern QString qAxInit(); +extern void qAxCleanup(); +extern HRESULT UpdateRegistry(BOOL bRegister); +extern HRESULT GetClassObject(const GUID &clsid, const GUID &iid, void **ppUnk); +extern ulong qAxLockCount(); +extern bool qax_winEventFilter(void *message); + +#if defined(Q_CC_BOR) +extern "C" __stdcall HRESULT DumpIDL(const QString &outfile, const QString &ver); +#else +STDAPI DumpIDL(const QString &outfile, const QString &ver); +#endif + +// Monitors the shutdown event +static DWORD WINAPI MonitorProc(void* pv) +{ + while (1) { + WaitForSingleObject(hEventShutdown, INFINITE); + DWORD dwWait=0; + do { + qAxActivity = false; + dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut); + } while (dwWait == WAIT_OBJECT_0); + // timed out + if (!qAxActivity && !qAxLockCount()) // if no activity let's really bail + break; + } + CloseHandle(hEventShutdown); + PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); + PostQuitMessage(0); + + return 0; +} + +// Starts the monitoring thread +static bool StartMonitor() +{ + dwThreadID = GetCurrentThreadId(); + hEventShutdown = CreateEventA(0, false, false, 0); + if (hEventShutdown == 0) + return false; + DWORD dwThreadID; + HANDLE h = CreateThread(0, 0, MonitorProc, 0, 0, &dwThreadID); + return (h != NULL); +} + +void qax_shutDown() +{ + qAxActivity = true; + if (hEventShutdown) + SetEvent(hEventShutdown); // tell monitor that we transitioned to zero +} + +/* + Start the COM server (if necessary). +*/ +bool qax_startServer(QAxFactory::ServerType type) +{ + if (qAxIsServer) + return true; + + const QStringList keys = qAxFactory()->featureList(); + if (!keys.count()) + return false; + + if (!qAxFactory()->isService()) + StartMonitor(); + + classRegistration = new DWORD[keys.count()]; + int object = 0; + for (QStringList::ConstIterator key = keys.begin(); key != keys.end(); ++key, ++object) { + IUnknown* p = 0; + CLSID clsid = qAxFactory()->classID(*key); + + // Create a QClassFactory (implemented in qaxserverbase.cpp) + HRESULT hRes = GetClassObject(clsid, IID_IClassFactory, (void**)&p); + if (SUCCEEDED(hRes)) + hRes = CoRegisterClassObject(clsid, p, CLSCTX_LOCAL_SERVER, + type == QAxFactory::MultipleInstances ? REGCLS_MULTIPLEUSE : REGCLS_SINGLEUSE, + classRegistration+object); + if (p) + p->Release(); + } + + qAxIsServer = true; + return true; +} + +/* + Stop the COM server (if necessary). +*/ +bool qax_stopServer() +{ + if (!qAxIsServer || !classRegistration) + return true; + + qAxIsServer = false; + + const QStringList keys = qAxFactory()->featureList(); + int object = 0; + for (QStringList::ConstIterator key = keys.begin(); key != keys.end(); ++key, ++object) + CoRevokeClassObject(classRegistration[object]); + + delete []classRegistration; + classRegistration = 0; + + Sleep(dwPause); //wait for any threads to finish + + return true; +} + +#if defined(Q_OS_WINCE) +extern void __cdecl qWinMain(HINSTANCE, HINSTANCE, LPSTR, int, int &, QVector<char *> &); +#else +extern void qWinMain(HINSTANCE, HINSTANCE, LPSTR, int, int &, QVector<char *> &); +#endif + +QT_END_NAMESPACE + +#if defined(QT_NEEDS_QMAIN) +int qMain(int, char **); +#define main qMain +#else +#if defined(Q_OS_WINCE) +extern "C" int __cdecl main(int, char **); +#else +extern "C" int main(int, char **); +#endif +#endif + + +EXTERN_C int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR, int nShowCmd) +{ + QT_USE_NAMESPACE + + qAxOutProcServer = true; + GetModuleFileNameA(0, qAxModuleFilename, MAX_PATH-1); + qAxInstance = hInstance; + + QByteArray cmdParam; + QT_WA({ + LPTSTR cmdline = GetCommandLineW(); + cmdParam = QString::fromUtf16((const ushort *)cmdline).toLocal8Bit(); + }, { + cmdParam = GetCommandLineA(); + }); + + QList<QByteArray> cmds = cmdParam.split(' '); + QByteArray unprocessed; + + int nRet = 0; + bool run = true; + bool runServer = false; + for (int i = 0; i < cmds.count(); ++i) { + QByteArray cmd = cmds.at(i).toLower(); + if (cmd == "-activex" || cmd == "/activex" || cmd == "-embedding" || cmd == "/embedding") { + runServer = true; + } else if (cmd == "-unregserver" || cmd == "/unregserver") { + nRet = UpdateRegistry(false); + run = false; + break; + } else if (cmd == "-regserver" || cmd == "/regserver") { + nRet = UpdateRegistry(true); + run = false; + break; + } else if (cmd == "-dumpidl" || cmd == "/dumpidl") { + ++i; + if (i < cmds.count()) { + QByteArray outfile = cmds.at(i); + ++i; + QByteArray version; + if (i < cmds.count() && (cmds.at(i) == "-version" || cmds.at(i) == "/version")) { + ++i; + if (i < cmds.count()) + version = cmds.at(i); + else + version = "1.0"; + } + + nRet = DumpIDL(QString::fromLatin1(outfile.constData()), QString::fromLatin1(version.constData())); + } else { + qWarning("Wrong commandline syntax: <app> -dumpidl <idl file> [-version <x.y.z>]"); + } + run = false; + break; + } else { + unprocessed += cmds.at(i) + " "; + } + } + + if (run) { + HRESULT hRes = CoInitialize(0); + + int argc; + QVector<char*> argv(8); + qWinMain(hInstance, hPrevInstance, unprocessed.data(), nShowCmd, argc, argv); + qAxInit(); + if (runServer) + QAxFactory::startServer(); + nRet = ::main(argc, argv.data()); + QAxFactory::stopServer(); + qAxCleanup(); + CoUninitialize(); + + } + + return nRet; +} + +#endif // QT_NO_WIN_ACTIVEQT |