CreateFile、WriteFile、ReadFile

读写文件每一个软件开发显目必定涉及的工作。CreateFile函数用于创建对应的文件句柄,WriteFile函数是用来写数据到文件,ReadFile函数是从文件里读取数据出来。

CreateFile
该函数用于生成设备(文件)的对应句柄(HANDLE)。

//CreateFile函数声明
HANDLE CreateFile(
LPCTSTP lpFileName,              //文件名
DWORD   dwDesiredAccess,         //访问方式
DWORD   dwShareMode,             //共享模式
LPSECURITY_ATTRIBUTES  lpSecurityAttributes,   
//一个指向安全结构的指针,一般设置为NULL即可
DWORD   dwCreationDisposition,    //创建方式 
DWORD   dwFlagAndAttributes,      //属性
HANDLE  hTemplateFile             //复制文件句柄

下面是具体参数的相关说明

●dwDesiredAccess 访问方式
GENERIC_READ 表示允许对设备(文件)进行读访问
GENERIC_WRITE 表示允许对设备(文件)进行写操作
NULL 表示仅允许获取一个与设备(文件)有关的信息

●dwShareMode 共享模式
NULL 表示不共享
FILE_SHARE_READ 表示允许设备(文件)进行共享读访问
FILE_SHARE_WRITE 表示允许设备(文件)进行共享写访问

●lpSecurityAttributes 安全指针
指向一个 LPSECURITY_ATTRIBUTES 结构的指针,该结构定义了设备(文件)的安全特性,一般情况取NULL。

●dwCreationDisposition 创建方式
CREATE_NEW 创建文件,若文件存在则会出错
CREATE_ALWAYS 创建文件,会改写前一个文件(常用)
OPEN_EXISTING 文件必须已经存在,由设备提出要求
OPEN_ALWAYS 如果文件不存在则创建它
TRUNCATE_EXISTING 将现有文件缩短为零长度

●dwFlagAndAttributes 属性
FILE_ATTRIBUTE_ARCHIVE 将文件标记为归档属性
FLIE_ATTRIBUTE_COMPRESSED 将文件标记为已压缩,或者标记为文件在目录中的默认方式
FILE_ATTRIBUTE_NORMAL 默认属性
FIEL_ATTRIBUTE_HIDDEN 隐藏文件或目录
FIEL_ATTRIBUTE_READONLY 文件为只读文件
FIEL_ATTRIBUTE_SYSTEM 文件为只读文件
FILE_FLAG_WAITE_THROUGH 操作系统不得推迟对文件的写操作
FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作
FILE_FLAG_NO_BUFFERING 禁止对文件进行缓存处理,只能写入磁盘的扇区块
FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件进行优化
FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件进行缓冲优化
FILE_FLAG_DELETE_ON_CLOSE 关闭句柄后将文件删除,适用于临时文件

ReadFile
ReadFile函数将文件数据读取到一个缓冲区中。

//ReadFile 函数声明
ReadFile(
     HANDLE hFile,                  //句柄
     LPVOID lpBuffer,               //缓冲区指针
     DWORD nNumberOfBytesToRead,    //读出的字节数
     LPDWORD lpNumberOfBytesRead,   //用于保存实际读出的字节数的存储区域,用于判断是否读取成功
     LPOVERLAPPED lpOverlapped      //OVERAPPED结构体指针,一般取NULL
    );

WriteFile
WriteFile函数将数据写入到一个文件中,该函数比fwrite更加灵活、方便、

//WriteFile 函数声明
WriteFile(
     HANDLE hFile,                  //句柄
     LPVOID lpBuffer,               //数据缓冲区指针
     DWORD nNumberOfBytesToRead,    //写入的字节数
     LPDWORD lpNumberOfBytesRead,   //用于保存实际写入的字节数的存储区域,用于判断是否读取成功
     LPOVERLAPPED lpOverlapped      //OVERAPPED结构体指针,一般取NULL
    );

下面一起看一个借助这些WINAPI接口完成的有趣的示例,(该示例来自网易云课堂择善教育)

将文件隐藏于一个BMP文件中

首先先简单介绍一下BMP文件格式,是Windows操作系统中的标准图像文件格式
我们使用UItraEdit打开一个BMP文件可以看到如下结果:
CreateFile、WriteFile、ReadFile_第1张图片

我们先来看一下BMP文件首部的结构

typedef struct tagBITMAPFILEHEADER
{
    WORD bfType;//位图文件的类型,必须为BM(1-2字节)
    DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
    WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
    WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
    DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
    //文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER;

这里最重要的就是需要获得位图的起始位置的偏移量,这样才能在不破坏原文件格式的情况下,将文件悄悄的写入。
CreateFile、WriteFile、ReadFile_第2张图片
对于每一个位图文件,第一个像素的偏移地址都是固定的,在上图最后一个红色方框处,00000036对应的就是首像素偏移字节量。

CreateFile、WriteFile、ReadFile_第3张图片
我们通过偏移量轻松的定位了第一个像素的位置,此时每一个数据由6个字节组成,反正就是后三个改了影响不大,很难看出来,所以可以藏数据(我想最强大脑那些人一定可以看出来吧)

不管了 ,下面一起看看代码,主要是文件读写操作。

// HideInBMP.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h> //winapi的头文件
#include <iostream>
#include <cstring>
using namespace std;

char * GetFileContent(char *filename, DWORD *filesize)
{
//用于创建句柄,并开辟缓冲区,将文件内容读入
    HANDLE hfile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
    //文件句柄
    //GENERIC_READ | GENERIC_WRITE 表示允许对文件进行读写操作
    // FILE_SHARE_WRITE | FILE_SHARE_READ 表示允许对文件进行共享读写操作
    //OPEN_EXISTING 表示文件必须已经存在
    if (hfile==INVALID_HANDLE_VALUE)
    {
        cout << "Can't open " << filename << endl;
        return NULL;
    }
    DWORD dwRead;  
    DWORD dwSize = GetFileSize(hfile, &dwRead); //读取文件大小
    *filesize = dwSize;
    char * cBuf = new char[dwSize];          //在堆上开辟缓冲区,等待读入文件数据
    RtlZeroMemory(cBuf, sizeof(cBuf));       //用0填充缓冲区
    ReadFile(hfile, cBuf, dwSize, &dwRead, 0);  
    //读入数据到缓冲区
    if (dwRead != dwSize)     //判断是否读入正常
    {
        cout<<"Read"<<filename<<" failed\n"<<endl;
        return NULL;
    }
    CloseHandle(hfile);
    return cBuf;              //返回指向缓冲区的指针
}

bool SaveFile(char * buf, int len, char * filename)
{
//用于向BMP文件中写数据
    HANDLE hfile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    //创建句柄
    //允许读写操作
    //允许共享读写操作
    //若文件不存在则创建它,当我们要从BMP中解析出txt文件时是不存在的 ,(不过我感觉这里用CREATE_ALWAYS会不会也可以)
    //默认属性
    if (hfile == INVALID_HANDLE_VALUE)
    {
        cout << "Can't open" << filename << endl;
        return false;
    }
    SetFilePointer(hfile, 0, 0, FILE_BEGIN);  //将句柄指到文件首
    DWORD dwWritten;     //保存写了多少字节到文件中
    WriteFile(hfile, buf, len, &dwWritten, 0);    
    //将数据写入文件 
    CloseHandle(hfile);
    return   true;
}
bool Hide(char *secretFileName,char *bmpFileName)
{
    DWORD dwBMPSize, dwSecretSize;
    char * lpBMP = GetFileContent(bmpFileName, &dwBMPSize);   //存储原始的文件名
    char * lpSecret = GetFileContent(secretFileName, &dwSecretSize);
    DWORD * lpFirstPoint = (DWORD *)(lpBMP + 10);
    //读取第一个像素的偏移字节
    char *  lpCurrentBMP = lpBMP + *lpFirstPoint + 3; 
    将指针指向作用较小的后三个字节 

    char * lpCurrentSecret = lpSecret;
    //第一个像素点保存Secret文件大小
    *((DWORD*)lpCurrentBMP) = dwSecretSize;
    //保存写入的TXT文件大小,解析时使用
    lpCurrentBMP += 6;   //每个像素占6个字节
    for (; lpCurrentBMP<(lpBMP + dwBMPSize) && lpCurrentSecret <(lpSecret + dwSecretSize); lpCurrentBMP += 6)   
        //不能大于bmp的大小
        //循环将整个TXT文件依次写入缓冲区
    {
        if (dwSecretSize>2)
        {
            *lpCurrentBMP = *lpCurrentSecret;
            *(lpCurrentBMP + 1) = *(lpCurrentSecret + 1);
            *(lpCurrentBMP + 2) = *(lpCurrentSecret + 2);
            lpCurrentSecret += 3;
            dwSecretSize -= 3;
        }
        else if(dwSecretSize==2)
        {
            *lpCurrentBMP = *lpCurrentSecret;
            *(lpCurrentBMP + 1) = *(lpCurrentSecret + 1);
            break;
        }
        else if (dwSecretSize==1)
        {
            *lpCurrentBMP = *lpCurrentSecret;
            break;
        }
        else
        {
            break;
        }

    }  
    SaveFile(lpBMP, dwBMPSize, bmpFileName);
    //将写入了txt数据的缓冲区,写入bmp图片
    delete[] lpBMP;
    delete[] lpSecret;
    return true;
}
bool Recovery(char *bmpFileName,char *secretFileName)
{
//将保存了的TXT信息读出到缓冲区,操作与写入类似
    DWORD dwBMPSize;
    char * lpBMP = GetFileContent(bmpFileName, &dwBMPSize);
    DWORD * lpFirstPoint = (DWORD *)(lpBMP + 10);
    cout << "First point offset : " << *lpFirstPoint << endl;
    DWORD dwSecretSize = *(DWORD *)(lpBMP + *lpFirstPoint + 3);
    //读取存储的TXT的文件大小
    cout << dwSecretSize << endl;
    cout << "Secret file size : " << dwSecretSize << endl;
    char * SecretBuf = new char[dwSecretSize];

    char *  lpCurrentBMP = lpBMP + *lpFirstPoint + 3 + 6;
    for (int i = 0; lpCurrentBMP<(lpBMP + dwBMPSize) && i<dwSecretSize; lpCurrentBMP += 6)
    {
        SecretBuf[i] = *lpCurrentBMP;
        SecretBuf[i + 1] = *(lpCurrentBMP + 1);
        SecretBuf[i + 2] = *(lpCurrentBMP + 2);
        i += 3;
    }
    SaveFile(SecretBuf, dwSecretSize, secretFileName);
    delete[] SecretBuf;
    delete[] lpBMP;
    return true;
}

int main(int argc, char *argv[])
{

    if (argc<3)
    {
        cout << "Usage " << argv[0] << " Encrypt secret_file_name BMP_file_name"<<endl;
        cout << "Usage " << argv[0] << " Decrypt secret_file_name BMP_file_name" << endl;
        return 0;
    }
    if (strcmp(argv[1], "Encrypt") == 0)
    {
        Hide(argv[2], argv[3]);
    }
    else if (strcmp(argv[1],"Decrypt")==0)
    {
        Recovery(argv[3], argv[2]);
    }
    else
    {
        cout << argc << endl;
        cout << argv[1] << endl;
        cout << "Invalid para" << endl;
    }
    cout << "Done!" << endl;
    return 0;
}

整个程序简单有趣,但将WINAPI文件读写的强大功能体现的淋漓尽致,相应的Win32控制台程序戳这里
http://download.csdn.net/detail/avalon_y/9532526

你可能感兴趣的:(软件开发,winapi)