***************************************************
更多精彩,歡迎進入:http://shop115376623.taobao.com
***************************************************
1.DLL編譯后導出函數名稱改變
?
在編寫一個DLL后,為了能被別的程序調用,需要將被使用的函數導出;
但是一般的編譯器都會將到處函數名稱改變;
例如:在VC中新建一個空的win32 dll工程,然后添加下面的文件;
- //dll1.h??
- #ifdef?DLL_API?_declspec(dllexport)???
- #else??
- #define?DLL_API?_declspec(dllexport)??
- #endif??
- ??
- DLL_API?int?add(int?a,?int?b);??
- //dll1.cpp??
- ??
- #include?"dll1.h"??
- ??
- DLL_API?int?add(int?a,?int?b)??
- {??
- ??return?a?+?b;??
- }??
?
編譯以后,得到dll1.dll與dll1.lib;使用Dumpbin.exe或者是Depends工具查(dll查看器)看發現,
dll1.dll中的到處函數名稱為:?add@@YAHH@Z?;這個新的函數名稱是C++編譯器對add函數的名稱進行了改變,
而且不同的編譯器的改編規則也不一樣,這就導致在通過add函數名對函數進行調用時無法找到add函數,
因為此時add的函數名稱已經被改編;
?
2.限定導出函數名稱
?
為了解決C與C++能在不同編譯器之間正常調用DLL,所以我們希望DLL在編譯過程中不要對函數名稱進行改編;
我們可以在定義導出函數時,加上限定符 extern "C"
我們把上面的例子修改下:
- //dll1.h??
- #ifdef?DLL_API?extern?"C"?_declspec(dllexport)???
- #else??
- #define?DLL_API?extern?"C"?_declspec(dllexport)??
- #endif??
- ??
- DLL_API?int?add(int?a,?int?b);??
?
- //dll1.cpp??
- #include?"dll1.h"??
- ??
- DLL_API?int?add(int?a,?int?b)??
- {??
- ??return?a?+?b;??
- }??
我們再次編譯得到dll1.dll,通過工具查看其到導出函數發現,此時的add函數的導出名稱仍然是add;
這樣我們就可以在其他編譯器上直接通過add調用該函數了;
?
3.__stdcall關鍵字將使限定無效
如果我們在第二個的基礎上給函數加上__stdcall關鍵字,導出函數的名稱將仍然被改編;
如果沒有添加__stdcall關鍵字,那么函數調用約定為C調用約定。如果加了__stdcall標準調用約定,
就是WINAPI調用約定,也就是pascal調用約定,這種約定與C調用約定不一樣。
- //dll1.h??
- #ifdef?DLL_API?extern?"C"?_declspec(dllexport)???
- #else??
- #define?DLL_API?extern?"C"?_declspec(dllexport)??
- #endif??
- ??
- DLL_API?int?__stdcall?add(int?a,?int?b);??
?
- //dll1.cpp??
- #include?"dll1.h"??
- ??
- DLL_API?__stdcall?int?add(int?a,?int?b)??
- {??
- ??return?a?+?b;??
- }??
重新編譯,然后通過工具查看DLL的導出函數,發現名稱為: _add@8;
也就是說如果函數的調用約定發生變化,即使在聲明時使用了 extern "C"限定符,函數名稱仍然會
改編;
C語言與Delphi的調用約定是不一樣的,Delphi使用的是pascal調用約定,如果我們要用C寫一個DLL供Delphi使用,
那么在導出函數時應指定其使用標準的函數調用約定,但此時 導出函數名稱就會被改編;
?
在這種情況下,我們需要通過一個稱為模塊定義文件(DEF)的方法解決名稱被改編的問題;
?
在上面例子的基礎上,我們給這個工程添加一個后綴為def的文件dll1.def;然后添加如下代碼:
- //dll1.def??
- ??
- LIBRARY?"dll1"??
- ??
- EXPORTS??
- add??
此文件中LIBRARY指定動態鏈接庫的內部名稱,該名稱與生成的動態鏈接庫名稱要匹配;
EXPORTS下面就是要導出的函數;
如果EXPORTS下的函數個數多時,方式為:
LIBRARY “dlll”
EXPORTS
ADD
SUB
MUL
……
如果導出的函數名稱與源文件中的函數名稱不一樣可以通過下面的語法指定導出函數名稱:
entryname = internalname
?
編譯時,編譯器會按照def中指定的函數名稱導出函數;
?
重新編譯,通過工具查看dll1.dll中的導出函數為add了;