导出Qt模块DLL给VC调用

原文链接导出Qt模块DLL给VC调用

由于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文件夹放入下列文件

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();
}

测试调用程序

  1. 新建一个MFC工程
  2. 加入上一步中的导出头文件(见底部链接下载即可)
  3. 对用到的头文件拷贝到当前文件夹下来(对于 VC6.0来说,必须将文件编码转为assic版本,不然会报错)
  4. 找到对应版本的.lib文件拷贝到当前目录下lib文件夹下
  5. 编写代码,如下
#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:(勉强可以接受)
//用指针
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的事件循环,嗯,就是这么浪..
}

下载链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值