PoEdu培训 Windows班 第七课 Windows同步IO操作
文章类别: 培训笔记 0 评论

PoEdu培训 Windows班 第七课 Windows同步IO操作

文章类别: 培训笔记 0 评论

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系统中, 文件大小分为以下两种:

同步IO操作

同步IO操作 不能 设置 FILE_FLAG_OVERLAPPED 标志.
IO操作分为两种

异步IO操作

同步IO操作 需要 设置 FILE_FLAG_OVERLAPPED 标志.
IO操作分为两种

修改文件位置

可以使用 SetFilePointerEx API来进行文件读取位置的修改.
SetFilePointer已经被抛弃, 因为它不够长了

打开文件后, 文件指针基本会在FILE_BEGIN

设置文件尾

作用:
文件本身大小为 22, 但是我想设置文件大小为 1024, 就可以用这个函数了

// 举个例子
LARGE_INTEGER largeFileSize = {0};
largeFileSize.QuadPart = 1024;
SetFilePointerEx(hFile, largeFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hFile);

未完待续...

如有错误,请提出指正!谢谢.

回复