How to include a COM tlb library in my C++ project
-
I have a COM .tlb library that I would like to include in my project but am really stuck on how to include it. With Visual Studio it is very simple, I just start a project, right click on References, Add reference and the select the COM library from the list.
The tlb file I am using can be found here and is from Autodesk Robot Structural Analysis.
https://we.tl/t-vN0yZjV0vBThe line:
RobotOM::RobotApplication robApp = new RobotApplication();
should work when the library is correctly included.
Any help is appreciated. Thank you.
-
Hi, first you need to "digest" your .tlb file for Qt to consume, you'll use the dumpcpp program in the bin directory, like this:
dumpcpp robotom.tlb
This will produce 2 files: robotom.h and robotom.cpp.
To test them, I created a new vanilla widget app. In the .pro file I added those 2 files, i.e. in SOURCES I added robotom.cpp after mainwindow.cpp and in HEADERS I added robotom.h after mainwindow.h.Also you need to add Qt COM support, add axcontainer to the QT line in the .pro file like this:
QT += core gui axcontainer
Then in mainwindow.h you can #include robotom.h and you should be set. Note: also you need to add the QT_NO_EMIT symbol, because the Robot API includes an emit method, which collides with Qt's emit keyword. I.e. like this:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #define QT_NO_EMIT #include "robotom.h" #include <QMainWindow> QT_BEGIN_NAMESPACE ...
Then to run some code, I tested and added this in my mainwindow.cpp:
RobotOM::RobotApplication robApp; qDebug() << robApp.Is360();
Compiled and builds fine, but when I run it there's no robotom.dll in my PC so it crashes :-(
-
This is amazing thank you!
Just a question to follow up. I followed all your instructions and it works great, but the robotom.cpp file is 295MB and the robotom.h file is 10MB. So when I compile my application, the executable is 75+MB! The original tlb file was only 1-2MB.
When I use Visual Studio, select the COM library and compile, the executable is 30-40KB.
Did you find the same issue?
-
@AhmedAlshawi
It does not sound right to me that you end up with so big a.cpp
to compile. However, I do not know as much as @hskoglund .Old post in this forum https://forum.qt.io/topic/44383/importing-a-managed-dll-into-qt-c-project/3 shows
This will give you a CyUSB.tlb which you can #import into your Qt app:
Start an empty Widgets app in QtCreator. In mainwindow.cpp insert
#import "c:/DirToTheTlb/CyUSB.tlb"
I wondered if that would be smaller/simpler, but I just noticed it was @hskoglund himself who posted this, he may be the expert, so maybe it's same principle!
I don't know whether
QAxObject
/QAxBase
provide a way to access it smaller. But then using them directly perhaps loses the benefit of the.tlb
.There is something wrong with you being 300MB while VS is 1MB!
-
@AhmedAlshawi Sorry I was a bit to quick!
You're right, the .exe file is way too big, that's because I took the easy way and included the .cpp file in the build as well, but in this case I think it's useless baggage and not needed (it contains the staticMetaObject).Let's try another run with dumpcpp, just generating the .h file:
dumpcpp --nometaobject robotom.tlb
This will create almost the same robotom.h file (but not exactly since there's no reference to the staticMetaObject).
Now remove the robotom.h and robotom.cpp from yesterday, and instead use today's new robotom.h in your project. And remove robotom.cpp from your .pro file.
The .h file is still humongous but the .exe file size should be in the same range as the Visual Studio one. And hopefully it will also work.
-
@JonB The #import statement is a Visual Studio/Microsoft extension, it runs the equivalent of Qt's dumpcpp during the compilation/linking, however it creates .tlh and .tli files instead of .h and .cpp, which might be confusing. Admittedly it's easier than first having to run dumpcpp, but it requires a MSVC-flavored Qt (not MinGW).
Anyway, I tried an #import on robotom.tlb but got some error messages about conflicting names, those you can fix by appending rename statements to the #import line. But dumpcpp compiles clean :-) -
@hskoglund Thanks a lot for your help!
So now I've run dumpcpp with --nometaobject and only the .h file was created. It came out at 10MB again which doesn't seem to be too humongous. It compiles fine and in release the executable is only 2-300KB which is fine.
The problem now is that I keep encountering SEGMENTATION faults which don't happen with your previous method (i.e. dumpcpp without the '--nometaobject' and having both a .h and .cpp file). Any ideas?Another issue is that the following code works in C# on VS
IRobotResultQueryReturnType Res; RobotResultQueryParams RobResQueryParams; RobotResultRowSet RobResRowSet = new RobotResultRowSet(); RobResQueryParams = robApp.CmpntFactory.Create(IRobotComponentType.I_CT_RESULT_QUERY_PARAMS); RobResQueryParams.SetParam(IRobotResultParamType.I_RPT_MULTI_THREADS, true); RobResQueryParams.SetParam(IRobotResultParamType.I_RPT_THREAD_COUNT, 4); RobResQueryParams.SetParam(IRobotResultParamType.I_RPT_NODE, 1); RobResQueryParams.SetParam(IRobotResultParamType.I_RPT_RESULT_POINT_COORDINATES, 1);
but when re-writing in Qt, I keep encountering this error on the commented line below
error: assigning to 'RobotOM::RobotResultQueryParams *' from incompatible type 'IDispatch *'IRobotResultQueryReturnType Res; RobotResultQueryParams* RobResQueryParams; RobotResultRowSet* RobResRowSet = new RobotResultRowSet(); //RobResQueryParams = robApp.CmpntFactory()->Create(IRobotComponentType::I_CT_RESULT_QUERY_PARAMS); RobResQueryParams->SetParam(IRobotResultParamType::I_RPT_MULTI_THREADS, true); RobResQueryParams->SetParam(IRobotResultParamType::I_RPT_THREAD_COUNT, 4); RobResQueryParams->SetParam(IRobotResultParamType::I_RPT_NODE, 1); RobResQueryParams->SetParam(IRobotResultParamType::I_RPT_RESULT_POINT_COORDINATES, 1);
Again, thank you and any help is appreciated!
edit: I found this code in the .h file and it mentioned IDispatch, but not sure how to implement as not come across "IDispatch" before.
class ROBOTOM_EXPORT IRobotResultQueryParams : public QAxObject { public: IRobotResultQueryParams(IDispatch *subobject = 0, QAxObject *parent = 0) : QAxObject((IUnknown*)subobject, parent)
-
@hskoglund said in How to include a COM tlb library in my C++ project:
@JonB The #import statement is a Visual Studio/Microsoft extension, it runs the equivalent of Qt's dumpcpp during the compilation/linking, however it creates .tlh and .tli files instead of .h and .cpp, which might be confusing. Admittedly it's easier than first having to run dumpcpp, but it requires a MSVC-flavored Qt (not MinGW).
Anyway, I tried an #import on robotom.tlb but got some error messages about conflicting names, those you can fix by appending rename statements to the #import line. But dumpcpp compiles clean :-)I have tried to use the #import statement but couldn't get it to work with the MSVC compiler :/ Although I am not versed enough with C++ to be adding rename statements.
The dumpcpp works but hopefully the file size has a solution.
-
Hmm, of the 3 approaches we've tried (dumpcpp, dumpcpp --nometaboject and #import) I think your best bet is (if you're willing to stick to the MSVC compiler and not use MinGW) to go with the #import statement. Fixing errors like the one you show "error: assigning to 'RobotOM::RobotResultQueryParams *' from incompatible type 'IDispatch *' is like opening a can of worms, you never know what lurks inside.
I made a nice customized #import statement for you:
#import "robotom.tlb" no_namespace \ rename("emit","robotEmit") \ rename("SendMessage","RobotSendMessage") \ rename("GetMessage","RobotGetMessage")
that should compile cleanly. With that you can remove robotom.h and robotom.cpp from your .pro file.
Note: if you place that #import statement in an .h file (like mainwindow.h) then you have to compile/link 2 times, first time you'll get an error that the .tlh file is not found, just try again. This is an ancient "feature" of the interaction Qt Creator/MSVC/#import statement :-)
(A workaround is to place the #import statement in a .cpp file, like mainwindow.cpp, then a normal, single compilation works fine.)I googled for how to write the C++ code when using the #import statement like above (with no namespace), found an old (from somewhere in the 90's) PDF that uses old style COM C++ code in the last pages of the document. Using that style will probably be the less painful way, also there are some more C++ code examples in those pages.
To begin, try something like this:
IRobotApplicationPtr pRobot(__uuidof(RobotApplication)); qDebug() << pRobot->ProgramVersion; qDebug() << pRobot->Is360;
-
Okay so I used the MSVC compiler with the import statement you provided and then the code from the PDF example (good find!)
This compiles and runs the code just fine BUT, it compiles through these errors:- error: unknown type name 'IRobotApplicationPtr'
- error: use of undeclared identifier 'RobotApplication'
So just to summarise:
- "dumpcpp robotom.tlb" gives .h and .cpp but the executable file is 75MB and I have IDispatch errors
- " dumpcpp --nometaobject robotom.tlb" gives only a .h and executable file is 300KB, except I get SEGMENTATION faults
- #import and using MSVC requires me to code in an old COM style of C++ while showing errors that can still be compiled and executed
I think I prefer to use the second method if there is a solution for the SEGMENTATION faults, and then for the IDispatch errors if they also occur after fixing the SEGMENTATION faults.
If there isn't a straight forward solution then I may have to stick to using C# in Visual Studio for this project, unfortunately. I may try using C++ in Visual Studio.
@hskoglund Thank you for all your help in looking into this!!
-
It's telling that the majority of the code examples in that PDF are in Visual Basic, and C++ is treated as a 2nd class citizen in it :-(
The 2nd method (dumpcpp with only an .h) is at least technology from this century so I understand why you prefer it.
Try to see if there's a pattern to the SEG faults, perhaps there's some extra initialization you need to do due to that missing staticMetaObject.
The IDispatch errors are a symptom of inconsistencies in the .tlb file but are glossed over by Visual Basic and C#, but I think they can be fixed with some type coercing, e.g. static_cast<IDispatch*>(ptr)
Good luck!
P.S. There might actually be a 4th alternative, especially if you're only using a subset of that Robot library:
write a "wrapper" COM .dll in C#, I think it's called a CCW (Com Callable Wraper) that exports the stuff you want in a more modern way, which then can be consumed by Qt C++.