1)創(chuàng)建DLL
創(chuàng)建DLL的時(shí)候,頭文件里在輸出變量,函數(shù),類(lèi)之前使用__declspec(dllexport)修飾符號(hào)。當(dāng)VC編譯器看到變量,函數(shù)或者類(lèi)之前的這個(gè)修改符的時(shí)候,它就將某些附加信息嵌入產(chǎn)生的.obj文件中。當(dāng)鏈接DLL的所有.obj文件時(shí),鏈接程序要查找關(guān)于輸出變量,函數(shù)或類(lèi)的信息,并自動(dòng)生成一個(gè).lib文件,它包含一個(gè)DLL輸出的符號(hào)列表。如果要鏈接引用該DLL的輸出符號(hào)的任何可執(zhí)行模塊,該.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;
- }
除了創(chuàng)建.lib文件外,鏈接程序還要將一個(gè)輸出符號(hào)表嵌入產(chǎn)生的DLL文件。可以使用dumpbin程序察看dll的輸出節(jié)。
- >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)創(chuàng)建可執(zhí)行模塊
可執(zhí)行模塊引用DLL的頭文件,使用__declspec(dllimport)符號(hào)進(jìn)行定義。當(dāng)編器看到修改變量,函數(shù)或類(lèi)的__declspec(dllimport)時(shí),它知道這個(gè)符號(hào)是從某個(gè)DLL模
塊輸入的。創(chuàng)建產(chǎn)生的可執(zhí)行模塊的鏈接程序必須確定哪些DLL包含代碼引用的所有輸入符號(hào)。因此你必須將DLL的.lib文件傳遞給鏈接程序。
實(shí)際上,當(dāng)輸入一個(gè)符號(hào)時(shí),不必使用關(guān)鍵字__declspec(dllimport),只要使用標(biāo)準(zhǔn)的C關(guān)鍵字extern即可。但是如果編譯器預(yù)先知道你引用的符號(hào)將從一個(gè)DLL的.lib文件輸入,那么編譯器就能夠生成運(yùn)行效率稍高的代碼。因此建議你盡量將__declspec(dllimport)關(guān)鍵字用于輸入函數(shù)和符號(hào)。
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;
- }
當(dāng)鏈接程序進(jìn)行輸入符號(hào)的轉(zhuǎn)換時(shí),它就將一個(gè)稱(chēng)為輸入節(jié)的特殊的節(jié)嵌入產(chǎn)生的可執(zhí)行模塊。輸入節(jié)列出了該模塊需要的DLL模塊以及由每個(gè)DLL模塊引用的符號(hào)。
可以使用dumpbin程序察看模塊的輸入節(jié)。
- 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)調(diào)用約定
__stdcall
Pascal方式清理C方式壓棧,通常用于Win32 Api中,.參數(shù)從右向左壓入堆棧,.函數(shù)被調(diào)用者自己在退出時(shí)清空堆棧。
__cdecl
C調(diào)用約定The C default calling convention)按從右至左的順序壓參數(shù)入棧,由調(diào)用者把參數(shù)彈出棧。對(duì)于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來(lái)維護(hù)的(正因?yàn)槿绱耍瑢?shí)現(xiàn)可變參數(shù)vararg的函數(shù)(如printf)只能使用該調(diào)用約定)。它是C和C++程序的缺省調(diào)用方式。每一個(gè)調(diào)用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會(huì)比調(diào)用__stdcall函數(shù)的大。
C編譯時(shí)函數(shù)名修飾約定規(guī)則:
__stdcall調(diào)用約定在輸出函數(shù)名前加上一個(gè)下劃線前綴,后面加上一個(gè)“@”符號(hào)和其參數(shù)的字節(jié)數(shù),格式為_(kāi)functionname@number。
__cdecl調(diào)用約定僅在輸出函數(shù)名前加上一個(gè)下劃線前綴,格式為_(kāi)functionname。
C++編譯時(shí)函數(shù)名修飾約定規(guī)則:
__stdcall調(diào)用約定:
1、以“?”標(biāo)識(shí)函數(shù)名的開(kāi)始,后跟函數(shù)名;
2、函數(shù)名后面以“@@YG”標(biāo)識(shí)參數(shù)表的開(kāi)始,后跟參數(shù)表;
3、參數(shù)表以代號(hào)表示:
X--void ,D--char,E--unsigned char,F(xiàn)--short,H--int,I--unsigned int,J--long,K--unsigned long,M--float,N--double,_N--bool,....
PA--表示指針,后面的代號(hào)表明指針類(lèi)型,如果相同類(lèi)型的指針連續(xù)出現(xiàn),以“0”代替,一個(gè)“0”代表一次重復(fù);
4、參數(shù)表的第一項(xiàng)為該函數(shù)的返回值類(lèi)型,其后依次為參數(shù)的數(shù)據(jù)類(lèi)型,指針標(biāo)識(shí)在其所指數(shù)據(jù)類(lèi)型前;
5、參數(shù)表后以“@Z”標(biāo)識(shí)整個(gè)名字的結(jié)束,如果該函數(shù)無(wú)參數(shù),則以“Z”標(biāo)識(shí)結(jié)束。
其格式為“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,
__cdecl調(diào)用約定:規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開(kāi)始標(biāo)識(shí)由上面的“@@YG”變?yōu)?a href="mailto:“@@YA">“@@YA”。
例如int Add(int lhs, int rhs) =〉 ?Add@@YAHHH@Z
可以使用extern "C" 防止VC編譯器修改函數(shù)名字。
- #ifdef __cplusplus
- extern "C" {
- #endif
- int MYMATHLIB_API Add(int lhs, int rhs);
- #ifdef __cplusplus
- }
- #endif
重新生成MyMathLib.dll后,使用dumpbin察看輸出節(jié):
- 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察看輸出節(jié):
- ordinal hint RVA name
-
- 1 0 00011087 _Add@8 = @ILT+130(_Add@8)
一般使用extern "C" 實(shí)現(xiàn)C++與C及其它語(yǔ)言的混合編程。
如果在C++中引用C語(yǔ)言中的函數(shù)和變量,在包含C語(yǔ)言頭文件時(shí),或者C++調(diào)用一個(gè)C語(yǔ)言編寫(xiě)的.DLL時(shí),當(dāng)包括.DLL的頭文件或聲明接口函數(shù)時(shí),應(yīng)加extern "C" { }。
在C中引用C++語(yǔ)言中的函數(shù)和變量時(shí),比如回調(diào)函數(shù),C++的頭文件需添加extern "C"。
4).def文件
模塊定義 (.def) 文件是包含一個(gè)或多個(gè)描述 DLL 各種屬性的 Module 語(yǔ)句的文本文件。如果不使用 __declspec(dllexport) 關(guān)鍵字導(dǎo)出 DLL 的函數(shù),則 DLL 需要 .def 文件。
.def 文件必須至少包含下列模塊定義語(yǔ)句:
文件中的第一個(gè)語(yǔ)句必須是 LIBRARY 語(yǔ)句。此語(yǔ)句將 .def 文件標(biāo)識(shí)為屬于 DLL。LIBRARY 語(yǔ)句的后面是 DLL 的名稱(chēng)。鏈接器將此名稱(chēng)放到 DLL 的導(dǎo)入庫(kù)中。
EXPORTS 語(yǔ)句列出名稱(chēng),可能的話還會(huì)列出 DLL 導(dǎo)出函數(shù)的序號(hào)值。通過(guò)在函數(shù)名的后面加上 @ 符和一個(gè)數(shù)字,給函數(shù)分配序號(hào)值。
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察看輸出節(jié):
- ordinal hint RVA name
-
- 100 0 0001106E Add = @ILT+105(?Add@@YAHHH@Z)
當(dāng)生成 DLL 時(shí),鏈接器使用 .def 文件創(chuàng)建導(dǎo)出 (.exp) 文件和導(dǎo)入庫(kù) (.lib) 文件。然后,鏈接器使用導(dǎo)出文件生成 DLL 文件。隱式鏈接到 DLL 的可執(zhí)行文件在生成時(shí)鏈接到導(dǎo)入庫(kù)。可以使用dumpbin程序察看可執(zhí)行文件的輸入節(jié)。可以看到現(xiàn)在是按序號(hào)進(jìn)行連接了。
- 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