首页 > 代码库 > C/C++ (函数、变量和类)动态库的创建、导出和使用(图文+示例代码)
C/C++ (函数、变量和类)动态库的创建、导出和使用(图文+示例代码)
一 Windows库
1引入库的原因:
a.项目的复杂程度大
b.提高代码的利益利用率
2库的分类
2.1静态库: *.lib,不能被加载的程序,可以理解为目标程序的归档。
2.2动态库:*.dll,可以被应用程序加载的程序。
二 动态库
1动态库优点
1.1可以提供模块化的方式,方便协调开发(对于大项目,每个人写的东西编译为动态库,直接链接即可)
1.2对源代码保护
1.3减小可执行文件大小
1.4提高代码重用率
2动态库的基本使用方法
2.1动态库的创建
2.2加载动态库
2.3获取并使用库函数、变量或类
2.4释放动态库
3动态库的函数
3.1动态库的创建
3.1.1创建DLL项目
创建Win32Dll项目,创建DLL项目,添加*.cpp文件。
3.1.2增加动态库函数
3.1.3导出动态库函数(告诉使用者动态库中可提供的函数,代码示例如上)
(1)使用__declspec(dllexport)方式,在函数前增加关键字。举例如下:
__declspec(dllexport) intDll_Add(intnLeft,intnRight){ return(nLeft + nRight); }//C++方式导出
(2)增加extern “C”方式,举例如下
extern"C" __declspec(dllexport)// 以C语言方式导出函数
(3)使用def方式导出(在项目中添加*.def文件)
//*.def文件中信息
LIBRARY dllfunc.dll //导出库
EXPORTS //导出表
Dll_Mul @1 //导出函数
Dll_Div @2
(VC6中def导出方式与 extern “C”导出方式导出.lib文件内容基本相同,可用于显式链接)
动态库导出函数代码示例如下:
#include<Windows.h> #include <iostream> using namespace std; BOOL WINAPI DllMain(//返回值代表是否记载成功 HINSTANCE hinstDll,//DLL句柄 DWORD fdwReason,//DLL被调用的原因 LPVOID lpvReserved)//保留值 { cout << "DLL = " << hinstDll << endl; cout << "fdwReason = " << fdwReason << endl; return TRUE; } //C++导出方式 extern "C" __declspec (dllexport) int Dll_Add(int nLeft, int nRight) { return (nLeft + nRight); } //C的导出方式 extern "C"__declspec (dllexport) int Dll_Sub(int nLeft, int nRight) { return (nLeft - nRight); } //DEF导出方式 int Dll_Mul(int nLeft, int nRight) { return (nLeft * nRight); }
3.2 使用
3.2.1隐式链接
(1)导入lib
a.在项目属性->链接器->输入->附加依赖项中设置即可。
b.使用#pragma导入,举例如下:
#pragma comment (lib,"../lib/Dll_Func.lib")
(2) 定义函数原型
声明一个和导出函数一致的函数原型,如下所示:
#pragma comment (lib,"../lib/Dll_Func.lib") extern "C" int Dll_Add(int nLeft, int nRight); extern "C" int Dll_Sub(int nLeft, int nRight);
(3) 使用函数(注意:生成.dll文件最好与调用其的.exe文件在同一个目录)
直接使用函数即可,示例代码如下:
#include <cstdio> #pragma comment (lib,"../lib/Dll_Func.lib") extern "C" int Dll_Add(int nLeft, int nRight); extern "C" int Dll_Sub(int nLeft, int nRight); int Dll_Mul(int nLeft, int nRight); int main() { int nAdd = Dll_Add(100, 100); int nSub = Dll_Sub(100, 100); int nMul = 0;//Dll_Mul(100, 100); printf("nAdd = %d\n", nAdd); printf("nSub = %d \n", nSub); printf("nMul = %d \n", nMul); return 0; }
(4) 应用程序查找Dll路径(最好将要调用的动态库与调用动态库的.exe文件放在同一目录下)
A查找当前应用程序的目录
B当前工作目录
C查找Windows System32目录
D查找Windows System的目录
E查找Windows目录
F查找环境变量Path指定的目录
3.2.2显式链接
(1)加载动态库
LoadLibraryW( LPCSTR lpLibFileName //DLL文件路径 );
(2) 定义函数原型对应的函数指针
(3)获取函数地址
FARPROC GetProcAddress(//返回对应函数地址 HMODULE hModule, // DLL句柄 LPCSTR lpProcName);//函数名称
注意: a.__declspec(dllexport)导出的函数由于函数名称发生变化,所以无法使用函数名称获取对应函
数地址,所以尽量采用隐式链接的方式。
b. extern “C”__declspec(dllexport)方式导出的函数可以正常使用函数名称获取函数地址。
c.使用def导出方式在VC6.0下可使用显示链接(本人在VS2013中对def导出方式导出动态库未成功使
用,希望哪位大神可以指点迷津)
(4)使用函数
(5)释放动态库
FreeLibrary(HMOUDLE)
显式链接示例代码如下:
#include <iostream> #include <Windows.h> / #include <tchar.h> using namespace std; typedef int(*DLL_ADD)(int nLeft, int nRight); typedef int(*DLL_SUB)(int nLeft, int nRight); typedef int(*DLL_MUL)(int nLeft, int nRight); void useDll() { HMODULE hDll = LoadLibrary(_T("Dll_Func.dll")); if (nullptr == hDll) { cout << "Load Failed!" << endl; return; } //定义函数指针 DLL_ADD Dll_Add = nullptr; DLL_SUB Dll_Sub = nullptr; DLL_MUL Dll_Mul = nullptr; //获取函数地址 Dll_Add = (DLL_ADD)GetProcAddress(hDll, "Dll_Add"); if (nullptr == Dll_Add) { cout << "Get Dll_Add Failed!" << endl; } cout << Dll_Add << endl; Dll_Sub = (DLL_SUB)GetProcAddress(hDll, "Dll_Sub"); if (nullptr == Dll_Sub) { cout << "Get Dll_Sub Failed!" << endl; } cout << Dll_Sub << endl; Dll_Mul = (DLL_MUL)GetProcAddress(hDll, "Dll_Mul"); if (nullptr == Dll_Mul) { cout << "Get Dll_Mul Failed!" << endl; } cout << Dll_Mul << endl; //使用函数 int nSub = Dll_Sub(100, 100); int nAdd = Dll_Add(100, 100); cout << "Add = " << nAdd << endl; cout << "Sub = " << nSub << endl; //释放动态库 FreeLibrary(hDll); } int main() { useDll(); return 0; }
4.动态库中的变量
4.1定义全局变量
4.2导出全局变量
4.2.1 __declspec(dllexport)导出
4.2.2 DEF导出
Int g_nValue1 = 100;
在DEF文件中到处列表增加 g_nValue1 @1 DATA
4.3导入LIB文件
4.4声明导入变量
需要使用__declspec(dllimport)声明变量,举例如下:
extern__declspec(dllimport)int g_nValuel;
4.5使用变量
动态库变量使用代码示例如下:
#include <iostream> using namespace std; #pragma comment(lib,"../lib/DllValue.lib") //声明DLL导入变量 extern __declspec(dllimport) int g_nValue1;//动态库导出文件中有此全局变量,在此再次声明此变量原型 int main() { cout << "g_nValue1 = " << g_nValue1 << endl; return 0; }
5动态库的类导出
5.1创建静态库并定义类
5.2导出类
在类名称前__declspec(dllexport)定义:
class__declspec(dllexport)Math
{……}
导出类示例代码如下:
//**.h文件 #ifndef _MATH_H_ #define _MATH_H_ //定义类导入导出宏, #ifdef _DLLCLASS_DLL__ #define DLLCLASS_EXT __declspec(dllexport) #else #define DLLCLASS_EXT __declspec(dllimport) #endif //_DLLCLASS_DLL_ //增加类的导入 导出符号 class DLLCLASS_EXT Math { public: Math() { } virtual ~Math() { } int Add(int nLeft, int nRight); int Sub(int nLeft, int nRight); }; #endif
//**.cpp文件 #include "Math.h" int Math::Add(int nLeft, int nRight) { return (nLeft + nRight); } int Math::Sub(int nLeft, int nRight) { return (nLeft - nRight); }
5.3使用时导入LIB文件
5.4导入类
5.5使用类
5.6关于类的导入和导出
5.6.1定义一个宏,举例如下(避免多次编写类头文件,使之可在导出、导入时使用同一个头文件):
#ifdef_DLLCLASS_DLL__ #defineDLLCLASS_EXT __declspec(dllexport) #else #define DLLCLASS_EXT __declspec(dllimport) #endif//_DLLCLASS_DLL_
5.6.2 根据编译项目修改_DLLCLASS_DLL__宏声明,对于导出类,需定义_DLLCLASS_DLL__,否则,不需要定义 _DLLCLASS_DLL__。
在导出类时,项目设置如下(即定义_DLLCLASS_DLL__宏):
6 DllMain函数(用于变量初始化等操作。使用可参照3.1.3 (3)中示例代码)
Dll文件的入口函数,当程序加载或者释放动态库的时候,会自动调用此函数
BOOL WINAPIDllMain(//返回值代表是否加载成功
HINSTANCE hinstDll;//DLL句柄
DWORD fdwReason;//DLL被调用的原因
LPVOID lpvReserved;)//保留值
FdwReason –
DLL_PROCESS_ATTACH 进程加载
DLL_THREAD_ATTACH 线程加载
DLL_PROCESS_DETACH 进程卸载
DLL_THREAD_DETACH 线程卸载
使用导出类代码示例如下:
#include <iostream> #include "..\DllClass\Math.h" using namespace std; #pragma comment(lib,"../lib/DllClass.lib") int main(int argc, char *argv[]) { Math math; int add = math.Add(100,100); int sub = math.Sub(100,100); cout << "Add = " << add << endl; cout << "Sub = " << sub << endl; return 0; }
C/C++ (函数、变量和类)动态库的创建、导出和使用(图文+示例代码)