qt windows linux
dll .dll .so
lib 对应 a archiver的缩写 为静态库,是好多个.o合在一起,用于静态连接
dll 对应 so share object 共享库
lib文件:(依据编译器) MSVC编译器是生成.lib 文件,文件名不变。 mingw编译器生成.a 文件,此外会在目标名称(即在.pro 文件中的TARGET)前面加lib
dll文件:(依据平台) windows平台是.dll 文件,unix平台是.so ,mac是.dylib
背景
在vs的mfc环境中:
设置x.dll 输出路径方法是在右键项目的"属性"->链接器->常规, 然后在常规属性界面中的 "输出文件" 中填入自己想要x.dll文件的输出路径.
设置x.lib 输出路径方法是在右键项目的"属性"->链接器->高级, 然后在高级属性界面中的 "导入库" 项中填入自己想要x.dll文件的输出路径.
设置x.pdb 输出路径方法是在右键项目的"属性"->链接器->调试, 然后在高级属性界面中的 "生成程序数据库文件" 项中填入自己想要x.pdb文件的输出路径.
在qt环境中
.pro文件通过宏DESTDIR设置生成的dll(或者so)文件,lib(或者a)文件。
另外可以通过DLLDESTDIR设置生成的dll(或者so)文件,但dll(或者so)文件在DESTDIR路径还保留有一份。
创建链接库
静态链接库
设计导出类,包括ui窗口类,对话框类。
在.pro文件中
TEMPLATE = lib
CONFIG += staticlib
TARGET =
编译静态链接库时,不管是debug还是release版本,编译生成都是相同的文件名,并不会为debug版本自动添加一个字母“d”.
但是在debug或者release版本编译应用程序时,需要使用相应版本库文件。所有需要手动将debug版本的静态库更名,即在文件名后加上字母“d”,如 myStaticLib.lib => myStaticLibd.lib .
使用静态库方法是:
1.在应用程序的源程序目录下新建一个include文件夹,把.h头文件和.lib静态链接库文件都拷贝过去。
2.在项目管理目录树右键,选择“add library”,选择库类型“external library”(即外部库),连接类型必须选择"static",因为是静态库。
勾选add "d"suffix for debug version.使在debug模式下,应用程序自动调用debug版本的库文件。
共享库
1,共享库会多一个特殊的头文件 xxx_global.h。用来代替qt的宏 Q_DECL_EXPORT 和Q_DECL_IMPORT.
2.在定义的导出类名称前面多了在xxx_global.h定义过的宏,用于表明该类为导出类。
3,编译后会生成.dll文件和.lib文件。同样,不管是debug还是release版本,编译生成都是相同的文件名。
导出函数
直接在函数声明加上Q_DECL_EXPORT 表示导出函数。为了兼容不同mingw和msvc编译器,加上extern "C".
在实际使用中,linux系统如果dll的导出函数没有加上extern "C",则exe程序调用resolve函数没法获取到函数符号,返回空指针。所以必须在导出函数前加上extern "C"。
#if defined(TESTDLL_LIBRARY)
# define TESTDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define TESTDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
extern "C" int TESTDLLSHARED_EXPORT dev(int a, int b);
int dev(int n1, int n2)
{
return n1 - n2;
}
//在exe include 该头文件,然后直接调用
导出类
在声明类加上Q_DECL_EXPORT 即可。
#if defined(TESTDLL_LIBRARY)
# define TESTDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define TESTDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
class TESTDLLSHARED_EXPORT Testdll
{
public:
Testdll();
int add(int n1,int n2);
};
#include "testdll.h"
Testdll::Testdll()
{
}
int Testdll::add(int n1, int n2)
{
return n1 + n2;
}
//在exe中include该头文件,定义导出类的对象,然后调用相应的函数。
注意:其实对类的导出,一般都是父类纯虚函数的实现。这样可以在不同mingw和msvc编译器中相互调用。
具体表现为:父类为导出类,该dll的类继承导出类。结果就是该dll的类可以使用导出类的函数,但自己本身就不是导出类,不能给其他dll调用。
class MSCVDLLSHARED_EXPORT test
{
public:
virtual int add(int a,int b) = 0;
};
class test1 :public test
{
public:
int add(int a,int b) override;
};
extern "C" MSCVDLLSHARED_EXPORT test* getInstance();
extern "C" MSCVDLLSHARED_EXPORT void DeleteInstance(test*pInstance );
int test1::add(int a, int b)
{
return a + b;
}
test *getInstance()
{
test *pInstance = new test1();
return pInstance;
}
exe调用
test *ptest = getInstance();
int bb = ptest->add(1,4);
DeleteInstance(ptest);
void DeleteInstance(test *pInstance)
{
if(pInstance)
delete pInstance;
}
使用共享库
隐式链接
1.在源程序目录下新建一个include文件夹,把2个.h头文件(即包括_global.h文件)拷贝过去。
2..在源程序目录下新建一个lib文件夹,把 .lib或者是.a库文件都拷贝过去。
3.在项目管理目录树右键,选择“add library”,选择库类型“external library”(即外部库),连接类型必须选择"dynamic",因为是动态库。在library file选中include目录下的.lib 或者.a文件作为库文件。
或者直接在.pro配置文件的LIBS +=添加上去。如 LIBS += $$PWD/lib/libPropertyBrowser.a
4.必须将动态链接库文件.dll拷贝到可执行文件的的目录下,即pro 配置中的DESTDIR
显示链接
1. 显示链接通过QLibrary实现,一个QLibrary对象只能对一个共享库进行操作。一般在QLibrary构造函数中传递一个文件名,表示共享库文件。可以不用带后缀,因为可以通过平台自动识别。
如 QLibrary myLib("DelphiDLL");
QLibrary 的load函数用于首动载入dll到内存,一般不需要手动调用,因为在dll的函数第一次被调用时QLibrary会自动调用此函数。isLoaded 判断dll 是否已经被载入内存,unload用于将dll从内存卸载。如果不调用卸载,那么只会在程序退出时才卸载。
一个动态链接库在内存只有一个实例,即使有多处调用了这个动态链接库里的函数,它也只是会被载入一次。
2 .声明函数原型类型
如 typedef int (*FunDef)(int); //函数原定定义
3. 使用QLibrary 的resolve函数解析需要调用的函数。resolve()用于解析库中的符号。如果库尚未加载,resolve()函数会隐式地尝试加载它
如 FunDef myTriple = (FunDef) myLib.resolve("triple"); //解析DLL中的函数 triple为在dll的函数名
4.调用函数
如 int V=myTriple(ui->spinInput->value()); //调用函数
注意事项
在windows系统上,mscv编译器的exe想调用mingw编译器的dll,需要在dll的导出函数或导出类声明为 extern "C" 如下:
dll 导出函数的声明及定义
extern "C" int MSCVDLLSHARED_EXPORT dev1(int a, int b);
int dev1(int a, int b)
{
return a - b;
}
导出的lib和dll为:mscvdll.lib 和mscvdll.dll
exe的.pro配置文件
LIBS += $$PWD/lib/mscvdll.lib
延伸:
DLL地狱
出类的DLL在维护和修改时有很多地方必需很小心,增加成员变量、修改导出类的基类等操作都可能导致意想不到的后果,也许用户更新了最新版本的DLL库后,应用程序就再也不能工作了。这就是著名的DLLHell(DLL地狱)问题。
主要原因