PDA

View Full Version : How to use standard COM method to link a Qt GCC C++ .exe and a project in MVS C++?



babygal
20th May 2010, 03:31
1)How to use standard COM method to link a Qt GCC C++ .exe and a project in MVS C++?
(Note: MVSC project code using class.)
2)Is COM method the correct method?or is there an easier method?

Fragment of the MVSC header file code which is supposed to be included in Qt project(filename:catDataPreparator.h):-
---------------------------------------------------------------------------------------------------------------------------------------------------
class catDataPreparator
{
public:
// constructor
__declspec(dllexport) catDataPreparator();

// destructor
__declspec(dllexport) ~catDataPreparator(void);

// methods
//=========
// METHODS FOR GUI FORM
__declspec(dllexport) void processInputData(const char*, const char* , const int, const float, const float, const int, const float, const float );
-------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------
mainwindow.h which is Qt project header file,where catDataPreparator.h is included, code as below:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#pragma once

#include <QMainWindow>
#include <QPrinter>
#include <QDialog>
#include <QFrame>

#include "mysharedlib_global.h"
#include "catDataPreparator.h"

QT_BEGIN_NAMESPACE
class QCheckBox;
class QAction;
class QLabel;
class QMenu;
class QPlainTextEdit;
class QListWidget;
class QScrollArea;
class QScrollBar;
class QString;
class QText;

//external class//

class catDataPreparator;

QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
#ifdef widget
MainWindow(QWidget *parent = 0);
Dialog(QWidget *parent = 0);
#endif
MainWindow();

protected:
void closeEvent(QCloseEvent *event);

private slots:
void newFile();
void openImageFile(); //open();
void open();
void openTextFile();

bool save();
bool saveAs();
void about();
void aboutCPT();

void documentWasModified();
void import();
void importdialog();
void trimdialog();
void analysis1();

void print();
void zoomIn();
void zoomOut();
void normalSize();
void fitToWindow();

void setText();
void setOpenFileName();
void setFileName();
void setCMRFileName();
void setFrameNumber();
void setEdEs();

void newimport();


private:
void createActions();
void createMenus();
void createToolBars();
void createStatusBar();
void readSettings();
void writeSettings();
void createDockWindows();
bool maybeSave();
void loadFile(const QString &fileName);
bool saveFile(const QString &fileName);
void setCurrentFile(const QString &fileName);
void showData(const QString &data);
void showMessage(const QString &message);

void updateActions();
void scaleImage(double factor);
void adjustScrollBar(QScrollBar *scrollBar, double factor);

#ifndef QT_NO_PRINTER
QPrinter printer;
#endif
QDialog *importwindow;
QLabel *imageLabel;
QText *text;
QScrollArea *scrollArea;
QPushButton *myButton;

double scaleFactor;

QString strippedName(const QString &fullFileName);
QString curFile;
QPlainTextEdit *textEdit;

QListWidget *dirView;
QListWidget *dataList;
QListWidget *messageList;

QMenu *fileMenu;
QMenu *editMenu;
QMenu *viewMenu;
QMenu *dataMenu;
QMenu *analysisMenu;
QMenu *helpMenu;

QToolBar *fileToolBar;
QToolBar *editToolBar;

QAction *newAct;
QAction *openAct;
QAction *openTextFileAct;

QAction *saveAct;
QAction *saveAsAct;
QAction *exitAct;
QAction *cutAct;
QAction *copyAct;
QAction *pasteAct;
QAction *importAct;
QAction *importdialogAct;
QAction *trimdialogAct;
QAction *filenameAct;
QAction *framenumberAct;
QAction *EdEsAct;

QAction *aboutAct;
QAction *aboutCPTAct;
QAction *analysis1Act;
QAction *analysis2Act;
QAction *analysis3Act;
QAction *printAct;
QAction *fitToWindowAct;
QAction *zoomInAct;
QAction *zoomOutAct;
QAction *normalSizeAct;
QCheckBox *native;

public:

QLabel *questionLabel;
QLabel *textLabel;
QLabel *openFileNameLabel;
QLabel *setFileNameLabel;
QLabel *setFrameLabel;
QLabel *setEdEsLabel;

catDataPreparator dataPrep;
};
---------------------------------------------------------------------------------------------------------------------------------------eom
-S.O.S

Coises
20th May 2010, 08:51
I think I can at least get you started on this.

First off, from what you’ve included, the DLL does not appear to implement COM interfaces at all, so this has nothing to do with COM.

Second, the interface is using C++ features, not just C features. This means there is a possibility that it will not work, no matter what you do, because C++ compilers are not necessarily binary-compatible in the way they use data structures. Even if you get past the next part, it might crash when you run it.

Third, C++ “name mangling” differs between GCC and MSVS: meaning that when you attempt to access the functions in the DLL, GCC won’t be looking for the same names MSVS assigned, so the functions won’t be found. However, there are ways of getting around that. This explanation should help. (http://www.emmestech.com/moron_guides/moron1.html)

squidge
20th May 2010, 10:34
In another thread, babygal wants to create a COM library, and then link against it in another project.

Secondly, babygal, please don't post the thread thread in multiple forums. It's confusing and bad etiquette.

Coises
20th May 2010, 16:17
After reading fatjuicymole’s post, I did a search and read quickly through the various threads about this problem. Let’s clarify a couple things:

This has nothing to do with COM. Forget that you ever heard about COM. It is a red herring that is unrelated to this problem.

Now, a question:

Are you free to alter the source code and the header file of the DLL and re-compile it, or do you have to use it just as it stands?

Everything else depends on the answer to that question. The key point to understand is that C has a standardized binary (compiled) interface, while C++ does not. Modules compiled with different C compilers still “speak the same language”; modules compiled with different C++ compilers do not. However, the C++ language includes the extern "C" {...} construction, which allows declaration of C-binary-compatible entities in a C++ program.

If you are free to change the DLL, there are ways to code the interface so that you will have no problems compiling the DLL with one compiler and the calling program with another. Basically, you must make the interface pure C and not C++. Both programs can use C++ code all they like; they just must communicate using only C constructs. The entire interface declaration then goes inside an extern "C" {...} declaration. If the result is inconvenient to use, you can create C++ “wrapper” classes to make the C structures and functions easier to use while still depending only on C to cross the library boundary.

If you must use the DLL as is, see my earlier post in this thread, and cross your fingers... there is no guarantee that it will work. If it doesn’t work, or if the method seems too messy, there is another choice, if you have access to MSVS: you can write a “wrapper” in MSVS that accesses the DLL but exposes only a C interface to its caller; then you can use that from a GCC program without problems.

babygal
21st May 2010, 05:44
Are you free to alter the source code and the header file of the DLL and re-compile it, or do you have to use it just as it stands?


- No and yes.. I do not have access to the DLL source code at the moment but I think I can get the developer of the DLL project to make changes. However, I can alter the header files that was provided together with the DLL, e. g : catDataPreparator.h .

So there is better luck to make both projects link if the DLL project can be altered and will it be easier?What is the percentage it will work?Please advise based on all possibilities.And also which is the best and fasted method to use??

Thank You.
-newbie

ChrisW67
21st May 2010, 07:18
Here are a few options:
You build Qt and your application using MSVC++ and the C++ ABI problem goes away.
If you can get the source and build it with MingW, or if the DLL author could build you a version using MingW, then C++ ABI problem goes away. You may have to do some work to adjust MSVC specific things in the code, and things missing in the MingW headers, to make it build.
If the DLL author can produce a C only interface to the functionality of catDataPreparator then the problem goes away (whether she uses MSVC or not).
If the DLL author can produce a COM wrapper around catDataPreparator then the problem goes away (whether she uses MSVC or not). In this case you use ActiveQt to access the functionality.

Which of these options is best really depends on what catDataPreparator does and how accommodating the DLL provider is.

Coises
21st May 2010, 20:36
So there is better luck to make both projects link if the DLL project can be altered

Yes.


will it be easier?

Probably. The only case in which using a name-mangling hack (http://www.emmestech.com/moron_guides/moron1.html) might be easier is if you just need to get this program working quickly, use it for some one-time purpose, and then throw it away. Even then, that’s only if you’re lucky enough that it does work.


What is the percentage it will work?

If the DLL author can successfully remove C++ dependencies from the interface and make sure that compiler-dependent operations on the same object don’t happen on different sides of the DLL boundary (for example, you can’t expect to open a file in the main program but have the DLL read from it, or to allocate memory in the DLL but have the main program free it), then the problem of compiler differences goes away completely — whether it will work is, of course, dependent on the code, but the MSVC++/MinGW mix won’t interfere.


And also which is the best and fasted method to use?

If it’s possible to use the same compiler for both the main program and the DLL, that’s the easiest and fastest.

If that’s not possible, the next-best choice depends on the relative availability and value of the DLL author’s time compared to yours.

If the DLL author is available and willing to help, there are probably three reasonable options — which to use would be more the DLL author’s choice than yours. All methods require care that the data structures used are either pure C structures or are known to be compatible between the Visual Studio and MinGW compilers (pure C being the most reliable) and that no services, like memory allocation or file I/O, will be mixed across the DLL boundary on the same object. Given that:

1. Expose a pure C interface — one that can go in an extern "C" {...} block — either instead of, or in addition to, the existing interface. That means using only C-style "plain old data" structures and non-overloaded global functions: no constructors or destructors, no member functions, etc. Pointers have to be either to C-style data or functions or to “opaque” data that will be allocated, used and freed only on one side of the DLL boundary. (For example, the main program and the DLL can use char* C-style strings to communicate, but not a std::string or a QString.)

2. Make it a COM object. This adds significant complexity; there is no need to do it unless the DLL author had been thinking about it anyway for other reasons, and would rather put in the effort to do it now than build yet another interface just for your project.

3. Use the technique outlined in Binary-compatible C++ Interfaces (http://chadaustin.me/cppinterface.html). You might call this “COM-lite” because it makes use of the fact that Windows C++ compilers (including MinGW) go out of their way to be able to use COM even though it’s not a C++ language standard, so in the particular ways needed to make COM work, they stay binary-compatible with Microsoft. One of those ways is that they implement pure interface classes (C++ classes that contain only public pure virtual functions) in which all the member functions are declared __stdcall in the same way. This method uses that compatibility to present an object-oriented interface that works across compilers (on Windows) without all the other complexity and features of COM. You end up with a pure interface class and a single “factory” global function that creates a object of a class (known only within the DLL) derived from the interface class and returns a pointer to it.

Note that method 1 will work anywhere; method 2 is applicable only to Windows; and method 3 can be used anywhere with a single compiler, but can only be counted on to work across different compilers on Windows.

If the DLL author cannot be troubled to this degree, and you cannot use the same compiler for both projects, the remaining choice is for you to write a “wrapper” that communicates with the DLL on its own terms but presents an interface to your program as in option 1 or 3 above. The wrapper must be compiled with MSVC++.

babygal
25th May 2010, 04:23
1. Expose a pure C interface — one that can go in an extern "C" {...} block — either instead of, or in addition to, the existing interface. That means using only C-style "plain old data" structures and non-overloaded global functions: no constructors or destructors, no member functions, etc. Pointers have to be either to C-style data or functions or to “opaque” data that will be allocated, used and freed only on one side of the DLL boundary. (For example, the main program and the DLL can use char* C-style strings to communicate, but not a std::string or a QString.)



- How to implement this in Qt? Any good examples or source code, in particular to my case(.exe in Qt link with DLL in MVSC)?

babygal
25th May 2010, 06:31
- How to implement this in Qt? Any good examples or source code, in particular to my case(.exe in Qt link with DLL in MVSC)?

- Can this method work if the MVSC DLL is written in class and the functions called in Qt,are members of the class ?

squidge
25th May 2010, 07:52
extern "C" will only work for standard "C", not C++ classes.

Coises
25th May 2010, 07:52
- Can this method work if the MVSC DLL is written in class and the functions called in Qt,are members of the class ?

I don’t know that I can explain this much beyond what I’ve already said. You will not be able to make it work completely as you are hoping, in a single class with no global functions. Method 3 comes close, though.

=== For method 1 (pure C interface) ===

In the header file that will be shared between the MinGW *.exe and MSVC++ *.dll, everything you will use must go within the braces of an extern "C" {...} block.

No namespaces. No member functions, constructors or destructors. No overloaded functions. Use structs instead of classes; do not use protected or private sections and do not derive from another class or struct.

=== For method 3 ("COM-lite") ===

Read the Binary-compatible C++ Interfaces (http://chadaustin.me/cppinterface.html) page.

Define the class in your shared header file as a completely abstract interface class; that means only public, pure virtual member functions: no data and no constructor. (For reasons explained in the article linked above, you should also avoid using a destructor.)

Declare at least one global function to be used as a “factory” to create a concrete instance of a class derived from the interface class. This class will be defined and used in the DLL, but not in the calling program. The return type of the factory function should be a pointer to the interface class.

Either declare a global function that takes a pointer to an object of the interface class and destroys it; declare a virtual member function in the interface class that destroys an object of the class; or use the technique explained in the linked article to redefine the delete operator for the interface class so that it will work correctly in the calling program.

Declare all functions (member and global) using __stdcall. Declare global functions extern "C" and export them from the DLL. (There is no need to “export” the interface class as there is nothing to export. All the information is in the class declaration included in the shared header file; since it is a completely abstract class, it has no implementation.)

=== For all methods ===

Avoid using data structures that are not defined by the plain C standard as interface elements. For example, don’t use a std::string (or a QString) to pass data across the DLL boundary: use a C-style char* or wchar_t string.

Don’t allocate memory on one side of the boundary and free it on the other, or use library functions on the same object on different sides of the boundary. For example, don’t open a file in the calling program and try to write to it in the DLL. Even if you stick to C library functions, the MinGW library and the MSVC++ library are different; you can’t mix them on the same object unless they are purely data manipulation operations on a plain C variable, array or struct.

babygal
26th May 2010, 05:33
Hi,
Thank you very much to all who replied to my questions, especially Coises,for all the useful replies. With the useful links and explanation , I finally resolved my project's link problem.
I used the extern "C" method and this solution finally work.



Thank you very much. :-) :-) :-)

babygal