Qt Commercial Support Weekly #7: Accessing custom datatypes with ActiveQt

Some people may think it is strange or surprising when I say that I am always learning in this job while I am supporting people.  That is one of the great things about Qt. There is always more to it as the versions pass by and I can learn more and more about how things work underneath.  One of these things recently was related to ActiveQt and custom interfaces.  For the benefit of those who don't know what ActiveQt is, it is primarily a wrapper around COM on Windows. It allows you to embed ActiveX controls in your application and also create ActiveX controls for use in other applications which aren't necessarily Qt based.  For a more detailed introduction you can find more information about it here.

 

So, as I mentioned before what I am going to talk about this week is custom interfaces in ActiveQt. What this provides is a means to interact with the COM control when you want to use a non-supported datatype by Qt.  What I mean by non-supported datatype is the ones that are not automatically converted from the native type to an existing Qt type.  When we want to handle this on the client side then we use queryInterface() on the object and get access to the interface that we want to use.  For example:

 

 

                IObjectSafety *objSafe = 0;

                axWidget->queryInterface(IID_IObjectSafety, (void **)&objSafe);

                objSafe->GetInterfaceSafetyOptions(…);

 

 

So, on the client side it is fairly straightforward. You get access to the interface you want to use and call the function directly on that interface.  So, what if you want to do it on the other side of things?  What if you want to provide a means to invoke functions on your control that is created with the help of ActiveQt?  For this we will have a struct called UserDefinedData. It doesn't matter what it is or what it provides - it is just so that we can use it as an example to demonstrate how to implement this.  So our struct looks like this:

 

 

                struct UserDefinedData

                {

                                int a;

                                int b;

                                int c;

                }

 

 

In order to be able to provide a means to access this or utilize this, we need to add a class that will provide access to it. For this case we will just add a means to get and to set the user defined data which will be a subclass of the IUnknown interface as this is a new interface not based on anything else.

 

 

                interface IUserDefinedClass : IUnknown

                {

                public:

                                HRESULT getUserDefinedData(UserDefinedData *data) = 0;

                                HRESULT setUserDefinedData(const UserDefinedData &data) = 0;

                };

 

 

In order to be consistent with what COM objects prefer for their functions, we have set HRESULT to be the return value of the functions.  What remains for us now is to make this available to our control that we are creating with the use of ActiveQt.  First of all we need to make sure we have an ID for this interface so that it is easy for the applications to request for this interface.  So to the header class that contains the UserDefinedClass definition we add the following:

 

 

                #include "Unknwn.h"

                #include <initguid.h>

 

                DEFINE_GUID(IID_IUserDefinedClass, 0x469849a5, 0x7272, 0x11d3, 0xb5, 0x15, 0xcc, 0x43, 0x83, 0x4e, 0x41, 0x67);

 

 

The GUID used should be unique, so use an UUID generator to get a unique one to use here.  The IID_ part should stay so as to be conformant, but the rest of the string does not need to be the same as the interface name, it just makes it easier to remember if you do this.  The next thing we need to do is ensure that this interface is used inside our ActiveX control.  To do this we need to use a class called QAxAggregated as this will enable us to provide our own queryInterface function which will enable users of the control to actually access the custom datatype.

 

 

                class UserDefinedClassAggregate : public QAxAggregated, public IUserDefinedClass   

                {

                public:

                                QAXAGG_IUNKNOWN

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

                                HRESULT getUserDefinedData(UserDefinedData *data);

                                HRESULT setUserDefinedData(const UserDefinedData &data);

                };

 

 

I won't show the implementation of the user defined data functions here as that can basically be what you want it to be.  Firstly a word on the QAXAGG_IUNKNOWN macro, this is a convenience macro that provides an implementation of the QueryInterface(), AddRef() and Release() functions for you to save you from having to do so as IUserDefinedClass inherits from IUnknown.  The only function left then is the queryInterface() function which looks like

 

 

                long UserDefinedClassAggregate::queryInterface(const QUuid &iid, void **iface)

                {

                                *iface = 0;

                                if (iid == IID_IUserDefinedClass)

                                                *iface = (IUserDefinedClass*)this;

                                else

                                                return E_NOINTERFACE;

                                AddRef();

                                return S_OK;

                }

 

 

The code is fairly straightforward and speaks for itself which makes it easy to adapt for your own classes.  All that is left now is to make it available to your ActiveX control for usage.  This is done by reimplementing the createAggregate() function in your QAxBindable subclass.

 

 

                class MyTestControl : public QObject, public QAxBindable

                {

                                Q_OBJECT

                public:

                                MyTestControl(QObject *parent = 0);

                                QAxAggregated *createAggregate()

                                {

                                                return new UserDefinedClassAggregate;

                                }

                };

 

 

And that is all you need to do on the control side. I haven't given a complete implementation here, but you have the building blocks necessary to create your own control that gives access to custom datatypes.  To finish off here is how you would access this specific case in your other application.

 

 

                UserDefinedData myData;

                IUserDefinedClass *userDefinedClass = 0;

                obj.queryInterface(IID_ IUserDefinedClass, (void **)&userDefinedClass);

                if (userDefinedClass)

                                thirdPartyClass->getUserDefinedData(&myData);

 

 

As always, if you run into trouble then contact Qt Commercial Support via the Qt Commercial customer portal.


Comments