C++ CreateFile,WriteFile,ReadFile,SetFilePointerEx,DeviceIOControl

本文介绍了C++中使用CreateFile创建或打开文件,ReadFile和WriteFile进行读写操作,以及如何通过SetFilePointerEx设置文件指针。详细讲解了各个函数的原型、作用及使用示例,并提到了DeviceIOControl接口在设备控制中的应用。

1. 文件指针

我们大概已经知道该如何创建或者打开一个文件,用CreateFile函数可以轻松做到,还不了解的小伙伴,请穿越到http://www.cnblogs.com/bigsponge/articles/4936599.html
CreateFile函数会使系统创建一个文件内核对象来管理对文件的操作。在这个内核对象内部有一个文件指针,这是我们今天的主角。
文件指针究竟是什么?
文件指针是一个64位的偏移量,指向文件起始位置的相对的位置,假设文件起始位置的地址为xxxx,文件指针为10,那么指针实际指向的地址是xxxx+10。
文件指针的作用是什么?
当我们要调用ReadFile或WriteFile函数对文件进行读和写操作的时候,读取和写入的起始位置永远在文件指针指向的位置。我们来看看ReadFile函数和WriteFile函数的原型:

ReadFile函数原型:

BOOL ReadFile(
        HANDLE    hFile,
        PVOID    pvBuffer,
        DWORD    nNumBytesToRead,
        PDWORD    pdwNumBytes,
        OVERLAPPED*    pOverlapped);

WriteFile函数原型:

BOOL WriteFile(
        HANDLE    hFile,
        CONST VOID    *pvBuffer,
        DWORD    nNumBytesToWrite,
        PDWORD    pdwNumBytes,
        OVERLAPPED*    pOverlapped);

hFile  标识要读或者写的文件的句柄
pvBuffer  pvBuffer指向一个缓存,调用ReadFile函数时,系统将读出的数据保存在pvBuffer指向的缓存中;调用WriteFile函数时,系统将pvBuffer指向的缓存中存储的数据写入文件
nNumBytesTo*  标识要从设备读出或者写入多少字节的数据
pdwNumBytes  指向一个DWORD变量,函数会将成功从设备读取的字节数和成功写入设备的字节数保存在这个参数中
pOverlapped  标识函数是用同步还是异步的方式完成
可以看到,不管是读还是写,函数中都没有参数用来存放在文件哪个位置读或者写,这个存放位置永远保存在文件指针中。当然,我们可以通过SetFilePointerEx函数来设置文件指针的位置。
当调用CreateFile函数创建文件内核函数时,文件指针被初始化为0,即指向文件起始位置,此时若调用ReadFile函数读取10个字节的数据,文件指针会+10,也就是指向。。。

SetFilePointerEx()

设置文件指针的位置:
函数原型:

BOOL SetFilePointerEx(
        HANDLE    hFile,
        LARGE_INTEGER    liDistanceToMove,
        PLARGE_INTEGER    pliNewFilePointer,
        DWORD    dwMoveMethod);

hFile  我们想要修改哪个文件内核对象的文件指针 liDistanceToMove  标识文件指针要移动多少个字节
pliNewFilePointer  系统会在pliNewFilePointer参数指向的LARGE_INTEGER结构体中保存文件指针的新值
dwMoveMethod  标识移动文件指针的方式,有三个值可以选择:FILE_BEGIN 标识文件指针的起始位置为文件首,
可以传给SetFilePointerEx的dwMoveMethod参数的值 值 含义
FILE_BEGIN 文件指针起始位置为文件起始位置,当调用SetFilePointerEx成功后,文件指针将指向liDistanceToMove指向的位置。
FILE_CURRENT 文件指针起始位置为当前文件指针指向的位置 FILE_END 文件指针起始位置为文件末尾

用SetFilePointerEx()查询文件指针的位置:
Windows并没有提供一个可以查询文件指针的GetFilePointerEx函数,但我们可以调用SetFilePointerEx()函数,将liDistanceToMove设为0来达到相同的效果,代码如下:

1 LARGE_INTEGER  liCurrentPosition;
2 liCurrentPosition.QuadPart = 0;
3 SetFilePointerEx(hFile, liCurrentPosition, &liCurrentPosition, FILE_CURRENT);

1. CreateFile

这个函数的功能是创建或者打开一个文件或者I/O设备,通常使用的I/O形式有文件、文件流、目录、物理磁盘、卷、终端流等。如执行成功,则返回文件句柄。 INVALID_HANDLE_VALUE 表示出错,会设置 GetLastError 。
函数的声明定义:

HANDLE WINAPI CreateFile(
  _In_      LPCTSTR lpFileName,              
  _In_      DWORD dwDesiredAccess,
  _In_      DWORD dwShareMode,
  _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  _In_      DWORD dwCreationDisposition,
  _In_      DWORD dwFlagsAndAttributes,
  _In_opt_  HANDLE hTemplateFile
);

简介:
函数作用:可以用来创建和打开磁盘文件以及其他设备。
返回值:若调用成功,返回句柄值;反之返回INVALID_HANDLE_VALUE(-1),而不是NULL!!
参数详解:
pszName 表示设备的类型,也表示该类设备的某个实例
dwDesiredAccess 指定我们想以何种方式来和设备进行数据传输。

                         可以传给CreateFile的dwDesiredAccess参数的通用标志
含义
0我们不希望从设备读取数据或向设备写入数据。如果只想改变设备的配置(比如只是修改文件的时间戳),那么可以传0
GENERIC_READ允许对设备进行只读访问
GENERIC_WRITE允许对设备进行只写访问。例如,备份软件会用到这个标志,如果想把数据发送到打印机,也可以使用这个标志。注意,GENERIC_WRITE标志并没有隐式地包含GENERIC_READ标志
GENERIC_READGENERIC_WRITE 允许对设备进行读写操作。由于这个标志允许我们和设备之间自由地交换数据,因此也最为常用

dwShareMode
用来指定设备共享特权。当我们仍然打开着一个设备的时候,该参数可以控制其他的CreateFile调用,能够以何种方式来打开设备。

可以传给CreateFile的dwShareMode参数的与I/O相关的标志

含义
0要求独占对设备的访问。如果设备已经打开,CreateFile调用会失败。如果我们成功地打开了设备,那么后续的CreateFile调用会失败
FILE_SHARE_READ如果有其他内核对象要使用该设备,我们要求它们不得修改设备的数据。如果设备已经以写入方式或独占方式打开,那么我们的CreateFile调用会失败。如果我们成功地打开了设备,那么后续的使用了
GENERIC_WRITE访问标志的CreateFile调用会失败
FILE_SHARE_WRITE如果有其他内核对象要使用该设备,我们要求它们不得读取设备的数据。如果设备已经以读取方式或独占方式打开,那么我们的CreateFile调用会失败。如果我们成功地打开了设备,那么后续的使用了GENERIC_READ访问标志的CreateFile调用会失败
FILE_SHARE_READ /FILE_SHARE_WRITE如果有其他内核对象要使用该设备,我们不关心它们会从设备读取数据还是会向设备写入数据。如果设备已经以独占方式打开,那么我们的CreateFile调用会失败。如果我们成功地打开了设备,那么后续的要求独占读取访问、独占写入访问或独占读写访问的CreateFile调用会失败
FILE_SHARE_DELETE当对文件进行操作的时候,我们不关心文件是否被逻辑删除或是被移动。在Windows内部,系统会先将文件标记为待删除,然后当该文件所有已打开的句柄都被关闭的时候,再将其真正的删除

psa
指向一个SECURITY_ATTRIBUTES结构,可以用来指定安全信息以及我们是否希望CreateFile返回的句柄能够被继承。通常,我们只需要传NULL给psa参数,这表示用默认的安全设定来创建文件,并且返回的句柄是不可继承的。

dwCreationDisposition

可以传给CreateFile的dwCreationDisposition参数的值

含义
CREATE_NEW告诉CreateFile创建一个新文件,如果同名文件已经存在,那么CreateFile会调用失败
CREATE_ALWAYS告诉CreateFile无论同名文件存在与否都创建一个新文件。如果同名文件已经存在,那么CreateFile会覆盖原来的文件
OPEN_EXISTING告诉CreateFile打开一个已有的文件或设备,如果文件或设备不存在,那么CreateFile会调用失败
OPEN_ALWAYS告诉CreateFile打开一个已有的文件,如果文件存在,那么CreateFile会直接打开文件,如果文件不存在,那么CreateFile会创建一个新文件
TRUNCATE_EXISTING告诉CreateFile打开一个已有的文件并将文件的大小截断为0字节,如果文件不存在,那么CreateFile会调用失败

注意:调用CreateFile来打开文件之外的其他设备时,必须将OPEN_EXISTING传给dwCreationDisposition参数

dwFlagsAndAttributes
参数有两个用途:其一,它允许我们设置一些标志来微调与设备之间的通信;其二,如果设备是一个文件,我们还能够设置文件的属性。这些通信标志中的大多数都是一些信号,用来告诉系统我们打算以何种方式来访问设备,这样系统就可以对缓存算法进行优化,来帮助我们提高应用程序的效率。

通信标志:

  1. CreateFile的高速缓存标志
标志含义
FILE_FLAG_NO_BUFFERING这个标志表示在访问文件的时候不要使用任何数据缓存。
FILE_FLAG_SEQUENTIAL_SCAN如果指定了这个标志,系统会认为我们将顺序地访问文件。当我们
FILE_FLAG_RANDOM_ACCESS这个标志告诉系统不要提前读取文件数据
FILE_FLAG_WRITE_THROUGH这个标志禁止对文件写入操作进行缓存以减少数据丢失的可能性。当我们对文件进行写入操作的时候,系统会将所有对文件的修改直接写入到磁盘中。
  1. CreateFile的其他标志
标志含义
FILE_FLAG_DELETE_ON_CLOSE在文件所有的句柄都关闭后,系统将删除该文件。这个标志通常和FILE_ATTRIBUTE_TEMPORARY
FILE_FLAG_BACKUP_SEMANTICS这个标志一般用于备份和恢复软件。。。。。
FILE_FLAG_POSIX_SEMANTICS
FILE_FLAG_OVERLAPPED告诉系统我们想要以异步的方式来访问设备
  1. 文件属性标志

可以传给CreateFile的dwFlagsAndAttributes参数的值

标志含义
FILE_ATTRIBUTE_ARCHIVE文件是一个存档文件。应用程序用这个标志来将文件标记为待备份或待删除。当CreateFile创建一个新文件的时候,会自动设置这个标志
FILE_ATTRIBUTE_ENCRYPTED文件是经过加密的
FILE_ATTRIBUTE_HIDDEN文件是隐藏的。它不会出现在通常的目录清单中
FILE_ATTRIBUTE_NORMAL文件没有其他属性。只有单独使用的时候,这个标志才有效
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED内容索引服务(content indexing service)不会对文件进行索引
FILE_ATTRIBUTE_OFFLINE文件虽然存在,但文件内容已经被转移到脱机存储器中。这个标志对层级存储系统比较有用
FILE_ATTRIBUTE_READONLY文件是只读的。应用程序可以读取文件,但不能写入文件或删除文件
FILE_ATTRIBUTE_SYSTEM文件是操作系统的一部分,或专供操作系统使用
FILE_ATTRIBUTE_TEMPORARY文件数据只会使用一小段时间。为了将访问时间降至最低,文件系统会尽量将文件数据保存在内存中,而不是保存在磁盘中

hFileTemplate
既可以是标识一个打开的文件的句柄,也可以是NULL。如果标识一个文件句柄,那么CreateFile会完全忽略dwFlagsAndAttributes参数,并转而使用hFileTemplate所标识的文件的属性。注意,hFileTemplate标识的文件必须是一个已经用GENERIC_READ标志打开的文件。如果CreateFile是打开已存在的文件(而不是创建新文件),那么它会忽略hFileTemplate参数。

常见错误:
当像pszName传入文档完整路径的时候,斜杠“\”必须用转义字符“\”代替。
例如:文档路径为D:\myfiles\ReadMe.txt
传入pszName参数的应为TEXT(“D:\myfiles\ReadMe.txt”)

2. ReadFile

从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,如果文件打开方式没有指明FILE_FLAG_OVERLAPPED的话,当程序调用成功时,它将实际读出文件的字节数保存到lpNumberOfBytesRead指明的地址空间中。FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作。
  函数声明定义:

BOOL WINAPI ReadFile(
  __in          HANDLE hFile,                   // 文件句柄
  __out         LPVOID lpBuffer,                // 接收数据用的 buffer
  __in          DWORD nNumberOfBytesToRead,     // 要读取的字节数
  __out         LPDWORD lpNumberOfBytesRead,    // 实际读取到的字节数
  __in          LPOVERLAPPED lpOverlapped       // OVERLAPPED 结构,一般设定为 NULL 
);

代码示例:

BOOL Read(char *filePath)
{
    HANDLE pFile;
    DWORD fileSize;
    char *buffer,*tmpBuf;
    DWORD dwBytesRead,dwBytesToRead,tmpLen;

    pFile = CreateFile(filePath,GENERIC_READ,          
        FILE_SHARE_READ,
        NULL,               
        OPEN_EXISTING,        //打开已存在的文件 
        FILE_ATTRIBUTE_NORMAL, 
        NULL);

    if ( pFile == INVALID_HANDLE_VALUE)
    {
        printf("open file error!\n");
        CloseHandle(pFile);
        return FALSE;
    }

    fileSize = GetFileSize(pFile,NULL);          //得到文件的大小

    buffer = (char *) malloc(fileSize);
    ZeroMemory(buffer,fileSize);
    dwBytesToRead = fileSize;
    dwBytesRead = 0;
    tmpBuf = buffer;

    do{                                       //循环读文件,确保读出完整的文件    

        ReadFile(pFile,tmpBuf,dwBytesToRead,&dwBytesRead,NULL);

        if (dwBytesRead == 0)
            break;

        dwBytesToRead -= dwBytesRead;
        tmpBuf += dwBytesRead;

        } while (dwBytesToRead > 0);

        //  TODO 处理读到的数据 buffer

    free(buffer);
    CloseHandle(pFile);

    return TRUE;
}

3. WriteFile

将数据写入一个文件。该函数比fwrite函数要灵活的多。也可将这个函数应用于对通信设备、管道、套接字以及邮槽的处理。返回时,TRUE(非零)表示成功,否则返回零。会设置GetLastError。
函数声明定义:

BOOL WINAPI WriteFile(
  __in          HANDLE hFile,                   // 文件句柄
  __in          LPCVOID lpBuffer,               // 要写入的数据
  __in          DWORD nNumberOfBytesToWrite,    // 要写入的字节数
  __out         LPDWORD lpNumberOfBytesWritten, // 实际写入的字节数
  __in          LPOVERLAPPED lpOverlapped       // OVERLAPPED 结构,一般设定为 NULL
);

代码示例:

BOOL Write(char *buffer, DWORD contentLen)
{
    HANDLE pFile;
    char *tmpBuf;
    DWORD dwBytesWrite,dwBytesToWrite;

    pFile = CreateFile(filePath,GENERIC_WRITE,          
        0,
        NULL,               
        CREATE_ALWAYS,        //总是创建文件
        FILE_ATTRIBUTE_NORMAL, 
        NULL);

    if ( pFile == INVALID_HANDLE_VALUE)
    {
        printf("create file error!\n");
        CloseHandle(pFile);
        return FALSE;
    }

    dwBytesToWrite = contentLen;
    dwBytesWrite = 0;

    tmpBuf = buffer;

    do{                                       //循环写文件,确保完整的文件被写入  

        WriteFile(pFile,tmpBuf,dwBytesToWrite,&dwBytesWrite,NULL);

        dwBytesToWrite -= dwBytesWrite;
        tmpBuf += dwBytesWrite;

        } while (dwBytesToWrite > 0);

    CloseHandle(pFile);

    return TRUE;
}

结合DeviceIOControl接口测试
DeviceIOControl

BOOL WINAPI DeviceIoControl(
  _In_        HANDLE       hDevice,
  _In_        DWORD        dwIoControlCode,
  _In_opt_    LPVOID       lpInBuffer,
  _In_        DWORD        nInBufferSize,
  _Out_opt_   LPVOID       lpOutBuffer,
  _In_        DWORD        nOutBufferSize,
  _Out_opt_   LPDWORD      lpBytesReturned,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);

若要检索设备的句柄,必须使用设备的名称或与设备关联的驱动程序的名称调用 CreateFile 函数。 若要指定设备名称,请使用以下格式:

\.\DeviceName

DeviceIoControl 可以接受特定设备的句柄。 例如,若要打开逻辑驱动器 A 的句柄:使用 CreateFile,请指定
\.\a:。 或者,可以使用名称 \.\PhysicalDrive0、.\PhysicalDrive1 等打开系统上物理驱动器的句柄。

调用 CreateFile 打开设备驱动程序的句柄时,应指定FILE_SHARE_READ和FILE_SHARE_WRITE访问标志。
但是,打开通信资源(如串行端口)时,必须指定独占访问权限。 打开设备句柄时,请使用其他 CreateFile 参数:

fdwCreate 参数必须指定OPEN_EXISTING。 hTemplateFile 参数必须为 NULL。
fdwAttrsAndFlags 参数可以指定FILE_FLAG_OVERLAPPED,以指示返回的句柄可用于重叠 (异步) I/O 操作。

HANDLE handle = CreateFileW(
		USER_PATH,
		(GENERIC_READ | GENERIC_WRITE),
		0,
		nullptr,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		nullptr);

	if (handle  == INVALID_HANDLE_VALUE)
	{
		CloseHandle(handle );
	}
	DWORD bytesWritten = 0;
	OVERLAPPED Overlapped = { 0 };
	Overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, 0);
	DWORD dic = DeviceIoControl(
		handle  ,
		user_path1,
		nullptr,
		0,
		nullptr,
		0,
		&bytesWritten,
		&Overlapped);
	BOOL waitResult = ::GetOverlappedResult(handle , &Overlapped, &bytesWritten, TRUE);
	::ResetEvent(Overlapped.hEvent);
	
	//写数据
	DWORD written_size = 0;
	unsigned char buffer_addr[2] = { 0x00, 0x01};
	::WriteFile(handle, buffer_addr, 2, &written_size,  &Overlapped);
	BOOL wait_result = ::GetOverlappedResult(handle,  &Overlapped, &written_size, TRUE);
	::ResetEvent(overlapped->hEvent);

	// 读数据
	DWORD read_size = 0;
	unsigned char edconfig_block_buffer[4] = { 0 };
	::ReadFile(handle, edconfig_block_buffer, 1, &read_size,  &Overlapped);
	wait_result = ::GetOverlappedResult(handle,  &Overlapped, &read_size, TRUE);
	::ResetEvent(overlapped->hEvent);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值