logo头像

技术引领生活!

导出Qt模块DLL给VC调用

本文于360天之前发表,文中内容可能已经过时。

由于Qt的强大特性,在VC开发时一直想能否导出Qt的各个模块为VC所用,本文介绍一种思路,抛砖引玉下,经测试满足 VC 6.0及以上版本

起源

大家都知道MFC框架没有很好的数据库框架来用,而Qt的数据库模块简直不要太好用,于是就想能否来个乾坤大挪移呢?经过一阵乱撸,终于搞定!!!

借鉴思路

雨田哥的博客封装QtCore,于是想除了QtCore能否将Qt的数据库模块也导出来呢?答案是肯定的

实现步骤

分为两个部分:

  1. DLL模块
  2. 测试调用程序

DLL编写

先说下版本,为了方便的在Windows下调用,肯定使用MSVC版本的Qt,我电脑上装了Qt 4.8.0 VS 2010版本

  • 新建Qt C++ library工程

偷巧的方法为先使用Qt Creator 创建一个DLL工程,然后再用VS插件打开进行编译,经测试需要在工程文件夹下建立include文件夹放入下列文件

1
2
qsqldatabase.h  qsqlerror.h  qsqlrecord.h
qsqldriver.h qsqlquery.h qvariant.h

请注意,以上文件为真实的文件,一般在Qt安装目录下的corelib目录下(可以通过QtCreator跳转过去,一直找到真实的文件为止)

  • 代码如下

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//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文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//代码 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();
}

测试调用程序

  1. 新建一个MFC工程
  2. 加入上一步中的导出头文件(见底部链接下载即可)
  3. 对用到的头文件拷贝到当前文件夹下来(对于 VC6.0来说,必须将文件编码转为assic版本,不然会报错)
  4. 找到对应版本的.lib文件拷贝到当前目录下lib文件夹下
  5. 编写代码,如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #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());
    }
    }
    }

经验

  1. VC 6.0版本要求文件编码为ASSIC,Qt默认版本应该是 utf-8,所以可以通过notepad++转化,本人已经将所有的格式转化完毕,见附件头文件
  2. 对于编译错误先注释起来(为毛这样能行?因为对于DLL来说只要有个声明就行.h文件中声明的东西在对应的DLL中都已经实现了)
  3. 对于数据库提示Driver not loader错误,需要将sqldriver路径加入到库路径,见代码中的QCoreApplication::addLibraryPath();
  4. 数据库关闭错误说明见Qt数据库removeDatabase注意事项
  5. 在C语言的DLL中(本文未给出)其他版本的都OK,但是oracle老是在退出的时候崩溃,一阵乱撸之下发现有两种方法可以搞定(貌似是资源释放的问题),都不是很理想,于是就有了C++的class版本

    • 方法1:(勉强可以接受)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //用指针
      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)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //用局部区域变量来自动释放
      {
      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类,需要在头文件中注释掉错误行即可

    1
    //Q_DECLARE_SHARED(QPixmap)
  • 需要注意的是在使用GUI相关的类时会提示在使用前必须初始化QApplication
    解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //在实例化QPixmap之前调用实例化QApplication

    void MFC_FOO()
    {
    int argc = 1;
    char *argv[] = {
    "foo"
    };

    QApplication a(argc, argv);

    QPixmap pix;

    pix.loadFromData(....);

    //MFC 代码 ......
    // .....


    //不需要调用a的事件循环,嗯,就是这么浪..
    }

下载链接

支付宝打赏 微信打赏

您的支持是我前行的动力!