阅读Qt的源代码,发现其中有许多宏,最后定义为__declspec(dllexport),由于以前没有写过库,对此很陌生,于是研究了下。
原来这是用于导入、导出函数用的,网上搜索了个例子,自己再总结下:
// File: SimpleDLLClass.h
#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
class DLL_EXPORT SimpleDLLClass
{
public:
SimpleDLLClass();
virtual ~SimpleDLLClass();
int getValue() { return m_nValue;}
private:
int m_nValue;
};
// File: SimpleDLLClass.cpp
#define SIMPLEDLL_EXPORT
#include "SimpleDLLClass.h"
SimpleDLLClass::SimpleDLLClass()
{
m_nValue=5;
}
SimpleDLLClass::~SimpleDLLClass()
{
}
以上便是写了一个类并将其进行了导出,其中比较需要注意的便是头文件中的#ifdef,通过这条预处理命令(库实现文件中定义了SIMPLEDLL_EXPORT),实现了库中类的导出以及使用时类的引入。
类导出来,我们还需要进行一下测试,于是写了个简单的测试函数:
#include "SimpleDLLClass.h"
#include <stdio.h>
#pragma comment(lib,"SimpleDLLClass.lib") //导入库文件,也可以在编译参数中加
int main()
{
SimpleDLLClass a;
printf("a: %d\n", a.getValue());
return 0;
}
为了测试方便,我也写了一个Makefile:
main: dll
#cl main.cpp /link SimpleDLLClass.lib /OUT:1.exe 如果不在头文件中导入库,则必须这样写
cl main.cpp /link /OUT:1.exe /MAP:xx.map #同时生成了下map文件,此处无用
1.exe
dll:SimpleDLLClass.dll
SimpleDLLClass.dll:
cl /LD SimpleDLLClass.cpp
clean:
del *.dll
del *.exp
del *.lib
del *.obj
del *.exe
.PHONY: clean
由于在windows中测试,需要安装nmake或者安装mingw才能使用Makefile,我安装的是mingw。编写完毕后运行make即可运行我们生产的1.exe。
运行完make后,在同一目录中还生成了许多其他文件:main.obj、SimpleDLLClass.obj、SimpleDLLClass.dll、SimpleDLLClass.exp、SimpleDLLClass.lib。
其中.obj、.dll均了解,.exp和.lib不知其意,后来baidu后,知道:
.exp文件是指导出库文件的文件,简称导出库文件,它包含了导出函数和数据项的信息。当LIB创建一个导入库,同时它也创建一个导出库文件。如果你的程序链接到另一个程序,并且你的程序需要同时导出和导入到另一个程序中,这个时候就要使用到exp文件(LINK工具将使用EXP文件来创建动态链接库),一般我们不需要关心。
.lib文件,可以代表静态库,也可以代表动态库。若为静态库,则直接进行链接;若为动态库,则.lib将指导可执行程序如何与dll进行链接。
此外,导出库不光可以通过__declspec(dllexport)进行导出,也可以通过.def文件进行导出,先写个导出单个函数:
// File :SimpleDLLClass.h#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
int DLL_EXPORT getValue();
// File: SimpleDLLClass.cpp
#define SIMPLEDLL_EXPORT
#include "SimpleDLLClass.h"
int getValue() { return 3;}
// File: main.cpp
#include "SimpleDLLClass.h"
#include <stdio.h>
#pragma comment(lib,"SimpleDLLClass.lib")
int main()
{
printf("a: %d\n", getValue());
return 0;
}
main: dll
#cl main.cpp /link SimpleDLLClass.lib /OUT:1.exe
cl main.cpp /link /OUT:1.exe
1.exe
dll:SimpleDLLClass.dll
SimpleDLLClass.dll:
cl /LD SimpleDLLClass.cpp /link /DEF:SimpleDLLClass.def
clean:
del *.dll
del *.exp
del *.lib
del *.obj
del *.exe
.PHONY: clean
SimpleDLLClass.def:
LIBRARY SimpleDLLClass
DESCRIPTION "our simple DLL"
EXPORTS getValue @1
def文件写得比较简单,只是将getValue导出到第一个位置上。比较奇怪的是,我直接写了导出函数名,并没有写出C++经过处理后的函数名,程序竟然能够正常运行,且通过nm查看生成的SimpleDLLClass.obj,看到符号名为00000000 T ?getValue@@YAHXZ,这还是让我比较奇怪的。
以下再写一个导出类,和之前用__declspec(dllexport)导出一样,仅仅修改了下SimpleDLLClass.h
// File: SimpleDLLClass.h
#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
class SimpleDLLClass //去掉了DLL_EXPORT
{
public:
SimpleDLLClass();
virtual ~SimpleDLLClass();
int getValue(); //将此函数改为非内联
private:
int m_nValue;
};
注意:一定得把需要把导出类中要使用的函数改为非内联,否则链接无法通过(原因:由于函数为内联,导致编译器并未生成符号表,最后将导致链接失败,可能有编译参数可以进行设置,但本人一直未找到,希望知道的朋友能告知我)。
至于def文件内容如下:
SimpleDLLClass.def:
LIBRARY SimpleDLLClass
DESCRIPTION "our simple DLL"
EXPORTS
??0SimpleDLLClass@@QAE@XZ @1
??1SimpleDLLClass@@UAE@XZ @2
?getValue@SimpleDLLClass@@QAEHXZ @3
也许大家对其中以?开头的文字有些疑惑,起始这是C++ 编译器的函数名修饰,可以通过如下方法获得:
cl /c SimpleDLLClass.cpp
nm SimpleDLLClass.obj
但是看到有好多含有SimpleDLLClass的,其实,只要将以上命令中SimpleDLLClass.cpp改为main.cpp,就能找到实际使用的是哪些导出函数,将这些函数导出就行了。
以上说的都是dll的隐式使用,其实还可以显示使用:
生成dll的方法一样,只是在window中使用:LoadLibrary()、GetProcAdress()、FreeLibrary()进行使用;在qt中可以使用QLibrary:load()、resolve(const char *)、unload(),比较简单,具体不详解了。