I developed an application along the lines of the SQL Browser example provided with QT in the Demos and Examples section. My development machine is Windows XP (visual studio compiler was used) and the application works well on it. It is able to connect to an external database (MySQL), and I am able browse through tables. I used the QODBC driver for connections. However when I deploy the executable (with all required .dll files) in another computer without QT, it says that I need to provide for the database drivers. I read the documentation and realized that I need to build a PlugIn for QODBC drivers.
First I looked at an example Plugin (Echo Plugin Example) given at http://doc.qt.digia.com/4.6/tools-echoplugin.html. Then I followed the instructions in http://doc.qt.digia.com/4.6/sql-driver.html#qodbc.
cd %QTDIR%\src\plugins\sqldrivers\odbc
qmake odbc.pro
nmake
The above commands built qsqlodbc4.dll. However, I am not successful in developing a Plugin for my application. Here are my steps, and the compilation output:
Created project odbcplugin.pro (see script below) under directory /odbcpluginTest
TEMPLATE = subdirs
SUBDIRS = sqlbrowser \
odbc
CONFIG += release
# install
target.path = $$PWD
sources.path = $$PWD
sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS
INSTALLS += target sources
Created subdirectories: /odbc, /sqlbrowser, /plugins
Inside the directroy /odbcpluginTest /odbc/
(i). Copied odbc.pro and suitably modified the paths and file names (for example, a file originally named as main.cpp has been renamed as mainODBC.cpp to avoid confusing with the file named main.cpp inside /sqlbrowser). See the scripts below:
TEMPLATE = lib
INCLUDEPATH += ../sqlbrowser
SOURCES = mainODBC.cpp
TARGET = qsqlodbc
DESTDIR = ../plugins
CONFIG += release
include(qsql_odbc.pri)
include(qsqldriverbase.pri)
sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS
INSTALLS += target sources
(ii). The file odbcinterface.h that describes the plugin interface is included as a header in odbc.pro. However, it is actually placed inside the directory /sqlbrowser. Hence, the line INCLUDEPATH += ../sqlbrowser is included in the above script.
(iii). Also, copied all related project files (qsql_odbc.pri, qsqldriverbase.pri, qpluginbase.pri, qt_targets.pri). Suitably modified the paths in all project files (there may be mistakes in here).
(iv). The header (qsql_odbc.h) and source (qsql_odbc.cpp) files of qsql_odbc.pri have also been copied.
Inside the directory /odbcpluginTest /sqlbrowser/
(i). Copied sqlbrowser.pro and all related files.
(ii). Created the header file odbcinterface.h that describes the plugin interface (see below) and added it to the HEADERS in sqlbrowser.pro.
#ifndef ODBCINTERFACE_H
#define ODBCINTERFACE_H
#include <QString>
#include <QtSql/qsqldriver.h>
class OdbcInterface
{
public:
virtual ~OdbcInterface() {}
virtual QSqlDriver* create(const QString &) = 0;
virtual QStringList keys() const = 0;
};
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(OdbcInterface,
"developed similar to com.trolltech.Plugin.EchoInterface/1.0");
QT_END_NAMESPACE
#endif // ODBCINTERFACE_H
iii. Also, modified the browser.h file by adding the lines
#include "odbcinterface.h"
private:
bool loadPlugin();
OdbcInterface *odbcInterface;
Public:
void TestCase1();
iv. Also, modified the browser.cpp file by adding the function definitions:
bool Browser::loadPlugin()
{
QDir pluginsDir(qApp->applicationDirPath());
#if defined(Q_OS_WIN)
pluginsDir.cdUp();
pluginsDir.cdUp();
#endif
pluginsDir.cd("plugins");
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *pluginI = pluginLoader.instance();
if (pluginI) {
odbcInterface = qobject_cast<OdbcInterface *>(pluginI);
if (odbcInterface)
return true;
}
}
return false;
}
void Browser::TestCase1()
{
loadPlugin();
QStringList list;
list = odbcInterface->keys();
QMessageBox msgBox;
if(list.length() >0)
{
msgBox.setText("Test1 success");
}
else
{
msgBox.setText("Test1 failure");
}
msgBox.exec();
}
Testing:
In browser.cpp file, the constructor Browser::Browser(QWidget *parent) was modified by appending a call to void Browser::TestCase1()
Compile Output:
15:09:18: Running build steps for project odbcplugin...
15:09:18: Configuration unchanged, skipping qmake step.
15:09:18: Starting: "C:\QtSDK\QtCreator\bin\jom.exe"
cd sqlbrowser\ && C:\QtSDK\QtCreator\bin\jom.exe -nologo -j 2 -f Makefile
C:\QtSDK\QtCreator\bin\jom.exe -nologo -j 2 -f Makefile.Debug
cd odbc\ && C:\QtSDK\QtCreator\bin\jom.exe -nologo -j 2 -f Makefile
C:\QtSDK\QtCreator\bin\jom.exe -nologo -j 2 -f Makefile.Release
link /LIBPATH:"c:\QtSDK\Desktop\Qt\4.8.1\msvc2010\lib" /NOLOGO /DYNAMICBASE /NXCOMPAT /INCREMENTAL:NO /DLL /MANIFEST /MANIFESTFILE:"release\qsqlodbc.intermediate.manifest" /VERSION:4.81 /OUT:..\plugins\qsqlodbc4.dll #C:\DOCUME~1\SHAINE~1\LOCALS~1\Temp\qsqlodbc4.dll.5076.0.jom
Creating library ..\plugins\qsqlodbc4.lib and object ..\plugins\qsqlodbc4.exp
mainODBC.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall QODBCDriverPlugin::metaObject(void)const " (?metaObject#QODBCDriverPlugin##UBEPBUQMetaObject##XZ)
mainODBC.obj : error LNK2001: unresolved external symbol "public: virtual void * __thiscall QODBCDriverPlugin::qt_metacast(char const *)" (?qt_metacast#QODBCDriverPlugin##UAEPAXPBD#Z)
mainODBC.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall QODBCDriverPlugin::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall#QODBCDriverPlugin##UAEHW4Call#QMetaObject##HPAPAX#Z)
..\plugins\qsqlodbc4.dll : fatal error LNK1120: 3 unresolved externals
jom 1.0.6 - empower your cores
command failed with exit code 1120
command failed with exit code 2
command failed with exit code 2
15:09:19: The process "C:\QtSDK\QtCreator\bin\jom.exe" exited with code 2.
Error while building project odbcplugin (target: Desktop)
When executing build step 'Make'
Related
I am currently learning C and am trying to understand the possibilities of dynamic libraries.
My current question is, if I have a simple "Hello World" application in C called "ProgA", and this program dynamically loads a shared library with some example code called "LibB", can LibB access a global variable in ProgA, which was declared as external?
Given is the following example code for demonstration of the problem:
file header.h
#ifndef TEST_H
#define TEST_H
typedef struct test_import_s {
int some_field;
} test_import_t;
extern test_import_t newtestimport;
#endif
file prog_a.c
#include <stdio.h>
#include <windows.h>
#include "header.h"
test_import_t newtestimport = {
.some_field = 42
};
int main()
{
HINSTANCE hinstLib;
typedef void (*FunctionPointer)();
newtestimport.some_field = 42;
hinstLib = LoadLibrary("lib_b.dll");
if (hinstLib != NULL)
{
FunctionPointer initialize_lib_b;
initialize_lib_b = (FunctionPointer)GetProcAddress(hinstLib, "initialize_lib_b");
if (initialize_lib_b != NULL)
{
initialize_lib_b();
}
FreeLibrary(hinstLib);
}
return 0;
}
file lib_b.c
#include <stdio.h>
#include "header.h"
test_import_t *timp;
void initialize_lib_b() {
timp = &newtestimport;
int some_field = timp->some_field;
printf("Result from function: %d\n", some_field);
}
file CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(dynamic-library-2 C)
set(CMAKE_C_STANDARD 23)
add_library(lib_b SHARED lib_b.c)
set_target_properties(lib_b PROPERTIES PREFIX "" OUTPUT_NAME "lib_b")
add_executable(prog_a prog_a.c)
target_link_libraries(prog_a lib_b)
In the above example, the headerfile header.h defines the struct test_import_t and an external variable newtestimport using this struct. In the C file of the main program prog_a.c one property of this struct is assigned the value 42. It then dynamically loads the library lib_b.c using the Windows API and executes a function in it. The function then should access the variable newtestimport of the main program and print out the value of the variable (42).
This example does not work. The compiler throws the following error:
====================[ Build | prog_a | Debug ]==================================
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\cmake\win\x64\bin\cmake.exe --build C:\Users\user1\projects\learning-c\cmake-build-debug --target prog_a -j 9
[1/2] Linking C shared library dynamic-library-2\lib_b.dll
FAILED: dynamic-library-2/lib_b.dll dynamic-library-2/liblib_b.dll.a
cmd.exe /C "cd . && C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin\gcc.exe -fPIC -g -Wl,--export-all-symbols -shared -o dynamic-library-2\lib_b.dll -Wl,--out-implib,dynamic-library-2\liblib_b.dll.a -Wl,--major-image-version,0,--minor-image-version,0 dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin/ld.exe: dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj:lib_b.c:(.rdata$.refptr.newtestimport[.refptr.newtestimport]+0x0): undefined reference to `newtestimport'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
How can the example be fixed to accomplish the described goal?
Windows DLLs are self-contained, and can not have undefined references similar to newtestimport, unless these references are satisfied by another DLL.
How can the example be fixed to accomplish the described goal?
The best fix is to pass the address of newtestimport into the function that needs it (initialize_lib_b() here).
If for some reason you can't do that, your next best option is to define the newtestimport as a dllexport variable in another DLL, e.g. lib_c.dll.
Then both the main executable and lib_b.dll would be linked against lib_c.lib, and would both use that variable from lib_c.dll.
P.S. Global variables are a "code smell" and a significant source of bugs. You should avoid them whenever possible, and in your example there doesn't seem to be any good reason to use them.
For some reason I am unable to load my plugins anymore, although it has worked previously. I have a plugin loader code in my MainWindow, which is supposed to load every .dll found in a specific folder. The MainWindow Code contains the following:
Interface
#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QtPlugin>
// forward declarations
class MainWindow;
struct P3DData;
class PluginInterface
{
public:
virtual bool createPublisher(MainWindow*, P3DData*) = 0;
};
#define PLUGIN_INTERFACE_iid "PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PLUGIN_INTERFACE_iid)
#endif // PLUGININTERFACE_H
loadPlugins()
bool MainWindow::loadPlugins()
{
QDir pluginsDir(qApp->applicationDirPath());
pluginsDir.cd("plugins");
const auto entryList = pluginsDir.entryList(QDir::Files);
for(const QString &fileName : entryList)
{
QString dllPath = pluginsDir.absoluteFilePath(fileName);
QPluginLoader* loader = new QPluginLoader(dllPath);
loaderList.push_back(loader);
QObject *plugin = loader->instance();
if (plugin)
{
pluginList.push_back(qobject_cast<PluginInterface *>(plugin));
pluginList.last()->createPublisher(this, simDataPtr);
pluginCount++;
continue;
}
else
{
qDebug() << "[PluginLoader] '" + fileName + "': " + loader->errorString();
delete loaderList.takeLast();
}
}
}
Besides my MainWindow, there is another subdir in my project, which is the plugin "Position". The plugin is deployed into the correct "plugins" folder, which the loadPlugin() method from the MainWindow iterates through. The plugin uses following code to implement the interface:
#include <QObject>
#include "MainWindow.h"
class PositionPublisher : public QObject, PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "PluginInterface")
Q_INTERFACES(PluginInterface)
public:
PositionPublisher();
~PositionPublisher();
bool createPublisher(MainWindow* _window, P3DData* _simDataPtr) override;
//...
};
When trying to run loadPlugins() now, it checks the correct Position.dll file but the "if(plugin)" part returns false and loader->errorString() is executed giving the following error:
[PluginLoader] 'Position.dll': Cannot load library
F:\DEV\build\simNET\bin\plugins\Position.dll: Cannot find the
specified module.
I have already checked and tried the following:
the Plugins folder actually contains the Position.dll file
both projects (MainWindow and Plugin) are built in release mode
the dependencies of the plugin (two libs) exist and the specified path in the pro file is correct
.pro file of plugin:
QT += widgets
TEMPLATE = lib
CONFIG += c++11
CONFIG += plugin
CONFIG += release
SOURCES += \
Position.cpp \
PositionPubSubTypes.cpp \
PositionPublisher.cpp
HEADERS += \
Position.h \
PositionPubSubTypes.h \
PositionPublisher.h
DISTFILES += \
Position.idl
INCLUDEPATH += ../../application
TARGET = $$qtLibraryTarget(Position)
DESTDIR = ../../bin/plugins
INCLUDEPATH += "F:/DEV/prog/FastRTPSv1.5/include"
DEPENDPATH += "F:/DEV/prog/FastRTPSv1.5/include"
LIBS += -L"F:/DEV/prog/FastRTPSv1.5/lib/x64Win64VS2015" -lfastrtps-1.5
PRE_TARGETDEPS += F:/DEV/prog/FastRTPSv1.5/lib/x64Win64VS2015/fastrtps-1.5.lib
INCLUDEPATH += 'F:/Programme/Prepar3D v4/SDK/inc/SimConnect'
DEPENDPATH += 'F:/Programme/Prepar3D v4/SDK/inc/SimConnect'
LIBS += -L'F:/Programme/Prepar3D v4/SDK/lib/SimConnect' -lSimConnect
PRE_TARGETDEPS += 'F:/Programme/Prepar3D v4/SDK/lib/SimConnect/SimConnect.lib'
Does anyone have an idea why it does not load the plugin??
Qt plugin loader is trying to tell you that a Qt module used in your plugin doesn't exist so first you need to check which Qt modules your plugin depends on then,
If you're trying to run the application from Qt Creator itself, and it gives you this error, then maybe the DLL file got deleted for some reason or you're trying to use the plugin with a Qt version that doesn't have its needed modules. (for example, Qt6 doesn't contain the QtSerialPort module but Qt5 did (at the time of writing this)).
If you're trying to run the application directly outside Qt Creator then you need to copy the required module's dll file into your applications root directory.
I am trying to connect to SQL Server 2008R2 with my qt application in windows but I am getting errors. Here's what I am doing:
#include "ui_test1.h";
#include "QtSql/QtSql";
void Test1::on_btnsnd_clicked()
{
QSqlDatabase db = QSqlDatabase::addDatabase("ODBC");
db.setHostName("ITPL_PC1");
db.setDatabaseName("Test");
db.setUserName("sa");
db.setPassword("insforia");
db.open();
QSqlQueryModel *model = new QSqlQueryModel;
QString query = "insert into qttable(PID) values('ARUP')";
model->setQuery(query, db);
db.close();
}
i am getting this error 27 times :
test1.obj:-1: error: LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall QSqlDatabase::~QSqlDatabase(void)" (__imp_??1QSqlDatabase##QAE#XZ) referenced in function "private: void __thiscall Test1::on_btnsnd_clicked(void)" (?on_btnsnd_clicked#Test1##AAEXXZ)
I don't know how to do this (I found this in google.)
What should I do to fix it?
If you use MSVC, you should add %QTDIR%/lib/QtSql4.lib to Release configuration of your project and %QTDIR%/lib/QtSqld4.lib to Debug one. You should change 4 in file names to 500, if you use Qt 5.0. So, file names would be %QTDIR%/lib/QtSql500.lib and %QTDIR%/lib/QtSqld500.lib
If you use QtCreator you should add the next line into your .pro file
QT += sql
Upd: added description for QtCreator's .pro file
I'm new to Qt, and trying to compile and link a simple "Hello, World" program using Microsoft's "CL.exe". Any advice on how to do this?
The program is:
#include <QtGui>
int main ( int argc, char * argv [] )
{
QApplication app ( argc, argv ) ;
QLabel label ( "Hello, world!" ) ;
label.show() ;
return app.exec() ;
}
I compile and link with:
C:\PROGRA~1\MICROS~1.0\VC\bin\cl.EXE -nologo -Zm200 -Zc:wchar_t- -O2 -MD ^
-W3 -w34100 -w34189 -DUNICODE ^
-DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB ^
-DQT_THREAD_SUPPORT -DQT_NO_DYNAMIC_CAST ^
-I"C:\Qt\2010.05\qt\include\QtCore" ^
-I"C:\Qt\2010.05\qt\include\QtGui" ^
-I"C:\Qt\2010.05\qt\include" ^
-I"." ^
-I"C:\Qt\2010.05\qt\include\ActiveQt" ^
-I"release" ^
-I"C:\Qt\2010.05\qt\mkspecs\win32-msvc2008" ^
-I"C:\Progra~1\MICROS~1.0\VC\include" ^
Hello.cpp ^
/link /LIBPATH:"C:\Qt\2010.05\qt\lib" ^
/LIBPATH:"C:\Progra~1\MICROS~1.0\VC\lib" ^
/LIBPATH:"C:\Progra~1\MID05A~1\VC\PLATFO~1\Lib"
My linker errors include:
Hello.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) pub
lic: virtual __thiscall QApplication::~QApplication(void)" (__imp_??1QApplicatio
n##UAE#XZ) referenced in function _main
Hello.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) pub
lic: virtual __thiscall QLabel::~QLabel(void)" (__imp_??1QLabel##UAE#XZ) referen
ced in function _main
Hello.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) pub
lic: static int __cdecl QApplication::exec(void)" (__imp_?exec#QApplication##SAH
XZ) referenced in function _main
Hello.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) pub
lic: void __thiscall QWidget::show(void)" (__imp_?show#QWidget##QAEXXZ) referenc
ed in function _main
.
.
.
My Hello.pro file contains:
######################################################################
# Automatically generated by qmake (2.01a) Mon Dec 13 15:58:13 2010
######################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
SOURCES += Hello.cpp
You should be linking against QtCore and QtGui libraries but you don't. What does your .pro file look like?
Edited to add after the .pro file was posted: Your .pro file looks all right. You're not overwriting the QT variable which by default includes core gui, which should result in QtCode and QtGui being linked to. It's also evident by the fact that both QtCore and QtGui are present in your include paths.
I haven't used cl.exe with Qt but you could try adding LIBS += -lQtCore -lQtGui to force linking to QtCore and QtGui. (There's probably a more elegant fix though.)
Your PATH should include the qt/bin directory. In a Visual Studio Command Prompt you should do a "qmake hello.pro" which creates three makefiles, and then "nmake". If you want to use vcxproj files, you should change your TEMPLATE to vcapp.
trying to compile and link a simple
"Hello, World" program using
Microsoft's "CL.exe"
Hey i think there are easier ways of getting qt applications compiled on visual studio. Instead of struggling with commandline options why not install the qt visual studio addin that makes compiling projects soooooooo easy? Just a suggestion :)
Greetings all,
I am trying to implement a QT Plugin with CMake. But this "Q_EXPORT_PLUGIN2" directive stops my class from compiling. I can compile the plugin if I commented this out,but it won't work as a plugin if I do so.
QT doc says:
Q_EXPORT_PLUGIN2 ( PluginName, ClassName )
The value of PluginName should
correspond to the TARGET specified in
the plugin's project file
What about in CMake case? What should be the value for 'PluginName'?
Here is my Plugin Interface :
#ifndef RZPLUGIN3DVIEWERFACTORY_H_
#define RZPLUGIN3DVIEWERFACTORY_H_
#include <QObject>
#include "plugin/IRzPluginFactory.h"
class RzPlugin3DViewerFactory :public QObject,public IRzPluginFactory{
Q_OBJECT
Q_INTERFACES(IRzPluginFactory)
private:
QString uid;
public:
RzPlugin3DViewerFactory();
virtual ~RzPlugin3DViewerFactory();
IRzPlugin* createPluginInstance();
IRzPluginContext* createPluginContextInstance();
QString & getPluginUID();
};
#endif /* RZPLUGIN3DVIEWERFACTORY_H_ */
And implementation
#include "RzPlugin3DViewerFactory.h"
#include "RzPlugin3DViewer.h"
RzPlugin3DViewerFactory::RzPlugin3DViewerFactory() {
uid.append("RzPlugin3DView");
}
RzPlugin3DViewerFactory::~RzPlugin3DViewerFactory() {
// TODO Auto-generated destructor stub
}
IRzPlugin* RzPlugin3DViewerFactory::createPluginInstance(){
RzPlugin3DViewer *p=new RzPlugin3DViewer;
return p;
}
IRzPluginContext* RzPlugin3DViewerFactory::createPluginContextInstance()
{
return NULL;
}
QString & RzPlugin3DViewerFactory::getPluginUID()
{
return uid;
}
Q_EXPORT_PLUGIN2(pnp_extrafilters, RzPlugin3DViewerFactory)
Error Message is :
[ 12%] Building CXX object
CMakeFiles/RzDL3DView.dir/RzPlugin3DViewerFactory.cpp
.obj
C:\svn\osaka3d\trunk\osaka3d\rinzo-platform\src\dlplugins\threedviewer\RzPlugin3
DViewerFactory.cpp:36: error: expected
constructor, destructor, or type
conversi on before '(' token make[2]:
*** [CMakeFiles/RzDL3DView.dir/RzPlugin3DViewerFactory.cpp.obj]
Error 1
make[1]: *
[CMakeFiles/RzDL3DView.dir/all] Error
2 make: * [all] Error 2
Ok , I fixed the problem by giving the project name specified in Cmake file.
PROJECT (RinzoDLPlugin3DViewer CXX C)
So,now in CPP file its
Q_EXPORT_PLUGIN2(RinzoDLPlugin3DViewer , RzPlugin3DViewerFactory)
and included qpluginh.h
#include <qplugin.h>
I think the macro should be Q_EXPORT_PLUGIN2(pnp_rzplugin3dviewerfactory, RzPlugin3DViewerFactory) or whatever you have listed as the target name in the .pro file. In fact, the "pnp" part stands for "Plug & Paint" which is the Qt demo program for writing plugins :)
Edit:
Since I misunderstood how CMake works, this information isn't really relevant to the OP. I did do a quick search however and turned up this discussion of Qt, plugins and CMake. I hope there is some useful info there.
http://lists.trolltech.com/qt-interest/2007-05/msg00506.html