Accessing OCX 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()"); }
-
-
@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?