一、DLL的创建

QT下DLL的创建很简单,点击“文件”,选择“新建文件或项目…”,项目选择Library->C++库->choose…,类型选择共享库,名称自己定义,这里为Dll,勾选QtGui.dll。项目自动生成如下图的文件结构:


关于共享库的一点说明:

         如果你打开一些 Windows 应用程序的目录,你会发现有很多程序的 exe 文件都很小,大约几百K 的样子,并且目录中不仅仅只有一个exe文件,还包含着一大堆 dll 文件。这些 dll 其实就是一些共享库,所谓共享库,其实就是一些动态链接库,能够由程序在运行时进行动态加载的库。既然说是共享,那就是说,这些库不仅仅自己的程序可以使用,并且其他程序也可以使用,例如某些通用算法。如果你发布一下自己编写的 Qt 程序,也会看到很多系统的共享库,就是那些 QtGui.dll 之类的东西。或许你会说,我写的程序没有同其他应用共享的库,就不需要这些了吧!其实不然。因为共享库的一个好处是可以动态加载,也就是说,如果你需要升级程序,那么就要简单的替换掉这个 dll 就好了,不需要要求用户重新安装全部文件。当然,这些 dll 也是有缺点的:动态加载的东西肯定会比静态编译的东西效率低一些。不过在现在的硬件环境下,这点性能损失已经可以忽略不计了。

dll.h文件声明的如下成员函数:


int add(constint&a,constint&b);//加法运算
int sub(constint&a,constint&b);//减法运算
QString helloWorld();
同时在类外声明以下几个全局函数:
extern"C"
{
   DLLSHARED_EXPORTDll* getDllObject();//获取类Dll的对象
    DLLSHARED_EXPORT void releseDllObject(Dll*);//释放Dll对象
}

这里需要用到extern"C",防止动态库文件编译时,导出的函数名称发生改变,这主要时因为C++支持函数重载而C语言不支持。DLLSHARED_EXPORT宏的作用是导出这个类导出。其实就是将其编译成一个DLL文件,其它的类可以使用这个导出类。

编译后会在构建目录下生成三个文件:dll.dll,dll.o, libdll.a,第一个用于windows系统,后两个用于unix/linux。


二、DLL调用

1、调用原理


将DLL文件映射到用户进程地址空间,然后就可进行函数调用,调用方法与进程内部一般函数的调用方法相同。Windodws提供两种将DLL映射到进程空间的方法,即隐式调用与显式调用。


两种方法对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样的。显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。

        隐式加载默认是加载到内存中的,始终占用内存。显示加载,你加载时占用内存,释放了就不占用内存了。如果该DLL已经载入,Loadlibrary(Qt下面为QLibrary::load)只是会增加一个引用计数,相同,freelibrary(Qt下面为QLibrary::unload)也只是减少引用计数,如果引用计数为0时,DLL才从内存中移除。显示调用能够更加有效的使用内存,编写大型程序时往往使用显示调用。

        显式和隐式只是对于代码编写时来说的,最后产生的可执行程序,不管是显式和隐式,都是用loadlibrary载入的。显式与隐式不是用在这些方面的,显式加载适合需要动态的选 用DLL的情况。

2.隐式调用        

新建一个控制台应用程序implicit。我们需要在implicit.pro文件下指定前面生成的dll.dll所在的目录以及需要用到的库的名称,这里我用的相对路径(你当然可以用绝对路径):LIBS+=-L ./-ldll,-L跟目录,-l紧跟库的名称,并去掉后缀。./表示构建目录下(这里我的为build-implicit-Qt_4_8_6_qt4_8_6-Debug)。将dll.h以及dll_global.h文件拷贝到implicit主目录下,并在main.cpp中包含头文件dll.h :#include"dll.h"。声明一个Dll对象dll,执行以下程序:


Dlldll;
    qDebug()<<dll.add(1,2);
    qDebug()<<dll.sub(1,2);
    qDebug()<<dll.helloWorld();
运行结果为:

可以看到隐式调用成功。
3.显式调用
我们在explicit.pro文件中同样添加LIBS+=-L./-ldll。并将dll.h以及dll_global.h文件拷贝到explicit主目录下。在mainwindow.h下包含头文件#include"dll.h"
#include<QLibrary>


并在公有成员中申明函数指针



typedef Dll*(*CreatDll)();

typedef bool(*ReleseDll)(Dll*);

typedef int(*Add)(constint&a,constint&b);

在私有成员中声明变量:QLibrary mylib;

在构造函数中指定mylib动态库的文件名:mylib.setFileName("dll.dll");

接下来为按钮添加槽函数:on_pushButton_clicked():

voidMainWindow::on_pushButton_clicked()
{
    if(mylib.load()) //加载动态库dll.dll,加载成功返回true,否则返回false
    {
       CreatDll createdll=(CreatDll)mylib.resolve("getDllObject");//解析函数getDllObject,resolve调用成功会返回函数名地址,否则返回0
       ReleseDll decDll=(ReleseDll)mylib.resolve("releseDllObject");
       if(createdll&&decDll)
       {
           Dll*testDll=createdll();
           int sum=testDll->add(ui->lineEdit->text().toInt(),ui->lineEdit_2->text().toInt());
           ui->lineEdit_3->setText(QString::number(sum));
           this->statusBar()->showMessage(testDll->helloWorld());
       }
       mylib.unload();//调用完后卸载
    }
    else
    {
       QMessageBox::warning(this,"Error","loadDllfailed!");
    }
}
运行结果如下图