- 静态库程序 - 运行时不存在,会被链接
到可执行文件或者动态库中,目标程序的归档。
文件扩展名:LIB - 动态库程序 - 运行时独立存在,不会被链接到可执行文件或其他动态库中。
文件扩展名:DLL
静态库程序
- 静态库特点
- 运行时不存在
- 链接到可执行文件或者动态库中
- 目标程序的归档
C语言静态库
- 静态库的创建
- 建项目
- 添加库程序,源文件使用C文件
- 库的路径设置
- 项目的“Settings”中设置库的路径
- 可以使用 pragma 关键字设置
- c语言静态库的使用
建立一个C文件,可以在文件中直接使用C库函数,不需要头文件。C编译器只是根据库函数名称,在库中找到对应的函数代码,进行链接。
- 静态库的创建
- C++语言的静态库
- 静态库的建立
- 建立项目
- 添加库程序,源文件使用CPP文件
- 库的导入
- 项目的“Setting”中设置库的路径
- 可以使用 pragma 关键字设置
- 注意
在CPP环境使用C静态库,库中函数原型定义要增加 extern “C”, 例如:extern "C" int Add(... );
- 静态库的建立
动态库程序
- 动态库特点
- 运行时独立存在
- 不会链接到执行程序
- 使用时要加载(使动态库程序运行起来)
- 与静态库的比较:
- 由于静态库是将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所以代码体积会增大。动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小。
- 静态库发生变化后,新的代码需要重新链接嵌入到执行程序中。动态库发生变化后,如果库中函数的定义(或地址)未变化,其他使用DLL的程序不需重新链接。
- 动态库的创建
- 建立项目
- 添加库程序
- 库程序导出 (导出函数偏移地址)- 提供给使用者库中的函数等信息。
- 动态库的使用
- 隐式链接(使动态库执行起来的过程不需要程序员负责,过程不明显)
- 显式链接(使动态库程序运行起来的过程需要程序员自己负责)
- 动态库的函数
- 实现动态库的函数
- 库函数的导出
- 声明导出
使用 _declspec(dllexport) 在函数定义前面,导出函数
注意:动态库编译链接后,也会有LIB文件,是作为动态库函数映射使用,与静态库不完全相同。 - 模块定义文件 .def
例如:
LIBRARY DLLFunc //库 EXPORTS //库导出表 DLL_Mul @1 //导出的函数 DLL_Sub @2
- 声明导出
- 制作动态库
- 声明到处:_declspec(dllexport)将函数的偏移地址导到了dll的文件头中,如果是C++编译器,dll文件头中记录的是换名之后的函数对应的偏移地址
lib文件记录的仅仅是换名之后的函数名对应的编号 - 模块定义文件到处:将函数的偏移地址导出到了dll的文件头中,即便C++编译器,dll文件头中记录的是为换名的函数名对应的偏移地址
lib文件中记录的仅仅是未换名的函数名对应的编号 - 使用动态库:
- 隐式链接---操作系统的加载负责使动态库执行(动态库的首地址)
链接器负责到lib文件中抓函数的编号,程序执行起来后操作
系统加载器负责拿着编号到dll文件中查询函数的偏移地址 - 显示链接---程序员调用LoadLibrary使动态库执行
程序员调用GetProcAddress()函数,在这个函数内部通过函
数名称到dll文件头中查询函数的偏移地址
例如:GetProcAddress(hDll, “CPPdll_add”);
- 声明到处:_declspec(dllexport)将函数的偏移地址导到了dll的文件头中,如果是C++编译器,dll文件头中记录的是换名之后的函数对应的偏移地址
- 库函数的使用
- 隐式链接
- 头文件和函数原型
可以在函数原型的定义前,增加declspec(dllimport), 例如
_declspec(dllimport) int DLL_Add( ... );
如果库函数使用C格式导出,需要在函数定义增加 extern “C” - 导入动态库的LIB文件
- 在程序中使用函数
- 隐式链接的情况,DLL可以存放的路径:
(1)与执行文件中同一个目录下-----推荐使用
(2)当前工作目录
(3)Windows目录
(4)Windows/System32目录
(5)Windows/System
(6)环境变量PATH指定目录
注意:高版本VC的配置文件
- 头文件和函数原型
- 显式链接
- 定义函数指针类型 typedef
- 加载动态库
HMODULE LoadLibrary( LPCTSTR lpFileName //动态库文件名(.exe与.dll文件在同一路径下)或全路径 ); 返回DLL的实例句柄(HMODULE 实际就是HINSTANCE)
- 获取函数地址
FARPROC GetProcAddress( HMODULE hModule, //DLL句柄 LPCSTR lpProcName //函数名称 ); 成功返回函数的绝对地址 返回地址DLL_ADD
- 使用函数
- 卸载动态库
BOOL FreeLibrary( HMODULE hModule //DLL的实例句柄 );
- 两种链接方式对比
- 在库函数的定义不变情况下:
隐式链接,由于库函数地址是在程序编译链接时设置,所以当动态库变化后,使用程序需要重新编译链接。
显式链接,由于库函数地址是在程序执行时,动态的从库中查询,所以库变化后,不需要重新编译链接。 - 动态库加载
隐式链接,动态库是在程序启动时就被加载,当DLL不存在,程序无法启动
显式链接,动态库只在使用LoadLibrary函数,才会被加载。
- 在库函数的定义不变情况下:
- 隐式链接
- DLL中类的使用
- DLL中类的导出
在类名称前增加 _declspec(dllexport) 定义,例如:
class _declspec(dllexport) CMath {};实际上是导出类的成员函数的函数名
通常使用预编译开关切换类的导入导出定义,例如:
#ifdef DLLCLASS_EXPORTS #define EXT_CLASS _declspec(dllexport)//DLL #else #define EXT_CLASS _declspec(dllimport)//使用者 #endif class EXT_CLASS CMath{
- 使用DLL中的类
* 导入DLL的LIb
* 类的定义 CMath math;
* 使用类 math.Add(…); 动态库的程序入口
入口程序不是DLL必须的。常用于DLL内部初始化或善后处理。BOOL WINAPI DllMain( HINSTANCE hinstDLL, //动态库实例句柄 DWORD fdwReason, //被调用的原因 LPVOID lpvReserved //保留值 ); 返回TRUE,表示动态库加载成功。
动态库的加载或卸载时会被调用。例如:使用LoadLibrary或FreeLibrary时会被调用。- DLLMAIN动态库入库函数
BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to the DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved // reserved );
- DLL中类的导出
windows文件
创建或打开Windows文件
HANDLE CreateFile( LPCTSTR lpFileName, //文件名称(带盘符的全路径名) DWORD dwDesiredAccess, //访问权限 DWORD dwShareMode, //共享方式,读共享,即可以同时多个程序读,也可以允许别人删除,写等方式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性,默认为NULL,windows下该属性基本都为NULL DWORD dwCreationDisposition, //创建或打开方式 DWORD dwFlagsAndAttributes, //文件属性 HANDLE hTemplateFile //文件句柄模板,默认为NULL,即为全双工 );
该函数既可以操作硬盘文件,也可以操作外围设备,如打印机
成功返回文件句柄。相当于Unix中的文件描述符DWORD dwShareMode, //共享方式,读共享,即可以同时多个程序读,也可以允许别人删除,写等方式 FILE_SHARE_DELETE---质询方式 FILE_SHARE_READ---读共享 FILE_SHARE_WRITE---写共享
DWORD dwCreationDisposition, //创建或打开方式 CREATE_NEW---没有则新建,有则失败 CREATE_ALWAYS---没有则新建,有则先删除再新建 OPEN_EXISTING---有就打开,没有就失败 OPEN_ALWAYS---有就打开,没有就创建然后再打开 TRUNCATE_EXISTING---打开文件的同时,清空文件
写数据
BOOL WriteFile( HANDLE hFile, //文件句柄 LPCVOID lpBuffer, //数据BUFF DWORD nNumberOfBytesToWrite, //数据长度 LPDWORD lpNumberOfBytesWritten, //返回实际写入的数据长度 LPOVERLAPPED lpOverlapped //默认为NULL );
读数据
BOOL ReadFile( HANDLE hFile, //文件句柄 LPVOID lpBuffer, //数据BUFF DWORD nNumberOfBytesToRead, //要读的字节数 LPDWORD lpNumberOfBytesRead, //实际读到字节数 LPOVERLAPPED lpOverlapped //默认为NULL );
- 关闭文件
BOOL CloseHandle( HANDLE hObject //文件句柄 );
文件长度
DWORD GetFileSize( HANDLE hFile, //文件句柄 LPDWORD lpFileSizeHigh //返回文件长度的高32位 ); 返回值是文件长度的低32位
DWOED就是无符号long类型,主要是为了方便存储大文件,函数返回值是以字节为单位,第二个参数以G为单位文件指针
DWORD SetFilePointer( HANDLE hFile, //文件句柄 LONG lDistanceToMove, //偏移量的低32位 PLONG lpDistanceToMoveHigh, //偏移量的高32位 DWORD dwMoveMethod//偏移的相对位置 );
DWORD dwMoveMethod//偏移的相对位置 FILE_BEGIN FILE_CURRENT FILE_END
SetFilePointer函数返回实际偏移量的低32位,lpDistanceToMoveHigh返回实际偏移量的高32位
文件相关操作
CopyFile - 拷贝文件
DeleteFile - 删除文件
MoveFile - 移动文件
- 文件遍历
- 查找文件
HANDLE FindFirstFile( LPCTSTR lpFileName, //查找路径 LPWIN32_FIND_DATA lpFindFileData //查找的数据 ); 返回查找句柄
- 获取下一个文件
BOOL FindNextFile( HANDLE hFindFile, //查找句柄 LPWIN32_FIND_DATA lpFindFileData //查找的数据 ); 找到返回TRUE
- 关闭查找
BOOL FindClose( HANDLE hFindFile //查找句柄 );
文件操作实例
```
// WinFile.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
HANDLE hd;
void Create()
{
hd=CreateFile(_T("d:/file.txt"),GENERIC_WRITE,
FILE_SHARE_READ,NULL,CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,NULL
);
}
void Write()
{
char szText[] = "hello file";
DWORD nLen = 0;
WriteFile(hd, szText, strlen(szText), &nLen, NULL);
printf("准备写入:%d,实际写入:%d\n", strlen(szText), nLen);
//CloseHandle(hd);
}
void Read()
{
char szText[256] = { 0 };
DWORD nLen = 0;
DWORD nHeigth = 0;
DWORD nLow=GetFileSize(hd, &nHeigth);
ReadFile(hd, szText, nLow, &nLen, NULL);
printf("数据:%s,准备读取:%d,实际读取:%d\n",szText, nLow,nLen);
CloseHandle(hd);
}
int main(int argc,char * argv[])
{
Create();
Write();
Read();
system("pause");
return 0;
}
```