内存映射文件

2023-05-16

内存映射文件
本文给出了一种方便实用的解决大文件的读取、存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍。 

   引言 

   文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 

   内存映射文件 

   内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 

   内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法: 

   首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。 

   内存映射文件相关函数 

   在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍: 

HANDLE CreateFile(LPCTSTR lpFileName, 
DWORD dwDesiredAccess, 
DWORD dwShareMode, 
LPSECURITY_ATTRIBUTES lpSecurityAttributes, 
DWORD dwCreationDisposition, 
DWORD dwFlagsAndAttributes, 
HANDLE hTemplateFile); 

   函数CreateFile()即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建/打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会导致相应操作时的失败。 

HANDLE CreateFileMapping(HANDLE hFile, 
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, 
DWORD flProtect, 
DWORD dwMaximumSizeHigh, 
DWORD dwMaximumSizeLow, 
LPCTSTR lpName); 

   CreateFileMapping()函数创建一个文件映射内核对象,通过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由CreateFile()函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统能够确定对页面采取何种保护属性,需要通过参数flProtect来设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,可以读取、读写文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只需要确保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的参数dwMaximumSizeHigh和dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64位,因此所支持的最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。 

LPVOID MapViewOfFile(HANDLE hFileMapping Object
DWORD dwDesiredAccess, 
DWORD dwFileOffsetHigh, 
DWORD dwFileOffsetLow, 
DWORD dwNumberOfBytesToMap); 

   MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMapping Object为CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度: 

SYSTEM_INFO sinf; 
GetSystemInfo(&sinf); 
DWORD dwAl locationGranularity = sinf.dwAl locationGranularity; 

   参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。 

   在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下: 

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); 

   唯一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留的区域将无法释放。除此之外,前面还曾由CreateFile()和CreateFileMapping()函数创建过文件内核对象和文件映射内核对象,在进程终止之前有必要通过CloseHandle()将其释放,否则将会出现资源泄漏的问题。 

   除了前面这些必须的API函数之外,在使用内存映射文件时还要根据情况来选用其他一些辅助函数。例如,在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,而且在处理文件映射视图时不立即更新文件的磁盘映像。为解决这个问题可以考虑使用FlushViewOfFile()函数,该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。 

使用内存映射文件处理大文件应用示例 

   下面结合一个具体的实例来进一步讲述内存映射文件的使用方法。该实例从端口接收数据,并实时将其存放于磁盘,由于数据量大(几十GB),在此选用内存映射文件进行处理。下面给出的是位于工作线程MainProc中的部分主要代码,该线程自程序运行时启动,当端口有数据到达时将会发出事件hEvent[0],WaitForMultiple Objects()函数等待到该事件发生后将接收到的数据保存到磁盘,如果终止接收将发出事件hEvent[1],事件处理过程将负责完成资源的释放和文件的关闭等工作。下面给出此线程处理函数的具体实现过程: 

…… 
// 创建文件内核对象,其句柄保存于hFile 
HANDLE hFile = CreateFile("Recv1.zip", 
GENERIC_WRITE | GENERIC_READ, 
FILE_SHARE_READ, 
NULL, 
CREATE_ALWAYS, 
FILE_FLAG_SEQUENTIAL_SCAN, 
NULL); 

// 创建文件映射内核对象,句柄保存于hFileMapping 
HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE, 
0, 0x4000000, NULL); 
// 释放文件内核对象 
CloseHandle(hFile); 

// 设定大小、偏移量等参数 
__int64 qwFileSize = 0x4000000; 
__int64 qwFileOffset = 0; 
__int64 T = 600 * sinf.dwAl locationGranularity; 
DWORD dwBytesInBlock = 1000 * sinf.dwAl locationGranularity; 

// 将文件数据映射到进程的地址空间 
PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping, 
FILE_MAP_ALL_ACCESS, 
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock); 
while(bLoop) 

// 捕获事件hEvent[0]和事件hEvent[1] 
DWORD ret = WaitForMultiple Objects(2, hEvent, FALSE, INFINITE); 
ret -= WAIT_ OBJECT_0; 
switch (ret) 

// 接收数据事件触发 
case 0: 
// 从端口接收数据并保存到内存映射文件 
nReadLen=syio_Read(port[1], pbFile + qwFileOffset, QueueLen); 
qwFileOffset += nReadLen; 

// 当数据写满60%时,为防数据溢出,需要在其后开辟一新的映射视图 
if (qwFileOffset > T) 

T = qwFileOffset + 600 * sinf.dwAl locationGranularity; 
UnmapViewOfFile(pbFile); 
pbFile = (PBYTE)MapViewOfFile(hFileMapping, 
FILE_MAP_ALL_ACCESS, 
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock); 

break; 

// 终止事件触发 
case 1: 
bLoop = FALSE; 

// 从进程的地址空间撤消文件数据映像 
UnmapViewOfFile(pbFile); 

// 关闭文件映射对象 
CloseHandle(hFileMapping); 
break; 


… 

   在终止事件触发处理过程中如果只简单的执行UnmapViewOfFile()和CloseHandle()函数将无法正确标识文件的实际大小,即如果开辟的内存映射文件为30GB,而接收的数据只有14GB,那么上述程序执行完后,保存的文件长度仍是30GB。也就是说,在处理完成后还要再次通过内存映射文件的形式将文件恢复到实际大小,下面是实现此要求的主要代码: 

// 创建另外一个文件内核对象 
hFile2 = CreateFile("Recv.zip", 
GENERIC_WRITE | GENERIC_READ, 
FILE_SHARE_READ, 
NULL, 
CREATE_ALWAYS, 
FILE_FLAG_SEQUENTIAL_SCAN, 
NULL); 

// 以实际数据长度创建另外一个文件映射内核对象 
hFileMapping2 = CreateFileMapping(hFile2, 
NULL, 
PAGE_READWRITE, 
0, 
(DWORD)(qwFileOffset&0xFFFFFFFF), 
NULL); 

// 关闭文件内核对象 
CloseHandle(hFile2); 

// 将文件数据映射到进程的地址空间 
pbFile2 = (PBYTE)MapViewOfFile(hFileMapping2, 
FILE_MAP_ALL_ACCESS, 
0, 0, qwFileOffset); 

// 将数据从原来的内存映射文件复制到此内存映射文件 
memcpy(pbFile2, pbFile, qwFileOffset); 

file://从进程的地址空间撤消文件数据映像 
UnmapViewOfFile(pbFile); 
UnmapViewOfFile(pbFile2); 

// 关闭文件映射对象 
CloseHandle(hFileMapping); 
CloseHandle(hFileMapping2); 

// 删除临时文件 
DeleteFile("Recv1.zip"); 

   结论 

   经实际测试,内存映射文件在处理大数据量文件时表现出了良好的性能,比通常使用CFile类和ReadFile()和WriteFile()等函数的文件处理方式具有明显的优势。本文所述代码在Windows 98下由Microsoft Visual C++ 6.0编译通过。
 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内存映射文件 的相关文章

  • curl时设置Expect的必要性

    curl 在项目中使用频率较高 xff0c 比如内部接口 第三方 api 图片存储服务等 xff0c 但是我们在使用 curl 时可能并没有注意到 Expect 这个请求头信息 xff0c 而 Expect 设置不正确 xff0c 会导致不
  • 奇偶校验原理

    奇校验 xff1a 求一个字节8位中 1 的个数 xff0c 添加一位校验位 xff0c 使9位中 1 的个数为奇数 xff1b 偶校验同理 奇校验就是让原有数据序列中 xff08 和要加上的一位 xff09 1的个数为奇数 如010001
  • CreateMutex函数函数用来实现进程互斥

    CreateMutex函数 正常情况下 xff0c 一个进程的运行一般是不会影响到其他正在运行的进程的 但是对于某些有特殊要求的如以独占方式使用串行口等硬件设备的程序就要求在其进程运行期间不允许其他试图使用此端口设备的程序运行的 xff0c
  • C++与QML交互总结

    一直对于QT的理解和使用都停留在主窗口程序和控制台程序 xff0c 虽然QT的新东西QML听过也接触过 xff0c 但是基本上没梳理过调用流程 趁着旧项目要使用QML技术 xff0c 现在就将C 43 43 和QML交互进行总结 目录 一
  • QT下TCP协议实现数据网络传输

    QT开发框架以其跨平台的优势 xff0c 在全世界IT界如雷贯耳 其封装了功能齐全的各种类 xff0c 大大的提高了开发者的效率 本篇内容将介绍如何使用QT 6 4 1框架开发服务器和客户端程序 xff0c 让两端能够首发消息 xff0c
  • 从零实现vins-mono+fast-planner+M100无人机实验在现实场景中的应用

    版权声明 本文为博主原创文章 未经博主允许不能随意转载 本文链接 https blog csdn net AnChenliang 1002 article details 109535355 最近由于科研的需要 要将VINS mono与fa
  • Linux下C语言实现HTTP文件服务器和TCP协议实现网络数据传输

    在实际开发中经常用到web框架 xff0c 比如Servlet xff0c SpringBoot等 xff0c 这些开发框架提高了我们的开发效率 xff0c 节省了开发时间 但是这会令我们技术人员处于浮云之上 xff0c 看不到其本质 说实
  • Linux下C语言UDP协议通信实践

    UDP和TCP协议一样 xff0c 都是传输层协议 是无连接的 xff0c 不安全的 xff0c 报式传输层协议 xff0c 通信过程默认也是阻塞的 其通信特点主要如下 xff1a xff08 1 xff09 不需要建立连接 xff0c 所
  • Ubuntu下PyQt5使用总结

    因为工作中需要给交付团队开发桌面工具 xff0c 考虑到交付团队多使用Mac xff0c 调研了一下发现PyQt5可以实现跨平台 xff0c 满足工具开发需要 xff0c 就用其开发了桌面工具 现以ubuntu开发环境为例总结一下开发过程
  • ubuntu下安装配置grpc

    目录 1 准备环境 2 安装protobuf 3 安装cares库 3 安装grpc 1 17 x 1 准备环境 sudo apt get install pkg config sudo apt get install autoconf a
  • cmake管理子程序,lib库和so库应用实践

    cmake在管理大型项目时经常被用到 xff0c 本文以简单程序演示来说明camke管理项目应用 xff0c 其中包括主程序 xff0c 子程序 xff0c so库程序 xff0c lib程序 目录 1 程序目录结构 2 编译执行 3 清除
  • GIt常用命令总结

    目录 1 创建新建分支 2 强制拉去代码 3 合并相邻提交 xff0c 保证只有一个commit信息 4 本地回退 5 查看git修改列表 6 提交代码 7 切换新分支并从服务端拉取最新 8 git cherry pick合并代码使用 9
  • Linux 下I/O多路复用总结

    xfeff xfeff select xff0c poll xff0c epoll都是IO多路复用的机制 I O多路复用就通过一种机制 xff0c 可以监视多个描述符 xff0c 一旦某个描述符就绪 xff08 一般是读就绪或者写就绪 xf
  • WAV文件头分析

    WAV语音文件头部含有44字节的标志信息 xff0c 其含义如下 xff1a ckid xff1a 4字节 RIFF 标志 xff0c 大写 wavHeader 0 61 39 R 39 wavHeader 1 61 39 I 39 wav
  • Linux环境下限制网速和取消限制网速

    查看网卡信息 ip addr root 64 rabbitmq01 ip addr 1 lo lt LOOPBACK UP LOWER UP gt mtu 65536 qdisc noqueue state UNKNOWN qlen 1 l
  • Linux 网络编程2 TCP并发服务器

    Linux 网络编程学习 TCP IP网络编程2 TCP多线程服务器TCP多进程服务器 在前面TCP网络编程代码的基础上进行改造 xff0c 实现并发服务器功能 TCP多线程服务器 实现功能 xff1a server端可以绑定在任意IP端s
  • HTTP Digest authentication

    什么是摘要认证 摘要认证 xff08 Digest authentication xff09 是一个简单的认证机制 xff0c 最初是为HTTP协议开发的 xff0c 因而也常叫做HTTP摘要 xff0c 在RFC2617中描述 其身份验证
  • 简单的netfilter hook函数注册以及内核链表的使用

    include lt linux netfilter h gt include lt linux init h gt include lt linux module h gt include lt linux netfilter ipv4
  • 详述GPS原理及RTK技术应用

    完整的PPT文档在这里 xff1a 详述GPS原理及RTK技术应用 1 GPS概述 1 1定义 全球定位系统GPS xff08 Global Position System xff09 xff0c 全称为NAVSTAR GPS xff08
  • PHP HTTP Digest校验

    PHP作为客户端进行HTTP Digest校验 span class token comment 请求方法 span span class token variable username span span class token oper

随机推荐

  • Http Digest认证协议

    其认证的基本框架为挑战认证的结构 xff0c 如下图所示 xff1a xfeff xfeff 1 客户端希望取到服务器上的某个资源 xff0c 向服务器发送Get请求 2 服务器收到客户端的请求后 xff0c 发现这个资源需要认证信息 xf
  • Postman 安装

    Postman 的下载安装 Postman是一个用于构建和使用API的API平台 xff08 接口的调试工具 xff09 选择对应的系统和版本进行下载 https github com hlmd Postman cn 这里我的电脑是wind
  • 【安卓自定义控件系列】自绘控件打造界面超炫功能超强的圆形进度条

    在前面我们讲过了安卓自定义控件三种方式中的组合控件 xff0c 现在我们来讲解一下通过自绘的方式来实现自定义控件 xff0c 本博客将以自定义圆形进度条为例向大家讲解自定义控件的知识 xff0c 首先来看一下效果图吧 xff0c 这个是本人
  • Linux网络编程3——多进/线程并发服务器

    视频链接 黑马程序员 Linux网络编程 哔哩哔哩 bilibili https www bilibili com video BV1iJ411S7UA p 61 37 目录 一 高并发服务器 1 1 图示 1 2 分类 二 多进程并发服务
  • http请求头和响应头

    文章目录 参考HTTP通用标头Cache ControlConnectionDatePragmaTrailerTransfer EncodingUpgradeViaWarning 请求头AcceptAccept RangesAccept E
  • C++如何使用libcurl发送post请求的三种content-type的封装

    作者 xff1a 虚坏叔叔 博客 xff1a https xuhss com 早餐店不会开到晚上 xff0c 想吃的人早就来了 xff01 x1f604 C 43 43 如何使用libcurl发送post请求的三种content type的
  • 开源四轴飞行器CC3D的稳定模式和PID算法简介

    本文主要介绍一下CC3D的两大飞行模式的原理 xff1a 1 Rate 2 Attitude 以及PID的基本原理 至于AxisLock模式及Manual xff0c 日后续说 笔者是CC3D开源飞控入门玩家 xff0c 遇到过不少磕磕碰碰
  • 了解git分支和版本管理

    0 Git分支和标签的命名规范 1 分支 dev test pre pro 即master 2 标签 Tag格式 xff1a 主版本号 次版本号 修订号 类型标签 xff0c 其中类型标签可为 xff1a alpha beta rc r T
  • ubuntu下解决高并发socket最大连接数限制,tcp默认1024个连接

    nux系统默认ulimit为1024个访问 用户最多可开启的程序数目 一般一个端口 xff08 即一个进程 xff09 的最高连接为2的16次方65536 通过这个命令 ulimit n 可以看到默认值为1024 查看全局文件句柄数限制 系
  • Linux下实现Post方式

    首先了解HTTP协议各字段的含义 xff0c 以下是部分较好的归纳 xff1a HTTP一个HTTP请求报文由请求行 xff08 request line xff09 请求头部 xff08 header xff09 空行和请求数据4个部分组
  • vins中的坐标系变换及g2r函数

    slam中经常会需要表示一个刚体的位姿 例如imu的位姿 xff0c 相机的位姿 首先我们需要在一个刚体上架上一个坐标系 这个坐标系为本体坐标系 怎么架一个坐标系 xff1f imu本身就有规定其本身的x y z轴的方向 相机一般认为 xf
  • yolo_v3安装试运行小记

    github源代码 xff1a https github com qqwweee keras yolo3 Cuda下载地址 xff1a https developer nvidia com cuda toolkit archive Cudn
  • pytorch安装使用

    pytorch官网 xff1a https pytorch org get started locally conda添加清华源 xff1a conda config add channels https mirrors tuna tsin
  • ORB_SLAM3初试

    源码地址 xff1a orb salm3 github 论文地址 orb slam3 paper ORB SLAM3提供了单目 xff0c 双目 xff0c rgbd 以及各自结合IMU的数据分析实现 demo运行效果展示 xff1a 在不
  • vins-fusion代码解读[五] imu在vins里的理解

    SLAM新手 xff0c 欢迎讨论 IMU作用 vins中 xff0c IMU只读取IMU六轴的信息 xff0c 3轴线加速度 xff08 加速度计 xff09 和3轴角速度 xff08 陀螺仪 xff09 通过对陀螺仪的一次积分 xff0
  • Gazebo仿真学习——2.为Gazebo中的机器人添加实用插件

    Gazebo plugins in ROS 1 要了解2 插件类型3 添加一个ModelPlugin4 添加一个SensorPlugin5 gazebo plugins中的所有可用插件5 1 Camera5 2 GPU Laser xff0
  • 一款直播电视接口分析

    一 初步了解 最近对一款网络电视app进行分析 xff0c 要求是找到其访问直播源的接口 xff0c 初次打开apk xff0c 先做一个简单的了解 xff0c 并没有加固等处理 xff0c 只是对代码做了混淆处理 我们目的是找到其访问直播
  • android串口通讯

    一 配置 1 下载文件 https github com cepr android serialport api 2 复制android serialport api文件夹至app src main java 3 复制jni文件至app s
  • boost库介绍以及使用

    C 43 43 boost库介绍以及使用 一 什么是boost库 boost 库是一个优秀的 xff0c 可移植的 xff0c 开源的 C 43 43 库 xff0c 它是由 C 43 43 标准委员会发起的 xff0c 其中一些内容已经成
  • 内存映射文件

    内存映射文件 本文给出了一种方便实用的解决大文件的读取 存储等处理的方法 xff0c 并结合相关程序代码对具体的实现过程进行了介绍 引言 文件操作是应用程序最为基本的功能之一 xff0c Win32 API和MFC均提供有支持文件处理的函数