《Windows核心编程》---文件操作

Windows提供了一类API来读、写和管理磁盘文件。

使用API函数读写文件时,首先要使用CreateFile函数创建文件对象,调用成功会返回文件句柄。我们以此句柄为参数调用ReadFileWriteFile函数,进行实际的读写操作;最后调用CloseHandle函数关闭不再使用的文件对象句柄。

1)打开和关闭文件:

CreateFile是一个功能强大的函数,Windows下的底层设备几乎都由它打开。它可以创建或打开文件、目录、物理磁盘、控制台缓冲区、油槽或管道等:

HANDLE CreateFile(

LPCTSTR lpFileName, //要创建或打开的对象的名称

DWORD dwDesiredAccess, //文件的存取方式

DWORD dwShareMode, //共享属性

LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性

DWORD dwCreationDisposition, //文件存在或不存在时系统采取的行动

DWORD dwFlagsAndAttributes, //新文件的属性

HANDLE hTemplateFile //一个文件模板的句柄

);

lpFileName—如果是打开文件,直接指定文件名称即可;如果操作对象是第一个串口,则指定“COM1”为文件名,然后就可以像操作文件一样操作串口了;如果要打开本地电脑上的一个服务,要以//./服务名称为文件名,其中”.”代表本地机器;如果要打开网络中其他主机的文件,则以//主机名/共享目录名/文件名为文件名。

dwDesiredAccessGENERIC_READ(只读)GENERIC_WRITE(只写)、两者组合。

dwShareMode—表示文件打开后是否允许其他代码以某种方式再次打开这个文件,可以是下面的组合:

0—不允许文件再被打开。C语言中的fopen函数就是这种方式;

FILE_SHARE_DELETE—允许其他程序代码删除文件;

FILE_SHARE_READ—允许其他程序代码以读方式打开文件;

FILE_SHARE_WRITE—允许其他程序代码以写方式打开文件;

dwCreationDisposition—指定当文件已存在或不存在时系统采取的动作:

CREATE_ALWAYS—创建新文件,若文件存在,则覆盖它,清除存在的属性;

CREATE_NEW—创建新文件。若文件存在,函数执行失败;

OPEN_ALWAYS—如果文件存在则打开它,不存在则创建新文件;

OPEN_EXISTING—打开存在的文件。若文件不存在,函数执行失败;

TRUNCATE_EXISTING—打开文件并将文件截断为0,当文件不存在时函数执行失败。

dwFlagsAndAttributes—指定新建文件属性和标志,可以是以下各项的组合:

FILE_ATTRIBUTE_ARCHIVE—标记归档属性;

FILE_ATTRIBUTE_HIDDEN—标记隐藏属性;

FILE_ATTRIBUTE_READONLY—标记只读属性;

FILE_ATTRIBUTE_SYSTEM—标记系统属性;

FILE_ATTRIBUTE_TEMPORARY—临时文件,操作系统会尽量把所有文件的内容保持在内存中以加快存取速度。使用完后要尽快将它删除。

FILE_FLAG_DELETE_ON_CLOSE—文件关闭后系统立即自动删除它;

FILE_FLAG_OVERLAPPED—使用异步读写文件的方式;

FILE_FLAG_WRITE_THROUGH—系统不会对文件使用缓冲,文件的任何改变都会被系统立即写入硬盘。

hTemplateFile—指定一个文件模板句柄。系统会复制文件模板的所有属性到当前创建的文件中。

打开或创建文件成功时,函数返回文件句柄,失败时返回INVALID_HANDLE_VALUE

2)移动文件指针:

SetFilePointer函数:系统为每个打开的文件维护一个文件指针,指定对文件的下一个读写操作从什么位置开始。随着数据的读出或写入,文件指针也随之移动。当文件刚被打开时,文件指针处于文件的头部。有时候需要随机读取文件内容,这就需要先调整文件指针:

DWORD SetFilePointer(

HANDLE hFile, //文件句柄

LONG lDistanceToMove, //要移动的距离

PLONG lpDistanceToMoveHigh, //移动距离的高32位,一般位置为NULL

DWORD dwMoveMethod //移动的模式

);

dwMoveMethod参数指明从什么地方开始移动:

FILE_BEGIN—开始移动的位置为0,即从文件头部开始移动;

FILE_CURRENT—开始移动位置是文件指针的当前值;

FILE_END—开始移动位置是文件的结尾,即从文件尾开始移动。

文件指针也可以移动到所有数据后面,比如现在文件长度是200KB,但可以成功地将文件指针移动到1000KB的位置。这样做可以达到扩展文件长度的目的。

SetEndOfFile函数就可以达到截断或者扩展文件的功能。该函数移动指定文件的结束标志(EOF)到文件指针指向的位置。如果文件扩展,旧的EOF位置和新的EOF位置间的内容是未定义的:

BOOL SetEndOfFile(HANDLE hFile);

截断或者扩展文件时,要首先调用SetFilePointer移动文件指针,然后再调用SetFilePointer函数设置新的文件指针位置为EOF

3)读写文件:

ReadFileWriteFile函数既可以同步读写文件,也可以异步读写文件;而函数ReadFileExWriteFileEx只能异步读写文件。

BOOL ReadFile(

HANDLE hFile, //文件句柄

LPVOID lpBuffer, //指向一个缓冲区,函数会将读出的数据返回到这里

DWORD nNumberOfBytesToRead, //要求读入的字节数

LPDWORD lpNumberOfBytesRead, //指向一个DWORD类型的变量,用于返回实际读入的字节数

LPOVERLAPPED lpOverlapped //一般设为NULL

);

BOOL WriteFile(

HANDLE hFile, //文件句柄

LPVOID lpBuffer, //指向一个缓冲区,包含了要写入文件的数据

DWORD nNumberOfBytesToWrite, //要求写入的字节数

LPDWORD lpNumberOfBytesWritten, //指向一个DWORD类型的变量,用于返回实际写入的字节数

LPOVERLAPPED lpOverlapped //一般设为NULL

);

使用WriteFile写文件时,写入的数据通常被Windows暂存在内部的高速缓存中,等合适的时候再一并写入磁盘。可以使用FlushFileBuffers函数来强制系统清空缓冲区:

BOOL FlushFileBuffers(HANDLE hFile);

4)锁定文件:

当我们对文件数据的一致性要求较高时,为了防止程序在写入过程中其他进程刚好在读取写入区域的数据,可以对已打开文件的某个部分加锁,加锁和解锁的函数分别是LockFileUnlockFile

BOOL LockFile(

HANDLE hFile, //文件句柄

DWORD dwFileOffsetLow, //加锁的开始位置

DWORD dwFileOffsetHigh,

DWORD nNumberOfBytesToLockLow, //加锁区域的大小

DWORD nNumberOfBytesToLockHigh

);

BOOL UnlockFile(

HANDLE hFile, //文件句柄

DWORD dwFileOffsetLow, //解锁的开始位置

DWORD dwFileOffsetHigh,

DWORD nNumberOfBytesToLockLow, //解锁区域的大小

DWORD nNumberOfBytesToLockHigh

);

5)获取文件类型:

Windows下许多对象都称为文件,如果想知道一个文件句柄到底是什么对象,可是使用函数GetFileType

DWORD GetFileType(HANDLE hFile);

返回值如下:

FILE_TYPE_CHAR---指定文件时字符文件,通常是LPT设备或控制台;

FILE_TYPE_DISK---指定文件时磁盘文件;

FILE_TYPE_PIPE---指定文件是套接字,一个命名的或未命名的管道;

FILE_TYPE_UNKNOWN—不能识别指定文件,或者函数调用失败。

获取文件大小:

如果确定操作的对象时磁盘文件,可以使用GetFileSize函数取得这个文件的长度:

DWORD GetFileSize(

HANDLE hFile, //文件句柄

LPDWORD lpFileSizeHigh //用于返回文件长度的高字节,可以指定这个参数为NULL

);

函数执行成功将返回文件大小的低双字,如果lpFileSizeHigh不为NULL,函数将文件大小的高双字放入它指向的DWORD变量中。

如果返回值是INVALID_FILE_SIZE,应用程序必须调用GetLastError来确定函数调用是否成功。因为当lpFileSizeHigh不为NULL或者文件大小为oxffffffff时,函数虽然调用成功,但依然会返回INVALID_FILE_SIZE,这种情况下,GetLastError会返回NO_ERROR来响应成功。

6)获取文件属性:

如果要查看文件或者目录的属性,可以使用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_TEMPORARY

这些属性对目录同样适用,函数执行失败返回INVALID_FILE_ATTRIBUTES

设置文件属性的函数是SetFileAttributes

BOOL SetFileAttributes(

LPCTSTR lpFileName, //目标文件名称

DWORD dwFileAttributes //要设置的属性值

);

下面是快速检查某个文件或目录是否存在的自定义函数:

BOOL FileExists(LPCTSTR lpszFileName, BOOL bIsDirCheck)

{

//取得文件的属性

DWORD dwAttributes = GetFileAttributes(lpszFileName);

if(dwAttributes == INVALID_FILE_ATTRIBUTES)

{

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;

}

}

7)拷贝文件:

函数CopyFileCopyFileEx的作用是复制一个存在的文件到一个新文件中:

BOOL CopyFile(

LPCTSTR lpExistingFileName, //指定已存在的文件的名称

LPCTSTR lpNewFileName, //指定新文件的名称

BOOL bFailIfExists //如果指定的新文件存在是否按出错处理

);

CopyFileEx函数的附加功能是允许指定一个回调函数,在拷贝过程中,函数每拷贝完一部分数据,就会调用回调函数。用户在回调函数中可以指定是否停止拷贝,还可以显示进度条来指示拷贝的进度。

8)删除文件:

BOOL DeleteFile(LPCTSTR lpFileName);

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);

}

}

9)移动文件:

函数MoveFileMoveFileEx的功能是移动一个存在的文件或目录:

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直到文件实际从磁盘移除之后函数才返回。

你可能感兴趣的:(windows)