Unsolved Borland to Qt migration, Qt ActiveX COM automation of Borland Builder 6 App
-
I am tring to communicate with a CoClass "GDAS" over COM from a Qt App with imported code from a from Studio Rad (Embarcadro) project (using the borland compiler and environment). The CoClass I need to access is contained in a locally created Borland Builder 6 App "GeDetekt.exe" as "GeDetekt.tlb"
I succesfully generated a namespace and the type wrappers GeDetekt.h and GeDetekt.cpp as explained [url]http://doc.qt.io/qt-5/activeqt-container.html[/url]
by including
TYPELIBS = "C:\Users\Jennifer McClellant\Documents\Development\Opus Rs Alpha\GeDetekt.exe"
in my .pro file.
With GeDetekt.h now included on the Qt side I can now create and initialize the server:
int ComClient::connectToServer() { STARTUPINFOA sicomServer; PROCESS_INFORMATION picomServer; memset(&picomServer, 0, sizeof(picomServer)); memset(&sicomServer, 0, sizeof(sicomServer)); sicomServer.dwFlags = STARTF_USESTDHANDLES; sicomServer.cb = sizeof(STARTUPINFO); sicomServer.lpReserved = nullptr; sicomServer.lpDesktop = nullptr; sicomServer.lpTitle = nullptr; sicomServer.cbReserved2 = 0; sicomServer.lpReserved2 = nullptr; sicomServer.dwFlags = 0; std::string path = getPathToServer(); std::replace(path.begin(), path.end(), '/', '\\'); LPSTR str = static_cast<LPSTR>(const_cast<char*>(path.c_str())); int res = CreateProcessA(nullptr, str, nullptr, nullptr, false, 0, nullptr, nullptr, &sicomServer, //(s)tartup (i)nformation &picomServer);//(p)rocess (i)nformation, handle if (res == 0) { QMessageBox msgBox; msgBox.setText("Unable to start COM Server."); msgBox.exec(); errorExit("GetProcessId"); } for (int j = 0; j < 100; j++)//time to let COM server establish { Sleep(10); if (!(j % 10)) { QApplication::processEvents(); } } comServer = new GeDetekt::GDAS(); isServerActive = true; dataRetrievalTimer->start(timerCount); statusRetrievalTimer->start(timerCount); return 1; } std::string ComClient::getPathToServer() { std::string path = QDir::currentPath().toStdString() + "\\System"; std::string iniFilePath = path + "\\" + "Sentinel.ini"; QSettings sentinelIni("Grandperspective", "Sentinel"); sentinelIni.setValue("GlobalSettings/OPUS_RS_PATH", "C:/Users/Jennifer McClellant/Documents/Development/Opus Rs Alpha/GeDetekt.exe"); std::string name = sentinelIni.value("GlobalSettings/OPUS_RS_PATH").toString().toStdString(); std::string geDetektPath = name.empty()? std::string(): name; return geDetektPath; }
GeDetekt.exe starts, the server Api on client side (Qt side) seems to be initialised however when tring to run the methods e.g "ReturnStatus" on the server and access the returned data from the COM buffer, the QVariants are not valid (do not return data content).
void ComClient::getStatusFromServer() { if (!isServerActive) { return; } statusRetrievalTimer->stop(); QVariant status; comServer->ReturnStatus(status); VARIANT varStatus; QVariantToVARIANT(status, varStatus); //check type: (array | real 8 byte (double)) if (varStatus.vt == (VT_I1 | VT_ARRAY)) { //--SafeArray: create with the VARIANT type SAFEARRAY *safeArr = V_ARRAY(&varStatus); //--SafeArray: dimensions (bounds information) long lLbound = 0; long lUbound = 0; SafeArrayGetLBound(safeArr, 1, &lLbound); SafeArrayGetUBound(safeArr, 1, &lUbound); int amountStatusFlags = static_cast <int>(lUbound - lLbound + 1); //--SafeArray: access the data bool *actualData; SafeArrayAccessData(safeArr, reinterpret_cast<void**>(&actualData)); serverStatus.clear(); for (int i = 0; i < amountStatusFlags; i++) { serverStatus.push_back(static_cast<bool>(actualData[i])); } SafeArrayUnaccessData(safeArr); } statusRetrievalTimer->start(timerCount); }
What could I have overseen? Please someone help.
-
Hi, if you do a qDebug() output/dump of the status variable before and after the
comServer->ReturnStatus(status);
call, is there any difference? -
@hskoglund said in Borland to Qt migration, Qt ActiveX COM automation of Borland Builder 6 App:
qDebug()
Thanks for the advice. I tried calling qDebug() on the status variable. The content is identical and empty before and after the ReturnStatus() call. I also get the Message: "QAxBase: Error calling IDispatch member ReturnStatus: Unknown error" when ReturnStatus() is called.
-
How does the function prototype for comServer->ReturnStatus() look like? Perhaps the argument is just an in-argument and not an out-argument i.e. a reference, that could explain why the call leaves the status variable untouched.
It could be that the correct call style isstatus = comServer->ReturnStatus();
(just guessing)
` -
I think the issue is that VARIANT* is not supported by Qt as well as that the COM communication between Borland Builder 6 and Qt has not been successfully completed or is supported by Qt. I am now using invoke() over the IDispatch as done here: https://www.qtcentre.org/threads/63972-ActiveX-from-MFC-to-Qt5. However I am still having issues regarding a "VARIANTARG vArg[7]" vector of parameters for the method on server side.
-
Hi @Miss_Mc,
This might be completely unrelated to your current problem, but I'm sure it will bite you sooner or later:
C:/Users/Jennifer McClellant/Documents/Development/Opus RsAlpha/GeDetekt.exe
Please, don't use paths with Non-ASCII or space characters in it. Put your projects in a folder
C:\Dev
or something like that. Really!Reference: QTCREATORBUG-20834
-
@aha_1980 Thanks I will!