1、创建和打开文件
HANDLE CreateFile(
LPCTSTR lpFileName, // 要创建或打开的文件的名称字符串
DWORD dwDesiredAccess, // 对文件的访问权限
DWORD dwShareMode, // 文件的共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 含义同其他内核对象的安全属性结构
DWORD dwCreationDisposition, // 创建或打开标志
DWORD dwFlagsAndAttributes, // 文件的标志和系统属性
HANDLE hTemplateFile // 模板文件的句柄,可以设置为NULL
);
- lpFileName 参数指定要创建或打开的文件的名称字符串,如果使用函数的 ANSI版本,则路径名称字符串限制为MAX_PATH 个字符;如果使用函数的 Unicode 版本,则路径名称字符串限制为 32767 个字符。
- dwDesiredAccess 参数指定对文件的访问权限,通过这个参数可以指定要对打开的文件进行什么操作。指定为GENERIC_READ标志表示需要读取文件数据,指定为GENERIC_WRITE标志表示需要向文件写入数据,如果要对一个文件进行读写则需要同时指定着两个标志GENERIC_READ | GENERIC_WRITE。
- dwShareMode 参数指定文件的共享模式,即文件被打开后是否还允许其他进程或线程以某种方式再次打开文件,可以如下表所示的值组合。
常量 | 值 | 含义 |
---|---|---|
0 | 不允许文件再被打开,即独占对该文件的访问 | |
FILE_SHARE_READ | 0x00000001 | 允许其他进程或线程同时以读方式打开文件 |
FILE_SHARE_WRITE | 0x00000002 | 允许其他进程或线程同时以写方式打开文件 |
FILE_SHARE_DELETE | 0x00000004 | 允许其他进程或线程同时对文件进行删除 |
- lpSecurityAttributes 参数的含义同其他内核对象的安全属性结构。
- dwCreationDisposition 参数指定创建或打开标志,该参数用来设置文件已经存在或不存在时系统采取的操作,在这里指定不同的标志就可以决定函数执行的功能究竟是创建文件还是打开文件,该参数可以是下面表中的值之一。
常量 | 值 | 含义 |
---|---|---|
CREATE_NEW | 1 | 仅在指定的文件尚不存在的情况下创建一个新文件,如果指定的文件已经存在,则函数调用失败,此时调用GetLastError函数返回错误码ERROR_FILE_EXISTS(80) |
CREATE_ALWAYS | 2 | 始终创建一个新文件,如果指定的文件不存在并且是有效路径,则创建一个新文件,函数调用成功,此时调用GetLastError函数返回错误码0;如果指定的文件已经存在并且可写,则函数将覆盖该文件(文件内存会被清空),函数调用成功,此时调用GetLastError函数返回错误码ERROR_ALREADY_EXISTS(183) |
OPEN_EXISTING | 3 | 仅打开存在的文件,如果指定的文件不存在,则函数调用失败,此时调用GetLastError函数返回错误码ERROR_FILE_NOT_FOUND(2) |
OPEN_ALWAYS | 4 | 始终打开文件。如果指定的文件已经存在,则函数调用失败,此时调用GetLastError函数返回错误码ERROR_ALREADY_EXISTS(183);如果指定的文件不存在并且是有效路径,则创建一个新文件,函数调用成功,此时调用GetLastError函数返回错误码0 |
TRUNCATE_EXISTING | 5 | 打卡文件并将其截断,使其大小为0字节(仅当文件存在时)。如果指定的文件不存在,函数调用失败,此时调用GetLastError函数返回错误码ERROR_FILE_NOT_FOUND(2) |
注: 简单来说,如果要创建文件,则可以指定CREATE_NEW(如果指定的文件已经存在,则函数调用失败)或CREATE_ALWAYS(如果指定文件已经存在,则函数将覆盖该文件);如果要打开文件,则可以指定为OPEN_EXISTING(如果指定的文件不存在,则函数调用失败)或OPEN_ALWAYS(如果指定文件不存在,则创建一个新文件)。
- dwFlagsAndAttributes 参数指定文件的标志和系统属性,一般设置为FILE_ATTRIBUTE_NORMAL即可,文件系统属性(FILE_ATTRIBUTE_)和文件标志(FILE_FLAG_)可以组合使用,参数可以指定的常用文件系统属性如下表。
文件系统属性 | 值 | 含义 |
---|---|---|
FILE_ATTRIBUTE_NORMAL | 0x80 | 普通文件 |
FILE_ATTRIBUTE_READONLY | 0x1 | 该文件是只读的,程序可以读取文件,但不能向文件写入数据或者删除文件 |
FILE_ATTRIBUTE_HIDDEN | 0x2 | 文件是隐藏的,不会出现在普通目录列表中 |
FILE_ATTRIBUTE_SYSTEM | 0x4 | 该文件是操作系统的一部分或仅由操作系统使用 |
FILE_ATTRIBUTE_ARCHIVE | 0x20 | 设置归档属性,程序使用这个标志将文件标记为备份或者待删除,CreateFile在创建一个新文件时,会自动设置这个标志 |
FILE_ATTRIBUTE_TEMPORARY | 0x100 | 该文件用于临时存储,即该文件的数据只会使用一小段时间,为了提高访问效率,系统会尽量将文件数据保存在内存中,而不是保存在硬盘中,程序不再使用文件时应该尽快将它删除,它通常与文件标志FILE_FLAG_DELETE_ON_CLOSE一起使用 |
FILE_ATTRIBUTE_ENCRYPTED | 0x4000 | 该文件已加密 |
注: 文件标志用于控制文件的缓存行为,访问模式等,dwFlagsAndAttributes参数可以指定的常用文件标志如下表。
文件标志 | 值 | 含义 |
---|---|---|
FILE_FLAG_DELETE_ON_CLOSE | 0x04000000 | 关闭文件句柄后立刻删除该文件 |
FILE_FLAG_OVERLAPPED | 0x40000000 | 创建或打开的文件使用异步I/O操作方式 |
FILE_FLAG_NO_BUFFERING | 0x20000000 | 对文件的读写操作不使用系统缓存,为了提高性能,系统在访问硬盘时会对数据进行缓存,系统会从文件中读取超出实际需要的数据字节量,如果后续要继续读取后面的字节,就可以直接从系统缓存中而不是从文件中读取 |
FILE_FLAG_WRITE_THROUGH | 0x80000000 | 对文件的写操作将不会通过任何中间缓存,文件的修改会被马上写入硬盘中 |
- hTemplateFile 参数指定模板文件的句柄,通常设置为NULL,函数会将该参数对应的文件的属性复制到新创建的文件上面,
即该参数可以用于将某个新创建的文件的属性设置为现有文件(一个已打开或已创建的文件)相同,这中情况下会忽略dwFlagsAndAttributes参数。
如果CreateFile函数执行成功,则返回一个文件对象句柄;如果函数执行失败,则返回值为INVALID_HANDLE_VALUE(-1),而不是NULL。
例:
HANDLE hFile;
//打开文件
hFile = CreateFile(TEXT("D:\\Test.txt"),GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
//函数调用失败
}
//创建文件
hFile = CreateFile(TEXT("D:\\Test.txt"),GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
//函数调用失败
}
注:当不在需要创建或打开文件句柄时,需要调用CloseHandle函数关闭文件对象句柄。
2、读写文件
BOOL ReadFile(
HANDLE hFile, // 文件句柄
LPVOID lpBuffer, // 接收文件数据的缓冲区
DWORD nNumberOfBytesToRead, // 要读取的字节数
LPDWORD lpNumberOfBytesRead, // DWORD类型变量的指针,返回实际读取到的字节数
LPOVERLAPPED lpOverlapped // 用于异步文件操作,不需要的话可以设置为NULL
);
BOOL WriteFile(
HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, // 要写入文件的数据的缓冲区
DWORD nNumberOfBytesToWrite, // 要写入的字节数
LPDWORD lpNumberOfBytesWritten, // DWORD类型变量的指针,返回成功写入的字节数
LPOVERLAPPED lpOverlapped // 用于异步文件操作,不需要的话可以设置为NULL
);
3、刷新文件缓冲区
BOOL FlushFileBuffers(
HANDLE hFile // 文件句柄
);
FlushFileBuffers函数可以刷新指定文件的缓冲区,并使所有缓冲区中的数据写入文件。
注:要使用无缓冲I/O,调用CreateFile函数创建或打开文件时需要使用指定FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH标志,这样可以防止文件内容被缓存,并在每次写入时将数据刷新到硬盘。
4、文件指针
BOOL SetFilePointerEx(
HANDLE hFile, // 文件句柄
LARGE_INTEGER liDistanceToMove, // 文件指针要移动的字节数
PLARGE_INTEGER lpNewFilePointer, // 返回新文件指针,不需要可以设置为NULL
DWORD dwMoveMethod // 文件指针移动的起点
);
- dwMoveMethod 参数指定文件指针移动的起点,该参数可以是下表所示的值之一。
常量 | 含义 |
---|---|
FILE_BEGIN | 起点为0,也就是文件的开头 |
FILE_CURRENT | 起点为文件指针的当前位置 |
FILE_END | 起点为文件的结束位置(末尾) |
例如:
TCHAR szStr[] = TEXT("你好,WindowsAPI");
static HANDLE hFile;
LARGE_INTEGER liDistanceToMove = { 900 };
LARGE_INTEGER liNewFilePointer;
hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_END);
WriteFile(hFile, szStr, _tcslen(szStr) * sizeof(TCHAR), NULL, NULL);
}
问: 如何获取当前文件指针呢?
答: 微软公司并没有提供一个获取当前文件指针的函数,我们可以通过调用SetFilePointerEx函数把liDistanceToMove 参数设置为0,把dwMoveMethod参数设置为FILE_CURRENT,即从文件的当前位置移动0字节,文件指针不会做任何移动,调用函数后在lpNewFilePointer参数中返回当前文件指针。
例如:
LARGE_INTEGER liDistanceToMove = { 0 };
SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_CURRENT);
注:SetEndOfFile函数用于把文件结尾设置为文件指针的当前位置,即把文件的文件大小设置为文件指针的当前位置,该函数可以实现文件的截断或扩展。
BOOL SetEndOfFile(HANDLE hFile // 文件句柄);
例如: 假设调用CreateFile函数打开了一个1KB大小的文件,调用SetFilePointerEx函数把文件指针设置为1000B的位置,然后调用SetEndOfFile函数可以把该文件从1000B的地方截断,截断以后的文件只有1000B大小。如果把文件指针设置为2000B然后调用SetEndOfFile函数即可把该文件扩展到2000B大小,新扩展的数据通常是0。
例如下面代码是把一个文件的大小扩展为8GB大小;
static HANDLE hFile;
LARGE_INTEGER liDistanceToMove;
liDistanceToMove.QuadPart = (LONGLONG)8 * 1024 * 1024 * 1024; //8GB
LARGE_INTEGER liNewFilePointer;
hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_BEGIN);
SetEndOfFile(hFile);
}
注:liDistanceToMove.QuadPart = (LONGLONG)8 * 1024 * 1024 * 1024; 中的liDistanceToMove.QuadPart字段是一个LONGLONG类型,表达式的右值8 * 1024 * 1024 * 1024默认为一个int类型,而8 * 1024 * 1024 * 1024作为int会溢出,因此需要强制转换为LONGLONG类型。
小技巧:在通过网络下载文件时通常是先创建一个文件,立即设置文件指针到要下载的文件大小处,然后调用SetEndOfFile函数扩展其大小,再慢慢填充数据,其目的是先占用磁盘空间。
5、文件属性
GetFileSizeEx函数获取该文件的文件大小;
BOOL GetFileSizeEx(
HANDLE hFile, // 文件句柄
PLARGE_INTEGER lpFileSize // 在这个PLARGE_INTEGER结构体中返回文件大小、字节数
);
GetFileType函数用于获取指定文件的文件类型,函数返回值如下表所示值之一
DWORD GetFileType(
HANDLE hFile // 文件句柄
);
常量 | 值 | 含义 |
---|---|---|
FILE_TYPE_UNKNOWN | 0x0000 | 指定的文件类型未知,或者函数调用失败 |
FILE_TYPE_DISK | 0x0001 | 指定的文件是磁盘文件 |
FILE_TYPE_CHAR | 0x0002 | 指定的文件是字符文件,通常是LPT设备或控制台 |
FILE_TYPE_PIPE | 0x0003 | 指定的文件是套接字、命名管道或匿名管道 |
6、获取/设置文件的创建时间、最后访问时间、最后修改时间
BOOL WINAPI GetFileTime(
__in HANDLE hFile, //文件或目录句柄
__out_opt LPFILETIME lpCreationTime, //返回的创建的日期和时间信息
__out_opt LPFILETIME lpLastAccessTime, //返回的最后访问的日期和时间信息
__out_opt LPFILETIME lpLastWriteTime //返回的最后修改的日期和时间信息
);
BOOL WINAPI SetFileTime(
__in HANDLE hFile, //文件或目录句柄
__out_opt FILETIME *lpCreationTime, //文件的创建时间
__out_opt FILETIME *lpLastAccessTime, //最后访问时间
__out_opt FILETIME *lpLastWriteTime //最后修改时间
);
注:调用GetFileTime函数后,为了得到年月日时间数据,可以调用FileTimeToSystemTime函数将FILETIME转换为SYSTEMTIME结构;调用SetFileTime函数设置时间时,可以先设置好SYSTEMTIME结构,再调用SystemTimeToFileTime函数将SYSTEMTIME结构转换为FILETIME结构。
注:要获取或设置文件系统属性可以调用GetFileAttributes、SetFileAttributes函数,这里的文件系统属性是指CreateFile函数的dwFlagsAndAttributes参数指定的FILE_ATTRIBUTE_*值。
BOOL SetFileAttributes( LPCTSTR lpFileName, // 文件名称
DWORD dwFileAttributes // 指定为FILE_ATTRIBUTE_*值得组合);
DWORD GetFileAttributes( LPCTSTR lpFileName // 文件或目录的名称);
函数返回FILE_ATTRIBUTE_*值得组合
如果需要获取更完整得文件属性信息,包括FILE_ATTRIBUTE_*值得文件系统属性,以及文件的创建时间、最后访问时间和最后修改时间,以及文件的大小信息,可以调用GetFileAttributesEx函数。
BOOL GetFileAttributesEx(
LPCTSTR lpFileName, // 文件或目录名
GET_FILEEX_INFO_LEVELS fInfoLevelId, // 指定为GetFileExInfoStandard
LPVOID lpFileInformation // 返回文件属性
);
- fInfoLevelId 参数是一个GET_FILEEX_INFO_LEVELS枚举类型;
typedef enum _GET_FILEEX_INFO_LEVELS {
GetFileExInfoStandard,
GetFileExMaxInfoLevel
} GET_FILEEX_INFO_LEVELS;
在这个函数中,该枚举值只能指定为GetFileExInfoStandard,表示lpFileInformation参数是一个指向WIN32_FILEATTRIBUTE_DATA结构的指针。
- lpFileInformation 参数需要指定为一个指向WIN32_FILE_ATTRIBUTE_DATA结构的指针,函数在这个结构中返回文件的属性信息。该结构在fileapi.h头文件中定义如下;
typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
DWORD dwFileAttributes; // 文件系统属性信息,FILE_ATTRIBUTE_*值得组合
FILETIME ftCreationTime; // 文件得创建时间
FILETIME ftLastAccessTime; // 最后访问时间
FILETIME ftLastWriteTime; // 最后修改时间
DWORD nFileSizeHigh; // 文件大小的高32位
DWORD nFileSizeLow; // 文件大小的低32位
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
GetFileAttributesEx函数通过指定一个文件名来获取其文件属性信息,还有一个功能类似的函数GetFileInformationByHandle,该函数通过指定一个文件句柄来获取文件属性信息,获取的文件信息更多一些。
BOOL GetFileInformationByHandle(
HANDLE hFile, // 文件句柄
LPBY_HANDLE_FILE_INFORMATION lpFileInformation // 返回文件信息
);
- lpFileInformation 参数是一个指向BY_HANDLE_FILE_INFORMATION结构的指针,函数在这个结构中返回文件信息,该结构在fileapi.h头文件中
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes; // 文件系统属性信息,FILE_ATTRIBUTE_*值得组合
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 最后访问时间
FILETIME ftLastWriteTime; // 最后修改时间
DWORD dwVolumeSerialNumber; // 文件所属的卷的序列号
DWORD nFileSizeHigh; // 文件大小的高32位
DWORD nFileSizeLow; // 文件大小的低32位
DWORD nNumberOfLinks; // 指向该文件的链接数
DWORD nFileIndexHigh; // 该文件ID的高32位
DWORD nFileIndexLow; // 该文件ID的低32位
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION, *LPBY_HANDLE_FILE_INFORMATION;
注: 卷序列号和文件ID组合才可以唯一地标识一台计算机上的文件,要确定两个打开的文件句柄是否代表同一个文件,卷序列号和文件ID必须相同,因为在不同的逻辑驱动器上可以有相同ID的文件。
7、复制文件
CopyFile函数用于将现有文件复制到新文件
BOOL CopyFile(
LPCTSTR lpExistingFileName, // 现有文件的名称
LPCTSTR lpNewFileName, // 新文件的名称
BOOL bFailIfExists // 文件存在时的操作
);
注:如果lpExistingFileName 参数指定的源文件不存在,则函数调用失败,如果bFailIfExists参数设置为TRUE并且lpNewFileName参数指定的目标文件已经存在,函数调用将失败,如果设置为FALSE并且指定的目标文件已经存在,则函数将覆盖已经存在的文件并成功执行,但是在这种情况下,如果目标文件设置了FILE_ATTRIBUTE_HIDDEN或FILE_ATTRIBUTE_READONLY属性,则函数调用还是会失败。
CopyFileEx函数提供了两个附加功能:每当一部分复制操作完成时可以调用指定的回调函数;在复制操作期间可以取消正在进行的复制操作。
BOOL CopyFileEx(
LPCTSTR lpExistingFileName, // 现有文件对应于源文件文件名
LPCTSTR lpNewFileName, // 新文件对应于目标文件文件名
LPPROGRESS_ROUTINE lpProgressRoutine, // 回调函数的地址,可以设置为NULL
LPVOID lpData, // 传递给回调函数的参数,可以设置为
LPBOOL pbCancel, // 该指针指向的变量设置为TRUE,则操作将被取消
DWORD dwCopyFlags // 指定如何复制文件的标志,可以设置为0
);
- lpProgressRoutine 参数指定为一个指向回调函数的指针,每当 一部分复制操作完成是系统会调用该回调函数,回调函数的定义如下。
DWORD CALLBACK CopyProgressRoutine(
LARGE_INTEGER TotalFileSize, // 文件的总大小,以字节为单位
LARGE_INTEGER TotalBytesTransferred, // 已经从源文件传输到目标文件的字节总数
LARGE_INTEGER StreamSize, // 当前文件流的总大小,以字节为单位
LARGE_INTEGER StreamBytesTransferred, // 当前流中已经从源文件传输到目标文件的字节总数
DWORD dwStreamNumber, // 当前流的编号
DWORD dwCallbackReason, // 调用该回调函数的原因
HANDLE hSourceFile, // 源文件的句柄
HANDLE hDestinationFile, // 目标文件的句柄
LPVOID lpData // 传递过来的回调函数参数
);
- lpData 参数是传递给回调函数的参数,可以指定为一个自定义数据,如果不需要则设置为NULL
- pbCancel 参数是一个指向布尔变量的指针,如果在复制操作过程中将该指针指向的变量设置为TRUE,则操作将被取消,例如当用户按下“取消”按钮时,程序可以把该参数指定的变量设置为TRUE,CopyFileEx函数会取消复制操作并立即返回FALSE,并删除目标文件。
- dwCopyFlags 参数是指定如何复制文件的标志,如果没有特殊需求,则设置为0。该参数可以是下表所以的值的组合。
常量 | 值 | 含义 |
---|---|---|
COPY_FILE_FAIL_IF_EXISTS | 0x00000001 | 如果目标文件已经存在,则函数调用失败,此时调用GetLastError函数返回错误码ERROR_FILE_EXISTS |
COPY_FILE_NO_BUFFERING | 0x00001000 | 复制操作使用无缓冲I/O,不使用系统的I/O缓存,该标志常用于传输非常大的文件 |
8、移动文件(目录)、删除文件
- MoveFile函数用于移动一个文件或者目录,移动成功后源文件或目录会被删除
BOOL MoveFile(
LPCTSTR lpExistingFileName, // 现有文件(目录)对应于源文件(目录)名
LPCTSTR lpNewFileName // 新文件(目录)对应于目标文件(目录)名
);
例
//移动文件
MoveFile(TEXT("D:\\Test.txt"),TEXT("D:\\Downloads\\Test.txt"));
//移动目录
MoveFile(TEXT("D:\\Test"),TEXT("D:\\Downloads\\Test"));
- MoveFileEx函数同样用于移动一个文件或者目录,在移动文件或目录时可以设置一些移动选项
BOOL MoveFileEx(
LPCTSTR lpExistingFileName, // 现有文件(目录)对应于源文件(目录)名
LPCTSTR lpNewFileName, // 新文件(目录)对应于目标文件(目录)名
DWORD dwFlags // 移动选项标志
);
- MoveFileWithProgress 函数的功能与MoveFileEx相同,可以指定一个结束进度通知的回调函数
BOOL MoveFileWithProgress(
LPCTSTR lpExistingFileName, // 现有文件(目录)对应于源文件(目录)名
LPCTSTR lpNewFileName, // 新文件(目录)对应于目标文件(目录)名
LPPROGRESS_ROUTINE lpProgressRoutine, // 回调函数的地址,可以设置为ULL
LPVOID lpData, // 传递给回调函数的参数,可以设置为NULL
DWORD dwFlags // 移动标志选项
);
- DeleteFile函数用于删除一个文件
BOOL DeleteFile(
LPCTSTR lpFileName // 目标文件名
);
注: 本内容为Windows API学习总结,方便后面复习