最初的網頁鏈接已經掛了, 在此貼一個中間的轉載鏈接
https://blog.csdn.net/zhazhiqiang/article/details/51577523
一 概要
vs中導出 dll的方法有兩種: 一種是使用__declspec(dllexport), 另一種就是使用.def文件方式,
這兩種方式的最主要區別是在導出函數的名字上, 其次還有一些操作的靈活性上以及功能的強弱
二 一個具體的例子
1 __declspec(dllexport) 方式
首先對C和C++編譯(extern "C")與調用約定(__cdecl、__stdcall、__fastcall)進行組合測試:
<1> C++編譯
__declspec(dllexport) int add(int, int);
__declspec(dllexport) int __cdecl add(int, int);
__declspec(dllexport) int __stdcall add(int, int);
__declspec(dllexport) int __fastcall add(int, int);
對於C++編譯器的函數名修飾規則:不管__cdecl, __fastcall還是__stdcall調用方式,函數修飾名都是以"?"開始,后面是函數在名字,再后面是函數返回類型和參數類型按照代號拼出的參數表。對於__stdcall方式,參數表的開始標示是"@@YG”,對於__cdecl方式則是"@@YA”,對於__fastcall方式則是"@@YI”.
參數表后以"@Z”標示整個名字的結束,如果該函數無參數,則以"Z”標識結束。
<2> C編譯
extern "C" __declspec(dllexport) int add(int, int);
extern "C" __declspec(dllexport) int __cdecl add(int, int);
extern "C" __declspec(dllexport) int __stdcall add(int, int);
extern "C" __declspec(dllexport) int __fastcall add(int, int);
<3> 備注
__declspec(dllexport)的位置:
To export functions, the __declspec(dllexport) keyword must appear to the left of the calling-convention keyword, if a keyword is specified.
For example:
__declspec(dllexport) void __cdecl Function1(void);
To export all of the public data members and member functions in a class, the keyword must appear to the left of the class name as follows:
class __declspec(dllexport) CExampleExport : public CObject
{ class definition };
2 def文件導出方式(項目中添加一個def后綴的文件, 然后在項目屬性->連接器->所有選項->模塊定義文件)
具體到測試實例,我們的def文件內容如下:
LIBRARY "win"
EXPORTS
add @1
其中LIBRARY指定dll的模塊名稱,即dll名字,EXPORTS后的每一行指定一個導出函數名字,這個名字和頭文件中的聲明一致,后面可以跟@序號指定該函數的序號(這個是可選的,后面按序號導入函數的時候再詳細說)。
然后再測試一下__stdcall和__fastcall是否會對導出函數改名,測試結果如下,均未改名:
extern "C" int __stdcall add();
extern "C" int __fastcall add();
另外一種方案是在代碼中給鏈接器指定導出函數名字:
extern "C" __declspec(dllexport) int __fastcall add(int a, int b);
#pragma comment(linker, "/export:add=@add@8")
這里告訴鏈接器,導出一個函數名為add的函數,函數入口點和@add@8相同
這樣,我們既可以使用add,也可以使用@add@8了。
__stdcall方式和這類似,為add=_add@8。
【按序號而不是按名稱從dll導出函數】
http://msdn.microsoft.com/zh-cn/library/e7tsx612%28VS.80%29.aspx
def文件定義如下:
LIBRARY "win"
EXPORTS
add @1 NONAME
函數名稱和Hint都不見了。
這樣也可以用來隱藏dll中一些重要函數。
隱藏了函數名稱,在應用程序中使用序號來導入函數:
#include <windows.h>
#include <stdio.h>
int main()
{
typedef int (* AddFunc)(int, int);
HMODULE hModule = LoadLibrary("dll.dll");
AddFunc add = (AddFunc)GetProcAddress(hModule, MAKEINTRESOURCE(1)); //注意這里序號的指定方式
printf("%d\n", add(1, 2));
return 0;
}
三 總結:
1 __declspec(dllexport)
若要輸出類的所有成員:數據or函數,__declspec(dllexport)要放在類名左邊聲明:
class __declspec(dllexport) Class1{}
如果類沒有數據成員,__declspec(dllexport)放在class關鍵字前聲明就會被編譯器忽略,就沒有lib生成,如下:
__declspec(dllexport) class Class1{}
使用 __declspec(dllexport) 非常方便,因為不必考慮維護 .def 文件和獲取導出函數的修飾名。例如,如果您設計的 DLL 供自己控制的應用程序使用,則此方法很適用。如果通過新的導出函數重新生成 DLL,還必須重新生成應用程序,因為如果使用不同版本的編譯器進行重新編譯,則導出的 C++ 函數的修飾名可能會發生變化。
使用 .DEF 文件的優缺點(zz)
在 .def 文件中導出函數使您得以控制導出序號。當將附加的導出函數添加到 DLL 時,可以給它們分配更高的序號值(高於任何其他導出函數)。當您進行此操作時,使用隱式鏈接的應用程序不必與包含新函數的新導入庫重新鏈接。這非常重要,例如,在設計將由許多應用程序使用的第三方DLL 時。可以通過添加附加功能不斷地增強 DLL,同時確保現有應用程序繼續正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。
DLL 的 .def 文件中的導出名相匹配。