网站推广怎么样做,电影网站域名需要备案,asp网站幻灯片不显示,专注高端品牌网站的设计文件的输入输出#xff08;I/O#xff09;服务是操作系统的重要部分。Windows提供了一类API函数来读、写和管理磁盘文件。MFC将这些函数转化为一个面向对象的类——CFile#xff0c;它允许将文件视为可以由CFile成员函数操作的对象#xff0c;如Read和Write等。CFile类实现…文件的输入输出I/O服务是操作系统的重要部分。Windows提供了一类API函数来读、写和管理磁盘文件。MFC将这些函数转化为一个面向对象的类——CFile它允许将文件视为可以由CFile成员函数操作的对象如Read和Write等。CFile类实现了程序开发者执行底层文件I/O需要的大部分功能。
并不是在任何时候使用CFile类都是方便的特别是要与底层设备如COM口、设备驱动进行交互的时候所以本节主要讨论管理文件的API函数。事实上了解这些函数之后自然就会使用CFile类了。
8.1.1 创建和读写文件
使用API函数读写文件时首先要使用CreateFile函数创建文件对象即打开文件调用成功会返回文件句柄然后以此句柄为参数调用ReadFile和WriteFile函数进行实际的读写操作最后调用CloseHandle函数关闭不再使用的文件对象句柄。
1打开和关闭文件 CreateFile是一个功能相当强大的函数Windows下的底层设备差不多都是由它打开的。它可以创建或打开文件、目录、物理磁盘、控制台缓冲区、邮槽和管道等。调用成功后函数返回能够用来访问此对象的句柄其原型如下。
HANDLE CreateFile ( LPCTSTR lpFileName, // 要创建或打开的对象的名称 DWORD dwDesiredAccess, // 文件的存取方式 DWORD dwShareMode, // 共享属性 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性 DWORD dwCreationDisposition, // 文件存在或不存在时系统采取的行动 DWORD dwFlagsAndAttributes, // 新文件的属性 HANDLE hTemplateFile // 一个文件模板的句柄
); 各参数含义如下。 1lpFileName参数是要创建或打开的对象的名称。如果打开文件直接在这里指定文件名称即可如果操作对象是第一个串口则要指定“COM1”为文件名然后就可以像操作文件一样操作串口了如果要打开本地电脑上的一个服务要以“\\.\服务名称”为文件名其中的“.”代表本地机器也可以使用CreateFile打开网络中其他主机上的文件此时的文件名应该是“\\主机名\共享目录名\文件名”。 2dwDesiredAcces参数是访问方式它指定了要对打开的对象进行何种操作。指定GENERIC_READ标志表示以只读方式打开指定GENERIC_WRITE标志表示以只写方式打开指定这两个值的组合表示要同时对打开的对象进行读写操作。 3dwShareMode参数指定了文件对象的共享模式表示文件打开后是否允许其他代码以某种方式再次打开这个文件它可以是下列值的一个组合
0 不允许文件再被打开 C语言中的fopen函数就是这样打开文件的
FILE_SHARE_DELETE 允许以后的程序代码对文件删除文件Win98系列的系统不支持这 个标志
FILE_SHARE_READ 允许以后的程序代码以读方式打开文件
FILE_SHARE_WRITE 允许以后的程序代码以写方式打开文件4dwCreationDisposition参数指定了当文件已存在或者不存在时系统采取的动作。在这里设置不同的标志就可以决定究竟是要打开文件还是要创建文件。参数的可能取值如下
CREATE_ALWAYS 创建新文件。如果文件存在函数会覆盖这个文件清除存在的属性
CREATE_NEW 创建新文件。如果文件存在函数执行失败
OPEN_ALWAYS 如果文件已经存在就打开它不存在则创建新文件
OPEN_EXISTING 打开存在的文件。如果文件不存在函数执行失败
TRUNCATE_EXISTING 打开文件并将文件截断为零当文件不存在时函数执行失败 5dwFlagsAndAttributes参数用来指定新建文件的属性和标志。文件属性可以是下面这些值的组合
FILE_ATTRIBUTE_ARCHIVE 标记归档属性
FILE_ATTRIBUTE_HIDDEN 标记隐藏属性
FILE_ATTRIBUTE_READONLY 标记只读属性
FILE_ATTRIBUTE_READONLY 标记系统属性
FILE_ATTRIBUTE_TEMPORARY 临时文件。操作系统会尽量把所有文件的内容保持在内 存中以加快存取速度。使用完后要尽快将它删除此参数还可同时指定对文件的操作方式下面是一些比较常用的方式
FILE_FLAG_DELETE_ON_CLOSE 文件关闭后系统立即自动将它删除
FILE_FLAG_OVERLAPPED 使用异步读写文件的方式
FILE_FLAG_WRITE_THROUGH 系统不会对文件使用缓存文件的任何改变都会被系统 立即写入硬盘6hTemplateFile参数指定了一个文件模板句柄。系统会复制该文件模板的所有属性到当前创建的文件中。Windows 98系列的操作系统不支持它必须设为NULL。
打开或创建文件成功时函数返回文件句柄失败时返回INVALID_HANDLE_VALUE1。如果想再详细了解失败的原因可以继续调用GetLastError函数。
用不同的参数组合调用CreateFile函数可以完成不同的功能例如下面的代码为读取数据打开了一个存在的文件。
HANDLE hFile;
hFile CreateFile(myfile.txt, // 要创建的文件 GENERIC_WRITE, // 要写这个文件 0, // 不共享 NULL, // 默认安全属性 CREATE_ALWAYS, // 如果存在就覆盖 FILE_ATTRIBUTE_NORMAL, // 普通文件 NULL); // 没有模板 if(hFile INVALID_HANDLE_VALUE) { ……// 不能够打开文件 } 要关闭打开的文件直接以CreateFile返回的文件句柄调用CloseHandle函数即可。
2移动文件指针 系统为每个打开的文件维护一个文件指针指定对文件的下一个读写操作从什么位置开始。随着数据的读出或写入文件指针也随之移动。当文件刚被打开时文件指针处于文件的头部。有时候需要随机读取文件内容这就需要先调整文件指针SetFilePointer函数提供了这个功能原型如下。
DWORD SetFilePointer ( HANDLE hFile, // 文件句柄 LONG lDistanceToMove, // 要移动的距离 PLONG lpDistanceToMoveHigh, // 移动距离的高32位一般设置为NULL DWORD dwMoveMethod // 移动的模式
); dwMoveMethod参数指明了从什么地方开始移动可以是下面的一个值
FILE_BEGIN 开始移动位置为0即从文件头部开始移动
FILE_CURRENT 开始移动位置是文件指针的当前值
FILE_END 开始移动位置是文件的结尾即从文件尾开始移动函数执行失败返回1否则返回新的文件指针的位置。
文件指针也可以移动到所有数据的后面比如现在文件的长度是100 KB但还是可以成功的将文件指针移动到1000 KB的位置。这样做可以达到扩展文件长度的目的。
SetEndOfFile函数可以截断或者扩展文件。该函数移动指定文件的结束标志end-of-fileEOF到文件指针指向的位置。如果文件扩展旧的EOF位置和新的EOF位置间的内容是未定义的。SetEndOfFile函数的用法如下。
BOOL SetEndOfFile(HANDLE hFile); 截断或者扩展文件时要首先调用SetFilePointer移动文件指针然后再调用SetFilePointer函数设置新的文件指针位置为EOF。
3读写文件 读写文件的函数是ReadFile和WriteFile这两个函数既可以同步读写文件又可以异步读写文件。而函数ReadFileEx和WriteFileEx只能异步读写文件。
从文件读取数据的函数是ReadFile向文件写入数据的函数是WriteFile操作的开始位置由文件指针指定。这两个函数的原型如下。
BOOL ReadFile(
HANDLE hFile, // 文件句柄
LPVOID lpBuffer, // 指向一个缓冲区函数会将读出的数据返回到这里
DWORD nNumberOfBytesToRead, // 要求读入的字节数
LPDWORD lpNumberOfBytesRead, // 指向一个DWORD类型的变量 // 用于返回实际读入的字节数
LPOVERLAPPED lpOverlapped // 以便设为NULL
);
BOOL WriteFile (hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); 当用WriteFile写文件时写入的数据通常被Windows暂时保存在内部的高速缓存中等合适的时候再一并写入磁盘。如果一定要保证所有的数据都已经被传送可以强制使用FlushFileBuffers函数来清空数据缓冲区函数的惟一参数是要操作的文件句柄。
BOOL FlushFileBuffers (HANDLE hFile ); 4锁定文件 当对文件数据的一致性要求较高时为了防止程序在写入的过程中其他进程刚好在读取写入区域的内容可以对已打开文件的某个部分进行加锁这就可以防止其他进程对该区域进行读写。加锁和解锁的函数是LockFile和UnlockFile它们的原形如下。
BOOL LockFile( HANDLE hFile, // 文件句柄 DWORD dwFileOffsetLow, // 加锁的开始位置 DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, // 加锁的区域的大小 DWORD nNumberOfBytesToLockHigh
);
UnlockFile ( hFile, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh); dwFileOffsetLow和dwFileOffsetHigh参数组合起来指定了加锁区域的开始位置nNumberOfBytesToLockLow和nNumberOfBytesToLockHigh参数组合起来指定了加锁区域的大小。这两个参数都指定了一个64位的值在Win32中只使用32位就够了。
如果加锁文件的进程终止或者文件关闭时还未解锁操作系统会自动解除对文件的锁定。但是操作系统解锁文件花费的时间取决于当前可用的系统资源。因此进程终止时最好显式地解锁所有已锁定的文件以免造成这些文件无法访问。
8.1.2 获取文件信息
1获取文件类型 Windows下的许多对象都称之为文件如果想知道一个文件句柄究竟对应什么对象可以使用GetFileType函数原型如下。
DWORD GetFileType(HANDLE hFile); 函数的返回值说明了文件类型可以是下面的一个值
FILE_TYPE_CHAR 指定文件是字符文件通常是LPT设备或控制台
FILE_TYPE_DISK 指定文件是磁盘文件
FILE_TYPE_PIPE 指定文件是套节字一个命名的或未命名的管道
FILE_TYPE_UNKNOWN 不能识别指定文件或者函数调用失败 2获取文件大小 如果确定操作的对象是磁盘文件还可以使用GetFileSize函数取得这个文件的长度。
DWORD GetFileSize( HANDLE hFile, // 文件句柄 LPDWORD lpFileSizeHigh // 用于返回文件长度的高字。可以指定这个参数为NULL
); 函数执行成功将返回文件大小的低双字如果lpFileSizeHigh参数不是NULL函数将文件大小的高双字放入它指向的DWORD变量中。
如果函数执行失败并且lpFileSizeHigh是NULL返回值将是INVALID_FILE_SIZE如果函数执行失败但lpFileSizeHigh不是NULL返回值是INVALID_FILE_SIZE进一步调用GetLastError会返回不为NO_ERROR的值。
如果返回值是INVALID_FILE_SIZE应用程序必须调用GetLastError来确定函数调用是否成功。原因是当lpFileSizeHigh不为NULL或者文件大小为0xffffffff时函数虽然调用成功了但依然会返回INVALID_FILE_SIZE。这种情况下GetLastError会返回NO_ERROR来响应成功。
3获取文件属性 如果要查看文件或者目录的属性可以使用GetFileAttributes函数它会返回一系列FAT风格的属性信息。
DWORD GetFileAttributes(LPCTSTR lpFileName); // lpFileName指定了文件或者目录的名称 函数执行成功返回值包含了指定文件或目录的属性信息可以是下列取值的组合
FILE_ATTRIBUTE_ARCHIVE 文件包含归档属性
FILE_ATTRIBUTE_COMPRESSED 文件和目录被压缩
FILE_ATTRIBUTE_DIRECTORY 这是一个目录
FILE_ATTRIBUTE_HIDDEN 文件包含隐含属性
FILE_ATTRIBUTE_NORMAL 文件没有其他属性
FILE_ATTRIBUTE_READONLY 文件包含只读属性
FILE_ATTRIBUTE_SYSTEM 文件包含系统属性
FILE_ATTRIBUTE_TEMPORARYT 文件是一个临时文件 这些属性对目录也同样适用。INVALID_FILE_ATTRIBUTES0xFFFFFFFF是函数执行失败后的返回值。
下面是快速检查某个文件或目录是否存在的自定义函数可以将它用在自己的工程中。
BOOL FileExists(LPCTSTR lpszFileName, BOOL bIsDirCheck)
{ // 试图取得文件的属性 DWORD dwAttributes GetFileAttributes(lpszFileName); if(dwAttributes 0xFFFFFFFF) return FALSE; if ((dwAttributes FILE_ATTRIBUTE_DIRECTORY) FILE_ATTRIBUTE_DIRECTORY) { if (bIsDirCheck) return TRUE; else return FALSE; } else { if (!bIsDirCheck) return TRUE; else return FALSE; }
} 第2个参数bIsDirCheck指定要检查的对象是目录还是文件。
与GetFileAttributes相对应的函数是SetFileAttributes这个函数用来设置文件属性。
BOOL SetFileAttributes( LPCTSTR lpFileName, // 目标文件名称 DWORD dwFileAttributes // 要设置的属性值
); 8.1.3 常用文件操作
1拷贝文件 拷贝文件的函数是CopyFile和CopyFileEx其作用都是复制一个存在的文件到一个新文件中。CopyFile函数的用法如下。
BOOL CopyFile( LPCTSTR lpExistingFileName, // 指定已存在的文件的名称 LPCTSTR lpNewFileName, // 指定新文件的名称 BOOL bFailIfExists // 如果指定的新文件存在是否按出错处理
); CopyFileEx函数的附加功能是允许指定一个回调函数在拷贝过程中函数每拷贝完一部分数据就会调用回调函数。用户在回调函数中可以指定是否停止拷贝还可以显示进度条来指示拷贝的进度。
2删除文件 删除文件的函数是DeleteFile仅有的参数是要删除文件的名称。
BOOL DeleteFile(LPCTSTR lpFileName); 如果应用程序试图删除不存在的文件DeleteFile将执行失败。如果目标文件是只读的函数也会执行失败出错代码为ERROR_ACCESS_DENIED。为了删除只读文件先要去掉其只读属性。
DeleteFile函数可以标识一个文件为“关闭时删除”。因此直到最后一个到此文件的句柄关闭之后文件才会被删除。
下面的自定义函数RecursiveDelete示例了如何删除指定目录下的所有文件和子目录。
void RecursiveDelete(CString szPath)
{ CFileFind ff; // MFC将查找文件的API封装到了CFileFind类 读者可参考下面的框架使用这个类 CString strPath szPath; // 说明要查找此目录下的所有文件 if(strPath.Right(1) ! \\) strPath \\; strPath *.*; BOOL bRet; if(ff.FindFile(strPath)) { do { bRet ff.FindNextFile(); if(ff.IsDots()) // 目录为“.”或者“..” continue; strPath ff.GetFilePath(); if(!ff.IsDirectory()) { // 删除此文件 ::SetFileAttributes(strPath, FILE_ATTRIBUTE_NORMAL); ::DeleteFile(strPath); } else { // 递归调用 RecursiveDelete(strPath); // 删除此目录RemoveDirectory只能删除空目录 ::SetFileAttributes(strPath, FILE_ATTRIBUTE_NORMAL); ::RemoveDirectory(strPath); } } while(bRet); }
} 用DeleteFile函数删除的文件不会被放到回收站它们将永远丢失所以请小心使用RecursiveDelete函数。
3移动文件 移动文件的函数是MoveFile和MoveFileEx函数。它们的主要功能都是用来移动一个存在的文件或目录。MoveFile函数用法如下。
BOOL MoveFile( LPCTSTR lpExistingFileName, // 存在的文件或目录 LPCTSTR lpNewFileName // 新的文件或目录
); 当需要指定如何移动文件时请使用MoveFileEx函数。
BOOL MoveFileEx(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, DWORD dwFlags);dwFlags参数可以是下列值的组合
MOVEFILE_DELAY_UNTIL_REBOOT 函数并不马上执行而是在操作系统下一此重新启动时才移动文件。在AUTOCHK执行之后系统立即移动文件这是在创建任何分页文件之前进行的。因此这个值使函数能够删除上一次运行时使用的分页文件。只有拥有管理员权限的用户才可以使用这个值
MOVEFILE_REPLACE_EXISTING 如果目标文件已存在的话就将它替换掉
MOVEFILE_WRITE_THROUGH 直到文件实际从磁盘移除之后函数才返回 如果指定了MOVEFILE_DELAY_UNTIL_REBOOT标记lpNewFileName参数可以指定为NULL这种情况下当系统下一次启动时操作系统会删除lpExistingFileName参数指定的文件。
8.1.4 检查PE文件有效性的例子
PE文件格式是任何可执行模块或者DLL的文件格式PE文件以64字节的DOS文件头IMAGE_DOS_HEADER结构开始之后是一小段DOS程序然后是248字节的NT文件头IMAGE_NT_HEADERS结构。NT文件头的偏移地址由IMAGE_DOS_HEADER结构的e_lfanew成员给出。
检查文件是不是有效PE文件的一个方法是检查IMAGE_DOS_HEADER和IMAGE_NT_HEADERS结构是否有效。IMAGE_DOS_HEADER结构定义如下。
typedef struct _IMAGE_DOS_HEADER
{ WORD e_magic; // DOS可执行文件标记为“MZ”。依此识别DOS头是否有效 ... // 其他成员没什么用途 LONG e_lfanew; // IMAGE_NT_HEADERS结构的地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; IMAGE_NT_HEADERS结构定义如下。
typedef struct _IMAGE_NT_HEADERS
{ DWORD Signature; // PE文件标识为“PE\0\0”。依此识别NT文件头是否有效 IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, 为了编程方便Windows为DOS文件标记和PE文件标记都定义了宏标识。
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00 检查文件是否为PE文件的步骤如下 1检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE是则说明DOS MZ头有效。 2一旦证明文件的DOS头有效后就可用e_lfanew来定位PE头了。 3比较PE头的第一个字是否等于IMAGE_NT_SIGNATURE。如果这个值也匹配那么就认为该文件是一个有效的PE文件。
下面是验证PE文件有效性的代码在配套光盘的08ValidPE工程下。
// ValidPE.cpp文件#include afxdlgs.h
#include ValidPE.hCMyApp theApp;BOOL CMyApp::InitInstance()
{// 弹出选色文件对话框CFileDialog dlg(TRUE);if(dlg.DoModal() ! IDOK)return FALSE;// 打开检查的文件HANDLE hFile ::CreateFile(dlg.GetFileName(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if(hFile INVALID_HANDLE_VALUE){MessageBox(NULL, 无效文件, ValidPE, MB_OK);}// 定义PE文件中的DOS头和NT头IMAGE_DOS_HEADER dosHeader;IMAGE_NT_HEADERS32 ntHeader; // 验证过程BOOL bValid FALSE;DWORD dwRead;// 读取DOS头::ReadFile(hFile, dosHeader, sizeof(dosHeader), dwRead, NULL);if(dwRead sizeof(dosHeader)){if(dosHeader.e_magic IMAGE_DOS_SIGNATURE) // 是不是有效的DOS头{// 定位NT头if(::SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) ! -1){// 读取NT头::ReadFile(hFile, ntHeader, sizeof(ntHeader), dwRead, NULL);if(dwRead sizeof(ntHeader)){if(ntHeader.Signature IMAGE_NT_SIGNATURE) // 是不是有效的NT头bValid TRUE;}}}}// 显示结果if(bValid)MessageBox(NULL, 是一个PE格式的文件, ValidPE, MB_OK);elseMessageBox(NULL, 不是一个PE格式的文件, ValidPE, MB_OK);::CloseHandle(hFile);return FALSE;
}上述代码简单明确先利用Windows定义的宏IMAGE_DOS_SIGNATURE判断DOS头比较DOS头的e_magic字段再通过DOS头的e_lfanew字段定位到NT头最后检查NT头的Signature字段是不是IMAGE_NT_SIGNATURE即“PE\0\0”。
8.1.5 MFC的支持CFile类
CFile是一个相当简单的封装了一部分文件I/O处理函数的类。它的成员函数用于打开和关闭文件、读写文件数据、删除和重命名文件、取得文件信息。它的公开成员变量m_hFile保存了与CFile对象关联的文件的文件句柄。一个受保护的CString类型的成员变量m_strFileName保存了文件的名称。成员函数GetFilePath、GetFileName和GetFileTitle能够用来提取整个或者部分文件名。比如如果完整的文件名是“C:\MyWork\File.txt”GetFilePath返回整个字符串GetFileName返回“File.txt”GetFileTitle返回“File”。
但是详述这些函数就会忽略CFile类的特色这就是用来写数据到磁盘和从磁盘读数据的函数。下面简单介绍CFile类用法。
**1打开和创建文件 ** 使用CFile类打开文件有两种方法。
1构造一个未初始化的CFile对象调用CFile::Open函数。下面的部分代码使用这个技术以读写权限打开一个名称为File.txt的文件。
CFile file;
if(file.Open(_T (File.txt), CFile::modeReadWrite))
{ // 打开文件成功
}CFile::Open函数的返回值是BOOL类型的变量。如果打开文件出错还想进一步了解出错的原因可以创建一个CFileException对象传递它的地址到Open函数的第3个参数。
CFile file;
CFileException e;
if (file.Open(_T (File.txt), CFile::modeReadWrite, e))
{ // 打开文件成功
}
else
{ // 打开文件失败告诉用户原因 e.ReportError();
} 如果打开失败CFile::Open函数会使用描述失败原因的信息初始化一个CFileException对象。ReportError成员函数基于这个信息显示一个出错对话框。可以通过检查CFileException类的公有成员m_cause找到导致这个错误的原因。
2使用CFile类的构造函数。可以将创建文件对象和打开文件合并成一步如下面代码所示。
CFile file(_T (File.txt), CFile::modeReadWrite); 如果文件不能被打开CFile的构造函数会抛出一个CFileException异常。因此使用CFile::CFile函数打开文件的代码通常使用try和catch块来捕获错误。
try
{ CFile file(_T (File.txt), CFile::modeReadWrite);
}
catch(CFileException* e)
{ // 出错了 e-ReportError(); e-Delete();
} 删除MFC抛出的异常是程序写作者的责任所以在程序中处理完异常之后要调用异常对象的Delete函数。
为了创建一个文件而不是打开一个存在的文件要在CFile::Open或者CFile构造函数的第二个参数中包含上CFile::modeCreate标记如下代码所示。
CFile file(_T(File.txt), CFile::modeReadWrite | CFile::modeCreate); 如果以这种方式创建的文件存在它的长度会被截为0。为了在文件不存在时创建它存在的时候仅打开而不截去应再包含上CFile::modeNoTruncate标记如下面代码所示。
CFile file(_T(File.txt), CFile::modeReadWrite | CFile::modeCreate | CFile::modeNoTruncate); 默认情况下由CFile::Open或CFile::CFile打开的文件使用的是独占模式即CreateFile API中的第3个参数dwShareMode被设为了0。如果需要在打开文件时也可以指定一个共享模式以明确同意其他访问此文件的操作。这里是4个可以选择的共享模式
CFile::shareDenyNone 不独占这个文件
CFile::shareDenyRead 拒绝其他代码对这个文件进行读操作
CFile::shareDenyWrite 拒绝其他代码对这个文件进行写操作
CFile::shareExclusive 拒绝其他代码对这个文件进行读和写操作默认另外还可以指定下面3个对象访问类型中的一个
CFile::modeReadWrite 请求读写访问
CFile::modeRead 仅请求读访问
CFile::modeWrite 仅请求写访问 常用的做法是允许其他程序以只读方式打开文件但是拒绝它们写入数据。
CFile file(_T(File.txt), CFile::modeReadWrite | CFile::modeCreate | CFile::modeNoTruncate); 如果在上面的代码执行之前文件已经以可写的方式打开了这个调用将会失败CFile类会抛出CFileException异常异常对象的m_cause成员等于CFileException::sharingViolation。
CFile类的成员函数Close会调用CloseHandle API关闭应用程序打开的文件对象句柄。如果句柄没有关闭类的析构函数也会调用Close函数关闭它。显式调用Close函数一般都是为了关闭当前打开的文件以便使用同样的CFile对象打开另一个文件。
2读写文件 CFile类中从文件中读取数据的成员函数是Read。例如下面的代码申请了一块4KB大小的文件I/O缓冲区每次从文件读取4KB大小的数据。
BYTE buffer[4096];
CFile file (_T(File.txt), CFile::modeRead);
DWORD dwBytesRemaining file.GetLength();
while(dwBytesRemaining)
{ UINT nBytesRead file.Read(buffer, sizeof(buffer)); dwBytesRemaining - nBytesRead;
} 文件中未读取的字节数保存在dwBytesRemaining变量里此变量由CFile::GetLength返回的文件长度初始化。每次调用Read之后从文件中读取的字节数nBytesRead会从dwBytesRemaining变量里减去。直到dwBytesRemaining为0整个while循环才结束。
CFile类还提供了Write成员函数向文件写入数据Seek成员函数移动文件指针它们都和相关API一一对应。可以通过跟踪程序的执行来查看这些函数的实现代码。