一 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;
}