[SOLVED] Handling COM event Qt/C++
-
Hi there,
I am porting an Excel macro to Qt4/C++.
This macro uses ActiveX to command and receive data from another application.
I need to receive events from this application and I don't know how to do it. Currently, the Excel macro uses the 'WithEvents' keyword to do so.
Is there any equivalent with Qt? How to do it?
Thanks
-
[quote author="Gerolf" date="1359970851"]Hi Maxbester,
I do not really get what you do.
You say you port an excel macro, an excel macro is something written in VBA within Excel.
How does Qt come into the game here? How do you use Qt here? And where? inprocess?[/quote]I am sorry for the misunderstanding. Actually, I am not really porting the macro. I am re-developing it with Qt.
Is it clearer?
-
To me, it looks as if Maxbester is trying to automate Excel using a Qt application that interfaces with Excel through ActiveX. That application is to replace an older Excel macro. Right?
Perhaps you should study the QAx* examples, especially the one automating Outlook.
-
[quote author="Andre" date="1359975781"]To me, it looks as if Maxbester is trying to automate Excel using a Qt application that interfaces with Excel through ActiveX. That application is to replace an older Excel macro. Right?[/quote]
Yes you are right.
[quote author="Andre" date="1359975781"]Perhaps you should study the QAx* examples, especially the one automating Outlook.[/quote]
I studied the Outlook example but, maybe I'm wrong, I didn't see any information about COM event handling.
-
[quote author="Andre" date="1359984176"]I didn't try it myself, but it looked to me like COM events are exposed as (dynamically generated) signals.[/quote]
Yes, I think it would be a good thing. However, Qt generated a header file from a .tlb file and it doesn't have any signal at all.
So I am stuck...
-
Hi,
You mean COM events (I expect you mean callbacks?) which you subscribe?
Those are callback methods implemented in a callback interface. If that is done correctly with COM, the ActiveQt creates a class with signals which you can connect to.You should check, whether the tlb file really contains outgoing interfaces for the class.
-
Did you try to just connect to a signal with the appropriate name at runtime? I think it needs to be something like this:
@
connect(activeXObject, SIGNAL(ItemAdd(IDispatch*)), this, SLOT(whatever()));
@
Where ItemAdd is the name of the ActiveX event. The documentation of QAxObject states:
[quote]You can subclass QAxObject, but you cannot use the Q_OBJECT macro in the subclass (the generated moc-file will not compile), so you cannot add further signals, slots or properties. This limitation is due to the metaobject information generated in runtime. To work around this problem, aggregate the QAxObject as a member of the QObject subclass.[/quote]
That makes me think that QAxObject does some magic behind the scenes where it dynamically, at runtime, creates the interface in terms of signals. So, there is nothing to inspect at design time. -
It doesn't seem to work. I've got the message:
@
QObject::connect: No such signal QAxBase::INotify2(...)
@I found a document about the software I try to interact with. It is "Pulse Labshop":http://homel.vsb.cz/~tum52/download/PULSEAutomation.pdf from "BrĂ¼el & Kjaer":http://www.bksv.com.
This link leads to a pdf with some basic information. Page 56 is an explanation about how to work with its notification system (to me, it just looks like events).
-
INotify2 doesn't look like the actual name of the event. However, what you could try is to use QMetaObject to just list the signals from your QAxObject at runtime.
@
const QMetaObject* mo = myQAxObject->metaObject();
int i(0);
while (i < mo->methodCount()) {
QMetaMethod mm = mo->metaMethod(i++);QString typeString;
switch (mm.type()) {
case QMetaMethod::Method: typeString = QString("Method"); break;
case QMetaMethod::Signal: typeString = QString("Signal"); break;
case QMetaMethod::Slot: typeString = QString("Slot"); break;
case QMetaMethod::Constructor: typeString = QString("Constructor"); break;
}QString accessString;
switch (mm.access()) {
case QMetaMethod::Private: typeString = QString("Private"); break;
case QMetaMethod::Protected: typeString = QString("Protected"); break;
case QMetaMethod::Public: typeString = QString("Public"); break;
}qDebug() << accessString << typeString << mm.signature();
}
@Something like the above can be used to get a good view of all the methods, signals and slots of your object. It should also list any dynamically generated ones, if there are any. Perhaps you can use it to find out the exact signature of the signals you're after, instead of guessing what they might be called and what arguments you need for them...
-
Thanks a lot! I didn't know a such thing was possible.
I changed a little bit your code because there are some mistakes:
@
PulseLabShop::IPulseLabShop2* app = new PulseLabShop::IPulseLabShop2();
const QMetaObject* mo = app->metaObject();
for (int i=0; i<mo->methodCount(); i++) {
QMetaMethod mm = mo->method(i);QString typeString; switch (mm.methodType()) { case QMetaMethod::Method: typeString = QString("Method"); break; case QMetaMethod::Signal: typeString = QString("Signal"); break; case QMetaMethod::Slot: typeString = QString("Slot"); break; case QMetaMethod::Constructor: typeString = QString("Constructor"); break; } QString accessString; switch (mm.access()) { case QMetaMethod::Private: typeString = QString("Private"); break; case QMetaMethod::Protected: typeString = QString("Protected"); break; case QMetaMethod::Public: typeString = QString("Public"); break; } qDebug() << accessString << typeString << mm.signature(); }
@
I found one signal for this class. I will try to connect it.
-
Finally, all classes and interfaces return the same signals and slots:
@
"Protected" "signal" destroyed(QObject*)
"Protected" "signal" destroyed()
"Public" "slot" deleteLater()
"Public" "slot" _q_reregisterTimers(void*)
"Protected" "signal" signal(QString,int,void*)
"Protected" "signal" propertyChanged(QString)
"Protected" "signal" exception(int,QString,QString,QString)
@I cannot connect a protected signal. I am still stuck... Any idea?
-
All of these are standard QObject signals and slots, nothing specific to QAxObject. Are you sure the QAxObject is properly initialized before you do this? That is, that it represents a valid COM object? If so, then I have no clue, and I am afraid I led you down a dead end. My apologies for that.
-
[quote author="Andre" date="1360050606"]Are you sure the QAxObject is properly initialized before you do this?[/quote]
It should be. However, the program license may be expired. I have to work on this and try to reinstall Pulse Labshop.
I will come back here after.Anyway, thanks for your help ;)
-
[quote author="Maxbester" date="1359997679"]Thanks a lot! I didn't know a such thing was possible.
I changed a little bit your code because there are some mistakes:
@
PulseLabShop::IPulseLabShop2* app = new PulseLabShop::IPulseLabShop2();
const QMetaObject* mo = app->metaObject();
for (int i=0; i<mo->methodCount(); i++) {
QMetaMethod mm = mo->method(i);QString typeString; switch (mm.methodType()) { case QMetaMethod::Method: typeString = QString("Method"); break; case QMetaMethod::Signal: typeString = QString("Signal"); break; case QMetaMethod::Slot: typeString = QString("Slot"); break; case QMetaMethod::Constructor: typeString = QString("Constructor"); break; } QString accessString; switch (mm.access()) { case QMetaMethod::Private: typeString = QString("Private"); break; case QMetaMethod::Protected: typeString = QString("Protected"); break; case QMetaMethod::Public: typeString = QString("Public"); break; } qDebug() << accessString << typeString << mm.signature(); }
@
I found one signal for this class. I will try to connect it.[/quote]
Better than that:
@
QString filename="Doc.html";
QFile file( filename );
if ( file.open(QIODevice::Write) )
{
QTextStream stream( &file );
stream << app->generateDocumentation();
}
@