dll的def文件與__declspec(dllexport)導出函數方式比較html
【__declspec(dllexport) 方式】
首先對C和C++編譯(extern "C")與調用約定(__cdecl、__stdcall、__fastcall)進行組合測試:
【C++編譯】
__declspec(dllexport) int add(int, int);windows
__declspec(dllexport) int __cdecl add(int, int);app
__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」標識結束。
更詳細的dll基礎知識請參考:
http://hi.baidu.com/luosiyong/blog/item/92bbdcfe860435375c600812.html
更深刻的C++函數名修飾編碼規則請參考:
http://hi.baidu.com/wanggang2008/blog/item/cd43e60756021bc07a89470a.html測試
【C編譯】
extern "C" __declspec(dllexport) int add(int, int);編碼
extern "C" __declspec(dllexport) int __cdecl add(int, int);spa
extern "C" __declspec(dllexport) int __stdcall add(int, int);設計
extern "C" __declspec(dllexport) int __fastcall add(int, int);htm
若是建立dll和可執行文件都是使用的VC,那用__declspec(dllexport)足夠解決問題。可是若是建立出來的dll要和別的廠商的工具包構建的可執行文件連接,那就有一些額外的問題來了。
在開發dll的時候,通常不讓編譯器改變函數名,因此一般是以C方式編譯,即加入了extern "C"說明。可是看上面的組合測試結果,__stdcall和__fastcall編譯出來的函數名仍是和原始的函數名不一樣。就拿__stdcall來講,它以C編譯導出的時候,會在函數前面加入下劃線,並在函數後面加入@和參數總大小的字節數。
或許如今你就想,__cdecl不就是沒有改變名稱的方式嗎,而且默認也是__cdecl調用約定,的確,咱們本身寫的dll幾乎均可以使用__cdecl方式。可是,Windows中最廣泛的調用方式都是__stdcall(好比CALLBACK、 WINAPI),一些經常使用的定義以下:
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
如今假如用VC的__stdcall方式開發的一個dll,裏面包含了上面那樣的add函數,若是要在VB中使用,VB的程序員須要以下聲明:
Declare Function add Lib "win.dll" Alias"_add@8"() As Integer
注意他須要寫的名稱是 "_add@8",而不是簡單的"add",不然就會出現函數未定義的連接錯誤。
【備註】
__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 };
Reference:
1.
http://msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx
2.
http://msdn.microsoft.com/en-us/library/a90k134d(VS.80).aspx
【def文件導出方式】
首先了解一下 使用def文件從dll導出:
http://msdn.microsoft.com/zh-cn/library/d91k01sh(v=VS.80).aspx
具體到測試實例,咱們的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;
}
【備註】
def文件和__declspec(dllexport)方式優缺點對比:
1、__declspec(dllexport)
若要輸出類的全部成員:數據or函數,__declspec(dllexport)要放在類名左邊聲明:
class __declspec(dllexport) Class1{}
若是類沒有數據成員,__declspec(dllexport)放在class關鍵字前聲明就會被編譯器忽略,就沒有lib生成,以下:
__declspec(dllexport) class Class1{}
使用 .DEF 文件的優缺點(zz)
在 .def 文件中導出函數使您得以控制導出序號。當將附加的導出函數添加到 DLL 時,能夠給它們分配更高的序號值(高於任何其餘導出函數)。當您進行此操做時,使用隱式連接的應用程序沒必要與包含新函數的新導入庫從新連接。這很是重要,例如,在設計將由許多應用程序使用的第三方DLL 時。能夠經過添加附加功能不斷地加強 DLL,同時確保現有應用程序繼續正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。