学习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也有这个问题。

 

    LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
    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:

View Code
1 int add(int a,int b)
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;}}; 就可以导出整个类。

View Code
 1 #pragma once
 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里实现,代码如下:

View Code
 1 #include <iostream>
 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来测试,代码如下

View Code
 1 #include <iostream>
 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来测试,代码如下

View Code
 1 #include <iostream>
 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,而显式加载时不必指定;

   但是显式加载调用类时很麻烦,没有隐式加载方便。


 

源码下载

 /Files/projectdevelop/dll.7z

posted on 2011-03-22 15:09  呼风唤雨  阅读(985)  评论(0编辑  收藏  举报

导航