CreateFile、WriteFile、ReadFile

来源:互联网 发布:腾讯办公软件 编辑:程序博客网 时间:2024/05/21 03:57

读写文件每一个软件开发显目必定涉及的工作。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文件可以看到如下结果:
这里写图片描述

我们先来看一下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;

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

这里写图片描述
我们通过偏移量轻松的定位了第一个像素的位置,此时每一个数据由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

0 0
原创粉丝点击