dll導出函數的兩種方式的比較


最初的網頁鏈接已經掛了, 在此貼一個中間的轉載鏈接

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)

在 32 位編譯器版本中,可以使用 __declspec(dllexport) 關鍵字從 DLL 導出數據、函數、類或類成員函數。__declspec(dllexport) 在link時會將導出指令添加到obj文件中,因此不需要使用 .def 文件。當然,即使用了__declspec(dllexport)依然可以使用*.def文件,因為不同編譯器對於類的成員函數的name mangling規則不同,可以定義.def文件通過序號調用。為每個dll寫def顯得很繁雜,目前def使用已經比較少了,更多的是使用__declspec(dllexport)在 源代碼中定義dll的輸出函數。

若要輸出類的所有成員:數據or函數,__declspec(dllexport)要放在類名左邊聲明:
class __declspec(dllexport) Class1{}
如果類沒有數據成員,__declspec(dllexport)放在class關鍵字前聲明就會被編譯器忽略,就沒有lib生成,如下:
__declspec(dllexport) class Class1{}

使用 __declspec(dllexport) 的優缺點(zz)
使用 __declspec(dllexport) 非常方便,因為不必考慮維護 .def 文件和獲取導出函數的修飾名。例如,如果您設計的 DLL 供自己控制的應用程序使用,則此方法很適用。如果通過新的導出函數重新生成 DLL,還必須重新生成應用程序,因為如果使用不同版本的編譯器進行重新編譯,則導出的 C++ 函數的修飾名可能會發生變化。
 
2 def文件
其實def文件的功能相當於extern “C” __declspec(dllexport)
def文件中PRIVTATE的作用
The optional keyword PRIVATE prevents entryname from being placed in the import library generated by LINK. It has no
effect on the export in the image also generated by LINK.用了PRIVATE,生成的lib里沒有對應方法或者數據的entryname因此不能被客戶隱式調用。

使用 .DEF 文件的優缺點(zz)
在 .def 文件中導出函數使您得以控制導出序號。當將附加的導出函數添加到 DLL 時,可以給它們分配更高的序號值(高於任何其他導出函數)。當您進行此操作時,使用隱式鏈接的應用程序不必與包含新函數的新導入庫重新鏈接。這非常重要,例如,在設計將由許多應用程序使用的第三方DLL 時。可以通過添加附加功能不斷地增強 DLL,同時確保現有應用程序繼續正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。

使用 .def 文件的另一個優點是:可以使用 NONAME 屬性導出函數,該屬性僅將序號放到 DLL 的導出表中。對具有大量導出函數的 DLL,使用NONAME 屬性可以減小 DLL 文件的大小。有關編寫模塊定義語句的信息,請參見模塊定義語句的規則。有關序號導出的更多信息,請參見按序號而不是按名稱從 DLL 導出函數。
使用 .def 文件的主要缺點是:在 C++ 文件中導出函數時,必須將修飾名放到 .def 文件中,或者通過使用外部“C”用標准 C 鏈接定義導出函數,以避免編譯器進行名稱修飾。如果需要將修飾名放到 .def 文件中,則可以通過使用 DUMPBIN 工具或 /MAP 鏈接器選項來獲取修飾名。請注意,編譯器產生的修飾名是編譯器特定的。如果將 Visual C++ 編譯器產生的修飾名放到 .def 文件中,則鏈接到 DLL 的應用程序必須也是用相同版本的 Visual C++ 生成的,這樣調用應用程序中的修飾名才能與
DLL 的 .def 文件中的導出名相匹配。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM