Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

ActiveQt / dumpcpp type library - skipping event interface



  • 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?



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



  • 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?



  • 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 :-)



  • 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.");
    


  • 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.");
    


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


Log in to reply