学习Windows DLL的笔记之二 【编写WIN32 DLL】
在编写DLL之前,确保你安装了VC或者VS IDE,并正确配置了环境变量,可以在dos下调用cl和link命令,你可以在dos下输入cl或者link来测试是否正确配置了环境变量。关于这方面的配置不作介绍。关于链接选项和编译选项,请参考MSDN。本文中提到的dumpbin和depends的使用,以后再做说明。
入口函数
动态链接库的入口函数时可选的,也就是说你可以不必指定,入口函数默认是DllMain,当然也可以自己指定入口函数,只要你不调用CRT的库函数,你就可以用连接选项ENTRY来指定你自己的入口函数;如果该DLL是一个纯资源动态连接库,那么没有入口函数,并且在连接选项中指定NOENTRY。
特别注意链接选项中的NOENTRY,必须要你在dll库中没有调用CRT的库函数时,才能通过,否则链接错误,提示找不到外部函数main,因此一般在纯资源DLL库中才使用该选项。ENTRY也有这个问题。
MY.DLL : fatal error LNK1120: 1 unresolved externals
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\link.EXE"' : return code '0x460' Stop.
编写第一个WIN32 DLL
你可以选择IDE的AppWizard来生成一个Win32 Dll项目,也可以用记事本来写DLL。下面介绍记事本方式来编写DLL。打开记事本,输入如下代码,保存为mydll.cpp:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 {
3 return a+b;
4 }
5
6 int subtract(int a,int b)
7 {
8 return a-b;
9 }
现在使用几种方式来导出add和subtract函数。
第1种 利用连接选项
1.在msdos下输入cl /EHsc /c mydll.cpp,将产生一个目标文件mydll.obj。
2.在msdos下输入link mydll.obj /DLL /NOENTRY /EXPORT:add,@1 /EXPORT:subtract,@2 /OUT:MY.DLL,将产生一个MY.DLL。你可以使用dumpbin或者depends来查看DLL导出的函数。
第2种 利用DEF
1.同上,编译产生一个目标文件
2.编写一个DEF文件,文件名为MY.DEF,该文件格式如下
LIBRARY MY.DLL
EXPORTS
add @1
subtract @2
3. 输入link mydll.obj /DLL /NOENTRY /DEF:my.def /OUT:MY.DLL
第3种 利用__declspec(dllexport)关键字
1.修改代码,在要导出的函数前面加入关键字,如__declspec(dllexport) int add(int a,int b){return a+b;};每一个导出函数前都要添加该关键字;
2.在dos下输入cl /EHsc /c mydll.cpp
3.在dos下输入link /DLL /NOENTRY /OUT:MY.DLL
这三种方式,第一种最不常用;因为太麻烦,利用def文件用来导出大量函数还是不错的选择,但是用来导出一个类,就显得捉襟见肘了;第3种方式来导出函数不如第2种方便,但是在导出一个类时,却方便的多。
如何利用关键字来导出一个类
在mydll.h里添加一个类 class __declspec(dllexport) Algorithm{public: int add(int a,int b){return a+b;}}; 就可以导出整个类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2
3 #ifdef EXPORT_API
4 #else
5 #define EXPORT_API __declspec(dllimport)
6 #endif
7
8 extern "C"
9 EXPORT_API int add(int,int);
10
11 class EXPORT_API Algorithm{
12 public:
13 int add(int a,int b);
14 };
在mydll.cpp里实现,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2
3 #define EXPORT_API __declspec(dllexport)
4
5 #include "mydll.h"
6 using namespace std;
7
8 int add(int a,int b)
9 {
10 return a+b;
11 }
12
13 int Algorithm::add(int a,int b)
14 {
15 return a+b;
16 }
如何修改DLL的基地址
一般DLL的加载地址是0X10000000,可以用link选项来改变该值/BASE:{address[,size] | @filename,key},比如要修改地址到0x1A000000可以用/BASE:0x1A000000来指定。
如何测试DLL
1.用隐式加载DLL来测试,代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 #include "mydll.h"
3 using namespace std;
4
5 #pragma comment(lib,"mydll.lib")
6
7 int main()
8 {
9 Algorithm alg;
10 cout<<alg.add(10,123)<<endl;
11 }
2. 显式加载dll来测试,代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 using namespace std;
3
4 int main()
5 {
6 typedef int (*MYPROC)(int,int);
7 HMODULE hModule = LoadLibrary(TEXT("mydll.dll"));
8 MYPROC add=GetProcAddress(hModule,TEXT("add"));
9 cout<<add(11,23)<<endl;
10 }
两者的区别是,隐式加载时必须要指定导入库mydll.lib,而显式加载时不必指定;
但是显式加载调用类时很麻烦,没有隐式加载方便。
源码下载