首先实现一个句柄自动关闭的类EnsureCloseFile
以下是头文件
#pragma once
#include <windows.h>
class EnsureCloseFile
{
public:
EnsureCloseFile(HANDLE hHandle);
~EnsureCloseFile();
EnsureCloseFile(const EnsureCloseFile&) = delete;
EnsureCloseFile& operator=(const EnsureCloseFile&) = delete;
BOOL IsInvalid();
HANDLE GetHandle();
private:
HANDLE m_hHandle;
};
EnsureCloseFile的实现:
#include "EnsureCloseFile.h"
EnsureCloseFile::EnsureCloseFile(HANDLE hHandle)
{
m_hHandle = hHandle;
}
EnsureCloseFile::~EnsureCloseFile()
{
CloseHandle(m_hHandle);
}
BOOL EnsureCloseFile::IsInvalid()
{
if (m_hHandle == INVALID_HANDLE_VALUE||m_hHandle==NULL)
return TRUE;
return FALSE;
}
HANDLE EnsureCloseFile::GetHandle()
{
return m_hHandle;
}
然后,在实现一个CIOReq类,该类的本质就是采用继承的关系将OVERLAPPED和其读取到的数据做一个联系,头文件如下:
#pragma once
#include <windows.h>
class CIOReq :
public OVERLAPPED
{
public:
CIOReq();
~CIOReq();
BOOL AllocBuffer(SIZE_T nBuffSize);
BOOL Read(HANDLE hDevice, PLARGE_INTEGER plioffset = NULL);
BOOL Write(HANDLE hDevice, PLARGE_INTEGER plioffset = NULL);
private:
SIZE_T m_nBuffSize;
PVOID m_pvData;
};
实现如下:
#include "CIOReq.h"
CIOReq::CIOReq()
{
Internal = InternalHigh = 0;
Offset = 0;
OffsetHigh = 0;
hEvent = NULL;
m_nBuffSize = 0;
m_pvData = NULL;
}
CIOReq::~CIOReq()
{
if (NULL != m_pvData)
VirtualFree(m_pvData, 0, MEM_RELEASE);
}
BOOL CIOReq::AllocBuffer(SIZE_T nBuffSize)
{
m_nBuffSize = nBuffSize;
m_pvData = VirtualAlloc(NULL, m_nBuffSize, MEM_COMMIT, PAGE_READWRITE);
return (m_pvData != NULL);
}
BOOL CIOReq::Read(HANDLE hDevice, PLARGE_INTEGER plioffset)
{
if (plioffset != NULL)
{
Offset = plioffset->LowPart;
OffsetHigh = plioffset->HighPart;
}
return ReadFile(hDevice, m_pvData, m_nBuffSize, NULL, this);
}
BOOL CIOReq::Write(HANDLE hDevice, PLARGE_INTEGER plioffset)
{
if (plioffset != NULL)
{
Offset = plioffset->LowPart;
OffsetHigh = plioffset->HighPart;
}
return WriteFile(hDevice, m_pvData, m_nBuffSize, NULL, this);
}
最后就是利用完成端口实现文件复制了,原理很简单,就是创建完成端口,然后发出4个IO请求,这些IO请求就是读取数据,如果读取完成了就发出写入IO请求,如此反复,直至文件复制完成,由于我只采用了一个主线程,因此这些IO请求之间不存在数据同步的问题。代码如下:
#include <iostream>
#include <string>
#include "CIOReq.h"
#include "EnsureCloseFile.h"
#define BUFFSIZE (64*1024)
#define CK_READ 1
#define CK_WRITE 2
#define MAX_PENDING_IO_REQS 4
template<class TV,class TM>
inline TV chROUNDDOWN(TV value, TM Multiple)
{
return ((value / Multiple) * Multiple);
}
template<class TV,class TM>
inline TV chROUNDUP(TV value, TM Multiple)
{
return (chROUNDDOWN(value, Multiple) + (((value % Multiple) > 0) ? Multiple : 0));
}
bool FileCopy(std::wstring srcFilePath, std::wstring dstFilePath)
{
bool fOk = false;
LARGE_INTEGER liFileSizeSrc = { 0 };
LARGE_INTEGER liFileSizeDst;
do
{
//创建文件句柄
EnsureCloseFile hFileSrc = CreateFile(srcFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
if (hFileSrc.IsInvalid())
break;
//获取文件大小
GetFileSizeEx(hFileSrc.GetHandle(), &liFileSizeSrc);
//使目标文件的大小保持为扇形大小的整数倍
liFileSizeDst.QuadPart = chROUNDUP(liFileSizeSrc.QuadPart,BUFFSIZE);
EnsureCloseFile hFileDst = CreateFile(dstFilePath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, hFileSrc.GetHandle());
if (hFileDst.IsInvalid())
break;
SetFilePointerEx(hFileDst.GetHandle(), liFileSizeDst, NULL, FILE_BEGIN);
//截断文件大小
SetEndOfFile(hFileDst.GetHandle());
//创建一个IO完成端口
HANDLE hIOCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
//将完成端口与设备关联
HANDLE hReadIOCompPort = CreateIoCompletionPort(hFileSrc.GetHandle(), hIOCompPort, CK_READ, 0);
if (hIOCompPort != hReadIOCompPort)
break;
HANDLE hWriteIoCompPort = CreateIoCompletionPort(hFileDst.GetHandle(), hIOCompPort, CK_WRITE, 0);
if (hIOCompPort != hWriteIoCompPort)
break;
CIOReq ior[MAX_PENDING_IO_REQS];
LARGE_INTEGER liNextReadOffset = { 0 };
int nReadsInProgress = 0;
int nWritesInProgress = 0;
for (int i = 0; i < MAX_PENDING_IO_REQS; i++)
{
ior[i].AllocBuffer(BUFFSIZE);
nWritesInProgress++;
//发送一个读取完成通知
PostQueuedCompletionStatus(hIOCompPort, 0, CK_WRITE, &ior[i]);
}
while ((nReadsInProgress > 0) || (nWritesInProgress > 0))
{
ULONG_PTR CompletionKey;
DWORD dwNumBytes;
CIOReq* pior;
//获取状态
GetQueuedCompletionStatus(hIOCompPort, &dwNumBytes, &CompletionKey, (OVERLAPPED**)&pior, INFINITE);
switch (CompletionKey)
{
case CK_READ:
{
nReadsInProgress--;
pior->Write(hFileDst.GetHandle());
nWritesInProgress++;
break;
}
case CK_WRITE:
{
nWritesInProgress--;
if (liNextReadOffset.QuadPart < liFileSizeDst.QuadPart)
{
pior->Read(hFileSrc.GetHandle(), &liNextReadOffset);
nReadsInProgress++;
liNextReadOffset.QuadPart += BUFFSIZE;
}
break;
}
}
}
fOk = true;
} while (0);
if (fOk)
{
EnsureCloseFile fHandle = CreateFile(dstFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (!fHandle.IsInvalid())
{
//重新设置文件大小
SetFilePointerEx(fHandle.GetHandle(), liFileSizeSrc, NULL, FILE_BEGIN);
SetEndOfFile(fHandle.GetHandle());
return true;
}
return false;
}
return false;
}
int main()
{
std::wstring src = L"N:\\1.zip";
std::wstring dst = L"N:\\2.zip";
FileCopy(src, dst);
std::cout << "Copy Completion!\n";
system("pause");
}
所有的代码我都贴出来了,我采用的编译器是V140,SDK8.1。可以直接运行。