【__declspec(dllexport) 方式】
首先對C和C++編譯(extern "C")與調用約定(__cdecl、__stdcall、__fastcall)進(jìn)行組合測試:
【C++編譯】
__declspec(dllexport) int add(int, int);




對于C++編譯器的函數名修飾規則:不管__cdecl, __fastcall還是__stdcall調用方式,函數修飾名都是以"?"開(kāi)始,后面是函數在名字,再后面是函數返回類(lèi)型和參數類(lèi)型按照代號拼出的參數表。對于__stdcall方式,參數表的開(kāi)始標示是"@@YG”,對于__cdecl方式則是"@@YA”,對于__fastcall方式則是"@@YI”.
參數表后以"@Z”標示整個(gè)名字的結束,如果該函數無(wú)參數,則以"Z”標識結束。
更詳細的dll基礎知識請參考:
http://hi.baidu.com/luosiyong/blog/item/92bbdcfe860435375c600812.html
更深入的C++函數名修飾編碼規則請參考:
extern "C" __declspec(dllexport) int __cdecl add(int, int);



如果創(chuàng )建dll和可執行文件都是使用的VC,那用__declspec(dllexport)足夠解決問(wèn)題。但是如果創(chuàng )建出來(lái)的dll要和別的廠(chǎng)商的工具包構建的可執行文件鏈接,那就有一些額外的問(wèn)題來(lái)了。
在開(kāi)發(fā)dll的時(shí)候,一般不讓編譯器改變函數名,所以通常是以C方式編譯,即加入了extern "C"說(shuō)明。但是看上面的組合測試結果,__stdcall和__fastcall編譯出來(lái)的函數名還是和原始的函數名不同。就拿__stdcall來(lái)說(shuō),它以C編譯導出的時(shí)候,會(huì )在函數前面加入下劃線(xiàn),并在函數后面加入@和參數總大小的字節數。
或許現在你就想,__cdecl不就是沒(méi)有改變名稱(chēng)的方式嗎,并且默認也是__cdecl調用約定,的確,我們自己寫(xiě)的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方式開(kāi)發(fā)的一個(gè)dll,里面包含了上面那樣的add函數,如果要在VB中使用,VB的程序員需要如下聲明:
Declare Function add Lib "win.dll" Alias"_add@8"() As Integer
注意他需要寫(xiě)的名稱(chēng)是 "_add@8",而不是簡(jiǎn)單的"add",否則就會(huì )出現函數未定義的鏈接錯誤。
【備注】
__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文件導出方式】 EXPORTS 其中LIBRARY指定dll的模塊名稱(chēng),即dll名字,EXPORTS后的每一行指定一個(gè)導出函數名字,這個(gè)名字和頭文件中的聲明一致,后面可以跟@序號指定該函數的序號(這個(gè)是可選的,后面按序號導入函數的時(shí)候再詳細說(shuō))。 然后再測試一下__stdcall和__fastcall是否會(huì )對導出函數改名,測試結果如下,均未改名: extern "C" int __stdcall add();
首先了解一下 使用def文件從dll導出:
add @1
extern "C" int __fastcall add();


這樣,我們既可以使用add,也可以使用@add@8了。
__stdcall方式和這類(lèi)似,為add=_add@8。
【按序號而不是按名稱(chēng)從dll導出函數】 EXPORTS
add @1 NONAME
函數名稱(chēng)和Hint都不見(jiàn)了。
這樣也可以用來(lái)隱藏dll中一些重要函數。
隱藏了函數名稱(chēng),在應用程序中使用序號來(lái)導入函數:
#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)方式優(yōu)缺點(diǎn)對比:
http://blog.vckbase.com/zaboli/archive/2006/10/29/22911.html
聯(lián)系客服