1。dll的建立
選擇win32的dll lib工程, 建立乙個cpp檔案,**如下:
_declspec(dllexport) int add(int a, int b)
注意前面的標識_declspec(dllexport),表示dll的輸出函式。每個輸出的函式都要用這個進行標識。
可以進行dll所在目錄用dumpbin -exports dll1.dll 檢視dll的輸出函式資訊。可以看到dll有輸出,但名字並不是add,
而是?add@@yahhh@z 這兒是因為c++編譯器對函式名字進行了改編。
2。dll的隱式呼叫(靜態呼叫)
隱式呼叫在引用dll檔案的lib檔案。(把dll1.dll ,dll1.lib兩個檔案複製到dlltest所在主目錄)
在工程setting屬性中,link標籤頁, 填入 dll1.lib
然後測試**如下:
extern add(int a, int b);
void ctestdll::onbtntest()
可以看到dll中的函式已經被執行了。
vs工具庫中depends工具可以選擇乙個執行檔案,檢視這個執行檔案所引用的dll檔案。
另外,對add函式的宣告是extern add(int a, int b); 這種不是專用的宣告方式,可以用下面的宣告
_declspec(dllimport) add(int a, int b);
從而指明函式是從dll中取出的,以增加程式的執行效率。
3。隱式引用,標頭檔案的使用
帶頭檔案的dll的編寫:
dll2.h:
_declspec(dllimport) add(int a, int b);
這裡是dllimport,是對呼叫檔案而言的。
dll2.cpp:
_declspec(dllexport) int add(int a, int b)
帶頭檔案的dll的呼叫:
同樣,dll2.lib要引入
呼叫檔案中,不用宣告dll2匯出的函式, 只在引用標頭檔案即可,如下:
#i nclude "../dll2/dll2.h"
呼叫函式不變。
4。隱式引用,標頭檔案的使用時,利用巨集簡化編寫
dll3.h:
#ifdef dll_api
#else
#define dll_api _declspec(dllimport)
#endif
dll_api add(int a, int b);
//說明:如果dll_api已經定義,不操作,否則,宣告為dll_api _declspec(dllimport),使用在標頭檔案。
//由於在cpp檔案中要引入dll3.h,在dll3.cpp中宣告了#define dll_api _declspec(dllexport), 使用dll_api
//可以在標頭檔案,原始檔中切換
dll3.cpp
#define dll_api _declspec(dllexport)
#i nclude "dll3.h"
int add(int a, int b)
5。類的輸出
dll3.h
#ifdef dll_api
#else
#define dll_api _declspec(dllimport)
#endif
dll_api int add(int a, int b);
class dll_api point
;dll3.cpp
#define dll_api _declspec(dllexport)
#i nclude "dll3.h"
#i nclude
#i nclude
#i nclude
int add(int a, int b)
void point::output(int a)
關鍵在於類的宣告:
class dll_api point
;注意在class後面加宣告標誌
6。輸出類中的部分函式
dll3.h
#ifdef dll_api
#else
#define dll_api _declspec(dllimport)
#endif
dll_api int add(int a, int b);
class /*dll_api*/ point
;只是在類宣告時,把對類的輸出去掉,然的在具體要輸出的函式的前面加上輸出宣告標誌,實現、呼叫都沒有變化。
7。防止函式名字改編
因為c++編譯器對dll匯出函式的名字進行了改編,c編譯器呼叫時就可能出錯。這裡提供乙個防止名字改編的方法。
這個解決方法解決c++, c互相呼叫的問題。
dll4.h
#ifdef dll_api
#else
#define dll_api extern "c" _declspec(dllimport)
#endif
dll_api int add(int a, int b);
dll4.cpp
#define dll_api extern "c" _declspec(dllexport)
#i nclude "dll4.h"
int add(int a, int b)
可以看到,只要在匯出函式的宣告和實現處加上 extern "c"
但是有個缺點,就是不能匯出類及類中的函式
注意這兒的"c" 為大寫
8。改變匯出函式的呼叫約定
dll4.h
#ifdef dll_api
#else
#define dll_api extern "c" _declspec(dllimport)
#endif
dll_api int _stdcall add(int a, int b);
dll4.cpp
#define dll_api extern "c" _declspec(dllexport)
#i nclude "dll4.h"
int _stdcall add(int a, int b)
可以看到在函式名的前面加上 _stdcall 即改變了函式的呼叫約定:標準呼叫
當vc寫的dll, 由dlephi呼叫時,就要加上_stdcall, 以符合object pascall 的約定。
這兒要注意,如果改變了呼叫約定,即使使用extern "c" 也會發生名字改編
9。利用模組定義檔案防止名字改編
新增乙個dll4.def檔案到原始檔, 內容如下:
library dll4
exports
add可以看到exports下面即為輸出的函式名。可以有下面的寫法:
library dll4
exports
export_add=add
export_add為要輸出的函式名,add為原始檔中的函式名
但是用模組定義檔案定義後,輸出的dll, 如果靜態呼叫則會找不到函式入口點。
此時應該如何靜態呼叫?
但至少可以動態呼叫
10。動態呼叫dll
dll檔案的編寫沒有變化。(有dll4.def檔案防止發生名字改編)
呼叫如下:
//動態呼叫
hinstance hist;
hist = loadlibrary("dll4.dll");
typedef int (_stdcall *addproc)(int a, int b);
addproc add = (addproc)getprocaddress(hist, "add");
if(!add)
int iret = add(5, 10);
cstring strmsg;
strmsg.format("%d", iret);
afxmessagebox(strmsg);
hist = loadlibrary("dll4.dll"); 是載入動態鏈結庫
typedef int (_stdcall *addproc)(int a, int b); 定義乙個和dll匯出函式一至的原型函式
addproc add = (addproc)getprocaddress(hist, "add"); 根據函式名,得到函式位址
注意:因為dll改變了呼叫約定,所以在宣告函式原型時,也加上了_stdcall ,否則應該如下:
typedef int (*addproc)(int a, int b);
注意:如果dll匯出函式發生了名字改編,再用dll中函式的名字則會出錯。要用dumpbin中看到的名字。或者用
addproc add = (addproc)getprocaddress(hist, makeintresource(1));
根據序號來取得函式位址,但這種方法不太好。
在不需要使用動態鏈結庫時,可以呼叫freelibrary(hist);來釋放動態鏈結庫
所以在以後dll的使用中可以:
a. 使用def檔案防止名字改編
b. 使用動態呼叫
靜態鏈結庫與動態鏈結庫總結
1 在生成lib檔案的時候並不發生連線的過程,編譯器僅僅把obj檔案裝載為乙個lib檔案。例如 static1 int add int a,int b int sub int a,int b static2 int myadd int a,int b int mysub int a,int b 這個...
動態鏈結庫使用 靜 動態鏈結庫使用總結
一 靜態庫編寫 1.首先當然是開vs然後建立乙個靜態庫工程啦 2.格式.一般有標頭檔案.h和原檔案.cpp,當然你也可以寫一在乙個cpp裡.mydll.h extends c mydll.cpp include mylib.h int sum int num1,int num2 int mult i...
動態鏈結庫 靜態鏈結庫
包含標頭檔案和庫 idir 指定編譯查詢標頭檔案的目錄,常用於查詢第三方的庫的標頭檔案,例 gcc test.c i.inc o test。ldir 指定鏈結時查詢lib的目錄,常用於查詢第三方庫。llibrary 指定額外鏈結的lib庫 巨集定義 dmacro 以字串 1 預設值 定義 macro...