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

The TQAxServer Module

Introduction

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

This module is part of the ActiveTQt framework. (To incorporate ActiveX controls in a TQt application see the TQAxContainer 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 TQt application into an ActiveX server using the TQAxServer 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 can modify these files as long as it includes any file as the type library entry, ie. you can add version information or use a different toolbox icon.

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

Additionally you can specify a version number using the VERSION variable, e.g.

    TEMPLATE = lib
    VERSION = 2.5
    ...
    
The version number specified will be used as the version of the type library and of the server when registering.

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 TQt either as a shared library, or statically linked into the server binary.

The TQAxServer build system

To be able to build ActiveX controls with TQt, the build system must be extended to include some additional build steps that are used when the .pro file includes activeqt in the CONFIG settings. The resulting makefile will:

Attaching resources to an executable is not supported by Windows 95/98/ME, but a server built on Windows NT/2000/XP will work on those versions.

Typical build problems

The compiler/linker errors listed are based on those issued by the Microsoft Visual C++ 6.0 compiler.

Compiler errors

"No overloaded function takes 2 parameters"

When the error occurs in code that uses the TQAXFACTORY_DEFAULT macro, the widget class had no constructor that can be used by the default factory. Either add a standard widget constructor or implement a custom factory that doesn't require one.

When the error occurs in code that uses the TQAXFACTORY_EXPORT macro, the TQAxFactory subclass had no appropriate constructor. Provide a public class constructor like

    MyFactory( const TQUuid &, const TQUuid & );
    
for your factory class.

"syntax error: bad suffix on number"

The unique identifiers have not been passed as strings into the TQAXFACTORY_EXPORT or TQAXFACTORY_DEFAULT macro.

Linker errors

"unresolved external symbol _ucm_instantiate"

The server does not export an implementation of a TQAxFactory. Use the TQAXFACTORY_EXPORT macro in one of the project's implementation files to instantiate and export a factory, or use the TQAXFACTORY_DEFAULT macro to use the default factory.

"_ucm_initialize already defined in ..."

The server exports more than one implementation of a TQAxFactory, or exports the same implementation twice. If you use the default factory, the TQAXFACTORY_DEFAULT macro must only be used once in the project. Use a custom TQAxFactory implementation and the TQAXFACTORY_EXPORT macro if the server provides multiple ActiveX controls.

"cannot open file ... "

The ActiveX server could not shut down properly when the last client stopped using it. It usually takes about two seconds for the application to terminate, but you might have to use the task manager to kill the process (e.g. when a client doesn't release the controls properly).

Postprocessing and runtime errors

The ActiveTQt build system performs four commands after the linking of the binary to make it into an ActiveX server.

For this to work the server has to meet some requirements:

If those requirements are not met one ore more of the following errors are likely to occure:

The server executable crashes

To generate the IDL the widgets exposed as ActiveX controls need to be instantiated (the constructor is called). At this point, nothing else but a TQApplication object exists. Your widget constructor must not rely on any other objects to be created, e.g. it should check for null-pointers.

To debug your server run it with -dumpidl outputfile and check where it crashes.

Note that no functions of the control are called.

The server executable is not a valid Win32 application

Attaching the type library corrupted the server binary. This is a bug in Windows and happens only with release builds.

The first linking step has to link a dummy type library into the executable that can later be replaced by idc. Add a resource file with a type library to your project as demonstrated in the examples.

"Unable to Locate DLL"

The build system needs to run the server executable to generate the interface definition, and to register the server. If a dynamic link library the server links against is not in the path this might fail (e.g. Visual Studio calls the server using the enivronment settings specified in the "Directories" option). Make sure that all DLLs required by your server are located in a directory that is listed in the path as printed in the error message box.

The Server does not respond

If the system is unable to start the server (check with the task manager whether the server runs a process), make sure that no DLL the server depends on is missing from the system path (e.g. the TQt DLL!). Use a dependency walker to view all dependencies of the server binary.

If the server runs (e.g. the task manager lists a process), see the following section for information on debugging your server.

The Object cannot be created

If the server could be built and registered correctly during the build process, but the object cannot be initiliazed e.g. by the OLE/COM Object Viewer application, make sure that no DLL the server depends on is missing from the system path (e.g. the TQt DLL). Use a dependency walker to view all dependencies of the server binary.

If the server runs, see the following section for information on debugging your server.

Debugging runtime errors

To debug an in-process server in Visual Studio, set the server project as the active project, and specify a client "executable for debug session" in the project settings (e.g. use the ActiveX Test Container). You can set breakpoints in your code, and also step into ActiveTQt and TQt code if you installed the debug version.

To debug an executable server, run the application in a debugger and start with the command line parameter "-activex". Then start your client and create an instance of your ActiveX control. COM will use the existing process for the next client trying to create an ActiveX control.

Implementing Controls

To implement an ActiveX control with TQt, create a subclass of TQWidget or any existing TQWidget subclass:

    #include <qwidget.h>

    class MyActiveX : public TQWidget
    {
        Q_OBJECT
    

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

        Q_PROPERTY( int value READ value WRITE setValue )
    

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

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

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

    };
    

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

Data Types

The TQt data types that are supported for properties are:

TQt data type COM property
bool VARIANT_BOOL
TQString BSTR
TQCString BSTR
int int
uint unsigned int
double double
Q_LLONG CY
Q_ULLONG CY
TQColor OLE_COLOR
TQDate DATE
TQDateTime DATE
TQTime DATE
TQFont IFontDisp*
TQPixmap IPictureDisp* (2)
TQVariant VARIANT
TQValueList<TQVariant> SAFEARRAY(VARIANT)
TQStringList SAFEARRAY(BSTR)
TQByteArray SAFEARRAY(BYTE)
TQRect User defined type
TQSize User defined type
TQPoint User defined type

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

TQt data type COM parameter
bool [in] VARIANT_BOOL
bool& [in, out] VARIANT_BOOL*
TQString, const TQString& [in] BSTR
TQString& [in, out] BSTR*
TQCString, const TQCString& [in] BSTR
TQString& [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*
TQColor, const TQColor& [in] OLE_COLOR
TQColor& [in, out] OLE_COLOR*
TQDate, const TQDate& [in] DATE
TQDate& [in, out] DATE*
TQDateTime, const TQDateTime& [in] DATE
TQDateTime& [in, out] DATE*
TQFont, const TQFont& [in] IFontDisp*
TQFont& [in, out] IFontDisp**
TQPixmap, const TQPixmap& [in] IPictureDisp*
TQPixmap& [in, out] IPictureDisp**
TQValueList<TQVariant>, const TQValueList<TQVariant>& [in] SAFEARRAY(VARIANT)
TQValueList<TQVariant>& [in, out] SAFEARRAY(VARIANT)*
TQStringList, const TQStringList& [in] SAFEARRAY(BSTR)
TQStringList& [in, out] SAFEARRAY(BSTR)*
TQByteArray, const TQByteArray& [in] SAFEARRAY(BYTE)
TQByteArray& [in, out] SAFEARRAY(BYTE)*
TQObject* [in] IDispatch*
TQRect& (3) [in, out] struct TQRect (user defined)
TQSize& [in, out] struct TQSize (user defined)
TQPoint& [in, out] struct TQPoint (user defined)

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 TQActiveX 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 TQObject subclass can be used as the type for a sub object in ActiveX. The TQAxFactory 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 TQAxBindable class:

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

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

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 TQAxFactory. Use the default factory when the server provides only a single ActiveX control, and implement a subclass of TQAxFactory 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:

    TQAXFACTORY_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 TQAxFactory class documentation explains how to use this macro, and how to implement and use custom factories.

For out-of-process executable servers you can implement a main() function to instantiate a TQApplication object and enter the event loop just like any normal TQt application. By default the application will start as a standard TQt application, but if you pass -activex on the command line it will start as an ActiveX server. Use TQAxFactory::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 )
    {
        TQApplication app( argc, argv );
        if ( !TQAxFactory::isServer() ) {
            // create and show main window...
        }
        return app.exec();
    }
    
This is however not necessary as ActiveTQt provides a default implementation of a main function. The default implemenation calls TQAxFactory::startServer(), creates a TQApplication instance and calls exec().

To build the ActiveX server executable run qmake to generate the makefile, and use your compiler's make tool as for any other TQt application. The make process will also register the controls in the system registry by calling the resulting executable with the -regserver command line option.

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> -version x.y Writes the server's IDL to the specified file. The type library will have version x.y

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

Distributing TQAxServer binaries

ActiveX servers written with TQt can use TQt either as a shared library, or have TQt linked statically into the binary. Both ways will produce rather large packages (either the server binary itself becomes large, or you have to ship the TQt DLL).

Installing stand-alone Servers

When your ActiveX server can also run as a stand-alone application, run the server executable with the -regserver command line parameter after installing the executable on the target system. After that the controls provided by the server will be available to ActiveX clients.

Installing In-process Servers

When your ActiveX server is part of an installation package, use the regsvr32 tool provided by Microsoft to register the controls on the target system. If this tool is not present, load the DLL into your installer process, resolve the DllRegisterServer symbol and call the function:

    HMODULE dll = LoadLibrary( "myserver.dll" );
    typedef HRESULT(__stdcall *DllRegisterServerProc)();
    DllRegisterServerProc DllRegisterServer = 
        (DllRegisterServerProc)GetProcAddress( dll, "DllRegisterServer" );

    HRESULT res = E_FAIL;
    if ( DllRegisterServer )
        res = DllRegisterServer();
    if ( res != S_OK )
        // error handling
    

Distributing Servers over the Internet

If you want to use controls in your server in web-pages you need to make the server available to the browser used to view your page, and you need to specify the location of the server package in your page.

To specify the location of a server, use the CODEBASE attribute in the OBJECT tag of your web-site. The value can point to the server file itself, to an INF file listing other files the server requires (e.g. the TQt DLL), or a compressed CAB archive.

INF and CAB files are documented in almost every book available about ActiveX and COM programming as well as in the MSDN library and various other Online resources. The examples include INF files that can be used to build CAB archives:

    [version]
        signature="$CHICAGO$"
        AdvancedINF=2.0
     [Add.Code]
        simpleax.exe=simpleax.exe
     [simpleax.exe]
        file-win32-x86=thiscab
        clsid={DF16845C-92CD-4AAB-A982-EB9840E74669}
        RegisterServer=yes

The CABARC tool from Microsoft can easily generate CAB archives:

 cabarc N simpleax.cab simpleax.exe simple.inf 

The INF files assume a static build of TQt, so no dependencies to other DLLs are listed in the INF files. To distribute an ActiveX server depending on DLLs you must add the dependencies, and provide the library files with the archive.

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.

Supported and Unsupported ActiveX clients

The following is largly based on our own experiements with ActiveX controls and client applications, and is by no means complete.

Supported Clients

These standard applications work with ActiveX controls developed with ActiveTQt. Note that some clients support only in-process controls.

Microsoft Office applications are supported, but you need to register the controls as "Insertable" objects. Reimplement TQAxFactory::registerClass to add this attribute to the COM class, or set the "Insertable" class info for your class to "yes" using the Q_CLASSINFO macro.

Unsupported Clients

We have not managed to make ActiveTQt based COM objects work with the following client applications.

Enhanced features

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 TQWidget.

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

    TQString MyFactory::exposeToSuperClass( const TQString &key ) const
    {
        if ( key == "SmallActiveX" )
            return key;
        return TQAxFactory::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 TQWidget. The SmallActiveX class can of course propagate some of the TQWidget functions and properties into its own interface.

Class Information and Tuning

An alternative way to reimplementing TQAxFactory to have more control about how objects are registered or exposed is to provide class specific information using the Q_CLASSINFO macro, which is part of TQt's meta object system.

Key Meaning of value
Version The version of the class (1.0 is default)
Description A string describing the class.
ClassID The class ID. You must reimplement TQAxFactory::classID if not specified.
InterfaceID The interface ID. You must reimplement TQAxFactory::interfaceID if not specified.
EventsID The event interface ID. No signals are exposed as COM events if not specified.
DefaultProperty The property specified represents the default property of this class. Ie. the default property of a push button would be "text".
DefaultSignal The signal specified respresents the default signal of this class. Ie. the default signal of a push button would be "clicked".
LicenseKey Object creation requires the specified license key. The key can be empty to require a licensed machine. By default classes are not licensed. Also see the following section.
StockEvents Objects expose stock events if value is "yes". See TQAxFactory::hasStockEvents()
ToSuperClass Objects expose functionality of all super classes up to and including the class name in value. See TQAxFactory::exposeToSuperClass()
Insertable If the value is "yes" the class is registered to be "Insertable" and will be listed in OLE 2 containers (ie. Microsoft Office). This attribute is not be set by default.
Aggregatable If the value is "no" the class does not support aggregation. By default aggregation is supported.
Creatable If the value is "no" the class cannot be created by the client, and is only available through the API of another class (ie. the class is a sub-type).
RegisterObject If the value is "yes" objects of this class are registered with OLE and accessible from the running object table (ie. clients can connect to an already running instance of this class). This attribute is only supported in out-of-process servers.

Note that both keys and values are case sensitive.

The following declares version 2.0 of a class that exposes only its own API, and is available in the "Insert Objects" dialog of Microsoft Office applications.

    class MyActiveX : public TQWidget
    {
        Q_OBJECT
        Q_CLASSINFO("Version", "2.0")
        Q_CLASSINFO("ClassID", "{7a4cffd8-cbcd-4ae9-ae7e-343e1e5710df}")
        Q_CLASSINFO("InterfaceID", "{6fb035bf-8019-48d8-be51-ef05427d8994}")
        Q_CLASSINFO("EventsID", "{c42fffdf-6557-47c9-817a-2da2228bc29c}")
        Q_CLASSINFO("Insertable", "yes")
        Q_CLASSINFO("ToSuperClass", "MyActiveX")

        Q_PROPERTY( ...
    public:
        MyActiveX(TQWidget *parent = 0, const char *name = 0);

        ...
    };
    

Developing licensed components

If you develop components you might want to control who is able to instantiate those components. Since the server binary can be shipped to and registered on any client machine it is possible for anybody to use those components in his own software.

Licensing components can be done using a variety of techniques, e.g. the code creating the control can provide a license key, or the machine on which the control is supposed to run needs to be licensed.

To mark a TQt class as licensed specify a "LicenseKey" using the Q_CLASSINFO macro.

    class MyLicensedControl : public TQWidget
    {
        Q_OBJECT
        Q_CLASSINFO("LicenseKey", "<key string>")
    ...
    };
    
The key is required to be able to create an instance of MyLicensedControl on a machine that is not licensed itself. The licensed developer can now redistributes the server binary with his application, which creates the control using the value of "LicenseKey", while users of the application cannot create the control without the license key.

If a single license key for the control is not sufficient (ie. you want differnet developers to receive different license keys) you can specify an empty key to indicate that the control requires a license, and reimplement TQAxFactory::validateLicenseKey() to verify that a license exists on the system (ie. through a license file).

More Interfaces

ActiveX controls provided by ActiveTQt servers support a minimal set of COM interfaces to implement the OLE specifications. When the ActiveX class inherits from the TQAxBindable class it can also implement additional COM interfaces.

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

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

        long queryInterface( const TQUuid &iid, void **iface );

        // IUnknown
        TQAXAGG_IUNKNOWN

        // ISomeCOMInterface
        ...
    }
    

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

    long AxImpl::queryInterface( const TQUuid &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 TQAXAGG_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, and use TQAxAggregated::Object() if you need to make calls to the TQObject subclass implementing the control.

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

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

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

  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 TQAXFACTORY_DEFAULT macro. If you cannot provide a standard constructor you must implement a TQAxFactory custom factory and call the constructor you have in your implementation of TQAxFactory::create. Back...
  2. COM cannot marshal IPictureDisp accross process boundaries, so TQPixmap 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. OLE needs to marshal user defined types by reference (ByRef), and cannot marshal them by value (ByVal). This is why const-references and object parameters are not supported for TQRect, TQSize and TQPoint. Also note that servers with this datatype require Windows 98 or DCOM 1.2 to be installed. Back...
  4. This is not required, but gives the client more control over the ActiveX control. Back...


Copyright © 2007 TrolltechTrademarks
TQt 3.3.8