Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions

The QAxServer Module

Introduction

The QAxServer module provides a static library implementing the functions required to turn a standard Qt binary into an ActiveX control server.

This module is part of the ActiveQt framework. (To incorporate ActiveX controls in a Qt application see the QAxContainer module.)

The module consists of three classes

Some example implementations of ActiveX controls are provided.

Building the library

In the activeqt directory (usually QTDIR/extensions/activeqt) enter the control subdirectory and run qmake to generate the makefile, and use the make tool (nmake for VC++, make for Borland) to build the library. The library qaxserver.lib will be linked into QTDIR/lib.

Using the library

To turn a standard Qt application into an ActiveX server using the QAxServer library you must add activeqt as a CONFIG setting in your .pro file.

An out-of-process executable server is generated from a .pro file like this:

    TEMPLATE = app
    CONFIG  += qt activeqt

    RC_FILE  = qaxserver.rc
    ...
    

To build an in-process server, use a .pro file like this:

    TEMPLATE = lib
    CONFIG  += qt activeqt dll
    
    DEF_FILE = qaxserver.def
    RC_FILE  = qaxserver.rc
    ...
    

The files qaxserver.rc and qaxserver.def are part of the framework and can be used from their usual location (specify a path in the .pro file), or copied into the project directory. You should not modify these files.

The activeqt configuration will cause the qmake tool to add the required build steps to the build system:

See the QAxServer FAQ for a list of common problems when building the ActiveX server.

Out-of-process vs. In-process

Whether your ActiveX server should run as a stand-alone executable or as a shared library in the client process depends mainly on the type of controls you want to provide in the server.

An executable server has the advantage of being able to run as a stand-alone application, but adds considerable overhead to the communication between the ActiveX client and the control. If the control has a programming error only the server process running the control will crash, and the client application will probably continue to run.

An in-process server is usually smaller and has faster startup time. The communication between client and server is done directly through virtual function calls and does not introduce the overhead required for remote procedure calls. But if the server crashes the client application is likely to crash as well.

Both server types can use Qt either as a shared library, or statically linked into the server binary.

Implementing Controls

To implement an ActiveX control with Qt, create a subclass of QWidget or any existing QWidget subclass:

    #include <qwidget.h>

    class MyActiveX : public QWidget
    {
        Q_OBJECT
    

The Q_OBJECT macro is required to provide the meta object information about the widget to the ActiveQt framework. Use the Q_PROPERTY macro to declare properties for the ActiveX control:

        Q_PROPERTY( int value READ value WRITE setValue )
    

Declare a standard QWidget constructor taking a parent widget and a name, and functions, signals and slots like any normal QWidget. (1)

    public:
        MyActiveX( QWidget *parent = 0, const char *name = 0 )
        ...
        
        int value() const;

    public slots:
        void setValue( int );
        ...
       
    signals:
        void valueChange( int );
        ...

    };
    

The ActiveQt framework will expose properties and public slots as ActiveX properties and methods, and signals as ActiveX events, and convert between the Qt data types and the equivalent COM data types.

Data Types

The Qt data types that are supported for properties are:

Qt data type COM property
bool VARIANT_BOOL
QString BSTR
QCString BSTR
int int
uint unsigned int
double double
QColor OLE_COLOR
QDate DATE
QDateTime DATE
QFont IFontDisp*
QPixmap IPictureDisp* (2)
QValueList SAFEARRAY(VARIANT)
QByteArray SAFEARRAY(BYTE)

The Qt data types that are supported for parameters in signals and slots are:

Qt data type COM parameter
bool [in] VARIANT_BOOL
bool& [in, out] VARIANT_BOOL*
QString, const QString& [in] BSTR
QString& [in, out] BSTR*
QCString, const QCString& [in] BSTR
QString& [in, out] BSTR*
int [in] int
int& [in,out] int
uint [in] unsigned int
uint& [in, out] unsigned int*
double [in] double
double& [in, out] double*
QColor, const QColor& [in] OLE_COLOR
QColor& [in, out] OLE_COLOR*
QDate, const QDate& [in] DATE
QDate& [in, out] DATE*
QDateTime, const QDateTime& [in] DATE
QDateTime& [in, out] DATE*
QFont, const QFont& [in] IFontDisp*
QFont& [in, out] IFontDisp**
QPixmap, const QPixmap& [in] IPictureDisp*
QPixmap& [in, out] IPictureDisp**
QValueList<QVariant>, const QValueList<QVariant>& [in] SAFEARRAY(VARIANT)
QValueList<QVariant>& [in, out] SAFEARRAY(VARIANT)*
QByteArray, const QByteArray& [in] SAFEARRAY(BYTE)
QByteArray& [in, out] SAFEARRAY(BYTE)*
QObject* [in] IDispatch*

Also supported are exported enums and sets (see Q_ENUMS and Q_SETS). The in-parameter types are also supported as return values.

Properties and signals/slots that have parameters using any other data types are ignored by the QActiveX framework.

Sub-Objects

COM objects can have multiple sub-objects that can represent a sub element of the COM object. A COM object representing a multi-document spread sheet application can for example provide one sub-object for each spread sheet.

Any QObject subclass can be used as the type for a sub object in ActiveX. The QAxFactory implementation (see below) needs to return the classname of the sub type as one key in the featureList() implementation, as well as the IDs for the COM class, the interface and event interface of that type. Then the type can be used as e.g. the return value or paramter of a slot.

Property Notification

To make the properties bindable for the ActiveX client, use multiple inheritance from the QAxBindable class:

    #include <qwidget.h>
    #include <qaxbindable.h>

    class MyActiveX : public QWidget, public QAxBindable
    {
        Q_OBJECT
    
When implementing the property write functions, use the QAxBindable class's requestPropertyChange() and propertyChanged() functions to allow ActiveX clients to bind to the control properties. (3)

Serving Controls

To make an ActiveX control available to the COM system it must be registered in the system registry using five unique identifiers. These identifiers are provided by tools like guidgen or uuidgen. The registration information allows COM to localize the binary providing a requested ActiveX control, marshall remote procedure calls to the control and read type information about the methods and properties exposed by the control.

To create the ActiveX control when the client asks for it the server must export an implementation of a QAxFactory. Use the default factory when the server provides only a single ActiveX control, and implement a subclass of QAxFactory to provide multiple ActiveX controls. The default factory is available through a macro that takes the identifiers COM requires to locate the ActiveX control on the target system:

    QAXFACTORY_DEFAULT ( MyActiveX,
                "{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
                "{87a5b65e-7fa9-4dc6-a176-47295988dcbd}",
                "{a6130ae9-8327-47ec-815b-d0b45a0d6e5e}",
                "{26c4e136-4e23-4347-af37-faf933b027e9}",
                "{a8f21901-7ff7-4f6a-b939-789620c03d83}" )
    

The QAxFactory class documentation explains how to use this macro, and how to implement and use custom factories.

For out-of-process executable servers, implement a main() function, instantiate a QApplication object, and enter the event loop just like any normal Qt application. By default the application will start as a standard Qt application, but if you pass -activex on the command line it will start as an ActiveX server. Use QAxFactory::isServer() to create and run a standard application interface, or to prevent a stand-alone execution:

    #include <qapplication.h>
    #include <qaxfactory.h>

    int main( int argc, char **argv )
    {
        QApplication app( argc, argv );
        if ( !QAxFactory::isServer() ) {
            // create and show main window...
        }
        return app.exec();
    }
    
In-process servers must also implement main(); but it is never called so a dummy implementation is sufficient.

    int main( int , char ** )
    {
        return 0;
    }
    

To build the ActiveX server executable run qmake to generate the makefile, and use your compiler's make tool as for any other Qt application. The make process will also register the controls in the system registry by calling the resulting executable with the -regserver command line option. See the Typical build errors list for information on problems that can occur when building the ActiveQt server executable.

If the ActiveX server is an executable, the following command line options are supported:

Option Result
-regserver Registers the server in the system registry
-unregserver Unregisters the server from the system registry
-activex Starts the application as an ActiveX server
-dumpidl <file> Writes the server's IDL to the specified file

In-process servers can be registered using the regsvr32 tool available on all Windows systems.

Using the Controls

To use the ActiveX controls, e.g. to embed them in a web page, use the <object> HTML tag.

    <object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f">
       ...
    <\object>
    

To initialize the control's properties, use

    <object ID=...>
        <param name="name" value="value">
    <\object>
    

If the web browser supports scripting use JavaScript, VBScript and forms to script the control. The examples include demonstration HTML pages for the example controls.

Enhanced features

More Interfaces

The ActiveX control provide by ActiveQt servers supports a minimal set of COM interfaces to implement the OLE specifications. When the ActiveX class inherits from the QAxBindable class it can also implement additional COM interfaces.

Create a new subclass of QAxAggregated and use multiple inheritance to subclass additional COM interface classes.

    class AxImpl : public QAxAggregated, public ISomeCOMInterface
    {
    public:
        AxImpl() {}

        long queryInterface( const QUuid &iid, void **iface )
        {
            *iface = 0;     
            if ( iid == IID_ISomeCOMInterface )
                *iface = (ISomeCOMInterface*)this;
            else
                return E_NOINTERFACE;

            AddRef();
            return S_OK;
        }
        
        // IUnknown
        QAXAGG_IUNKNOWN

        // ISomeCOMInterface
        ...
    }
    

Reimplement the queryInterface() function to support the additional COM interfaces.

    long AxImpl::queryInterface( const QUuid &iid, void **iface )
    {
        *iface = 0;
        if ( iid == IID_ISomeCOMInterface )
            *iface = (ISomeCOMInterface*)this;
        else
            return E_NOINTERFACE;

        AddRef();
        return S_OK;
    }
    

Since ISomeCOMInterface is a subclass of IUnknown you will have to implement the QueryInterface, AddRef and Release functions. Use the QAXAGG_IUNKNOWN macro in your class definition to do that. If you implement the IUnknown functions manually, delegate the calls to the interface pointer returned by the controllingUnknown() function, e.g.

    HRESULT AxImpl::QueryInterface( REFIID iid, void **iface )
    {
        return controllingUnknown()->QueryInterface( iid, iface );
    }
    
Do not support the IUnknown interface itself in your queryInterface() implementation.

Implement the methods of the COM interfaces. If you need to make calls to the QWidget implementing the ActiveX control, use QAxAggregated::widget().

In your QAxBindable subclass, implement createAggregate() to return a new object of the QAxAggregated subclass.

    class MyActiveX : public QWidget,                       
                      public QAxBindable
    {
    	Q_OBJECT
    public:
        MyActiveX( QWidget *parent, const char *name = 0 );

        QAxAggregated *createAggregate()
        {
            return new AxImpl();
        }
        
    };
    

Fewer methods and properties

By default all ActiveX controls expose not only their own methods and properties to ActiveX clients, but also those of all super classes, including QWidget.

This can be controlled by reimplementing QAxFactory's exposeToSuperClass() function. Reimplement the function to return the last (furthest up the inheritance hierarchy) super class that should be exposed:

    QString MyFactory::exposeToSuperClass( const QString &key ) const
    {
        if ( key == "SmallActiveX" )
            return key;
        return QAxFactory::exposeToSuperClass( key );
    }
    

The SmallActiveX control will only expose its own functions and properties to clients, while all other ActiveX controls provided by this factory will expose their own functions and properties and also those of all their super classes including QWidget. The SmallActiveX class can of course propagate some of the QWidget functions and properties into its own interface.


  1. If a standard constructor is not present the compiler will issue an error "no overloaded function takes 2 parameters" when using the default factory through the QAXFACTORY_DEFAULT macro. If you cannot provide a standard constructor you must implement a QAxFactory custom factory and call the constructor you have in your implementation of QAxFactory::create. Back...
  2. COM cannot marshal IPictureDisp accross process boundaries, so QPixmap properties cannot be called for out-of-process servers. You can however marshal the image data via e.g. temporary files. See the Microsoft KB article Q150034 for more information. Back...
  3. This is not required, but gives the client more control over the ActiveX control. Back...


    Copyright © 2003 TrolltechTrademarks
    Qt 3.2.1