Accessing OCX APIs using QAxContainer



  • Is there any way to get the list of the registered OCX's APIs using QAxContainer?



  • Hi, I have an old example for listing the API's of an application (Excel). see below (note: the output is more than 10000 lines).

    It should work the same for an OCX loaded into your QAxContainer, you only need an IDispatch pointer to the OCX, which this program uses to obtain the ITypeInfo pointer and then list the API of the COM object.

    #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);
            }
        }
    }
    
    void main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
    // launch Excel and try to obtain an IDispatch ptr
        auto excel = new QAxObject("Excel.Application");
        CComPtr<IDispatch> pDispatch;
        excel->queryInterface(QUuid(IID_IDispatch),(void**) &pDispatch);
    
        if (nullptr == pDispatch)
            return qDebug("Error: an IDispatch interface to \"Excel.Application\" was not found.");
    
    // get the ITypeInfo ptr
        UINT uTypeInfo;
        HRESULT hr = pDispatch->GetTypeInfoCount(&uTypeInfo);
        if (FAILED(hr) || (uTypeInfo < 1))
            return qDebug("Sorry, could not locate any type information");
        if (1 != uTypeInfo)
            return qDebug("Expected GetTypeInfoCount() to return 1 but alas");
    
        CComPtr<ITypeInfo> pTypeInfo;
        if FAILED(pDispatch->GetTypeInfo(0,LOCALE_SYSTEM_DEFAULT,&pTypeInfo))
            return qDebug("Error: GetTypeInfo() failed");
    
    // ok have ITypeInfo, use it to get the ITypeLib
        CComPtr<ITypeLib> pTypeLib;
        UINT uTypeInfoIndex;
        if FAILED(pTypeInfo->GetContainingTypeLib(&pTypeLib,&uTypeInfoIndex))
            return qDebug("Error: GetContainingTypeLib() failed");
    
    // party on this type library
        partyOnTypeLibrary(pTypeLib);
    
    // sayonara
        excel->dynamicCall("Quit()");
    }
    


  • @hskoglund said in Accessing OCX APIs using QAxContainer:

    TypeLib(&p

    Thank you!



  • @hskoglund Any workaround to get the datatype of the interface arguments?



  • @Ketan The type discovery (reflection is the more modern term) in my example is via the OLE Automation API:s (VARIANTS, SAFEARRAY and IDispatch-based interfaces) so the datatypes are expected to be Automation-compatible, i.e. callable from Visual Basic or VBScript.

    But there's no rule saying an .OCX has to restrict itself to just using VARIANT-based datatypes, from C++ you can use any type of struct or class in an .OCX.

    If your .OCX has those type flavors, have you tried the dumpcpp.exe tool in Qt's bin directory? (That tool will generate .h and .cpp files which you #include in your project).



  • @hskoglund Using dumpcpp.exe and including header file everytime isn't much convenient.
    The above code works, but the problem is type discovery. Ex: Using dynamicCall I can open a pdf file in Adobe Reader. i.e. dynamicCall("LoadFile(QString&)","FileName"). But to end user, the question is about which interface is to use to open a file (in this case "LoadFile"). Now, I can get the API through the code above, but the problem still persists with the arguments (in this case "QString&") . Also, the ActiveX control is not limited to WMP, end user decides which one to use. So, I'm stuck in between displaying API's(either through code or using dumpcpp.exe) along with it's arguments.



  • Ok I know you can dig deeper with those Automation APIs. That example you gave, Adobe Reader, are you using an .OCX? Could you give me the path on your machine so I can download a similar one and test. Also do you have another example of an .OCX I can test on?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.