ActiveQt / dumpcpp type library - skipping event interface
-
wrote on 12 Oct 2020, 12:47 last edited by
Hello,
I want to use a com component with ActiveQt.
For this, I created a type library with the dumpcpp utility.Unfortunately, there are a lot of "// skipping event interface..." remarks inside the header file which was created from the dumpcpp utility.
I have also seen an unsolved bug report https://bugreports.qt.io/browse/QTBUG-28383?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&showAll=true about this topic.
Does anybody know a workaround for the skipped event interfaces?
or
a better approach (without the dumpcpp utility) to use com components with mingw/Qt?
-
wrote on 12 Oct 2020, 13:29 last edited by
Hi, while dumpcpp is helpful, it's not really necessary if you have some documentation for the com component you want to use with ActiveQt, like the names of the callable functions and what arguments they expect.
For example, when ActiveQt is used for Excel or Word, usually dumpcpp fails for those programs, but ActiveQt works anyway, just google and you'll see.
-
wrote on 12 Oct 2020, 14:19 last edited by
Hi,
The whole com component topic is new to me. I have no documentation, only a .tlb file.
Oleview has no search functionality and also fails when I try to load a .tlb file. But there is a tool called Olewoo which can load the .tlb file and shows some information about the com interface.The created classes from the dumpbin utility have no signals (maybe? because of the "skipping event interface" warnings): I do not know how to catch the com events.
Can you give me a short example how to catch com events without dumpcpp?
How can I list the available events of Qaxobject? -
wrote on 12 Oct 2020, 15:39 last edited by
Re. short example: I have not, but examples show be easy to find (but probably just vanilla C++ and not using Qt)
Re. list the available events, first thing you can try is to query your com component's type info and type library using your own C++ code. All well-behaved com components should implement those 2 interfaces, they're called ITypeInfo and ITypeLib.
I have some old code, to test it, create an empty, vanilla widget in Qt Creator, and then change your main.cpp to this:
#include "mainwindow.h" #include <QApplication> #include "qaxobject.h" #include "quuid.h" #include "qdebug.h" #include <atlbase.h> void partyOnTypeLibrary(CComPtr<ITypeLib> pTypeLib) { // BSTR to QString converter auto bstr2QString = [](BSTR bstr) { return QString::fromUtf16((ushort*) bstr); }; // show type library's doc strings CComBSTR bstrName,bstrDocString; pTypeLib->GetDocumentation(-1,&bstrName,&bstrDocString,NULL,NULL); qDebug() << bstr2QString(bstrName) << bstr2QString(bstrDocString) << ":\n"; // step thru all the type info for (UINT u = 0; (u < pTypeLib->GetTypeInfoCount()); ++u) { // get the iTypeInfo ptr CComPtr<ITypeInfo> pTypeInfo; if FAILED(pTypeLib->GetTypeInfo(u,&pTypeInfo)) continue; // get type flavor and name TYPEKIND typeKind; if FAILED(pTypeLib->GetTypeInfoType(u,&typeKind)) continue; CComBSTR bstrName; if FAILED(pTypeInfo->GetDocumentation(-1,&bstrName,NULL,NULL,NULL)) continue; QString sName = bstr2QString(bstrName); // get the type attribute TYPEATTR* pTypeAttr; if FAILED(pTypeInfo->GetTypeAttr(&pTypeAttr)) continue; // check the type flavor, we support enums, interfaces and coclasses if (TKIND_ENUM == typeKind) { QString sEnums; for (int v = 0; (v < pTypeAttr->cVars); ++v) { VARDESC* pVarDesc; if FAILED(pTypeInfo->GetVarDesc(v,&pVarDesc)) break; if (v > 0) sEnums += " "; if (VAR_CONST == pVarDesc->varkind) sEnums += QString::number(pVarDesc->lpvarValue->lVal); pTypeInfo->ReleaseVarDesc(pVarDesc); } qDebug() << "Enum type:" << sName << sEnums; } if (TKIND_INTERFACE == typeKind) qDebug() << "Interface type:" << sName; if (TKIND_DISPATCH == typeKind) qDebug() << "IDispatch callable type:" << sName; // any function descriptors? they usually exist for interfaces and IDispatch for (int f = 0; (f < pTypeAttr->cFuncs); ++f) { FUNCDESC* pFuncDesc; if FAILED(pTypeInfo->GetFuncDesc(f,&pFuncDesc)) break; if (pFuncDesc->wFuncFlags > 0) continue; // skip these chaps (boring) if (pFuncDesc->invkind > INVOKE_PROPERTYGET) continue; // skip put and putref properties // get the prop/func name and all the arg names QStringList slNames; BSTR aBS[1000]; // 1000 should suffice UINT uNames = 0; if FAILED(pTypeInfo->GetNames(pFuncDesc->memid,aBS,1000,&uNames)) break; for (uint u = 0; (u < uNames); ++u) slNames.append(bstr2QString(aBS[u])); QString sKind = "Property"; if (pFuncDesc->invkind == INVOKE_FUNC) sKind = "Function"; QString sPropOrFuncName = slNames.first(); slNames.removeFirst(); qDebug() << sKind << sPropOrFuncName << slNames; } qDebug() << ""; // we only care about coclasses at this point if (TKIND_COCLASS != typeKind) continue; qDebug() << "CoClass:" << bstr2QString(bstrName) << ":"; // step through the implemented types for this coclass for (int i = 0; (i < pTypeAttr->cImplTypes); ++i) { HREFTYPE hRefType; if FAILED(pTypeInfo->GetRefTypeOfImplType(i,&hRefType)) continue; CComPtr<ITypeInfo> pRefTypeInfo; if FAILED(pTypeInfo->GetRefTypeInfo(hRefType,&pRefTypeInfo)) continue; CComBSTR bstrInterfaceName; if FAILED(pRefTypeInfo->GetDocumentation(-1,&bstrInterfaceName,NULL,NULL,NULL)) continue; qDebug() << " " << bstr2QString(bstrInterfaceName); } } } int main(int argc, char *argv[]) { QApplication a(argc, argv); QString sCOMName = "Excel.Application"; // replace with your component's name // try to obtain an IDispatch ptr auto obj = new QAxObject(sCOMName); CComPtr<IDispatch> pDispatch; obj->queryInterface(QUuid(IID_IDispatch),(void**) &pDispatch); if (nullptr == pDispatch) qFatal("Error: an IDispatch interface was not found."); // get the ITypeInfo ptr UINT uTypeInfo; HRESULT hr = pDispatch->GetTypeInfoCount(&uTypeInfo); if (FAILED(hr) || (uTypeInfo < 1)) qFatal("Sorry, could not locate any type information"); if (1 != uTypeInfo) qFatal("Expected GetTypeInfoCount() to return 1 but alas"); CComPtr<ITypeInfo> pTypeInfo; if FAILED(pDispatch->GetTypeInfo(0,LOCALE_SYSTEM_DEFAULT,&pTypeInfo)) qFatal("Error: GetTypeInfo() failed"); // ok have ITypeInfo, use it to get the ITypeLib CComPtr<ITypeLib> pTypeLib; UINT uTypeInfoIndex; if FAILED(pTypeInfo->GetContainingTypeLib(&pTypeLib,&uTypeInfoIndex)) qFatal("Error: GetContainingTypeLib() failed"); // party on this type library partyOnTypeLibrary(pTypeLib); // that's all folks }
In theory it should be possible to compile using MinGW, but I recommend using Visual Studio for compiling this example :-)
Note: this example does not explicitly list the available COM events, but if it lists anything at all it's a good start :-)
-
wrote on 13 Oct 2020, 15:41 last edited by
Thanks for your code.
I have no Qt/VC++ toolchain (only mingw) so I had to do some modifications to your code, but got it finally running.The interesting part of the output which I get is that it seems as all interfaces skipped by dumpcpp are IDispatch types
for example:
The Wrapper created by dumcpp utility has this line. And for ITubeInterfaceEvents there is no wrapper class created.// skipping event interface ITubeInterfaceEvents
The output of your code is:
CoClass: "TubeInterfaceCOM" : "_Object" "ITubeInterface" "ITubeInterfaceEvents" IDispatch callable type: "ITubeInterfaceEventArgsCOM" "Property" "ToString" () "Property" "Code" () "Property" "Help" () "Property" "Info" () "Property" "Text" () "Property" "Type" () "Property" "Ip" () CoClass: "TubeFlagCOM" : "_Object" "IAccessibleBaseCOM" "ITubeFlagCOM" "ITubeFlagCOMEvents" "IAccessibleBaseCOMEvents" IDispatch callable type: "ITubeInterfaceEvents" "Function" "Initialized" () "Function" "TubeStateChanged" () "Function" "TubeInterfaceError" ("tubeErrorCode") "Function" "ConfigurationChanged" () "Function" "RefreshLimitableChanged" ()
Do you know how to use such IDispatch interface types? Even creating an instance like this is not working for me...
auto obj = new QAxObject(UUID_TubeInterfaceCOM); boost::intrusive_ptr<IUnknown> pITubeInterfaceEvents; obj->queryInterface(QUuid(UUID_ITubeInterfaceEvents),(void**) &pITubeInterfaceEvents); if (nullptr == pITubeInterfaceEvents) qFatal("Error: the interface for UUID_ITubeInterfaceEvents was not found.");
-
wrote on 13 Oct 2020, 18:09 last edited by
Hi, that's good news, your com component has some iDispatch interfaces (most but not all com components have them), that means you have a foothold into the API.
Using boost::intrusive_ptr looks dangerously modern, though. All of the COM technology is from last century, so my advice is to stick with those vintage types and macros. For an example of using IDispatch, you can use a pattern similar to my code in the main() function:
auto obj = new QAxObject(UUID_TubeInterfaceCOM); CComPtr<IDispatch> pDispatch; obj->queryInterface(QUuid(IID_IDispatch),(void**) &pDispatch); if (nullptr == pDispatch) qFatal("Error: an IDispatch interface was not found.");
-
wrote on 18 Oct 2020, 12:29 last edited by
I use the boost::intrusive_ptr because the VC com smart pointer CComPtr ist not available in mingw (at least I did not find a header, but maybe missed it) and a sort of smart pointer seems to be needed with com. I choose intrusive_ptr because COM already counts the references internally.
Here you will find some explanation:
https://dieboostcppbibliotheken.de/boost.smartpointers-spezielle-smartpointer
-> it is german, but maybe you can translate it.Do you know where I can find CComPtr in mingw?
//-----------------------------------------------------------------
Here is my workaround/solution for the initial topic (The skipped event interface) for others who have this problem.
I recognized that I can connect a generic signal handler to the com component (QAxObject) which contains the skipped event interface and can catch the events there. Something like this:
connect( m_qaxobject, SIGNAL(signal(const QString&, int, void*)), this, SLOT(eventSink(const QString&, int, void*)) ); void AClass::eventSink( const QString & name, int argc, void * argv ) { // See what events fired qDebug() << "Event: " << name << "argc:" << argc; if( name == "EventInterfaceEvent()" ) { ... } }
I have another issue with the wrapper which the dumpp utility created, therefore I will open another topic.
1/7