之前遇到要用C++编写一个动态链接库(Dynamic Link Library, dll)的问题,因为自从之前编写过一个Java调用的dll之后就没怎么再碰dll了(况且Java的JNI极大地简化了dll的开发),所以这次必须得查了一些文档才成功的写了出来,我于是就想把编写的方法写到博客里面来,以后再有问题就再看看。
首先先说明一下,我用的是Windows XP + Visual C++ 6.0的组合,如果大家跟我不一样而出现了问题,可以去查一下差别,再自己修正。
好,其实dll文件就是一堆函数的集合,我们只不过是给普通函数的定义加点东西罢了。下面是一个函数的定义方式,这里给出了判断两数大小的例子:
// Example.cpp
extern "C" _declspec(dllexport) int Max(int first, int second)
{
return (first > second ? first : second);
}
这个就是dll中的函数的编写方法了。现在来解释一下这段代码。首先,最前面的 extern "C" 是为了防止不同编译器间的差异的,它的意思是以C语言方式命名,这样不仅C语言也可以调用它,不同的编译器也不会因为命名方式不同而产生冲突。不过因为C语言不支持重载,所以这个以C语言方式命名的函数也是不能重载的。具体这个 extern "C" 有什么用,还不明白的可以去问百度,虽然上面的解释貌似也不清楚。有空我也写篇文章专门讲这个 extern "C" 。后面那个 _declspec(dllexport) 是动态链接库里的调用协议,其实还有什么 stdcall 的,反正我这里用这个。不过这样写的函数只能被 C/C++ 调用(好像),但是这已经够了。其实我也不是非常明白这个 declspec 啥意思,管他呢,就当这个是规范。再后面就只是一个普通的函数的定义而已,大家应该都会。
好了,dll的源代码算是大功告成。接下来我们进行编译。可以在 VC++ 的 IDE 里面编译(dll工程),但是Jerry Yang其实并不喜欢在 VC++ 里直接写程序(当然指除了 MFC 程序),我喜欢用其他文本编辑器写好后命令行编译,但是其实无所谓。这里我假设文件名是 Example.cpp 。在命令行中键入 "cl /LD Example.cpp",如果环境变量已经设好就应该可以成功编译了。其实除了dll文件还会产生几个其他文件,那个是给静态调用使用的(就是编译时直接把链接库里的东西全部直接放到自己的程序里,程序文件会更大,但是不用依赖其他文件;同样,动态调用就是程序运行时找dll调用,适用于dll共用的情况),这里我只示范动态调用,静态调用大家自行百度吧(其实主要是我觉得程序里一堆dll特帅特霸气)。下面是调用的代码:
// CallLibrary.cpp
#include <iostream>
#include <windows.h>
using namespace std;
typedef int(*pFunc)(int, int);
int main() {
HINSTANCE hDLL = LoadLibrary("Example.dll");
if(hDLL == NULL) {
cerr<<"Example.dll doesn't exist."<<endl;
system("pause>nul");
return 1;
}
pFunc max = (pFunc)GetProcAddress(hDLL, "Max");
if(max == NULL) {
cerr<<"No function called Max was found."<<endl;
FreeLibrary(hDLL);
system("pause>nul");
return 1;
}
cout<<"Between 11 and 7, the bigger one is "<<max(11, 7)<<".\n";
system("pause>nul");
FreeLibrary(hDLL);
return 0;
}
再来解释一下这些代码。前四行就不解释了,大家应该都明白。然后后面是一个 typedef 指令,定义了一个函数指针,这个指针类型要和我们想调用的函数相一致(返回类型、参数)。这里定义的是有两个整形参数的返回整形的函数指针,叫 pFunc 。接下来来看主函数。我们先声明了一个 HINSTANCE 的变量。这个变量就是我们用来代表动态链接库的。我们使用 LoadLibrary 函数导入链接库,参数是链接库路径。补充说明一下,扩展名不一定是 .dll ,事实上任何扩展名都可以,只要是有效的。接下来我们检查一下是不是成功导入了(即与 NULL 比较,也可以写作 !hDLL ,个人喜好问题)。如果没有成功导入就退出。接下来声明一个函数指针,叫做 max 。我们用 GetProcAddress从 hDLL 代表的动态链接库导入一个叫 Max 的函数(不能重载,所以没有问题),强制转化一下赋给 max 。再检查一下是否成功获取了这个函数,如果没有就退出。但是注意一点,即使没有成功导入,也应该使用 FreeLibrary 函数释放动态链接库,因为链接库本身是成功导入了。如果函数的获取也没有问题,那么就可以使用了。最后还是要记得写上释放的语句,否则会资源泄露,耗尽内存。
好了,动态链接库的动态调用就讲到这儿,大家都会了吗?大家下次见!