20170531Windows08_CopyFile

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;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值