Win-API -1 文件相关

在Windows系统中,创建和打开文件都是使用API函数CreateFile,CreateFile通过指定不同的参数来表示是新建一个文件,打开已经存在的文件,还是重新建立文件等。读写文件最为直接的方式是使用ReadFile和WriteFile函数,也可以使用文件镜像,获取文件大小一般使用GetFileSize函数,也可以使用GetFileAttributesEx等函数(在上节介绍)。读写文件、获取文件大小之前都需要使用CreateFile创建或打开的文件,获得文件句柄。
在文件操作中,文件句柄是一个关键的概念。文件句柄惟一标识了一个文件,ReadFile、WriteFile、GetFileSize等函数是使用文件句柄作为参数来表示,用户需要读、写、获取大小的文件是哪一个文件。在对文件进行操作前,都必须要使用CreateFile获得文件句柄。

CreateFile

CreateFile是文件操作中最主要的一个函数。几乎所有的文件操作都需要使用到文件句柄。而CreateFile函数为这些操作建立文件句柄。CreateFile函数的参数多,而且比较复杂,涉及文件名、文件的共享模式、存取方式、操作模式、权限、标志和文件属性等。每一个参数都可以有多种值可供选择,代表了不同的意义。

◇参数
lpFileName:输入参数,操作对象文件的相对路径或绝对路径。
dwDesiredAccess:输入参数,指明对文件对象的操作存取方式,可以是GENERIC_READ,表示需要读文件;可以是GENERIC_WRITE,表示需要写文件;也可以是GENERIC_READGENERIC_WRITE,表示既可以读也可以写。
dwShareMode:输入参数,共享模式。指明与其他进程是否共享该文件,可以是共享读(FILE_SHARE DELETE)、共享写(FILE_SHARE_WRITE)、共享删除(FILE_SHARE.READ),如果要指明多个属性,使用“位与~‘I”运算。如果指明上述参数,其他进程就可以对文件进行相关操作。如果本进程需要独占本文件,则将本参数设置为0。
lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构的指针,表示本文件句柄的安全属性,能影响其是否可被子进程继承等操作。如果设定为NULL,则子进程不可继承本句柄。SECURITY ATTRIBUTES结构不常用,对此数据结构的设置,涉及Windows系统中对权限管理的原理,在本章中不作详细说明。
dwCreationDisposition:输入参数,操作模式。

dwFlagsAndAttributes:输入参数,文件属性和文件标志

一般情况下文件属性较常用,而操作标志不常用,可以使用“1”运算符指定多个属性和标志。
hTemplateFile:输入参数,当存取权限包括GENERIC_WRITE时,可以设置为一个模板文件的句柄。一般情况下,可设置为NULL,表示不使用模板文件。
◇返回值
返回HANDLE数据类型值,表示文件的句柄,如果返回INVALID_HANDLE_VALUE,表示操作失败。


ReadFile

ReadFile动能是从文件中读出数据。需要使用CreateFile所返回的文件句柄。

◇参数
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时需要指明GENERIC_READ读取操作模式。
lpBuffer:输入参数,指向读取文件数据存储的内存缓冲区。
nNumberOfBytesToRead:输入参数,指明需要从文件中读出的数据的大小,不能大于lpBuffer内存块的大小,否则会造成溢出。
lpNumberOfBytesRead:输出参数,指向存储实际读出的数据大小的DWORD变量。在读文件时,可能由于已经到达文件尾等原因,其值可能小于rtNumberOfBytesToRead。如果lpOverlapped为NULL,则该参数不能为NULL。
lpOverlapped:输入参数,指向OVERLAPPED结构体的指针,如果调用CreateFile时设置了FILE_FLAG_OVERLAPPED标志,则需要使用该参数,否则可以为NULL。
◇返回值
返回BOOL值,表示读文件是否成功。
◇使用说明
在读的过程中,文件指针会随着读操作的进行而自动移动,在循环调用本函数时,会顺序读出文件的内容,如果程序返回失败,可以使用GetLastError函数获取错误信息。


WriteFile

WriteFile函数的功能是将数据写入到文件中,写入到文件指针所在的位置,写入操作完成后,文件指针会移动到写入的数据之后

◇参数
hFile:输入参数,写入数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_WRITE读取操作模式。
lpBuffer:输入参数,指向需写入文件数据存储的内存缓冲区。
nNumberOBytesToWrite:输入参数,指明需要写入文件中的数据的大小。
lpNumberOiBytesWritten:输出参数,指向存储真实写入的数据大小的变量,可能由于已经到达文件尾等原因,其值可能与nNumberOfBytesToWrite不同。如果lpOverlapped为NULL,则该参数不能为NULL。
lpOverlapped:输入参数,指向OVERLAPPED结构体的指针,如果调用CreateFile时设置了FILE_FLAG_OVERIAPPED标志,则需要使用该参数。可以为NULL。
◇返回值
返回BOOL值,表示写文件是否成功。
◇使用说明

在读的过程中,文件指针会随着写操作的进行而移动,在循环调用本函数时,会按顺序写入文件内容。如果程序返回失败,可以使用 GetLastError函数获取错误信息。


GetFileSize、GetFileSizeEX

GetFileSize、GetFileSizeEX的功能是一致的,都是获取文件大小

◇参数
GetFileSize的参数如下。
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时需要指明GENERIC_READ读取操作模式或者GENERIC_WRITE写入操作模式。
lpFileSizeHigh:输出参数,表示得到的文件大小的高32位。该参数可以为NULL,为NULL时表示文件大小可以用DWORD表示。具体解释请参考返回值部分。
GetFileSizeEx的参数如下。
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_READ读取操作模式或者GENERIC_WRITE写入操作模式。
lpFileSize:输出参数,指向储存文件大小的一个LARGE_ INTEGER联合体,前面已经介绍过了LARGE.1NTEGER结构的相关内容,参见API 9 小节关键数据结构部分。
◇返回值
GetFileSize成功时,返回值为一个表示文件大小DWORD值。
GetFileSize失败时,如果lpFileSizeHigh为NULL,返回INVALID_ FILE_SIZE并可以调用GetLastError函数获取更详细的错误信息;如果lpFileSizeHigh为非NULL,那么同样返回INVALID_FILE_SIZE并且尝试读取lpFileSizeHigh指向的值放在高32位,并将其和返回的低32位DWORD连起来成为一个64位的值来表示文件大小。lpFileSizeHigh为合法值则GetLastError返回NO_ERROR,表示该API仍然成功;否则,GetLastError返回一个不等于NO_ERROR的值。

GetFileSizeEx直接返回BOOL值,表示读文件是否成功。

◇使用说明
实际上这两个函数的区别就在于,对文件大小超出DWORD的情况采取不同处理方式。由于历史原因,GetFileSize用两个32位值来分别储存64位文件大小的高位和地位;而GetFileSizeEx直接将文件大小储存在64位的联合体中。


完成创建、打开、读写文件

#include <Windows.h>
#include <stdio.h>

DWORD ReadFileContent(LPSTR szFilePath)
{
    //文件大小
    HANDLE hFileRead;
    //保存文件大小
    LARGE_INTEGER liFileSize;
    //成功读取的文件数据大小
    DWORD dwReadedSize;
    //累加计算已经读取的数据的大小
    LONGLONG liTotalRead=0;
    //文件数据缓存
    BYTE lpFileDataBuffer[32];

    //打开已经存在的文件,读取内容
    hFileRead = CreateFileA(szFilePath,//name
        GENERIC_READ,                   //以读方式打开
        FILE_SHARE_READ,               //可共享读
        NULL,                           //默认安全设置
        OPEN_EXISTING,                   //只打开已经存在的文件
        FILE_ATTRIBUTE_NORMAL,           //常规文件属性
        NULL);                           //无模板
    //打开文件是否成功
    if(hFileRead==INVALID_HANDLE_VALUE)
    {
        printf("打开文件失败: %d",GetLastError());
    }
    if(!GetFileSizeEx(hFileRead,&liFileSize))
    {
        printf("获取文件大小失败: %d",GetLastError());
    }
    else
    {
        printf("文件大小为: %d\n",liFileSize.QuadPart);
    }
    //循环读取并打印内容
    while(TRUE)
    {
        DWORD i;
        if(!ReadFile(hFileRead,//读取文件句柄
            lpFileDataBuffer,  //存储读取文件内容
            32,                   //读取的大小(字节)
            &dwReadedSize,       //实际读取的大小
            NULL))               //不使用Overlapped
        {
            printf("读取文件错误: %d\n",GetLastError());
            break;
        }
        printf("读取了%d字节,文件内容是: ",dwReadedSize);

        for(i=0;i<dwReadedSize;i++)
        {
            printf("0x%x ",lpFileDataBuffer[i]);
        }
        printf("\n");
        liTotalRead+=dwReadedSize;
        if(liTotalRead==liFileSize.QuadPart)
        {
            printf("读取文件结束\n");
            break;
        }
    }
    CloseHandle(hFileRead);
    return 0;
}

DWORD SaveDataToFile(
    LPSTR szFilePath,
    LPVOID lpData,
    DWORD dwDataSize)
{
    //文件句柄
    HANDLE hFileWrite;
    //成功写入的数据大小
    DWORD dwWritedDateSize;
    //打开已经存在的文件,读取内容
    hFileWrite=CreateFileA(szFilePath,//要打开的文件名
        GENERIC_WRITE,//以写方式打开开
        0,//可共享读
        NULL,//默认安全设置
        OPEN_ALWAYS,//打开已经存在的文件
        FILE_ATTRIBUTE_NORMAL,//常规属性打开
        NULL);//无模板
    //判断是否成功打开
    if(hFileWrite==INVALID_HANDLE_VALUE)
    {
        printf("打开文件失败: %d\n",GetLastError());
    }
    //设置文件指针到文件为
    SetFilePointer(hFileWrite,0,0,FILE_END);
    //将数据写入文件
    if(!WriteFile(hFileWrite,lpData,dwDataSize,&dwWritedDateSize,NULL))
    {
        printf("写文件失败: %d\n",GetLastError());
    }
    else
    {
        printf("写文件成功,写入%d字节。\n",dwWritedDateSize);
    }
    CloseHandle(hFileWrite);
    return 0;
}
int main()
{
    LPSTR szFileData="这是一个例子";
    SaveDataToFile("D:\\fanrong_s.txt",szFileData,lstrlenA(szFileData));
    ReadFileContent("D:\\fanrong_r.txt");
    return 0;
}