Win32编程day11 学习笔记
一 Windows的库
由于项目的复杂程度,或者为了提高代码的重用率等等,所以才引入了库程序。
库包含两种:
1 静态库:扩展名为LIB的文件,是不能被加载的程序,可以理解为目标程序的归档。
2 动态库:扩展名是DLL的文件,是可以被应用程序加载的程序。
二 静态库
1 静态库的特点
1.1 目标程序的归档
1.2 静态库的代码会被嵌入到程序当中。
1.3 程序执行时不需要静态库存在
2 C语言静态库
2.1 创建静态库
创建Win32静态库项目,使用*.C文件建立项目。
在Setting里将library选项卡下的output路径改为 ../lib/*.lib
2.2 添加静态库函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//winclib.c int C_Add( int nAdd1, int nAdd2 ) { return ( nAdd1 + nAdd2 ); } int C_Sub( int nSub1, int nSub2 ) { return ( nSub1 - nSub2 ); }
2.3 在程序中将静态库导入
程序的设置里连接选项卡下的output路径也要修改。改为../bin/*.exe
2.3.1 项目的Setting里设置
2.3.2 使用关键字 pragma
#pragma comment(lib, "../lib/winclib.lib")
2.4 使用静态库提供的函数
在C语言程序中,直接使用函数即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//useclib.c //导入静态库 #pragma comment(lib, "../lib/winclib.lib") int main( ) { int nAdd = 0; int nSub = 0; //使用C静态库的函数 nAdd = C_Add( 100, 100 ); nSub = C_Sub( 100, 100 ); printf( "ADD: %d\n", nAdd ); printf( "SUB: %d\n", nSub ); return 0; }
3 C++语言的静态库
3.1 创建静态库(设置 library ..\lib\*.lib)
创建Win32静态库项目,使用*.CPP文件建立项目。
3.2 添加静态库的函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//wincpplib.cpp int CPP_Add( int nAdd1, int nAdd2 ) { return ( nAdd1 + nAdd2 ); } int CPP_Sub( int nSub1, int nSub2 ) { return ( nSub1 - nSub2 ); }
3.3 导入静态库(设置 link ../bin/*.exe)
3.3.1 项目的Setting里设置
3.3.2 使用关键字 pragma
#pragma comment(lib, "../lib/wincpplib.lib")
3.4 定义库函数的原形
int CPP_Add( int nAdd1, int nAdd2 );
3.5 使用库函数
3.6 注意:
如果在CPP文件使用C语言静态库,定义的静态库函数原形,需要增加 extern "C".
例如:
extern "C" int C_Add( int nAdd1, int nAdd2 );
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// usecpplib.cpp : Defines the entry point for the console application. // #include "stdafx.h" //导入C++的静态库 #pragma comment( lib, "../lib/wincpplib.lib" ) #pragma comment( lib, "../lib/winclib.lib" ) //定义函数原形 int CPP_Add( int nAdd1, int nAdd2 ); int CPP_Sub( int nSub1, int nSub2 ); extern "C" { int C_Add( int nAdd1, int nAdd2 ); int C_Sub( int nSub1, int nSub2 ); } int main(int argc, char* argv[]) { //使用C++库函数 int nAdd = CPP_Add( 100, 100 ); int nSub = CPP_Sub( 100, 100 ); printf( "ADD: %d\n", nAdd ); printf( "SUB: %d\n", nSub ); //使用C库函数 int nAdd2 = C_Add( 100, 100 ); int nSub2 = C_Sub( 100, 100 ); printf( "C_ADD: %d\n", nAdd ); printf( "C_SUB: %d\n", nSub ); return 0; }
三 动态库
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的项目
使用Win32 DLL项目,创建DLL,添加相应的文件。
设置--常规---输出 改为“../lib”
设置--连接---输出 改为“../bin/*.dll”
3.1.2 增加动态库函数
3.1.3 导出动态库函数
3.1.3.1 使用__declspec(dllexport)方式
在函数前增加这个关键字,例如
__declspec(dllexport) int Dll_Add()
3.1.3.2 增加 extern "C" 方式,即
extern "C" __declspec(dllexport)
以C语言方式导出函数
3.1.3.3 使用DEF文件导出
增加扩展名为DEF的文件到项目中(新建文本类型的文件,文件名以.def结尾).
在DEF文件中添加导出定义.
LIBRARY dllfunc.dll //导出库
EXPORTS //导出表
Dll_Mul @1 //导出函数
Dll_Div @2
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//动态库的代码文件dllfunc.cpp //C++导出方式 __declspec(dllexport) int Dll_Add( int nAdd1, int nAdd2 ) { return ( nAdd1 + nAdd2 ); } //C的导出方式 extern "C" __declspec(dllexport) int Dll_Sub( int nSub1, int nSub2 ) { return ( nSub1 - nSub2 ); } //DEF的导出方式(不能出现同名函数) int Dll_Mul( int nMul1, int nMul2 ) { return ( nMul1 * nMul2 ); }
用DEF导出方式时,新建一个文本文件 dllfunc.def, 接着到刚才新建def文件中:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
LIBRARY dllfunc.dll EXPORTS Dll_Mul @1
然后就可以使用了
3.2 使用
设置--连接---输出 改为“../bin/CallDllFunc.exe”
3.2.1 隐式链接
3.2.1.1 导入LIB
项目的Setting或者使用#pragma导入,例如:
#pragma comment( lib, "../lib/dllfunc.lib")
3.2.1.2 定义函数原形
声明一个和导出函数一致的函数定义.
如果DLL采用extern "C"导出函数,
需要定义extern "C"方式函数原形
3.2.1.3 使用函数
直接函数即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// CallDllFunc.cpp : Defines the entry point for the console application. // #include "stdafx.h" //导入DLL的Lib文件 #pragma comment( lib, "../lib/dllfunc.lib") //定义函数原形 int Dll_Add( int nAdd1, int nAdd2 ); extern "C" int Dll_Sub( int nSub1, int nSub2 ); int Dll_Mul( int nMul1, int nMul2 ); int main(int argc, char* argv[]) { // 使用函数 int nAdd = Dll_Add( 100, 100 ); int nSub = Dll_Sub( 100, 100 ); int nMul = Dll_Mul( 100, 100 ); printf("Dll_Add: %d\n", nAdd ); printf("Dll_Sub: %d\n", nSub ); printf("Dll_Mul: %d\n", nMul ); return 0; }
3.2.1.4 应用程序查找DLL的路径
1) 查找当前应用程序的目录.
2) 当前的工作目录
3) 查找Windows System32的目录
4) 查找Windows System的目录
5) 查找Windows目录
6) 查找环境变量PATH指定路径
3.2.2 显式链接 (程序要修改设置里面的link ../bin/*.exe)
3.2.2.1 加载动态库
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName );//DLL的路径
返回加载好DLL的句柄
3.2.2.2 定义函数原形对应的函数指针
3.2.2.3 获取函数地址
FARPROC GetProcAddress(
HMODULE hModule,//DLL的句柄
LPCSTR lpProcName );//函数的名称
返回对应函数地址
注意:
1 对于__declspec(dllexport)导出的函数,由于函数名称发生变化,所以无法使用函数名称获取对应的函数地址,所以尽量采用隐式链接的方式.
2 extern "C"或DEF方式导出的函数,
可以正常的使用函数名称获取函数地址.
3.2.2.4 使用函数
3.2.2.5 释放动态库
FreeLibrary
下面还是以调用dllfunc.dll为例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// InvokeDllFunc.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" typedef int ( * DLL_ADD )( int nAdd1, int nAdd2 ); typedef int ( * DLL_SUB )( int nSub1, int nSub2 ); typedef int ( * DLL_MUL )( int nMul1, int nMul2 ); void UseDll( ) { //加载动态库 HMODULE hDll = (HMODULE) LoadLibrary( "dllfunc.dll" ); if( hDll == NULL ) { printf( "Load Failed\n"); return; } printf( "DLL Handle: %p\n", hDll ); //定义函数指针 DLL_ADD Dll_Add = NULL; DLL_SUB Dll_Sub = NULL; DLL_MUL Dll_Mul = NULL; //获取函数地址 Dll_Add = ( DLL_ADD ) GetProcAddress( hDll, "Dll_Add" ); if( NULL == Dll_Add ) { printf( "Get Dll_Add Failed\n"); //打印。因为找不到Dll_Add函数。C++会重命名函数 } printf( "Dll_Add: %p\n", Dll_Add ); Dll_Sub = ( DLL_SUB ) GetProcAddress( hDll, "Dll_Sub" ); if( NULL == Dll_Sub ) { printf( "Get Dll_Sub Failed\n"); } printf( "Dll_Sub: %p\n", Dll_Sub ); Dll_Mul = ( DLL_MUL ) GetProcAddress( hDll, "Dll_Mul" ); if( NULL == Dll_Mul ) { printf( "Get Dll_Mul Failed\n"); } printf( "Dll_Mul: %p\n", Dll_Mul ); //使用函数 int nSub = Dll_Sub( 100, 100 ); int nMul = Dll_Mul( 100, 100 ); printf( "Dll_Sub: %d\n", nSub ); printf( "Dll_Mul: %d\n", nMul ); //释放动态库 FreeLibrary( hDll ); } int main(int argc, char* argv[]) { UseDll( ); return 0; }
4 动态库的变量
4.1 定义全局变量(设置 常规 ../lib link ../bin/*.dll)
4.2 导出全局变量
4.2.1 __declspec(dllexport)导出
__declspec(dllexport) int g_nValue1 = 100;
4.2.2 DEF文件导出
int g_nValue1 = 100;
在DEF文件的导出列表中增加
g_nValue1 @1 DATA
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//ddlvalue.cpp //导出DLL的全局变量 __declspec(dllexport) int g_nValue1 = 100; //DEF导出的全局变量 int g_nValue2 = 200;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//dllvalue.def LIBRARY dllvalue EXPORTS g_nValue2 @1 DATA
4.3 导入LIB文件(设置 link ../bin/*.exe)
4.4 定义导入变量
需要使用__declspec(dllimport)定义变量.
extern __declspec(dllimport) int g_nValue1;
4.5 使用变量
5 动态库的类
5.1 创建动态库并定义类(设置 常规 ../lib link ../bin/*.dll)
5.2 从DLL中导出类
在类名称前增加__declspec(dllexport)定义.
class __declspec(dllexport) CMath
{ ... };
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//math.h #ifndef _MATH_H_ #define _MATH_H_ //定义类导入导出宏 #ifdef _DLLCLASS_DLL_ //(在设置 C/C++ Category--Preprocessor 下 增加_DLLCLASS_DLL_) #define DLLCLASS_EXT __declspec(dllexport) #else #define DLLCLASS_EXT __declspec(dllimport) #endif //_DLLCLASS_DLL_ //增加类的导入导出宏 class DLLCLASS_EXT CMath { public: int Add( int nAdd1, int nAdd2 ); int Sub( int nSub1, int nSub2 ); }; #endif //_MATH_H_
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//math.cpp #include "math.h" int CMath::Add( int nAdd1, int nAdd2 ) { return ( nAdd1 + nAdd2 ); } int CMath::Sub( int nSub1, int nSub2 ) { return ( nSub1 - nSub2 ); }
5.3 使用时导入LIB文件
5.4 导入类
在类名称前增加__declspec(dllimport)定义.
class __declspec(dllimport) CMath
{ ... };
5.5 使用类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// useclass.cpp : Defines the entry point for the console application. // #include "stdafx.h" //导入DLL的LIB文件 #pragma comment( lib, "../lib/dllclass.lib") //需要类的导入方式的声明 #include "../DllClass/math.h" int main(int argc, char* argv[]) { //使用DLL中的类 CMath math; int nAdd = math.Add( 100, 100 ); int nSub = math.Sub( 100, 100 ); printf("math.Add = %d\n", nAdd ); printf("math.Sub = %d\n", nSub ); return 0; }
5.6 关于类的导入和导出
1) 定义一个宏,例如:
#ifdef _DLLCLASS_DLL_
#define DLLCLASS_EXT __declspec(dllexport)
#else
#define DLLCLASS_EXT __declspec(dllimport)
#endif //_DLLCLASS_DLL_
2) 根据编译项目,修改_DLLCLASS_DLL_宏声明对于导出类,需要定义_DLLCLASS_DLL_,否则不需要定义 _DLLCLASS_DLL_ 宏
3) 类的定义为
class DLLCLASS_EXT CMath
{ ... };
6 DllMain 函数
是DLL文件入口函数.当程序加载或者释放动态库的时候,会自动调用这个函数.
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,//DLL的句柄
DWORD fdwReason,//DLL被调用的原因
LPVOID lpvReserved ); //保留值
fdwReason -
DLL_PROCESS_ATTACH 进程加载
DLL_THREAD_ATTACH 线程加载
DLL_THREAD_DETACH 线程卸载
DLL_THREAD_DETACH 进程卸载
返回值表示是否加载成功.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//dllfunc.cpp #include "windows.h" #include "stdio.h" BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { printf( "DLL=%p, Reason=", hinstDLL ); switch( fdwReason ) { case DLL_PROCESS_ATTACH: printf( "DLL_PROCESS_ATTACH\n" ); break; case DLL_THREAD_ATTACH: printf( "DLL_THREAD_ATTACH\n" ); break; case DLL_THREAD_DETACH: printf( "DLL_THREAD_DETACH\n" ); break; case DLL_PROCESS_DETACH: printf( "DLL_PROCESS_DETACH\n" ); break; } return TRUE; } //C++导出方式 __declspec(dllexport) int Dll_Add( int nAdd1, int nAdd2 ) { return ( nAdd1 + nAdd2 ); } //C的导出方式 extern "C" __declspec(dllexport) int Dll_Sub( int nSub1, int nSub2 ) { return ( nSub1 - nSub2 ); } //DEF的导出方式 int Dll_Mul( int nMul1, int nMul2 ) { return ( nMul1 * nMul2 ); }