1)创建DLL
创建DLL的时候,头文件里在输出变量,函数,类之前使用__declspec(dllexport)修饰符号。当VC编译器看到变量,函数或者类之前的这个修改符的时候,它就将某些附加信息嵌入产生的.obj文件中。当链接DLL的所有.obj文件时,链接程序要查找关于输出变量,函数或类的信息,并自动生成一个.lib文件,它包含一个DLL输出的符号列表。如果要链接引用该DLL的输出符号的任何可执行模块,该.lib文件是必不可少的。(DLL工程需要定义MYMATHLIB_EXPORTS宏)
MyMathLib.h
- #ifdef MYMATHLIB_EXPORTS
- #define MYMATHLIB_API __declspec(dllexport)
- #else
- #define MYMATHLIB_API __declspec(dllimport)
- #endif
-
- int MYMATHLIB_API Add(int lhs, int rhs);
MyMathLib.cpp
- #include "MyMathLib.h"
-
- int MYMATHLIB_API Add(int lhs, int rhs)
- {
- return lhs + rhs;
- }
除了创建.lib文件外,链接程序还要将一个输出符号表嵌入产生的DLL文件。可以使用dumpbin程序察看dll的输出节。
- >dumpbin -exports MyMathLib.dll
- Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
- Copyright (C) Microsoft Corporation. All rights reserved.
-
-
- Dump of file MyMathLib.dll
-
- File Type: DLL
-
- Section contains the following exports for MyMathLib.dll
-
- 00000000 characteristics
- 4E258F37 time date stamp Tue Jul 19 22:05:43 2011
- 0.00 version
- 1 ordinal base
- 1 number of functions
- 1 number of names
-
- ordinal hint RVA name
-
- 1 0 0001106E ?Add@@YAHHH@Z = @ILT+105(?Add@@YAHHH@Z)
-
- Summary
-
- 1000 .data
- 1000 .idata
- 2000 .rdata
- 1000 .reloc
- 1000 .rsrc
- 4000 .text
- 10000 .textbss
2)创建可执行模块
可执行模块引用DLL的头文件,使用__declspec(dllimport)符号进行定义。当编器看到修改变量,函数或类的__declspec(dllimport)时,它知道这个符号是从某个DLL模
块输入的。创建产生的可执行模块的链接程序必须确定哪些DLL包含代码引用的所有输入符号。因此你必须将DLL的.lib文件传递给链接程序。
实际上,当输入一个符号时,不必使用关键字__declspec(dllimport),只要使用标准的C关键字extern即可。但是如果编译器预先知道你引用的符号将从一个DLL的.lib文件输入,那么编译器就能够生成运行效率稍高的代码。因此建议你尽量将__declspec(dllimport)关键字用于输入函数和符号。
MyEXE.cpp
- #include <iostream>
- #include "MyMathLib.h"
-
- #pragma comment(lib, "MyMathLib.lib")
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- int ret = Add(1, 2);
- std::cout << ret << std::endl;
- return 0;
- }
当链接程序进行输入符号的转换时,它就将一个称为输入节的特殊的节嵌入产生的可执行模块。输入节列出了该模块需要的DLL模块以及由每个DLL模块引用的符号。
可以使用dumpbin程序察看模块的输入节。
- Dump of file MyEXE.exe
-
- File Type: EXECUTABLE IMAGE
-
- Section contains the following imports:
-
- MSVCP100D.dll
- ......
- MyMathLib.dll
- 4183BC Import Address Table
- 4181F8 Import Name Table
- 0 time date stamp
- 0 Index of first forwarder reference
-
- 0 ?Add@@YAHHH@Z
-
- MSVCR100D.dll
- ......
- KERNEL32.dll
- ......
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编译器修改函数名字。
- #ifdef __cplusplus
- extern "C" {
- #endif
- int MYMATHLIB_API Add(int lhs, int rhs);
- #ifdef __cplusplus
- }
- #endif
重新生成MyMathLib.dll后,使用dumpbin察看输出节:
- ordinal hint RVA name
-
- 1 0 000110AF Add = @ILT+170(_Add)
如果加上__stdcall修饰符,
- #ifdef MYMATHLIB_EXPORTS
- #define MYMATHLIB_API __declspec(dllexport) __stdcall
- #else
- #define MYMATHLIB_API __declspec(dllimport) __stdcall
- #endif
-
-
- #ifdef __cplusplus
- extern "C" {
- #endif
- int MYMATHLIB_API Add(int lhs, int rhs);
- #ifdef __cplusplus
- }
- #endif
重新生成MyMathLib.dll后,使用dumpbin察看输出节:
- ordinal hint RVA name
-
- 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
- int Add(int lhs, int rhs);
MyMathLib.cpp
- #include "MyMathLib.h"
-
- int Add(int lhs, int rhs)
- {
- return lhs + rhs;
- }
MyMathLib.def
- LIBRARY MyMathLib
- EXPORTS
- Add @100
重新生成MyMathLib.dll后,使用dumpbin察看输出节:
- ordinal hint RVA name
-
- 100 0 0001106E Add = @ILT+105(?Add@@YAHHH@Z)
当生成 DLL 时,链接器使用 .def 文件创建导出 (.exp) 文件和导入库 (.lib) 文件。然后,链接器使用导出文件生成 DLL 文件。隐式链接到 DLL 的可执行文件在生成时链接到导入库。可以使用dumpbin程序察看可执行文件的输入节。可以看到现在是按序号进行连接了。
- MyMathLib.dll
- 4183BC Import Address Table
- 4181F8 Import Name Table
- 0 time date stamp
- 0 Index of first forwarder reference
-
- Ordinal 100
http://blog.csdn.net/fw0124/article/details/6618405