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