• 3850阅读
  • 5回复

导出Qt模块DLL给VC调用 [复制链接]

上一主题 下一主题
离线spygg
 

只看楼主 倒序阅读 楼主  发表于: 2019-10-22

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



起源

大家都知道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. qsqldatabase.h  qsqlerror.h  qsqlrecord.h
  2. qsqldriver.h    qsqlquery.h  qvariant.h
请注意,以上文件为真实的文件,一般在Qt安装目录下的corelib目录下(可以通过QtCreator跳转过去,一直找到真实的文件为止)
代码如下

头文件
  1. //qsqllib.h
  2. #ifndef QSQLLIB_H
  3. #define QSQLLIB_H
  4. #include "include/qsqldatabase.h"
  5. #include "include/qsqlquery.h"
  6. #include "include/qsqldriver.h"
  7. #include "include/qsqlerror.h"
  8. #include "include/qsqlrecord.h"
  9. #include "include/qvariant.h"
  10. #if defined(QSQLLIB_LIBRARY)
  11. #define QSQLLIBSHARED_EXPORT  __declspec(dllexport)
  12. #else
  13. #define QSQLLIBSHARED_EXPORT  __declspec(dllimport)
  14. #endif
  15. class QSQLLIBSHARED_EXPORT CSql
  16. {
  17. public:
  18.     CSql();
  19.     ~CSql();
  20.     bool exec( QSqlQuery *query, QString queryString);
  21.     void closeDataBase();
  22.     bool connectDataBase(QString dbName,
  23.         QString type = "QSQLITE",
  24.         QString host = "127.0.0.1",
  25.         int port = -1,
  26.         QString userName = "",
  27.         QString passWd = "");
  28.     QString lastError();
  29.     QStringList drivers();
  30.     QSqlDatabase *dataBase();
  31.     QSqlRecord tableInfo(QString table);
  32.     QStringList tables();
  33. private:
  34.     QSqlDatabase m_db;
  35.     QStringList m_tables;
  36. };
  37. #if !defined(QSQLLIB_LIBRARY)
  38. #ifdef _DEBUG
  39. #pragma comment(lib, "lib/qSqlLibd.lib")
  40. #pragma comment(lib, "lib/QtSqld4.lib")
  41. #pragma comment(lib, "lib/QtCored4.lib")
  42. #else
  43. #pragma comment(lib, "lib/qSqlLib.lib")
  44. #pragma comment(lib, "lib/QtSql4.lib")
  45. #pragma comment(lib, "lib/QtCore4.lib")
  46. #endif
  47. #endif
  48. #endif // QSQLLIB_H

CPP文件
  1. //代码 qsqllib.cpp
  2. #include "qsqllib.h"
  3. #include <QCoreApplication>
  4. #include <QDir>
  5. CSql::CSql()
  6. {
  7. }
  8. CSql::~CSql()
  9. {
  10.     if(m_db.isOpen()){
  11.         closeDataBase();
  12.     }
  13. }
  14. void CSql::closeDataBase()
  15. {
  16.     {
  17.         QString connectionName = m_db.connectionName();
  18.         m_db.close();
  19.         m_db = QSqlDatabase();
  20.         QSqlDatabase::removeDatabase(connectionName);
  21.     }
  22. }
  23. bool CSql::connectDataBase(QString dbName, QString type,
  24. QString host, int port, QString userName, QString passWd)
  25. {
  26.     QCoreApplication::addLibraryPath(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
  27.     QCoreApplication::addLibraryPath(QDir::toNativeSeparators("./"));
  28.     //关闭已有连接,将m_db重置
  29.     closeDataBase();
  30.     m_db = QSqlDatabase::addDatabase(type);
  31.     m_db.setHostName(host);
  32.     m_db.setPort(port);
  33.     m_db.setUserName(userName);
  34.     m_db.setPassword(passWd);
  35.     m_db.setDatabaseName(dbName);
  36.     if (!m_db.open()) {
  37.         //关闭数据库,必须这样..
  38.         closeDataBase();
  39.         return false;
  40.     }
  41.     if(type.compare("QOCI") == 0 || type.compare("QODBC") == 0){
  42.         QStringList tl;
  43.         QSqlQuery query;
  44.         const QLatin1String tableQuery("select table_name from user_tables");
  45.         exec(&query, tableQuery);
  46.         while (query.next()) {
  47.             tl.append(query.value(0).toString());
  48.         }
  49.         m_tables = tl;
  50.     }
  51.     else{
  52.         m_tables = m_db.tables();
  53.     }
  54.     return true;
  55. }
  56. QSqlDatabase *CSql::dataBase()
  57. {
  58.     return &m_db;
  59. }
  60. QSqlRecord CSql::tableInfo(QString table)
  61. {
  62.     //开启事务
  63.     m_db.transaction();
  64.     QSqlRecord record =  m_db.record(table);
  65.     m_db.commit();
  66.     return record;
  67. }
  68. QStringList CSql::tables()
  69. {
  70.     return m_tables;
  71. }
  72. QStringList drivers()
  73. {
  74.     return QSqlDatabase::drivers();
  75. }
  76. bool CSql::exec( QSqlQuery *query, QString queryString)
  77. {
  78.     bool bok = false;
  79.     //开启事务
  80.     m_db.transaction();
  81.     if(!queryString.isEmpty()){
  82.         bok = query->exec(queryString);
  83.     }
  84.     else{
  85.         bok = query->exec();
  86.     }
  87.     m_db.commit();
  88.     return bok;
  89. }
  90. QString CSql::lastError()
  91. {
  92.     return m_db.lastError().text();
  93. }


测试调用程序
  1. 新建一个MFC工程
  2. 加入上一步中的导出头文件(见底部链接下载即可)
  3. 对用到的头文件拷贝到当前文件夹下来(对于 VC6.0来说,必须将文件编码转为assic版本,不然会报错)
  4. 找到对应版本的.lib文件拷贝到当前目录下lib文件夹下
  5. 编写代码,如下
  1. #include "include/qsqllib.h"
  2. void foo()
  3. {
  4.     bool bok = false;
  5.         
  6.     char dbName[255] = {0};
  7.     char type[20] = {0};    //"QSQLITE"
  8.     char host[20] = {0};   //"127.0.0.1",
  9.     char szPort[20] = {0};
  10.     char userName[50] = {0};
  11.     char passWd[50] = {0};
  12.     //......
  13.     CSql sql;
  14.     bok = sql.connectDataBase(dbName, type, host, atoi(szPort), userName, passWd);
  15.     if(!bok){
  16.         AfxMessageBox(sql.lastError().toLocal8Bit().constData());
  17.         return FALSE;
  18.     }
  19.     QSqlQuery query;
  20.     bok = sql.exec(&query, "select * from table");
  21.     if(bok){
  22.         while(query.next()){
  23.             TRACE("%d, %s\n", query.value(0).toInt(),query.value(1).toString().toLocal8Bit().constData());
  24.         }
  25.     }
  26. }

经验
  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. QSqlQuery *query = new QSqlQuery();
  3. bok = exec(query, "select * from table");
  4. if(bok){
  5.     while(query->next()){
  6.         TRACE("%d, %s\n", query->value(0).toInt(),query->value(1).toString().toLocal8Bit().constData());
  7.     }
  8. }
  9. //这里要delete
  10. delete query;


方法2:(有点2)
  1. //用局部区域变量来自动释放
  2. {
  3.     QSqlQuery query;
  4.     bok = exec(&query, "select * from table");
  5.     if(bok){
  6.         while(query.next()){
  7.             TRACE("%d, %s\n", query.value(0).toInt(), query.value(1).toString().toLocal8Bit().constData());
  8.         }
  9.     }
  10. }


PS
也曾尝试将Gui相关的导出,比如QPixmap类,但是没有成功(经测试可以成功, 使用前需要实例化QApplication)
PPS







4条评分好评度+1贡献值+1金钱+1威望+1
20091001753 好评度 +1 - 2019-10-25
20091001753 贡献值 +1 - 2019-10-25
20091001753 威望 +1 - 2019-10-25
20091001753 金钱 +1 - 2019-10-25
签名就是这么浪
离线liulin188

只看该作者 1楼 发表于: 2019-11-19
    
https://wiki.qt.io/Qt_5.12_Release
https://wiki.qt.io/New_Features_in_Qt_5.12
https://wiki.qt.io/Qt_5.12.0_Known_Issues
https://www.qt.io/blog/qt-5.13.2-released
https://www.qt.io/blog/qt-creator-4.10.2-released
https://wiki.qt.io/Qt_5.12_Tools_and_Versions
离线1003780259

只看该作者 2楼 发表于: 2019-11-26
QT的代码要跑起来不是必须有一个QApplication的实例吗? 单独导出一个模块,不创建一个QApplication也能用啊?求指教!
离线spygg

只看该作者 3楼 发表于: 2019-11-30
回 1003780259 的帖子
1003780259:QT的代码要跑起来不是必须有一个QApplication的实例吗? 单独导出一个模块,不创建一个QApplication也能用啊?求指教! (2019-11-26 10:03) 

依赖事件循环的才需要,比如显示模块
签名就是这么浪
离线rpdhunter

只看该作者 4楼 发表于: 2019-12-31
两个问题啊
一。为何还要用MFC,难道是旧项目维护?
二。为何用vc6.0,难道还是旧项目维护?
离线1003780259

只看该作者 5楼 发表于: 2020-01-04
回 spygg 的帖子
spygg:依赖事件循环的才需要,比如显示模块 (2019-11-30 13:42) 

哦哦,明白了,我之前用了通信模块的内容,是依赖事件循环的,谢谢回复!
快速回复
限100 字节
 
上一个 下一个