yehao's Blog

DLL编程总结

1)创建DLL

创建DLL的时候,头文件里在输出变量,函数,类之前使用__declspec(dllexport)修饰符号。当VC编译器看到变量,函数或者类之前的这个修改符的时候,它就将某些附加信息嵌入产生的.obj文件中。当链接DLL的所有.obj文件时,链接程序要查找关于输出变量,函数或类的信息,并自动生成一个.lib文件,它包含一个DLL输出的符号列表。如果要链接引用该DLL的输出符号的任何可执行模块,该.lib文件是必不可少的。(DLL工程需要定义MYMATHLIB_EXPORTS宏)

MyMathLib.h

  1. #ifdef MYMATHLIB_EXPORTS   
  2. #define MYMATHLIB_API __declspec(dllexport)   
  3. #else   
  4. #define MYMATHLIB_API __declspec(dllimport)   
  5. #endif   
  6.   
  7. int MYMATHLIB_API Add(int lhs, int rhs);  


MyMathLib.cpp

  1. #include "MyMathLib.h"   
  2.   
  3. int MYMATHLIB_API Add(int lhs, int rhs)  
  4. {  
  5.     return lhs + rhs;  
  6. }  


除了创建.lib文件外,链接程序还要将一个输出符号表嵌入产生的DLL文件。可以使用dumpbin程序察看dll的输出节。

  1. >dumpbin -exports MyMathLib.dll  
  2. Microsoft (R) COFF/PE Dumper Version 10.00.30319.01  
  3. Copyright (C) Microsoft Corporation. All rights reserved.  
  4.   
  5.   
  6. Dump of file MyMathLib.dll  
  7.   
  8. File Type: DLL  
  9.   
  10. Section contains the following exports for MyMathLib.dll  
  11.   
  12. 00000000 characteristics  
  13. 4E258F37 time date stamp Tue Jul 19 22:05:43 2011  
  14. 0.00 version  
  15. 1 ordinal base  
  16. 1 number of functions  
  17. 1 number of names  
  18.   
  19. ordinal hint RVA name  
  20.   
  21. 1 0 0001106E ?Add@@YAHHH@Z = @ILT+105(?Add@@YAHHH@Z)  
  22.   
  23. Summary  
  24.   
  25. 1000 .data  
  26. 1000 .idata  
  27. 2000 .rdata  
  28. 1000 .reloc  
  29. 1000 .rsrc  
  30. 4000 .text  
  31. 10000 .textbss  


2)创建可执行模块

可执行模块引用DLL的头文件,使用__declspec(dllimport)符号进行定义。当编器看到修改变量,函数或类的__declspec(dllimport)时,它知道这个符号是从某个DLL模
块输入的。创建产生的可执行模块的链接程序必须确定哪些DLL包含代码引用的所有输入符号。因此你必须将DLL的.lib文件传递给链接程序。

实际上,当输入一个符号时,不必使用关键字__declspec(dllimport),只要使用标准的C关键字extern即可。但是如果编译器预先知道你引用的符号将从一个DLL的.lib文件输入,那么编译器就能够生成运行效率稍高的代码。因此建议你尽量将__declspec(dllimport)关键字用于输入函数和符号。

MyEXE.cpp

  1. #include <iostream>   
  2. #include "MyMathLib.h"   
  3.   
  4. #pragma comment(lib, "MyMathLib.lib")    
  5.   
  6. int _tmain(int argc, _TCHAR* argv[])  
  7. {  
  8.     int ret = Add(1, 2);  
  9.     std::cout << ret << std::endl;  
  10.     return 0;  
  11. }  


当链接程序进行输入符号的转换时,它就将一个称为输入节的特殊的节嵌入产生的可执行模块。输入节列出了该模块需要的DLL模块以及由每个DLL模块引用的符号。
可以使用dumpbin程序察看模块的输入节。

  1. Dump of file MyEXE.exe  
  2.   
  3. File Type: EXECUTABLE IMAGE  
  4.   
  5.   Section contains the following imports:  
  6.   
  7.     MSVCP100D.dll  
  8.                 ......  
  9.     MyMathLib.dll  
  10.                 4183BC Import Address Table  
  11.                 4181F8 Import Name Table  
  12.                      0 time date stamp  
  13.                      0 Index of first forwarder reference  
  14.   
  15.                     0 ?Add@@YAHHH@Z  
  16.   
  17.     MSVCR100D.dll  
  18.                 ......  
  19.     KERNEL32.dll  
  20.                 ......  


3)调用约定

__stdcall
Pascal方式清理C方式压栈,通常用于Win32 Api中,.参数从右向左压入堆栈,.函数被调用者自己在退出时清空堆栈。

__cdecl
C调用约定The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。它是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stdcall函数的大。

C编译时函数名修饰约定规则:
__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。

C++编译时函数名修饰约定规则:
__stdcall调用约定:
  1、以“?”标识函数名的开始,后跟函数名;
  2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;
  3、参数表以代号表示:
  X--void ,D--char,E--unsigned char,F--short,H--int,I--unsigned int,J--long,K--unsigned long,M--float,N--double,_N--bool,....
  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;
  4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
  5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
  其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,
__cdecl调用约定:规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。

例如int Add(int lhs, int rhs) =〉 ?Add@@YAHHH@Z

 

可以使用extern "C" 防止VC编译器修改函数名字。

  1. #ifdef  __cplusplus   
  2. extern "C" {  
  3. #endif   
  4. int MYMATHLIB_API Add(int lhs, int rhs);  
  5. #ifdef  __cplusplus   
  6. }  
  7. #endif  

重新生成MyMathLib.dll后,使用dumpbin察看输出节:

  1. ordinal hint RVA      name  
  2.   
  3.       1    0 000110AF Add = @ILT+170(_Add)  

 

如果加上__stdcall修饰符,

  1. #ifdef MYMATHLIB_EXPORTS   
  2. #define MYMATHLIB_API  __declspec(dllexport) __stdcall   
  3. #else   
  4. #define MYMATHLIB_API  __declspec(dllimport) __stdcall   
  5. #endif   
  6.   
  7.   
  8. #ifdef  __cplusplus   
  9. extern "C" {  
  10. #endif   
  11. int MYMATHLIB_API Add(int lhs, int rhs);  
  12. #ifdef  __cplusplus   
  13. }  
  14. #endif  

重新生成MyMathLib.dll后,使用dumpbin察看输出节:

  1. ordinal hint RVA      name  
  2.   
  3.       1    0 00011087 _Add@8 = @ILT+130(_Add@8)  

 

一般使用extern "C" 实现C++与C及其它语言的混合编程。
如果在C++中引用C语言中的函数和变量,在包含C语言头文件时,或者C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。
在C中引用C++语言中的函数和变量时,比如回调函数,C++的头文件需添加extern "C"。

4).def文件

模块定义 (.def) 文件是包含一个或多个描述 DLL 各种属性的 Module 语句的文本文件。如果不使用 __declspec(dllexport) 关键字导出 DLL 的函数,则 DLL 需要 .def 文件。
.def 文件必须至少包含下列模块定义语句:
文件中的第一个语句必须是 LIBRARY 语句。此语句将 .def 文件标识为属于 DLL。LIBRARY 语句的后面是 DLL 的名称。链接器将此名称放到 DLL 的导入库中。
EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。

MyMathLib.h

  1. int Add(int lhs, int rhs);  

MyMathLib.cpp

  1. #include "MyMathLib.h"   
  2.   
  3. int Add(int lhs, int rhs)  
  4. {  
  5.     return lhs + rhs;  
  6. }  

MyMathLib.def

  1. LIBRARY MyMathLib  
  2. EXPORTS  
  3.     Add     @100  

重新生成MyMathLib.dll后,使用dumpbin察看输出节:

  1. ordinal hint RVA      name  
  2.   
  3.     100    0 0001106E Add = @ILT+105(?Add@@YAHHH@Z)  

当生成 DLL 时,链接器使用 .def 文件创建导出 (.exp) 文件和导入库 (.lib) 文件。然后,链接器使用导出文件生成 DLL 文件。隐式链接到 DLL 的可执行文件在生成时链接到导入库。可以使用dumpbin程序察看可执行文件的输入节。可以看到现在是按序号进行连接了。

  1. MyMathLib.dll  
  2.             4183BC Import Address Table  
  3.             4181F8 Import Name Table  
  4.                  0 time date stamp  
  5.                  0 Index of first forwarder reference  
  6.   
  7.                   Ordinal   100  


http://blog.csdn.net/fw0124/article/details/6618405

posted on 2011-09-26 19:06 厚积薄发 阅读(224) 评论(0)  编辑 收藏 引用 所属分类: Windows编程


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿

随笔分类

文章分类

文章档案

搜索

最新评论