Introduction
Using COM Binary interface from VB.NET or VBS is easy thanks to Visual Studio, but getting this to work in C++ has been a bit trickier.
Quite a few software provide COM objects as an inter communication mechanism and I recently had to interact with one from a C++ project.
The software is written using the Qt framework, so in this tip, I will talk about how to use the TLB file and interact it with COM from a Qt Application. Most of the resources I read on using a Type Library File (.tlb) in C++ were dependent upon VC++. I hope this tip will provide you with basics on how to interact with a COM using a Type Library File from C++.
Requirements
This solution is done under Windows 7 using Qt 5.2.0, compiling with MinGW 4.8 (32b) and the software providing the .tlb file is E3.Series.
Extract Data from the Type Library
The first step is to generate the required files we will need for our project to be able to interact with COM. Fortunately, Qt comes with great software that will ease this process, and save us quite some time. It is called dumpcpp. This is how it is done:
Opening
the Qt Console from the Windows Menu:
Opening
the Qt Console from the Windows Search:
C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.2.0\5.2.0\mingw48_32\bin\qtenv2.bat
This is what Qt Console looks like when it is just opened:
Now it is time to run dumpcpp on the .tlb file to generate the C++ namespace:
dumpcpp "C:\Program Files (x86)\CIM-Team\E3.series_2009\e3.tlb"
This will create two files based on our .tlb file: our namespace in e3.h and our metadata in e3.cpp.
Just to make sure that our namespace is correct, we can open our e3.h file and we should see that the namespace extracted from the e3.tlb is named "e3
".
Preparing the Qt Project
Next, we will take the two generated files and integrate them in our Qt project. To use those, we will use Qt's AtiveX Framework (ActiveQt) we should edit the .pro file and add the following line to enable ActiveQt usage:
QT += axcontainer
At this stage, Qt Creator should look something as follows:
Right now, you should be able to compile the project without issues. This means it is time to dive into coding.
How to Code
There are three things you should consider while trying to use this method to use COM. First, use the auto-completion functionality of the IDE you will be working with, like this:
Second, use the software's scripting manual if one is provided. In my case, e3.Series provides a really extensive one, which helps save time.
Alternatively, you can also try to guess what functions you can use by digging into the e3.h file. This can take some time, since the generated e3.h file is 70000 lines long. For instance, in the file we can find the following class:
class E3_EXPORT e3Application : public QAxObject
We can also find the methods we want to use, for example printing a message looks as follows:
inline int PutInfo(int ok, const QString& text);
It can be a bit harder for more complex functions, since you have to guess how to get the data back, and more importantly, the type of the returned data. For instance, if you want to get all signals, you can look into the e3Job
class and
inline int GetSignalIds(QVariant& ids);
The function itself does not provide much information. However, we can guess two things from its signature. Since the return type is an integer, it gives us either the count of signal Ids, or tells us if everything went well. After a bit of testing, you understand that it was the former and that ids is an array of integers.
Coding
To use the COM, I have made a simple class that serves two purposes. First is to show our proof of concept. Second is to show how to use the library correctly. The code is as follows:
#include <QObject>
#include "e3.h"
class intwe3 : public QObject {
Q_OBJECT
private:
e3::e3Application App; e3::e3Job Job;
e3::e3Signal Signals;
e3::e3Device Devices;
e3::e3Connection Connection;
public:
intwe3();
void test();
};
As I said, this is a simple class that contains several variables coming from the generated e3 files. A constructor, that will let us test the communication, and the test method that will hold anything I want to try before starting to work with it.
In the Constructor, I will do two things, test if App exists (COM enabled) and try to write a message in the software's output.
intwe3::intwe3() {
if (App.isNull()){
qDebug() << "Issue with e3";
}else{
App.ClearOutputWindow();
App.PutInfo(0, "Text wrote from Qt");
}
}
After creating the object in the main function and running the software, we can see our message in the output of the software, meaning that the library is working correctly.
Calling Methods that Use QVariant
For other functions, like the aforementioned GetSignalIds
, you have to pass a QVariant
pointer that will hold the list of Signal Ids.
You can read the Qt-project reference page about QVariant
s, available at:
For now, our code will be held in our test()
function with the following contents:
void
intwe3::test(){
QList<QVariant> liste; QVariant *ConnectionIds = new QVariant(liste); int number = 0;
number = Job.GetSignalIds(*SignalIds);
qDebug() << "Number of signals returned : " << number;
if (SignalIds->canConvert<QVariantList>()) {
QSequentialIterable iterable = SignalIds->value<QSequentialIterable>();
foreach (const QVariant &v, iterable) {
if (v.isValid()){
qDebug() << "QVariant content : " << v;
int SignalId = v.toInt(); sig.SetId(SignalId); qDebug() << "Signal Name :" << sig.GetSignalName; }
}
}
}
Conclusion
I hope this will help you start working with third party software's COM from Qt. Have a good day.
History
- 28th March, 2014: Initial post
- 28th March, 2014: Content update