動態庫(非MFC動態庫)
一、C語言動態庫
1、創建C語言動態庫,並封裝函數:
1)創建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:Cdll.c
Cdll.c中的內容:
//創建C的動態庫 //_declspec(dllexport)聲明導出 _declspec(dllexport)int Cdll_add(int add1,int add2){ return add1+add2; } _declspec(dllexport)int Cdll_sub(int sub1,int sub2){ return sub1-sub2; }
3)編譯、鏈接
註意:調用動態庫中的函數時,須執行函數導出,庫函數的導出有兩種方法:
①方法一:用_declspec(dllexport)聲明導出
②方法二:模塊定義文件.def
如:LIBRARY 庫文件名(無後綴)
EXPORTS
函數名1 @1
函數名2 @2
Build後在當前工程的Debug文件夾下生成Cdll.dll和Cdll.lib文件
這裏生成的Cdll.dll文件中保存了函數的實際偏移地址和對應編號,Cdll.lib文件中並非函數的源代碼而是存放的動態庫dll中的函數偏移地址編號
將Cdll.dll文件置於工作區下的bin文件夾中,將Cdll.lib文件置於工作區下的lib文件夾中
2、調用C語言動態庫中的函數:
①靜態調用(隱式鏈接)
註意:靜態調用(隱式鏈接)調用動態庫中的函數時,在聲明函數時須在函數原型前加_declspec(dllimport)聲明導入
但我們這裏是用C程序來調用C語言動態庫中的函數,無須函數聲明
1)創建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCdll.c
UseCdll.c中的內容:
//靜態調用C的動態庫 //告訴鏈接器去哪兒抓偏移地址編號 #pragma comment(lib,"../lib/Cdll.lib") int main(){ int sum,sub; sum=Cdll_add(5,3); sub=Cdll_sub(5,3); printf("sum=%d,sub=%d\n",sum,sub); return 0; }
3)編譯、鏈接
註意:調用動態庫的情況下,須將生成的dll文件(即Cdll.dll)與執行文件(即UseCdll.exe)放在同一目錄下,程序才可運行
這裏因為之前已將Cdll.dll文件統一置於工作區下的bin文件夾中,因此在本次調用時須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCdll.exe
②動態調用(顯式鏈接)
二、C++動態庫
1、創建C++動態庫,並封裝函數:
註意:調用動態庫中的函數時需要執行函數導出,庫函數的導出有兩種方法:
①方法一:用_declspec(dllexport)聲明導出
②方法二:模塊定義文件.def
如:
LIBRARY 庫文件名(無後綴)
EXPORTS
函數名1 @1
函數名2 @2
註:.def 文件中的註釋格式為“;註釋”,且為單行註釋。
①“_declspec(dllexport)聲明導出”方式創建C++動態庫
1)創建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:CPPdll.cpp
CPPdll.cpp中的內容:
//用"聲明導出_declspec(dllexport)"方式創建C++動態庫 //函數導出 _declspec(dllexport)int CPPdll_add(int add1,int add2){ return add1+add2; } _declspec(dllexport)int CPPdll_sub(int sub1,int sub2){ return sub1-sub2; }
3)編譯、鏈接
Build後,在當前工程的Debug文件夾下生成CPPdll.dll和CPPdll.lib文件
CPPdll.dll文件保存了函數的實際偏移地址和對應編號,CPPdll.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號
將CPPdll.dll文件置於工作區下的bin文件夾中,將CPPdll.lib文件置於工作區下的lib文件夾中
②“模塊定義文件.def”方式創建C++動態庫
1)創建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:CPPdll2.def
CPPdll2.def中的內容:
LIBRARY CPPdll2 EXPORTS CPPdll_add @1 CPPdll_sub @2
3)添加SourceFiles文件:CPPdll2.cpp
CPPdll2.cpp中的內容:
//用"模塊定義文件導出.def"方式創建C++動態庫 //函數導出 int CPPdll_add(int add1,int add2){ return add1+add2; } int CPPdll_sub(int sub1,int sub2){ return sub1-sub2; }
4)編譯、鏈接
Build後,在當前工程的Debug文件夾下生成CPPdll2.dll和CPPdll2.lib文件
CPPdll2.dll文件保存了函數的實際偏移地址和對應編號,CPPdll2.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號
將CPPdll2.dll文件置於工作區下的bin文件夾中,將CPPdll2.lib文件置於工作區下的lib文件夾中
2、調用C++動態庫中的函數:
①靜態調用(隱式鏈接)
1)創建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCPPdll.cpp
UseCPPdll.cpp中的內容:
//用"隱式鏈接"方式調用C++動態庫 //對應的在創建C++動態庫時使用的是"聲明導出_declspec(dllexport)"方式 #include <stdio.h> int CPPdll_add(int add1,int add2); int CPPdll_sub(int sub1,int sub2); //告訴編譯器到哪去抓函數的導出偏移地址編號 #pragma comment(lib,"../lib/CPPdll.lib") /*****************************************/ //C++編譯器調用C語言動態庫中的函數 extern "C"int Cdll_add(int add1,int add2); extern "C"int Cdll_sub(int sub1,int sub2); #pragma comment(lib,"../lib/Cdll.lib") /*****************************************/ int main(){ int sum=CPPdll_add(5,6); int sub=CPPdll_sub(5,6); printf("sum=%d,sub=%d\n",sum,sub); /*********************************/ sum=Cdll_add(5,8); sub=Cdll_sub(5,8); printf("sum=%d,sub=%d\n",sum,sub); /********************************/ return 0; }
3)編譯、鏈接
註意:調用動態庫的情況下,須將生成的dll文件(即CPPdll.dll)與執行文件(即UseCPPdll.exe)放在同一目錄下,程序才可運行
這裏因為之前已將CPPdll.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCPPdll.exe
4)C++編譯器在調用動態庫中的函數時,須進行函數聲明:
#include <stdio.h>
int CPPdll_add(int add1,int add2);
int CPPdll_sub(int sub1,int sub2);
5)C++編譯器在調用C語言動態庫中的函數時,會對函數名進行換名,須使用extern “C”來抑制C++編譯器的換名
extern "C"int Cdll_add(int add1,int add2);
extern "C"int Cdll_sub(int sub1,int sub2);
#pragma comment(lib,"../lib/Cdll.lib")
6)這裏C++編譯器在調用動態庫中的函數時,未見將函數導入,是因為將函數聲明放在了調用程序內部,如果單獨放在頭文件中,則須將函數導入:
//UseCPPdll.h
_declspec(dllimport)int CPPdll_add(int add1,int add2);
_declspec(dllimport)int CPPdll_sub(int sub1,int sub2);
若調用的是C語言動態庫(.c生成的.dll、.lib)中的函數,還須在int前加extern "C"
②動態調用(顯式鏈接)
註意:C++程序在動態調用(顯式鏈接)C++動態庫中的函數時,會對函數進行換名,故推薦用“模塊定義文件.def”的方式導出函數
1)創建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCPPdll2.cpp
UseCPPdll2.cpp中的內容:
//用"顯示連接"方式調用C++動態庫 //用此方法調用時,C++編譯器會對庫中函數的調用進行換名 //因此在創建C++動態庫時,推薦使用"模塊定義導出.def"方式將函數導出 #include <windows.h> #include <stdio.h> typedef int(*DLL_ADD)(int m,int n); typedef int(*DLL_SUB)(int m,int n); int main(){ //找到dll文件並使文件中的內容進入內存 HINSTANCE hDll=LoadLibrary("CPPdll2.dll"); printf("hDll:%d\n",hDll); //獲取函數的絕對地址並進行函數調用 DLL_ADD myAdd=(DLL_ADD)GetProcAddress(hDll,"CPPdll_add"); printf("myAdd:%p\n",myAdd); int sum=myAdd(5,5); printf("sum=%d\n",sum); DLL_SUB mySub=(DLL_SUB)GetProcAddress(hDll,"CPPdll_sub"); printf("mySub:%p\n",mySub); int sub=mySub(5,5); printf("sub=%d\n",sub); FreeLibrary(hDll); return 0; }
3)編譯、鏈接
註意:調用動態庫的情況下,須將生成的dll文件(即CPPdll2.dll)與執行文件(即UseCPPdll2.exe)放在同一目錄下,程序才可運行
這裏因為之前已將CPPdll2.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCPPdll2.exe
動態加載:
1.定義函數指針類型:typedef ...
2.加載動態庫
HMODULE LoadLibrary (
LPCTSTR lpFileName // 動態庫文件名(按路徑規則搜索)或者用絕對/相對路徑(按指定路徑加載)
);
成功返回動態庫實例句柄(HINSTANCE),失敗返回NULL。
3.獲取函數地址
FARPROC GetProcAddress (
HMODULE hModule, // 動態庫實例句柄
LPCSTR lpProcName, // 函數名(註意C++換名問題)
);
成功返回函數地址,失敗返回NULL。
4.卸載動態庫
BOOL FreeLibrary (
HMODULE hModule // 動態庫實例句柄
);
成功返回TRUE,失敗返回FALSE。
5.可執行程序調用LoadLibrary時加載動態庫,調用FreeLibrary時卸載動態庫
3、在C++動態庫中封裝類
註意:動態庫中類的導出只能用“_declspec(dllexport)”方式,不能使用“模塊定義文件.def”方式
1)創建新工程:Win32 Dynamic-Link Library
2)添加HeaderFiles文件:dllClass.h
dllClass.h中的內容:
#ifndef DLLCLASS_H #define DLLCLASS_H //宏開關 //定義關於庫中類的“導出”(我們編寫庫時為導出)或“導入”(用戶使用庫時需導入)的宏定義 #ifdef DLLCLASS_EXPORTS #define EXT_CLASS _declspec(dllexport) #else #define EXT_CLASS _declspec(dllimport) #endif class EXT_CLASS CMath{ public: int Add(int add1,int add2); int Sub(int sub1,int sub2); }; #endif
類導出的宏開關:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport)
#else
#define EXT_CLASS _declspec(dllimport)
#endif
class EXT_CLASS CMath{
......
}
3)添加SourceFiles文件:dllClass.cpp
dllClass.cpp中的內容:
//在庫中封裝類 //在.cpp源文件中實現函數的功能,編譯連接後生成的.dll文件用戶才不可見具體的功能實現過程 //必須在頭文件前打開宏開關,設為導出:_declspec(dllexport) #define DLLCLASS_EXPORTS #include "dllClass.h" #include <windows.h> #include <stdio.h> //入口函數 BOOL WINAPI DllMain(HINSTANCE hDll,DWORD fdwReason,LPVOID pParam){ switch(fdwReason){ case DLL_PROCESS_ATTACH: //動態庫被別的進程加載 //申請資源、初始化工作 printf("Loading...\n"); break; case DLL_PROCESS_DETACH: //動態庫被別的進程卸載 //善後處理 printf("UnLoading...\n"); break; } return TRUE; } int CMath::Add(int add1,int add2){ return add1+add2; } int CMath::Sub(int sub1,int sub2){ return sub1-sub2; }
4)編譯、鏈接
Build後,在當前工程的Debug文件夾下生成dllClass.dll和dllClass.lib文件
dllClass.dll文件保存了函數的實際偏移地址和對應編號,dllClass.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號
將dllClass.dll文件置於工作區下的bin文件夾中,將dllClass.lib文件置於工作區下的lib文件夾中
4、調用C++動態庫中的類:
1)創建工作工程:Win32 Console Application
2)添加SourceFiles文件:UsedllClass.cpp
UsedllClass.cpp中的內容:
//調用dllClass.dll庫 #include <stdio.h> #include "../dllClass/dllClass.h" #pragma comment(lib,"../lib/dllClass.lib") int main(){ CMath math; int sum=math.Add(5,2); int sub=math.Sub(5,2); printf("sum=%d,sub=%d\n",sum,sub); return 0; }
3)編譯、鏈接
在UsedllClass.cpp未見有對宏開關的定義語句“#define DLLCLASS_EXPORTS”,故宏開關自動設為導入:_declspec(dllimport)
註意:調用動態庫的情況下,須將生成的dll文件(即dllClass.dll)與執行文件(即UsedllClass.exe)放在同一目錄下,程序才可運行
這裏因為之前已將dllClass.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UsedllClass.exe
三、動態庫的入口函數:
入口函數不是動態庫所必須的,常用於動態庫內部初始化或善後處理
BOOL WINAPI DllMain (
HANDLE hDLL, // 動態庫實例句柄
DWORD fdwReason, // 被調用的原因
LPVOID lpvReserved // 保留值
);
返回TRUE表示動態庫加載成功,FALSE表示失敗。
fdwReason取值:
DLL_PROCESS_ATTACH - 進程加載,在主線程中調用LoadLibrary
DLL_PROCESS_DETACH - 進程卸載,在主線程中調用FreeLibrary
DLL_THREAD_ATTACH - 線程加載,在子線程中調用LoadLibrary
DLL_THREAD_DETACH - 線程卸載,在子線程中調用FreeLibrary
動態庫(非MFC動態庫)