win32学习记录day-11
- 静态库程序 - 运行时不存在,会被链接
到可执行文件或者动态库中,目标程序的归档。
文件扩展名: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的实例句柄 );
- 隐式链接
3. 两种链接方式对比
* 在库函数的定义不变情况下:
隐式链接,由于库函数地址是在程序编译链接时设置,所以当动态库变化后,使用程序需要重新编译链接。
显式链接,由于库函数地址是在程序执行时,动态的从库中查询,所以库变化后,不需要重新编译链接。
* 动态库加载
隐式链接,动态库是在程序启动时就被加载,当DLL不存在,程序无法启动
显式链接,动态库只在使用LoadLibrary函数,才会被加载。
7. 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
);
```
###windows文件
1. 创建或打开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---打开文件的同时,清空文件
```
2. 写数据
```
BOOL WriteFile(
HANDLE hFile, //文件句柄
LPCVOID lpBuffer, //数据BUFF
DWORD nNumberOfBytesToWrite, //数据长度
LPDWORD lpNumberOfBytesWritten, //返回实际写入的数据长度
LPOVERLAPPED lpOverlapped //默认为NULL
);
```
3. 读数据
```
BOOL ReadFile(
HANDLE hFile, //文件句柄
LPVOID lpBuffer, //数据BUFF
DWORD nNumberOfBytesToRead, //要读的字节数
LPDWORD lpNumberOfBytesRead, //实际读到字节数
LPOVERLAPPED lpOverlapped //默认为NULL
);
```
4. 关闭文件
```
BOOL CloseHandle(
HANDLE hObject //文件句柄
);
```
5. 文件长度
```
DWORD GetFileSize(
HANDLE hFile, //文件句柄
LPDWORD lpFileSizeHigh //返回文件长度的高32位
); 返回值是文件长度的低32位
```
DWOED就是无符号long类型,主要是为了方便存储大文件,函数返回值是以字节为单位,第二个参数以G为单位
6. 文件指针
```
DWORD SetFilePointer(
HANDLE hFile, //文件句柄
LONG lDistanceToMove, //偏移量的低32位
PLONG lpDistanceToMoveHigh, //偏移量的高32位
DWORD dwMoveMethod//偏移的相对位置
);
```
```
DWORD dwMoveMethod//偏移的相对位置
FILE_BEGIN
FILE_CURRENT
FILE_END
```
SetFilePointer函数返回实际偏移量的低32位,lpDistanceToMoveHigh返回实际偏移量的高32位
7. 文件相关操作
CopyFile - 拷贝文件
DeleteFile - 删除文件
MoveFile - 移动文件
* 文件遍历
1. 查找文件
```
HANDLE FindFirstFile(
LPCTSTR lpFileName, //查找路径
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); 返回查找句柄
```
2. 获取下一个文件
```
BOOL FindNextFile(
HANDLE hFindFile, //查找句柄
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); 找到返回TRUE
```
3. 关闭查找
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;
}
```