diff options
author | Timothy Pearson <[email protected]> | 2011-07-10 15:24:15 -0500 |
---|---|---|
committer | Timothy Pearson <[email protected]> | 2011-07-10 15:24:15 -0500 |
commit | bd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch) | |
tree | 7a520322212d48ebcb9fbe1087e7fca28b76185c /src/tools/qcomponentfactory.cpp | |
download | qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip |
Add Qt3 development HEAD version
Diffstat (limited to 'src/tools/qcomponentfactory.cpp')
-rw-r--r-- | src/tools/qcomponentfactory.cpp | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/src/tools/qcomponentfactory.cpp b/src/tools/qcomponentfactory.cpp new file mode 100644 index 0000000..d47c174 --- /dev/null +++ b/src/tools/qcomponentfactory.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Implementation of the QComponentFactory class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the tools module of the Qt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.QPL +** included in the packaging of this file. Licensees holding valid Qt +** Commercial licenses may use this file in accordance with the Qt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qcomponentfactory_p.h" + +#ifndef QT_NO_COMPONENT +#include "qsettings.h" +#include <private/qcomlibrary_p.h> +#include "qdir.h" +#include "qapplication.h" + +/*! + \class QComponentFactory qcomponentfactory.h + \brief The QComponentFactory class provides static functions to create and register components. + + \internal + + The static convenience functions can be used both by applications to instantiate components, + and by component servers to register components. + + The createInstance() function provides a pointer to an interface implemented in a specific + component if the component requested has been installed properly and implements the interface. + + Use registerServer() to load a component server and register its components, and unregisterServer() + to unregister the components. The component exported by the component server has to implement the + QComponentRegistrationInterface. + + The static functions registerComponent() and unregisterComponent() register and unregister a single + component in the system component registry, and should be used when implementing the + \link QComponentRegistrationInterface::registerComponents() registerCompontents() \endlink and + \link QComponentRegistrationInterface::unregisterComponents() unregisterCompontents() \endlink functions + in the QComponentRegistrationInterface. + + A component is registered using a UUID, but can additionally be registered with a name, version and + description. A component registered with a name and a version can be instantiated by client applications + using the name and specific version number, or the highest available version number for that component by + just using the name. A component that is registered calling + + \code + QComponentFactory::registerComponent( QUuid(...), filename, "MyProgram.Component", 1 ); + \endcode + + can be instantiated calling either: + + \code + QComponentFactory::createInstance( QUuid(...), IID_XYZ, (QUnknownInterface**)&iface ); + \endcode + or + \code + QComponentFactory::createInstance( "MyProgram.Component", IID_XYZ, (QUnknownInterface**)&iface ); + \endcode + or + \code + QComponentFactory::createInstance( "MyProgram.Component.1", IID_XYZ, (QUnknownInterface**)&iface ); + \endcode + + The first and the last way will always instantiate exactly the component registered above, while + the second call might also return a later version of the same component. This allows smoother upgrading + of components, and is easier to use in application source code, but should only be used when new versions + of the component are guaranteed to work with the application. + + The component name can be anything, but should be unique on the system the component is being + installed on. A common naming convention for components is \e application.component. + + \sa QComponentRegistrationInterface QComponentFactoryInterface +*/ + + +static QPtrList<QComLibrary> *libraries = 0; + +static void cleanup() +{ + delete libraries; + libraries = 0; +} + +static QPtrList<QComLibrary> *liblist() +{ + if ( !libraries ) { + libraries = new QPtrList<QComLibrary>(); + libraries->setAutoDelete( TRUE ); + qAddPostRoutine( cleanup ); + } + return libraries; +} + +/*! + Searches for the component identifier \a cid in the system component registry, + loads the corresponding component server and queries for the interface \a iid. + \a iface is set to the resulting interface pointer. \a cid can either be the + UUID or the name of the component. + + The parameter \a outer is a pointer to the outer interface used + for containment and aggregation and is propagated to the \link + QComponentFactoryInterface::createInstance() createInstance() \endlink + implementation of the QComponentFactoryInterface in the component server if + provided. + + The function returns QS_OK if the interface was successfully instantiated, QE_NOINTERFACE if + the component does not provide an interface \a iid, or QE_NOCOMPONENT if there was + an error loading the component. + + Example: + \code + QInterfacePtr<MyInterface> iface; + if ( QComponentFactory::createInstance( IID_MyInterface, CID_MyComponent, (QUnknownInterface**)&iface ) == QS_OK ) + iface->doSomething(); + ... + } + \endcode +*/ +QRESULT QComponentFactory::createInstance( const QString &cid, const QUuid &iid, QUnknownInterface** iface, QUnknownInterface *outer ) +{ + QSettings settings; + settings.insertSearchPath( QSettings::Windows, "/Classes" ); + bool ok = FALSE; + QString cidStr = cid; + QRESULT res = QE_NOCOMPONENT; + + QUuid uuid( cidStr ); // try to parse, and resolve CLSID if necessary + if ( uuid.isNull() ) { + uuid = settings.readEntry( "/" + cid + "/CLSID/Default", QString::null, &ok ); + cidStr = uuid.toString().upper(); + } + + if ( cidStr.isEmpty() ) + return res; + + QString file = settings.readEntry( "/CLSID/" + cidStr + "/InprocServer32/Default", QString::null, &ok ); + if ( !ok ) + return res; + + QComLibrary *library = new QComLibrary( file ); + library->setAutoUnload( FALSE ); + + QComponentFactoryInterface *cfIface =0; + library->queryInterface( IID_QComponentFactory, (QUnknownInterface**)&cfIface ); + + if ( cfIface ) { + res = cfIface->createInstance( uuid, iid, iface, outer ); + cfIface->release(); + } else { + res = library->queryInterface( iid, iface ); + } + QLibraryInterface *libiface = 0; + if ( library->queryInterface( IID_QLibrary, (QUnknownInterface**)&libiface ) != QS_OK || !qApp ) { + delete library; // only deletes the object, thanks to QLibrary::Manual + } else { + libiface->release(); + library->setAutoUnload( TRUE ); + liblist()->prepend( library ); + } + return res; +} + +/*! + Loads the shared library \a filename and queries for a + QComponentRegistrationInterface. If the library implements this interface, + the \link QComponentRegistrationInterface::registerComponents() + registerComponents() \endlink function is called. + + Returns TRUE if the interface is found and successfully called, + otherwise returns FALSE. +*/ +QRESULT QComponentFactory::registerServer( const QString &filename ) +{ + QComLibrary lib( filename ); + lib.load(); + QComponentRegistrationInterface *iface = 0; + QRESULT res = lib.queryInterface( IID_QComponentRegistration, (QUnknownInterface**)&iface ); + if ( res != QS_OK ) + return res; + QDir dir( filename ); + bool ok = iface->registerComponents( dir.absPath() ); + iface->release(); + return ok ? QS_OK : QS_FALSE; +} + +/*! + Loads the shared library \a filename and queries for a + QComponentRegistrationInterface. If the library implements this interface, + the \link QComponentRegistrationInterface::unregisterComponents() + unregisterComponents() \endlink function is called. + + Returns TRUE if the interface is found and successfully unregistered, + otherwise returns FALSE. +*/ +QRESULT QComponentFactory::unregisterServer( const QString &filename ) +{ + QComLibrary lib( filename ); + lib.load(); + QComponentRegistrationInterface *iface = 0; + QRESULT res = lib.queryInterface( IID_QComponentRegistration, (QUnknownInterface**)&iface ); + if ( res != QS_OK ) + return res; + bool ok = iface->unregisterComponents(); + iface->release(); + return ok ? QS_OK : QS_FALSE; +} + +/*! + Registers the component with id \a cid in the system component registry and + returns TRUE if the component was registerd successfully, otherwise returns + FALSE. The component is provided by the component server at \a filepath and + registered with an optional \a name, \a version and \a description. + + This function does nothing and returns FALSE if a component with an identical + \a cid does already exist on the system. + + A component that has been registered with a \a name can be created using both the + \a cid and the \a name value using createInstance(). + + Call this function for each component in an implementation of + \link QComponentRegistrationInterface::registerComponents() registerComponents() \endlink. + + \sa unregisterComponent(), registerServer(), createInstance() +*/ +bool QComponentFactory::registerComponent( const QUuid &cid, const QString &filepath, const QString &name, int version, const QString &description ) +{ + bool ok = FALSE; + QSettings settings; + settings.insertSearchPath( QSettings::Windows, "/Classes" ); + + QString cidStr = cid.toString().upper(); + settings.readEntry( "/CLSID/" + cidStr + "/InprocServer32/Default", QString::null, &ok ); + if ( ok ) // don't overwrite existing component + return FALSE; + + ok = settings.writeEntry( "/CLSID/" + cidStr + "/InprocServer32/Default", filepath ); + if ( ok && !!description ) + settings.writeEntry( "/CLSID/" + cidStr + "/Default", description ); + + // register the human readable part + if ( ok && !!name ) { + QString vName = version ? name + "." + QString::number( version ) : name; + settings.writeEntry( "/CLSID/" + cidStr + "/ProgID/Default", vName ); + ok = settings.writeEntry( "/" + vName + "/CLSID/Default", cidStr ); + if ( ok && !!description ) + settings.writeEntry( "/" + vName + "/Default", description ); + + if ( ok && version ) { + settings.writeEntry( "/CLSID/" + cidStr + "/VersionIndependentProgID/Default", name ); + QString curVer = settings.readEntry( "/" + name + "/CurVer/Default" ); + if ( !curVer || curVer < vName ) { // no previous, or a lesser version installed + settings.writeEntry( "/" + name + "/CurVer/Default", vName ); + ok = settings.writeEntry( "/" + name + "/CLSID/Default", cidStr ); + if ( ok && !!description ) + settings.writeEntry( "/" + name + "/Default", description ); + } + } + } + + return ok; +} + +/*! + Unregisters the component with id \a cid from the system component registry and returns + TRUE if the component was unregistered successfully, otherwise returns FALSE. + + Call this function for each component in an implementation of + \link QComponentRegistrationInterface::unregisterComponents() unregisterComponents() \endlink. + + \sa registerComponent(), unregisterServer() +*/ +bool QComponentFactory::unregisterComponent( const QUuid &cid ) +{ + QSettings settings; + bool ok = FALSE; + settings.insertSearchPath( QSettings::Windows, "/Classes" ); + + QString cidStr = cid.toString().upper(); + if ( cidStr.isEmpty() ) + return FALSE; + + // unregister the human readable part + QString vName = settings.readEntry( "/CLSID/" + cidStr + "/ProgID/Default", QString::null, &ok ); + if ( ok ) { + QString name = settings.readEntry( "/CLSID/" + cidStr + "/VersionIndependentProgID/Default", QString::null ); + if ( !!name && settings.readEntry( "/" + name + "/CurVer/Default" ) == vName ) { + // unregistering the current version -> change CurVer to previous version + QString version = vName.right( vName.length() - name.length() - 1 ); + QString newVerName; + QString newCidStr; + if ( version.find( '.' ) == -1 ) { + int ver = version.toInt(); + // see if a lesser version is installed, and make that the CurVer + while ( ver-- ) { + newVerName = name + "." + QString::number( ver ); + newCidStr = settings.readEntry( "/" + newVerName + "/CLSID/Default" ); + if ( !!newCidStr ) + break; + } + } else { + // oh well... + } + if ( !!newCidStr ) { + settings.writeEntry( "/" + name + "/CurVer/Default", newVerName ); + settings.writeEntry( "/" + name + "/CLSID/Default", newCidStr ); + } else { + settings.removeEntry( "/" + name + "/CurVer/Default" ); + settings.removeEntry( "/" + name + "/CLSID/Default" ); + settings.removeEntry( "/" + name + "/Default" ); + } + } + + settings.removeEntry( "/" + vName + "/CLSID/Default" ); + settings.removeEntry( "/" + vName + "/Default" ); + } + + settings.removeEntry( "/CLSID/" + cidStr + "/VersionIndependentProgID/Default" ); + settings.removeEntry( "/CLSID/" + cidStr + "/ProgID/Default" ); + settings.removeEntry( "/CLSID/" + cidStr + "/InprocServer32/Default" ); + ok = settings.removeEntry( "/CLSID/" + cidStr + "/Default" ); + + return ok; +} + +#endif // QT_NO_COMPONENT |