Qt中的动态链接库编程(Q_DECL_IMPORT、Q_DECL_EXPORT)

Qt中的动态链接库编程(Q_DECL_IMPORT、Q_DECL_EXPORT)http blog sina com cn s blog 667fe4a50100 html1 Qt 中的动态链接库编程使用 C 面向对象的类编写 DLL 是要注意很多细节的 主要是二进制 ABI 兼容问题 COM 是一个很成功的例子 只要符合 COM 的规范 我们就能编写出很好的 DLL 来 然而 COM 是微软私生的 要想跨平台 我们还得另找它路 Qt 的跨平台特性同样令

一:Qt中的动态链接库编程

二、QT共享库和插件范例

    1> 编写共享库——Bil 项目的实现
该项目定义一个抽象的 IAnimal 类作为导出接口,供客户项目和插件项目使用。项目类型为共享库,将生成Bil.lib和Bil.dll两个文件,Bil.lib供Plugin项目和Test 项目引用,而Bil.dll将给Test.exe运行时动态加载。

新建一个头文件Bil.h,输入如下代码:

#ifndef BIL_H         #define BIL_H         #include  
  
             // 定义BIL_SHARE,使用者可以不用再处理符号的导入和导出细节         #ifdef BIL_LIB         # define BIL_SHARE Q_DECL_EXPORT         #else         # define BIL_SHARE Q_DECL_IMPORT         #endif         #endif // BIL_H       
  
  • 你现在可能不知道BIL_SHARE宏有何用处。没关系,请继续看下面的IAnimal接口定义代码
#ifndef IANIMAL_H         #define IANIMAL_H         #include "Bil.h"         class BIL_SHARE IAnimal         {         public:             IAnimal();             virtual ~IAnimal();         public:             virtual void Eat() = 0;             virtual void Run() = 0;             virtual void Sleep() = 0;         };                 #endif // IANIMAL_H       
  • 现在知道BIL_SHARE宏的妙用了吧。BIL_SHARE宏会根据项目编译选项 BIL_LIB有没有定义,自动声明IAnimal是导出类,还是导入类。所以,使用BIL_SHARE宏,我们只需要向IAnimal插件的开发者提供 同一份IAnimal定义文件(IAnimal.h)即可。

当然,我们得先在Bil项目的编译选项中定义BIL_LIB宏,使得在Bil项目内,BIL_SHARE就是导出符号的声明。插件项目就不要定义BIL_LIB了,因为在Animal插件项目中,IAnimal是导入符号。

编译选项如何定义宏?如果使用Visual Studio工程文件,依次展开:项目属性->配置属性->C/C++->预处理器,在预处理器定义中添加宏BIL_LIB即可;如果是QT工程文件,请在QT工程文件Bil.pro中加入如下定义:

        DEFINES += BIL_LIB    

在IAnimal接口中,我们定义了三个纯虚函数Eat()、Run()和 Sleep(),表示吃、跑和睡眠的动作,这是抽象的,因为不同的动物有不同的吃相和睡眠姿态,而世间的动物何止千千万——无所谓,让这些具体动物的不同 表现交给IAnimal插件的编写者发挥吧——这就是接口的魅力,加上插件的思想,整个应用程序就变成开放的,可扩展的了!

继续编写Anima类的实现文件Anima.cpp:

 #include "IAnimal.h"      IAnimal::IAnimal()      {      }      IAnimal::~IAnimal()      {      }      

虽然只实现了构造和析构函数,并且什么工作也不做,但这是必要的,我们暂时不要使用内联的构造和析构函数,否则在插件项目实现IAnimal时可能会出现链接错误。

好了,我们开始编译吧,生成整个Bil项目。最终我们得到两个输出文件:Bil.lib 和 Bil.dll。
我们向Animal插件开发者提供:
两个头文件:Bil.h 和 IAnimal.h
两个库文件:Bil.lib 和 Bil.dll
下面的插件类项目和客户项目就是依赖这些文件实现的,也许你更愿意把Bil看作是一个通用的DLL类库,就像QT或MFC一样——事实上也是如此,Bil就是这样一个动态的共享类库。











    2>. 编写Animal插件——BilDog和BilPanda项目的实现
现在,让我们来实现两个小插件。BilDog插件很简单,只是汇报下“我是Dog,我正在啃骨头”;BilPanda也是如此——这里仅仅是测试而已,实现的项目中,你可以尽情的发挥——没错,是在遵循IAnimal接口的前提下。
创建BilDog项目,把Bil项目输出的Bil.h、IAnimal.h和Bil.lib加入到工程。




创建Dog类的头文件Dog.h:

#ifndef CLASS_DOG_H #define CLASS_DOG_H #include "IAnimal.h" class Dog : public IAnimal { public: Dog(void); virtual ~Dog(void); public: virtual void Eat(); virtual void Run(); virtual void Sleep(); }; #endif // CLASS_DOG_H 

创建Dog类的实现文件Dog.cpp:

#include  
  
             #include "Dog.h"         Dog::Dog(void)         {         }         Dog::~Dog(void)         {         }         void Dog::Eat()         {             QMessageBox::information(NULL, "Hello", "Dog eating ...");         }         void Dog::Run()         {             QMessageBox::information(NULL, "Hello", "Dog running ...");         }         void Dog::Sleep()         {             QMessageBox::information(NULL, "Hello", "Dog sleeping ...");         }       
  
#ifndef BILDOG_H      #define BILDOG_H      #include "Dog.h"           // extern "C" 生成的导出符号没有任何修饰,方便主程序找到它      extern "C"     {       Q_DECL_EXPORT IAnimal * CreateAnimal();       Q_DECL_EXPORT void ReleaseAnimal(IAnimal * animal);      }      #endif // BILDOG_H      

调用QT的QMessageBox::information()函数弹出一个信息提示框。

还有一个非常重要的工作,我们得提供一个能够创建(释放)Animal具体对象(这里是Dog)的接口,并且把这些函
数导出,让主程序(Test.exe)能够解析这个接口函数,动态创建Animal对象,并访问其功能。

新建BilDog.h文件,输入下面的代码:





这两个函数的工作很简单,直接创建和释放对象即可。

下面是BilDog.cpp的代码:

#include "bildog.h"         IAnimal * CreateAnimal()         {             return new Dog();         }         void ReleaseAnimal(IAnimal * animal)         {             delete animal;         }       

至此,一个Animal插件总算完成了。编译,生成BilDog项目,输出BilDog.dll插件文件,以供主程序Test.exe动态调用。

BilPanda项目和BilDog项目类似,在这里就不把代码贴出来了。以后开发Animal插件(即使是第三方)的过程都是如此。
我们不打算输出该项目的.lib文件和那些头文件,因为我们打算让主程序在运行时刻根据需要装载dll插件和调用插件的功能,而不是让主程序项目在编译时就指定具体的插件。





   

    3> 编写客户程序——Test项目的实现

Test项目是一个测试程序项目,但它的角色是主程序,是能使用Animal插件的客户程序。

同样,这个项目用到了Bil共享库,所以得先把Bil项目的几个输出文件导入到Test项目。

我们假设Test主程序是一个对话框,上面有一个编辑框和一个“加载并调用”按钮,终端用户在编辑框中输入Animal插件的文件名(比如BilDog, 后缀名可省略,Qt会根据平台判断该查找.dll还是.so),点击“加载并调用”进行共享库的加载,并调用动态创建的IAnimal对象的Eat()函 数(当然你可以调用Run()函数或Sleep(),这里仅仅是一个示例)。

下面的函数将被“加载并调用”按钮的触发事件调用:

// ...         #include  
  
             #include  
   
              #include  
    
               // ...                 // strPluginName为插件的名称,可省略后缀         void MainDlg::LoadAndAction(QString strPluginName)         {             // 加载插件dll             QLibrary lib(strPluginName);             if (lib.load())             {                 // 定义插件中的两个导出函数的原型                 typedef IAnimal* (*CreateAnimalFunction)();                 typedef void (*ReleaseAnimalFunction)(IAnimal* animal);                 // 解析导出函数                 CreateAnimalFunction createAnimal =                          (CreateAnimalFunction) lib.resolve("CreateAnimal");                 ReleaseAnimalFunction releaseAnimal =                          (ReleaseAnimalFunction) lib.resolve("ReleaseAnimal");                   if (createAnimal && releaseAnimal)                 {                     // 创建Animal对象                     IAnimal * animal = createAnimal();                     if (animal)                     {                         // 使用插件功能                         animal->Eat();                         animal->Sleep();                         // 插件使用完毕,删除对象                         releaseAnimal(animal);                     }                 }                 // 卸载插件                 lib.unload();             }         }        // ...     
     
    
  

生成Test项目,输出Test.exe。我们把Test.exe、 Bil.dll、BilDog.dll、BilPanda.dll放在同一目录,双击运行Test.exe,赶快试下效果吧!注意BilDog.dll或 BilPanda.dll依赖于基础接口库Bil.dll,如果系统找不到Bil.dll,将不能加载BilDog.dll或BilPanda.dll, 所以请把它们放在同一目录。

三、一些遗憾

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220999.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午7:09
下一篇 2026年3月17日 下午7:10


相关推荐

  • 关于异步FIFO设计,这7点你必须要搞清楚「建议收藏」

    关于异步FIFO设计,这7点你必须要搞清楚「建议收藏」这篇文章可能帮助您:了解异步FIFO设计中的7个要点。

    2022年8月13日
    16
  • P2P协议入门

    P2P协议入门一 P2P 定义 nbsp nbsp nbsp nbsp P2P PeertoPeer 对等计算机或对等网络 nbsp nbsp nbsp nbsp P2P 的核心是数据存储在客户本地 通过存储信息 名称 地址 分块 的查询 让终端之间直接数据传递 P2P 网络让网络上的数据流量分散化 同时管理点不仅没有服务容量的压力 而且只存储数据的索引与链接 不对数据本身负责 避免了版权与管理的麻烦 nbsp nbsp nbsp nbsp 我为人人 人人为我 的思想

    2026年3月26日
    3
  • wireshark抓取dns数据包_2021年dns

    wireshark抓取dns数据包_2021年dns实验步骤一根据实验环境,本实验的步骤如下:1、测试环境中获取DNS数据包。2、分析DNS数据包。任务描述:获取两种类型的DNS数据包(1)通过浏览器访问域名(www.baidu.com)来获取DNS数据虽然合天实验室环境下,无法抓取此部分数据包(其实也可以自己搭建服务器,配置DNS服务器,但设计者比较懒,就将就下吧),但设计者希望学习的人能根据例子在自己的笔记本上抓取,然后分析。我们以登录www.baidu.com来获取DNS数据,启动Wireshark,在Filter中输入dns。打开浏览器

    2025年5月27日
    4
  • 简述计算机的发展史和应用领域_简述计算机的发展史

    简述计算机的发展史和应用领域_简述计算机的发展史计算机发展史`分类和应用领域第1章计算机基础知识计算机发展史、分类和应用领域数值、字符、汉字和图像编码计算机组成和原理计算机安全基础知识1.1计算机概述计算机技术的迅猛发展,促使人类走向丰富多彩的信息社会。信息时代的生产方式和生活方式具有数字化、集成化、智能化、移动化、个性化等特点。1.1.1计算机的发展世界上第一台电子数字式计算机ENIAC于1946年2月15日诞生在美国宾夕法…

    2022年10月11日
    4
  • nvicat15激活码_通用破解码

    nvicat15激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    48
  • oracle number类型null值的大小验证[通俗易懂]

    oracle number类型null值的大小验证[通俗易懂]oraclenumber类型null值的大小验证

    2022年7月24日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号