1、 多文档界面(MDI)
- MDI的组成
MDI主窗口(主框架窗口MainFrame)
MDI客户窗口(MDICLIENT)
MDI 子窗口1
MDI 子窗口2
…….
- 创建过程
1)创建MDI主窗口
a) 没有父窗口,需要注册窗口类,定义窗口过程函数
b) MDI主窗口的窗口过程函数,不再调用DefWindowProc。代之以调用DefFramProc 。
LRESULT DefFrameProc(
HWND hWnd, // handle to MDI frame window
HWND hWndMDIClient, // handle to MDI client window
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
2) 创建MDI客户窗口
A. 窗口类:MDI主窗口
B.父窗口是MDI主窗口
C.创建时需要使用CLIENTCREATRUCT结构,指定MDI子窗口的起始ID。
3)创建MDI子窗口
A. 父窗口是MDI客户窗口
B.所有的MDI子窗口都需要注册并定义窗口过程函数
C.MDI子窗口的窗口函数过程函数,不再调用DefWindProc。代之以调用DefMDIChildProc 。
4)相关函数:
DrawMenuBar(HWND hwnd);//重绘指定窗口菜单栏,用于切换多文档时刷新重绘相应菜单栏。
EnumChildWindows();//枚举指定窗口的所有子窗口执行lpEnumFunc回调函数。
BOOL EnumChildWindows(
HWND hWndParent, // handle to parent window
WNDENUMPROC lpEnumFunc, // pointer to callback function
LPARAM lParam // application-defined value
);
SetWindowLong/ GetWindowLong;
IsWindow;等等
5)消息相关:
WM_MDIACTIVATE
WM_MDIDESTROY
WM_MDIGETACTIVE
WM_MDICASCADE
WM_MDITILE
WM_MDIICONARRANGE
2、 Windows库程序
1. 分类:
静态库程序 - 运行时不依赖,在链接阶段会被链接到可执行文件或者动态库中,本质是目标文件程序的归档。文件扩展名:LIB。
动态库程序 - 运行时依赖,在链接阶段不会被复制到可执行文件或其他动态库中。文件扩展名:DLL。
- 静态库程序
所有的win32函数都没有静态库版本,但标准C/C++函数既有静态库也有动态库版本。可以在工程选项中配置。
C语言静态库
1. 静态库的使用
建立一个C文件,可以在文件中直接使用C库函数,不需要头文件。C编译器只是根据库函数名称,在库中找到对应的函数代码,进行链接。
2. 静态库的创建
1)建项目
2)添加库程序(),编写源文件代码使用C文件
3. 链接静态库的路径设置
法1)项目的“project/ Setting”中设置库的路径
法2)可以使用pragma关键字设置#pragma comment( lib, "..\lib\clib.lib")(指定路径)
C++语言的静态库
1. 静态库的建立
1)建立项目
2)添加库程序,源文件使用CPP文件
2. 库的导入
1)项目的“Setting”中设置库的路径
2)可以使用 pragma 关键字设置
3. 注意
在CPP环境使用C静态库,库中函数原型定义要增加 extern “C”, 例如:extern "C" int Add(... ); extern “C” {#include “myc.h”}.等。防止C++编译器对C语言库函数做换名,使得采用C下的函数原型,正确调用C库函数。C中使用C++库时,需要在C++库编译时,对extern “C” { 与 } 用c++编译器宏#ifdef _ _cplusplus 来处理(由于C语言中无法识别 extern "C" 串。)。使得可对C编译器与C++编译器公用。
如:#ifdef __cplusplus
extern "C" {
#endif
int Add(... );
#ifdef __cplusplus
}
#endif
- 动态库的程序
1. 与静态库的比较:
1)由于静态库是将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所以代码体积会增大。动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小。
2)静态库发生变化后,新的代码需要重新链接嵌入到执行程序中。动态库发生变化后,如果库中函数的定义(或地址)未变化,其他使用DLL的程序不需重新链接。
2. 动态库的创建
1 ) 建立项目
2 ) 添加库程序
3 ) 库程序导出 - 提供给使用者库中的函数等信息。
3 动态库的使用
3.1 隐式链接
3.2 显式链接
4 动态库的函数
4.1 实现动态库的函数
4.2 库函数的导出
1)C++的导出
使用 _ _declspec(dllexport) 导出函数
注意:动态库编译链接后,也会有LIB文件,是作为动态库函数映射使用,与静态库不完全相同,另外也要加载它。
2)C的导出方式 extern “C” _ _ declspec(dllexport) int Sub(...);
3)导入: _ _declspec(dllimport) 导出函数.。一般VC6.0自动导入,无需自己做
4)若不用dllexport导出,可定义模块定义文件(.def)来导出。
例如:
EXPORTS //库导出表
Sub //导出的函数名
4.3 库函数的使用
4.3.1 隐式链接
1)头文件和函数原型
可以在函数原型的定义前,增加declspec(dllimport), 例如
_declspec(dllimport) int DLL_Add( ... );
如果库函数使用C格式导出,需要在函数定义增加 extern “C”
2)导入动态库的LIB文件
3)在程序中使用函数
4)隐式链接的情况,DLL可以存放的路径:
(1)与执行文件中同一个目录下 推荐使用
(2)当前工作目录 依赖开发环境,用于调试
(3)Windows目录 注意版本问题
(4)Windows/System32目录 注意版本问题
(5)Windows/System 注意版本问题
(6)环境变量PATH指定目录 依赖与系统环境
5)可执行程序- 启动即加载所依赖的动态库,直到程序结束才卸载 动态库。
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{
...
};
4.3.2 动态加载HMODULE LoadLibrary(
LPCTSTR lpFileName //动态库文件名或全路径
); 返回DLL的实例句柄(HINSTANCE)
1)获取函数地址
FARPROC GetProcAddress(
HMODULE hModule, //DLL句柄
LPCSTR lpProcName //函数名称
); 成功返回函数地址,失败返回NULL
a. 函数名需要注意C++换名问题
b. 函数指针的类型问题,要强制转换函数指针类型,参数等,可用typedef type (*fun)(type a, type b ,…)。
2)使用函数
3)卸载动态库
BOOL FreeLibrary(
HMODULE hModule //DLL的实例句柄
);//成功返回TRUE,失败返回FALSE
注意:高版本VC的配置文件
4.3.3 两种链接方式对比
1)在库函数的定义不变情况下:
隐式链接,由于库函数地址是在程序编译链接时设置,所以当动态库变化后,使用程序需要重新编译链接。
显式链接,由于库函数地址是在程序执行时,动态的从库中查询,所以库变化后,不需要重新编译链接。
2)动态库加载
隐式链接,动态库是在程序启动时就被加载,当DLL不存在,程序无法启动
显式链接,动态库只在使用LoadLibrary函数,才会被加载。
动态库的程序入口:
入口程序不是DLL必须的。常用于DLL内部初始化或善后处理。
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, //动态库实例句柄
DWORD fdwReason, //被调用的原因
LPVOID lpvReserved //保留值
); 返回TRUE,表示动态库加载成功。失败返回FALSE
动态库的加载或卸载时会被调用。例如:使用LoadLibrary或FreeLibrary时会被调用。
fdwReason 参数说明:
DLL_PROCESS_ATTACH 进程加载 主线程中调用LoadLibrary
DLL_THREAD_ATTACH 线程链接 子线程中调用LoadLibrary
DLL_THREAD_DETACH 线程卸载 子线程中调用FreeLibrary
DLL_PROCESS_DETACH 进程卸载 主线程中调用FreeLibrary
3、 Windows 文件系统
- Windows常见文件系统
操作系统管理磁盘数据的方式。
FAT、FAT32、NTFS
硬盘的管理
磁道:包括若干个扇区
扇、扇区: - 大小为512字节
文件系统最小管理单位 簇 - 由连续的几个扇区组成8/16/32/64
FAT32 - 1簇32扇区.即16K字节
NTFS - 1簇 8扇区即4K字节
文件存储时,是以簇为单位占用硬盘空间,
即使文件只有1个字节,也要占用1簇的空间。故一般下,NTFS比FAT32占有有效空间更多,浪费更少。
- Windows 目录相关API函数
1) 获取磁盘驱动器信息
DWORD GetLogicalDrives(void );//返回当前可用逻辑驱动器的掩码。
磁盘表示与掩码关系:
L-----------------------------------------------------------------àH
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
HGFEDCBA ……KJI
0:表示无此驱动器
1:表示存在此驱动器
2) 获取系统驱动器
DWORD GetLogicalDriveStrings(
DWORD nBufferLength, // 缓冲区大小(以字符为单位)
LPTSTR lpBuffer // 返回指向系统驱动器缓冲区字符串
);//成功返回串长度,失败返回0.
3) 获取当前工作目录
DWORD GetCurrentDirectory(
DWORD nBufferLength, // 缓冲区大小(以字符为单位)
LPTSTR lpBuffer // 缓冲区指针
);//成功返回缓冲区串长度,失败返回0
4) 设置当前工作目录
BOOL SetCurrentDirectory(
LPCTSTR lpPathName //指定新的工作路径
);//成功返回非0值,失败返回0.。
5) 获取Windows 目录
UINT GetWindowsDirectory(
LPTSTR lpBuffer, // 指向缓冲区的指针
UINT uSize // 目录缓冲区大小(以字符为单位)
);//成功返回字符串长度,失败返回0。
6) 获取系统目录
UINT GetSystemDirectory(
LPTSTR lpBuffer, //指向缓冲区的指针
UINT uSize //缓冲区目录的大小
);///成功返回字符串长度,失败返回0。
6) 获取临时文件目录
DWORD GetTempPath(
DWORD nBufferLength, // 缓冲区大小(以字符为单位)
LPTSTR lpBuffer // 缓冲区指针
);//成功返回字符串长度,失败返回0。
7) 目录创建
BOOL CreateDirectory(
LPCTSTR lpPathName,//目录名称
LPSECURITY_ATTRIBUTES lpSecurityAttributes//安全属性默认为NULL
);//成功返回非0值,失败返回0。当目录文件已存在,无法创建该文件。
8)目录删除
BOOL RemoveDirectory(
LPCTSTR lpPathName //目录名称
);成功返回true,失败返回false。只能删除空目录,若内有文件则无法删除。
9) 目录修改/ 移动
BOOL MoveFile(
LPCTSTR lpExistingFileName, //当前的名称
LPCTSTR lpNewFileName //新的名称
);//成功返回true,失败返回false。
///当新目录或文件已存在,无法移动或更改该目录或文件。
////注意:不能跨逻辑驱动器移动目录,但是可以跨逻辑驱动器移动文件。
- Windows 文件相关API
1) 创建或打开文件
HANDLE CreateFile(
LPCTSTR lpFileName, //文件名称
DWORD dwDesiredAccess, //访问权限,方式
DWORD dwShareMode, //共享方式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全属性默认NULL
DWORD dwCreationDisposition, //创建或打开方式
DWORD dwFlagsAndAttributes, //文件属性
HANDLE hTemplateFile //文件句柄模板(磁盘文件,默认为NULL)
); //成功返回文件句柄,失败返回INVALID_HANDLE_VALUE(-1)。
小记:CreateFile 可以创建或打开以下文件:
files 文件
pipes 管道
mailslots 邮槽
communications resources 通信资源
disk devices (Windows NT only) 磁盘驱动
consoles 控制台
directories (open only) 目录
参数说明:
dwDesiredAccess:
0 咨询 (判断文件是否存在)
GENERIC_READ 读取
GENERIC_WRITE 写入
dwShareMode:
FILE_SHARE_DELETE 允许其他进程删除此文件
FILE_SHARE_READ 允许其他进程读取此文件
FILE_SHARE_WRITE 允许其他进程写入此文件
dwCreationDisposition:
CREATE_NEW 不存在则创建。已存在则失败
CREATE_ALWAYS 不存在则创建,已存在则删除再创建
OPEN_EXISTING 已存在则打开,不存在则失败
OPEN_ALWAYS 已存在则打开,不存在则创建再打开
TRUNCATE_EXITSTING 已存在先清空再打开,不存在则失败
2) 关闭文件
BOOL CloseHandle(
HANDLE hObject //句柄
);//成功返回true,失败返回false 。
小记:CloseHandle()也可以关闭其他的。
如下:
- Communications device
- Console input
- Console screen buffer
- Event
- File
- File mapping
- Job
- Mailslot
- Mutex
- Named pipe
- Process
- Semaphore
- Socket
- Thread
- Token
3) 写入数据
BOOL WriteFile(
HANDLE hFile, //文件句柄
LPCVOID lpBuffer, //数据BUFF
DWORD nNumberOfBytesToWrite, //期望写入数据字节数
LPDWORD lpNumberOfBytesWritten, //返回实际写入的数据字节数
LPOVERLAPPED lpOverlapped //异步传输结构,默认为NULL
);///成功返回true,失败返回false。
4) 读出数据
BOOL ReadFile(
HANDLE hFile, //文件句柄
LPVOID lpBuffer, //数据BUFF
DWORD nNumberOfBytesToRead, //要读的字节数
LPDWORD lpNumberOfBytesRead, //实际读到字节数
LPOVERLAPPED lpOverlapped //异步传输结构,默认为NULL
);///成功返回true,失败返回false
5) 获取文件大小(字节数)
DWORD GetFileSize(
HANDLE hFile, //文件句柄
LPDWORD lpFileSizeHigh //文件字节数的高32位
); 返回值是文件字节数的低32位
///在64位系统下,需要将高32位和低32位组合为64位的,得到实际文件大小,
////而在32为系统下,无需考虑高32位的。
6) 移动文件指针
DWORD SetFilePointer(
HANDLE hFile, //文件句柄
LONG lDistanceToMove, //字节偏移量的低32位
PLONG lpDistanceToMoveHigh, //返回实际字节偏移量的高32位
DWORD dwMoveMethod//偏移的相对位置
); //返回文件指针实际新的位置字节偏移量的低32位,
///对于高32位情况同GetFileSize()。若不要高32位且若后移,则应该设置高32为NULL,否则可能失败。纵使设置相应高32位为NULL指针变量也不行。其他情况下无要求。
dwMoveMethod 参数说明:
FILE_BEGIN 文件开始位置
FILE_CURRENT 文件当前位置
FILE_END 文件末尾位后一个位置
7) 文件其他相关
复制文件:
BOOL CopyFile(
LPCTSTR lpExistingFileName, // 源文件
LPCTSTR lpNewFileName, // 复制的目标文件
BOOL bFailIfExists // flag for operation if file exists
);//
// bFailIfExists 参数说明:
若为true,则若复制相同位置、名文件,则返回失败。
若为false,则若复制相同位置、名文件,则覆盖返回成功。
删除文件:
BOOL DeleteFile(
LPCTSTR lpFileName //删除的文件名
);//成功返回true,失败返回false。
移动文件:同移动目录的MoveFile()
8) 文件属性
获取文件属性:
DWORD GetFileAttributes(
LPCTSTR lpFileName //文件名或目录名
);///成功返回指定文件的属性,失败则返回0xFFFFFFFF。
获取文件属性、时间等:
BOOL GetFileAttributesEx(
LPCTSTR lpFileName, // 目录或文件名
GET_FILEEX_INFO_LEVELS fInfoLevelId, //设置获取文件或目录属性
LPVOID lpFileInformation // 由fInfoLevelId指定的属性而获取的文件或目录属性信息缓冲区
);////成功返回true,失败返回false
//////fInfoLevelId参数说明:(枚举类型结构),
其枚举值:GetFileExInfoStandard。
由结构体WIN32_FILE_ATTRIBUTE_DATA返回其值。
typedef struct _WIN32_FILE_ATTRIBUTE_DATA{
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; //创建时间
FILETIME ftLastAccessTime; //最近访问时间
FILETIME ftLastWriteTime; //最近写入时间
DWORD nFileSizeHigh; //文件高32位字节数
DWORD nFileSizeLow; //文件低32位字节数
}
WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
/// FILETIME结构体
typedef struct _FILETIME { // ft
DWORD dwLowDateTime; //低32位日期
DWORD dwHighDateTime; //高32位日期
} FILETIME;
另外可用FileTimeToSystemTime转化为系统时间,
BOOL FileTimeToSystemTime(
CONST FILETIME *lpFileTime,//转变的文件时间
LPSYSTEMTIME lpSystemTime,// 接收的系统时间
);
SYSTEMTIME(结构体):见MSDN
设置文件属性:
BOOL SetFileAttributes(
LPCTSTR lpFileName, // 文件名
DWORD dwFileAttributes //设置文件属性,可组合设置
);///成功返回true,失败返回false。
增加文件属性:获取原文件属性,与需要增加的属性做位或,再设置回去。
删除文件属性:获取原文件属性,与需要删除的属性取反且位与,再设置回去。
9) 文件查找
HANDLE FindFirstFile(
LPCTSTR lpFileName, //查找路径
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); //返回查找句柄,此句柄可用于findnextfile,findclose,失败返回INVALID_HANDLE_VALUE 。
参数说明:LPWIN32_FIND_DATA(结构体),保存查找文件或子目录的数据
typedef struct _WIN32_FIND_DATA { // wfd
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; //创建时间
FILETIME ftLastAccessTime; ///最后访问时间
FILETIME ftLastWriteTime; ///最后写入时间
DWORD nFileSizeHigh; //文件字节数高32位
DWORD nFileSizeLow; ///文件字节数低32位
DWORD dwReserved0; //
DWORD dwReserved1; //保留
TCHAR cFileName[ MAX_PATH ]; //文件名
TCHAR cAlternateFileName[ 14 ]; ///可选文件名
} WIN32_FIND_DATA;
10) 获取下一个文件
BOOL FindNextFile(
HANDLE hFindFile, //查找句柄,由
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); //找到返回TRUE,失败返回FALSE。,查找基于文件名
11) 关闭查找
BOOL FindClose (
HANDLE hFindFile // 查找句柄
);//成功返回TRUE,失败返回FALSE。