Windows同步IO操作
封装File类
我们在Windows下进行编程, 打开文件就要用到 CreateFile API
在进行 CreateFile 操作的时候, 属性 GENERIC_ALL 代表所有权限, 慎用
一般我们都使用 GENERIC_READ | GENERIC_WRITE
我们在执行 CreateFile 之后, 需要检测是否成功
如果成功了, 需要进行 CloseHandle 操作.
我们应该将这样的操作封装为一个类.
为了能够多适配, 我们使用 TCHAR 这个"伟大"的类型.
// HadesFileOp.h
#pragma once
#include <Windows.h>
#include <tchar.h>
#include "WindowsException.h"
class CHadesFileOp
{
public:
CHadesFileOp(LPCTSTR szFilePath = TEXT(""));
~CHadesFileOp();
BOOL OpenFile(_In_ DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE,
_In_ DWORD dwShareMode = FILE_SHARE_READ,
_In_ DWORD dwCreationDisposition = OPEN_ALWAYS,
_In_ DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL,
_In_opt_ HANDLE hTemplateFile = NULL);
VOID SetPath(LPCTSTR szFilePath);
LPCTSTR GetPath() CONST;
VOID CloseFile();
LONGLONG GetFileSize();
LONGLONG GetCompressedFileSize();
private:
HANDLE m_hFile;
PTCHAR m_szFilePath;
};
// HadesFileOp.cpp
#include "HadesFileOp.h"
CHadesFileOp::CHadesFileOp(LPCTSTR szFilePath)
: m_hFile(INVALID_HANDLE_VALUE), m_szFilePath(NULL)
{
SetPath(szFilePath);
}
CHadesFileOp::~CHadesFileOp()
{
CloseFile();
}
BOOL CHadesFileOp::OpenFile(_In_ DWORD dwDesiredAccess /*= GENERIC_READ | GENERIC_WRITE*/,
_In_ DWORD dwShareMode /*= FILE_SHARE_READ*/,
_In_ DWORD dwCreationDisposition /*= OPEN_ALWAYS*/,
_In_ DWORD dwFlagsAndAttributes /*= FILE_ATTRIBUTE_NORMAL*/,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes /*= NULL*/,
_In_opt_ HANDLE hTemplateFile /*= NULL*/)
{
BOOL bRet = FALSE;
do
{
if (INVALID_HANDLE_VALUE != m_hFile)
{
SetLastError(ERROR_FILE_IS_OPEN);
break;
}
m_hFile = CreateFile(m_szFilePath, dwDesiredAccess,
dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
if (INVALID_HANDLE_VALUE == m_hFile)
{
break;
}
bRet = TRUE;
} while (FALSE);
if (!bRet)
throw CWindowsException(GetLastError());
return bRet;
}
VOID CHadesFileOp::SetPath(LPCTSTR szFilePath)
{
if (m_szFilePath)
delete[] m_szFilePath;
size_t nStrLen = _tcslen(szFilePath) + sizeof(TCHAR);
m_szFilePath = new TCHAR[nStrLen];
_tcscpy_s(m_szFilePath, nStrLen, szFilePath);
}
LPCTSTR CHadesFileOp::GetPath() const
{
return m_szFilePath;
}
VOID CHadesFileOp::CloseFile()
{
if (INVALID_HANDLE_VALUE != m_hFile)
CloseHandle(m_hFile);
}
LONGLONG CHadesFileOp::GetFileSize()
{
LONGLONG llRet = 0;
if (m_hFile == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_FILE_IS_NOT_OPEN);
throw CWindowsException(GetLastError());
}
else
{
LARGE_INTEGER largeFileSize = { 0 };
GetFileSizeEx(m_hFile, &largeFileSize);
llRet = largeFileSize.QuadPart;
}
return llRet;
}
LONGLONG CHadesFileOp::GetCompressedFileSize()
{
LONGLONG llRet = 0;
if (m_hFile == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_FILE_IS_NOT_OPEN);
throw CWindowsException(GetLastError());
}
else
{
LARGE_INTEGER largeFileSize = { 0 };
GetFileSizeEx(m_hFile, &largeFileSize);
largeFileSize.LowPart = ::GetCompressedFileSize(m_szFilePath, (LPDWORD)(&(largeFileSize.HighPart)));
llRet = largeFileSize.QuadPart;
}
return llRet;
}Windows体系自定义异常
由于Windows的API众多, 错误返回不仅仅靠返回值, 还需要靠GetLastError
下面我们使用C++思想封装一个类, 来进行我们自己的Windows异常体系.
class CWindowsException
{
public:
CWindowsException(DWORD dwErrCode): m_dwErrCode(dwErrCode), m_szErrMsg(NULL)
{
InitWindowsException(dwErrCode);
}
~CWindowsException()
{
LocalFree(m_szErrMsg);
}
CWindowsException(CONST CWindowsException& ex)
{
InitWindowsException(ex.m_dwErrCode);
}
CWindowsException& operator=(CONST CWindowsException& ex)
{
if (&ex != this)
{
InitWindowsException(ex.m_dwErrCode);
}
return *this;
}
LPCTSTR what() CONST
{
return m_szErrMsg;
}
private:
DWORD m_dwErrCode;
LPTSTR m_szErrMsg;
VOID InitWindowsException(DWORD dwErrCode)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
dwErrCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPTSTR>(&m_szErrMsg), 0, NULL);
}
};取文件大小
在Windows系统中, 文件大小分为以下两种:
物理大小
- GetFileSize 已弃用, 因为对于小文件还可以, 但是大文件会超过输出参数的范围
- GetFileSizeEx
占用大小
- GetCompressedFileSize
同步IO操作
同步IO操作 不能 设置 FILE_FLAG_OVERLAPPED 标志.
IO操作分为两种
读
ReadFile
- 参数1: 设备句柄
- 参数2: 被读入的内存地址
- 参数3: 想要读取的大小(多少个BYTE)
- 参数4:
输出参数, 已经读取的BYTE数,如果为0, 需要进行 GetLastError() - 参数5: 同步状态下设置为
NULL - 返回值:
成功返回非0 - 同步状态下, 如果返回值为非零, 读取的字节数为零, 则表示已经读到了文件尾.
写
WriteFile
- 参数1: 设备句柄
- 参数2: 被写入的内存地址
- 参数3: 写入的BYTE个数
- 参数4:
输出参数, 已经被写入的BYTE数. - 参数5: 同步状态下设置为
NULL - 返回值:
成功返回非0
异步IO操作
同步IO操作 需要 设置 FILE_FLAG_OVERLAPPED 标志.
IO操作分为两种
读
ReadFile
- 参数1: 设备句柄
- 参数2: 被读入的内存地址
- 参数3: 想要读取的大小(多少个BYTE)
- 参数4:
输出参数, 已经读取的BYTE数,如果为0, 需要进行 GetLastError() - 参数5: 一个指向
OVERLAPPED结构体的指针. 异步状态下,必须指向一个有效的 OVERLAPPED 结构体指针. - 返回值:
成功返回非0 - 异步状态下, 参数5(lpOverlapped)不为NULL时, 当读到文件结尾时, 返回值为FALSE, GetLastError为ERROR_HANDLE_EOF.
写
WriteFile
- 参数1: 设备句柄
- 参数2: 被写入的内存地址
- 参数3: 写入的BYTE个数
- 参数4:
输出参数, 已经被写入的BYTE数. - 参数5: 一个指向
OVERLAPPED结构体的指针. 异步状态下,必须指向一个有效的 OVERLAPPED 结构体指针. - 返回值:
成功返回非0
修改文件位置
可以使用 SetFilePointerEx API来进行文件读取位置的修改.
SetFilePointer已经被抛弃, 因为它不够长了
SetFilePointerEx
- 参数1: 设备句柄
- 参数2: 移动位置,
LARGE_INTEGER类型 - 参数3:
输出参数, 移动完成后所在的位置的指针, 不需要可传NULL - 参数4: 开始移动的位置, 可取值: FILE_BEGIN, FILE_CURRENT, FILE_END
打开文件后, 文件指针基本会在FILE_BEGIN
设置文件尾
SetEndOfFile
- 参数: 设备句柄
作用:
文件本身大小为 22, 但是我想设置文件大小为 1024, 就可以用这个函数了
// 举个例子
LARGE_INTEGER largeFileSize = {0};
largeFileSize.QuadPart = 1024;
SetFilePointerEx(hFile, largeFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hFile);未完待续...
如有错误,请提出指正!谢谢.
本文由 花心胡萝卜 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2017-05-20 at 08:14 am