原文链接导出Qt模块DLL给VC调用
由于Qt的强大特性,在VC开发时一直想能否导出Qt的各个模块为VC所用,本文介绍一种思路,抛砖引玉下,经测试满足 VC 6.0及以上版本
起源
大家都知道MFC框架没有很好的数据库框架来用,而Qt的数据库模块简直不要太好用,于是就想能否来个乾坤大挪移呢?经过一阵乱撸,终于搞定!!!
借鉴思路
雨田哥的博客之 封装QtCore,于是想除了QtCore能否将Qt的数据库模块也导出来呢?答案是肯定的
实现步骤
分为两个部分:
- DLL模块
- 测试调用程序
DLL编写
先说下版本,为了方便的在Windows下调用,肯定使用MSVC版本的Qt,我电脑上装了Qt 4.8.0 VS 2010版本
- 新建Qt C++ library工程
偷巧的方法为先使用Qt Creator 创建一个DLL工程,然后再用VS插件打开进行编译,经测试需要在工程文件夹下建立include文件夹放入下列文件
qsqldatabase.h qsqlerror.h qsqlrecord.h
qsqldriver.h qsqlquery.h qvariant.h
请注意,以上文件为真实的文件,一般在Qt安装目录下的corelib目录下(可以通过QtCreator跳转过去,一直找到真实的文件为止)
- 代码如下
头文件
//qsqllib.h
#ifndef QSQLLIB_H
#define QSQLLIB_H
#include "include/qsqldatabase.h"
#include "include/qsqlquery.h"
#include "include/qsqldriver.h"
#include "include/qsqlerror.h"
#include "include/qsqlrecord.h"
#include "include/qvariant.h"
#if defined(QSQLLIB_LIBRARY)
#define QSQLLIBSHARED_EXPORT __declspec(dllexport)
#else
#define QSQLLIBSHARED_EXPORT __declspec(dllimport)
#endif
class QSQLLIBSHARED_EXPORT CSql
{
public:
CSql();
~CSql();
bool exec( QSqlQuery *query, QString queryString);
void closeDataBase();
bool connectDataBase(QString dbName,
QString type = "QSQLITE",
QString host = "127.0.0.1",
int port = -1,
QString userName = "",
QString passWd = "");
QString lastError();
QStringList drivers();
QSqlDatabase *dataBase();
QSqlRecord tableInfo(QString table);
QStringList tables();
private:
QSqlDatabase m_db;
QStringList m_tables;
};
#if !defined(QSQLLIB_LIBRARY)
#ifdef _DEBUG
#pragma comment(lib, "lib/qSqlLibd.lib")
#pragma comment(lib, "lib/QtSqld4.lib")
#pragma comment(lib, "lib/QtCored4.lib")
#else
#pragma comment(lib, "lib/qSqlLib.lib")
#pragma comment(lib, "lib/QtSql4.lib")
#pragma comment(lib, "lib/QtCore4.lib")
#endif
#endif
#endif // QSQLLIB_H
Cpp文件
//代码 qsqllib.cpp
#include "qsqllib.h"
#include <QCoreApplication>
#include <QDir>
CSql::CSql()
{
}
CSql::~CSql()
{
if(m_db.isOpen()){
closeDataBase();
}
}
void CSql::closeDataBase()
{
{
QString connectionName = m_db.connectionName();
m_db.close();
m_db = QSqlDatabase();
QSqlDatabase::removeDatabase(connectionName);
}
}
bool CSql::connectDataBase(QString dbName, QString type,
QString host, int port, QString userName, QString passWd)
{
QCoreApplication::addLibraryPath(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
QCoreApplication::addLibraryPath(QDir::toNativeSeparators("./"));
//关闭已有连接,将m_db重置
closeDataBase();
m_db = QSqlDatabase::addDatabase(type);
m_db.setHostName(host);
m_db.setPort(port);
m_db.setUserName(userName);
m_db.setPassword(passWd);
m_db.setDatabaseName(dbName);
if (!m_db.open()) {
//关闭数据库,必须这样..
closeDataBase();
return false;
}
if(type.compare("QOCI") == 0 || type.compare("QODBC") == 0){
QStringList tl;
QSqlQuery query;
const QLatin1String tableQuery("select table_name from user_tables");
exec(&query, tableQuery);
while (query.next()) {
tl.append(query.value(0).toString());
}
m_tables = tl;
}
else{
m_tables = m_db.tables();
}
return true;
}
QSqlDatabase *CSql::dataBase()
{
return &m_db;
}
QSqlRecord CSql::tableInfo(QString table)
{
//开启事务
m_db.transaction();
QSqlRecord record = m_db.record(table);
m_db.commit();
return record;
}
QStringList CSql::tables()
{
return m_tables;
}
QStringList drivers()
{
return QSqlDatabase::drivers();
}
bool CSql::exec( QSqlQuery *query, QString queryString)
{
bool bok = false;
//开启事务
m_db.transaction();
if(!queryString.isEmpty()){
bok = query->exec(queryString);
}
else{
bok = query->exec();
}
m_db.commit();
return bok;
}
QString CSql::lastError()
{
return m_db.lastError().text();
}
测试调用程序
- 新建一个MFC工程
- 加入上一步中的导出头文件(见底部链接下载即可)
- 对用到的头文件拷贝到当前文件夹下来(对于 VC6.0来说,必须将文件编码转为assic版本,不然会报错)
- 找到对应版本的.lib文件拷贝到当前目录下lib文件夹下
- 编写代码,如下
#include "include/qsqllib.h"
void foo()
{
bool bok = false;
char dbName[255] = {0};
char type[20] = {0}; //"QSQLITE"
char host[20] = {0}; //"127.0.0.1",
char szPort[20] = {0};
char userName[50] = {0};
char passWd[50] = {0};
//......
CSql sql;
bok = sql.connectDataBase(dbName, type, host, atoi(szPort), userName, passWd);
if(!bok){
AfxMessageBox(sql.lastError().toLocal8Bit().constData());
return FALSE;
}
QSqlQuery query;
bok = sql.exec(&query, "select * from table");
if(bok){
while(query.next()){
TRACE("%d, %s\n", query.value(0).toInt(),query.value(1).toString().toLocal8Bit().constData());
}
}
}
经验
-
VC 6.0版本要求文件编码为ASSIC,Qt默认版本应该是 utf-8,所以可以通过notepad++转化,本人已经将所有的格式转化完毕,见附件头文件
-
对于编译错误先注释起来(为毛这样能行?因为对于DLL来说只要有个声明就行.h文件中声明的东西在对应的DLL中都已经实现了)
-
对于数据库提示Driver not loader错误,需要将sqldriver路径加入到库路径,见代码中的QCoreApplication::addLibraryPath();
-
数据库关闭错误说明见Qt数据库removeDatabase注意事项
-
在C语言的DLL中(本文未给出)其他版本的都OK,但是oracle老是在退出的时候崩溃,一阵乱撸之下发现有两种方法可以搞定(貌似是资源释放的问题),都不是很理想,于是就有了C++的class版本
- 方法1:(勉强可以接受)
//用指针
QSqlQuery *query = new QSqlQuery();
bok = exec(query, "select * from table");
if(bok){
while(query->next()){
TRACE("%d, %s\n", query->value(0).toInt(),query->value(1).toString().toLocal8Bit().constData());
}
}
//这里要delete
delete query;
- 方法2:(有点2)
//用局部区域变量来自动释放
{
QSqlQuery query;
bok = exec(&query, "select * from table");
if(bok){
while(query.next()){
TRACE("%d, %s\n", query.value(0).toInt(), query.value(1).toString().toLocal8Bit().constData());
}
}
}
PS
也曾尝试将Gui相关的导出,比如QPixmap类,但是没有成功
更新
- GUI类也可以成功,比如QImage,可以直接使用, QPixmap类,需要在头文件中注释掉错误行即可
//Q_DECLARE_SHARED(QPixmap)
- 需要注意的是在使用GUI相关的类时会提示在使用前必须初始化QApplication
解决方案:
//在实例化QPixmap之前调用实例化QApplication
void MFC_FOO()
{
int argc = 1;
char *argv[] = {
"foo"
};
QApplication a(argc, argv);
QPixmap pix;
pix.loadFromData(....);
//MFC 代码 ......
// .....
//不需要调用a的事件循环,嗯,就是这么浪..
}
下载链接
- 附件中的头文件链接在来一遍头文件
- 对于最难缠的oracle数据来说,OCI版本的DLL见oracle的oci驱动DLL