CopeFile:
1:在CopyFile的时候,如果使用了多线程进行Copy,可能每个线程负责复制的地方不一样,而新文件是属于自动维护增长(写入后,游标就会自动后移),在自动设置的时候,可能每个进程写的不同位置,就可能导致整个文件的顺序变乱。在Copy的时候,必须将其大小限定好。
2:PostQueuedCompletionStatus(hIOCP, 0, IOCP_KEY_WRITE, &oWrite);
BOOL WINAPI PostQueuedCompletionStatus(
_In_ HANDLE CompletionPort, //指定向那个完成端口发送数据包(指定完成端口的句柄)
_In_ DWORD dwNumberOfBytesTransferred,//发送给Get的信息,可自己指定参数的意义,例如,发送0代表结束,Get解析到0的时候就执行结束。
_In_ ULONG_PTR dwCompletionKey, //完成端口绑定设备的时候,传入了Key,与这个参数对应,代表绑定的设备,Get到的时候,就对对应设备操作
_In_opt_ LPOVERLAPPED lpOverlapped
);
该函数可以向指定完成端口的每一个线程发送一个数据包,每个线程会对GetQueuedCompletionStatus的返回值进行检查,如果发现应用程序正在终止,那么它们就可以进行清理工作并正常地退出。
完成端口是一个异步IO模型,每一次完成之后有提醒(每一次事件完成之后,他会将完成的对象放在完成队列的端口里面去)。
Post的这个信息是一个已经完成的消息(已经放在完成队列端口的消息),完成端口是异步的,他每一步都有几部分,在不同线程中完成。
这就是异步IO的意义,每一个操作使用post,get到消息,看其完成状态是怎样的,根据完成状态判断下一步做什么事情。
3:GetQueuedCompletionStatus(hIOCP, &dwByteTrans, &ulKey, &lpoverlapped, INFINITE);
BOOL WINAPI GetQueuedCompletionStatus(
_In_ HANDLE CompletionPort, //指定的IOCP
_Out_ LPDWORD lpNumberOfBytes, //一次完成后的I/O操作所传送数据的字节数。
_Out_ PULONG_PTR lpCompletionKey, //与绑定设备的key,判断哪一个事件完成
_Out_ LPOVERLAPPED *lpOverlapped, //为调用IOCP机制所引用的OVERLAPPED结构。
_In_ DWORD dwMilliseconds //用于指定调用者等待CP的时间。
);
获取来自Post发过来的信息,得到完成状态,根据完成状态判断下一步该怎么做。执行成功返回非0,失败返回0。
4:完成端口实现文件复制:
#include <windows.h>
#include <iostream>
#include <iomanip>
#define IOCP_KEY_READ 1
#define IOCP_KEY_WRITE 2
void console_Pos_XY(int x, int y)
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x, y };
SetConsoleCursorPosition(hOut, pos);
}
int main()
{
LPCTSTR lpstrSrcFilePath = TEXT("D:\\软件\\Adobe Photoshop CS6 Extended_setup.exe");
LPCTSTR lpstrDestFilePath = TEXT("D:\\软件\\Adobe Photoshop CS6 Extended_setup1.exe");
BOOL bOk = FALSE;
do
{
HANDLE hSrcFile = CreateFile(lpstrSrcFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);//不做缓存,异步
if (INVALID_HANDLE_VALUE == hSrcFile)
break;
HANDLE hDestFile = CreateFile(lpstrDestFilePath, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
if (INVALID_HANDLE_VALUE == hDestFile)
break;
LARGE_INTEGER liFileSize;
if (!GetFileSizeEx(hSrcFile, &liFileSize))
break;
if (!SetFilePointerEx(hDestFile, liFileSize, nullptr, FILE_BEGIN))//从begin向后移到文件最后
break;
if (!SetEndOfFile(hDestFile))//设置这个位置为文件尾
break;
DWORD dwBytePerSecTro = 0;
if(!GetDiskFreeSpace(TEXT("D:"), nullptr, &dwBytePerSecTro, nullptr, nullptr))//获取磁盘扇区大小
break;//在复制文件以FILE_FLAG_NO_BUFFERING的时候就是不经过内存,直接作用于磁盘,需要按扇区大小来复制。
SYSTEM_INFO sysInfo = { 0 };
GetSystemInfo(&sysInfo);//动态地获取当前的系统的信息
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, sysInfo.dwNumberOfProcessors);//系统处理器数目
if (NULL == hIOCP)//上面函数不成功返回的是NULL,并不是INVALID_HANDLE_VALUE。
{
DWORD dwError = GetLastError();
if (dwError != ERROR_ALIAS_EXISTS)//如果等于他,还是创建成功(已存在)
break;
}
hIOCP = CreateIoCompletionPort(hSrcFile, hIOCP, IOCP_KEY_READ, sysInfo.dwNumberOfProcessors);
hIOCP = CreateIoCompletionPort(hDestFile, hIOCP, IOCP_KEY_WRITE, sysInfo.dwNumberOfProcessors);
OVERLAPPED oRead = { 0 }, oWrite = { 1 };
PostQueuedCompletionStatus(hIOCP, 0, IOCP_KEY_WRITE, &oWrite);//插入项,最开始默认为写完成,一进去就开始读
DWORD dwByteTrans = 0;
ULONG_PTR ulKey = 0;
LPOVERLAPPED lpoverlapped = nullptr;
SIZE_T sizeLen = dwBytePerSecTro * 1024;//512Kb
LPVOID lpAddr = VirtualAlloc(nullptr, sizeLen, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
LARGE_INTEGER liReadedLen = { 0 };//用于保存读取了多少
SIZE_T percent = 0;//百分比变化了才显示
while (TRUE)
{
BOOL bRet = GetQueuedCompletionStatus(hIOCP, &dwByteTrans, &ulKey, &lpoverlapped, INFINITE);//接收到的参数来自Post
if (bRet == FALSE)
{
if (lpoverlapped == NULL)//超时,失败
break;
else
continue;
}
switch (ulKey)
{
case IOCP_KEY_READ://读操作完成
{
//写操作
WriteFile(hDestFile, lpAddr, sizeLen, nullptr, &oWrite);
DWORD dwError = GetLastError();
if (ERROR_IO_PENDING != dwError)
{
std::cout << "WriteFile出错,错误代码:" << dwError << std::endl;
}
LARGE_INTEGER liReadLen;
liReadLen.QuadPart = dwByteTrans;
oRead.Offset += liReadLen.LowPart;
oRead.OffsetHigh += liReadLen.HighPart;
//记录
liReadedLen.LowPart = oRead.Offset;//如果下次读取的长度小于512Kb,那么这个值目前为小于文件长度
liReadedLen.HighPart = oRead.OffsetHigh;
//overlapoped结构体要更新
}
break;
case IOCP_KEY_WRITE:
{
//更新offset,
LARGE_INTEGER liWriteLen;
liWriteLen.QuadPart = dwByteTrans;
oWrite.Offset += liWriteLen.LowPart;
oWrite.OffsetHigh += liWriteLen.HighPart;
//ReadFile,判断是否超过文件长度
if (sizeLen > liFileSize.QuadPart - liReadedLen.QuadPart)
{
SIZE_T i = (SIZE_T)((liFileSize.QuadPart - liReadedLen.QuadPart) / dwBytePerSecTro);
if (0 == ((liFileSize.QuadPart - liReadedLen.QuadPart) % dwBytePerSecTro))
sizeLen = i*dwBytePerSecTro;
else
sizeLen = (i + 1)*dwBytePerSecTro;
}
ReadFile(hSrcFile, lpAddr, sizeLen, nullptr, &oRead);
}
break;
}
if (liReadedLen.QuadPart >= liFileSize.QuadPart)
{
SetEndOfFile(hDestFile);
break;//文件复制完成
}
if (percent != (liReadedLen.QuadPart / (liFileSize.QuadPart / 100)))
{
percent = liReadedLen.QuadPart / (liFileSize.QuadPart / 100);
console_Pos_XY(0, 0);
std::cout << "文件大小:" << liFileSize.QuadPart / 1024 / 1024 << "Mb" << std::endl
<< "已复制大小:" << liReadedLen.QuadPart / 1024 / 1024 << "Mb" << std::endl;
std::cout << "完成进度:" << std::setprecision(4) << percent << "%";
}
}
CloseHandle(hSrcFile);
CloseHandle(hDestFile);
bOk = TRUE;
} while (FALSE);
if (!bOk)
{
DWORD dwError = GetLastError();
std::cout << "Error" << dwError << std::endl;
}
return 0;
}