Trolltech | Documentation | Qt Quarterly | Extension Dialogs »

ActiveX under Control
by Volker Hilsheimer
Microsoft's COM technology allows applications and libraries to use components provided by component servers, and to be component servers in their own right. ActiveX is built on the COM technology and defines a set of interfaces for both the client side and the server side. This article introduces the new ActiveQt framework that provides the QAxContainer and QAxServer modules supplied with Qt 3.1's Windows port.

Qt's new ActiveX support allows Qt/Windows developers to access and use ActiveX controls provided by ActiveX servers, and to make their own applications and shared libraries into ActiveX servers. For example, a Qt application could make use of Internet Explorer's ActiveX controls to provide a web page display and navigation to its users. Instead of working with the low-level generic API provided by Microsoft, developers use the QAxWidget class to write standard Qt code using familiar techniques such as signals and slots.

Using a pre-3.1 Qt or a generic ActiveX SDK to create ActiveX controls is very difficult. Qt 3.1's ActiveX support is designed to take the pain out of ActiveX programming. It insulates developers from all the different kinds of generated code that is normally part of an ActiveX implementation. Instead, developers use the QAxServer module to expose any QWidget class as a scriptable ActiveX control.

Using an ActiveX Control

The QAxWidget class uses the COM introspection interfaces (e.g. IDispatch) to generate Qt meta-object information for the ActiveX control it has been initialized with.

    #define CLSID_InternetExplorer "{8856F961-340A-11D0-A96B-00C04FD705A2}"
    QAxWidget *activeX = new QAxWidget( this );
    activeX->setControl( CLSID_InternetExplorer );

The meta-object information adds all the properties and methods exposed by the ActiveX control as Qt properties and slots. This means that we can use Qt's signals and slots mechanism with ActiveX controls. For example, Internet Explorer's GoHome() method becomes a GoHome() slot in Qt:

    connect( homeButton, SIGNAL(clicked()), activeX, SLOT(GoHome()) );

If the ActiveX control provides an event source, the QAxWidget object registers itself as an event sink and makes the events available as Qt signals. Qt's ActiveX framework handles all the type conversions between COM and Qt data types.

    connect( activeX, SIGNAL(TitleChange(const QString&)), 
             this, SLOT(setTitle(const QString&)) );

ActiveX events can be used like any other Qt signals. In this example we've connected Internet Explorer's TitleChange event to our own setTitle() slot.

    activeX->dynamicCall( "Navigate(const QString&)", 
                          "http://doc.trolltech.com" );

The dynamicCall() function allows developers to call a method in an ActiveX control. Qt's ActiveX framework dispatches the call through the IDispatch interface provided by the ActiveX control after doing the type conversions. If a lot of calls need to be made it may be inefficient to use the dynamicCall() approach.

    IWebBrowser *webBrowser = 0;
    activeX->queryInterface( IID_IWebBrowser, &webBrowser );
    if ( webBrowser ) {
       do_something_with( webBrowser );
       webBrowser->Release();
    }

The QAxWidget class provides direct access to the ActiveX control itself through the queryInterface() function. Using the COM interface directly is also useful for accessing methods and properties that have data types which cannot be converted by Qt's ActiveX framework, e.g. properties of type IDispatch *. A notable difference from standard Qt programming is that we must call Release() when we have finished using the control.

Creating an ActiveX Control Server

The ActiveQt framework provides a static library implementing the functions required to turn a standard Qt application or DLL into an ActiveX control server. The qmake build tool adds the necessary modifications to the build system when the list of CONFIG settings in the .pro file includes "activeqt":

    CONFIG += activeqt

Every class inheriting QWidget can be exposed by the server as an ActiveX control. The server must provide a factory that the framework can use to create QWidget objects when they are requested by an ActiveX client. To create an ActiveX server exposing only a single control, it is sufficient to use the default factory as a macro.

    #include <qaxfactory.h>

    QAXFACTORY_DEFAULT( QTetrix, 
                "{852558AD-CBD6-4f07-844C-D1E8983CD6FC}", 
                "{2F5D0068-772C-4d1e-BCD2-D3F6BC7FD315}", 
                "{769F4820-9F28-490f-BA50-5545BD381DCB}", 
                "{5753B1A8-53B9-4abe-8690-6F14EC5CA8D0}", 
                "{DE2F7CE3-CFA7-4938-A9FC-867E2FEB63BA}" );

If the code shown above is added to the Qt Tetrix example (in file qtetrix.cpp), the game will become an ActiveX server. The parameters passed into the macro are unique IDs used by COM to identify the ActiveX control, the interfaces it implements, and its type information. Microsoft supply a tool, guidgen, for generating such IDs. To provide multiple ActiveX controls the factory must be implemented manually. This is achieved by subclassing the QAxFactory class and reimplementing the virtual functions to return information about the supported controls.

The qmake tool will add a build step after the linking of the executable to generate an interface definition file from the information provided by the Qt meta object system. The class's properties, signals and slots will be exposed by the framework as COM properties, methods and events, and will be accessible through the IDispatch automation interface.

Clients using the Tetrix ActiveX control ought to be able to start the game, and be able to read and write the current score, for example to maintain a high score table.

    class QTetrix : public QWidget
    {
        Q_OBJECT
        Q_PROPERTY( int score READ score WRITE setScore )
    public:
        QTetrix( QWidget *parent = 0, const char *name = 0 );
        int score() const;
    public slots:
        void startGame();
        void setScore( int score );
    signals:
        void gameOver();
    };

The Q_PROPERTY macro makes the score accessible to the Qt property system, and the ActiveQt framework exposes it as an ActiveX property. The public slots allow the score to be set and the game to be started using OLE automation, and the gameOver() signal is sent to the hosting client as an ActiveX event.

Applications that are ActiveX servers can also be run as stand alone applications. The following code removes widget functionality that might be inappropriate when they are used as ActiveX controls.

    if ( QAxFactory::isServer() )
        quitButton->hide();

Calling the application with -regserver registers it so that it can be instantiated and scripted by ActiveX clients.

    tetrix -regserver

ActiveX clients can use registered controls, for example

    <OBJECT ID="QTetrix" width="550" height="370"
        CLASSID="CLSID:852558AD-CBD6-4f07-844C-D1E8983CD6FC">
    </OBJECT>
    <FORM>
        <INPUT TYPE="button" VALUE="Start Game..."
            onClick="QTetrix.startGame()">
    </FORM>

The HTML shown above embeds the QTetrix control into a webpage, and shows a "Start Game" button that will call the startGame() slot of the QTetrix widget.

Summing Up

The QAxContainer module provides QAxWidget, a subclass of QWidget. QAxWidget makes ActiveX methods, properties and events available as Qt slots, properties and signals so that developers can use ActiveX controls just like standard QObjects. The QAxServer module provides an interface between Qt widgets and ActiveX controls. It includes the QAxFactory class that is used to create ActiveX components. Widgets and components created with the QAxServer module can be instantiated and used as standard ActiveX controls by ActiveX client applications and scripting engines such as Internet Explorer and Visual Basic.


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2002 Trolltech. Trademarks Extension Dialogs »