#include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <iostream>
using namespace std;

//完成键
#define CK_READ  1
#define CK_WRITE 2

void ShowErrMsg(LPCSTR lpMsg);

//传给线程函数的参数
typedef struct _tagThreadParam
{
    HANDLE hIOCP;                //IOCP
    LPVOID lpAddr;                //读入的内存地址
    LARGE_INTEGER liFileSize;   //源文件大小
    size_t nDataBlockSize;      //每次读写的数据块大小
    HANDLE hSrc;                //源文件
    HANDLE hDest;                //目的文件
    LPOVERLAPPED lpOLPSrc;      //源文件的OVERLAPPED结构指针
    LPOVERLAPPED lpOLPDest;     //目的文件的OVERLAPPED结构指针
}ThreadParam, *LPTHREADPARAM;

int _tmain(int argc, _TCHAR* argv[])
{
   /*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\myeclipse-8.5.0.rar");
      LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\myeclipse-8.5.0_copy.rar");*/

/*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\VS2010\\cn_visual_studio_2010_ultimate_x86_dvd_532347.iso");
    LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\VS2010\\cn_visual_studio_2010_ultimate_x86_dvd_532347_copy.iso");*/

/*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724.iso");
    LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724_copy.iso");
*/
    /*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724.rar");
    LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724_copy.rar");*/

LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\VS2012旗舰版\\VS2012_ULT_chs.iso");
      LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\VS2012旗舰版\\VS2012_ULT_chs_copy.iso");

HANDLE hSrcFile = INVALID_HANDLE_VALUE;  //源文件句柄
    HANDLE hDestFile = INVALID_HANDLE_VALUE; //目标文件句柄
    HANDLE hIOCP = NULL;                     //IOCP
    LPVOID lpAddr = NULL;                     //申请内存地址

__try
    {
        cout << endl << "开始打开源文件" <<endl;
        //源文件
        hSrcFile = CreateFile(
            lpSrc,                                        //源文件
            GENERIC_READ,                                  //读模式
            FILE_SHARE_READ,                              //读共享
            NULL,                                         //安全属性
            OPEN_EXISTING,                                  //必须存在
            FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,//异步 | 不用缓存
            NULL                                          //文件模板为空
            );
        if(INVALID_HANDLE_VALUE == hSrcFile)
        {
            ShowErrMsg("源文件打开错误");
            return -1;
        }

cout << endl << "开始打开目的文件" << endl;

//目的文件
        hDestFile = CreateFile(
            lpDest,                                        //目的文件
            GENERIC_WRITE,                                 //写模式
            0,                                               //独占访问
            NULL,                                           //安全属性
            CREATE_ALWAYS,                                   //总是创建
            FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, //异步 | 不用缓存
            hSrcFile                                       //文件属性同源文件
            );
        if (INVALID_HANDLE_VALUE == hDestFile)
        {
            ShowErrMsg("目的文件打开错误");
            return -2;
        }

cout << endl << "开始获取文件尺寸" << endl;
        //源文件尺寸
        LARGE_INTEGER liFileSize;
        BOOL bRet = GetFileSizeEx(hSrcFile, &liFileSize);
        if (FALSE == bRet)
        {
            ShowErrMsg("获取源文件尺寸失败");
            return -3;
        }

cout << endl << "开始用源文件尺寸设置目的文件大小" << endl;
        
        //设置目的文件指针位置为源文件尺寸 并 设置文件尾
        BOOL bRet2 = SetFilePointerEx(hDestFile, liFileSize, NULL, FILE_BEGIN);
        BOOL bRet3 = SetEndOfFile(hDestFile);
        if (FALSE == bRet2 || FALSE == bRet3)
        {
            ShowErrMsg("设置目的文件尺寸失败");
            return -4;
        }

cout << endl << "开始获取磁盘扇区大小 和 系统信息" << endl;
        SYSTEM_INFO sysInfo = {0};
        GetSystemInfo(&sysInfo);
        DWORD dwBytesPerSector = 0UL;
        bRet = GetDiskFreeSpace(TEXT("D:"), NULL, &dwBytesPerSector, NULL, NULL);
        if (FALSE == bRet)
        {
            ShowErrMsg("开始获取磁盘扇区大小 错误");
            return -5;
        }

//读 
        OVERLAPPED ovlpRead;
        ovlpRead.Offset = 0;
        ovlpRead.OffsetHigh = 0;
        ovlpRead.hEvent = NULL;

//写
        OVERLAPPED ovlpWrite;
        ovlpWrite.Offset = 0;
        ovlpWrite.OffsetHigh = 0;
        ovlpWrite.hEvent = NULL;

//创建IOCP 并和 文件关联
        hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, sysInfo.dwNumberOfProcessors);
        if (NULL == hIOCP)
        {
            DWORD dwErr = GetLastError();
            if (ERROR_ALREADY_EXISTS != dwErr)
            {
                ShowErrMsg("创建IOCP 失败");
                return -6;
            }
        }

hIOCP = CreateIoCompletionPort(hSrcFile, hIOCP, CK_READ, sysInfo.dwNumberOfProcessors);
        hIOCP = CreateIoCompletionPort(hDestFile, hIOCP, CK_WRITE, sysInfo.dwNumberOfProcessors);

//申请扇区大小的5倍的内存
        size_t sizeMAX = dwBytesPerSector * 1024 * 64 * 2; //512K * 64 * 2
        size_t sizeMIN = dwBytesPerSector * 1024 * 64 * 2;

//申请内存
        lpAddr = VirtualAlloc(NULL, sizeMAX, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        if (NULL == lpAddr)
        {
            ShowErrMsg("申请内存错误");
            return -7;                
        }
         
         
        //先往IOCP的完成队列插入一个 写完成 项
        //写0字节
        PostQueuedCompletionStatus(
            hIOCP,     //IOCP
            0,           //GetQueuedCompletionStatus取到的传送字节为0
            CK_WRITE,  //写操作
            &ovlpWrite //写OVERLAPPED
            );

DWORD dwBytesTrans = 0;                                    //传输字节数
        ULONG_PTR ulCompleteKey = 0;                            //完成键
        LPOVERLAPPED lpOverlapped = NULL;                        //OVERLAPPED结构
        BOOL bLastTime = FALSE;                                    //最后一个读操作
        int i = 0;
        int j = 0;
        int nCountZero = 0;                                        //计数

/************************************************************************/
        /* 因为前一次只是往IOCP的完成队列插入了一项【写完成】,而并非真的写
        只是让下面的代码从 【读操作】开始, 
        执行序列为: 读-写, 读-写, ... ,读-写
        当每个【读操作】完成时:把缓冲区中的数据写入【目的文件】,并更新【源文件】的偏移量

当每个【写操作】完成时:更新【目的文件】的偏移量,
        同时,因为操作序列是写操作在后,因此写操作完成后,根据更新后的【源文件】的偏移量
        和【源文件】大小做比较,如果大于等于源文件大小,则说明这是最后一次读取操作,则当下一次
        写操作完成时 退出循环。 如果当前【源文件偏移量】没有达到【源文件大小】则再次从【源文件】
        中读取数据进缓冲区,
        /************************************************************************/
        while(TRUE)
        {
            BOOL bRet = GetQueuedCompletionStatus(hIOCP, &dwBytesTrans, &ulCompleteKey, &lpOverlapped, INFINITE);
            if (FALSE == bRet)
            {
                DWORD dwErr = GetLastError();
                if (NULL != lpOverlapped)
                {
                    ShowErrMsg("线程函数返回错误, 错误原因:");
                    cout << dwErr <<endl; 
                    break;
                } //if
                else
                {
                    if (ERROR_TIMEOUT == dwErr)
                    {
                        ShowErrMsg("等待超时"); 
                    }
                    else
                    {
                        ShowErrMsg("线程函数返回错误, 错误原因2:");
                        cout << dwErr <<endl; 
                    }

continue;  
                } //else

} //if

//读操作完成 
            if (ulCompleteKey == CK_READ)
            { 
                cout << endl << "-------------第 " << ++ i << " 次操作完成,开始写文件 ---------------- "<<endl;
                WriteFile(hDestFile, lpAddr, sizeMIN, NULL, &ovlpWrite);

//读操作完成 更新 源文件的偏移量
                LARGE_INTEGER liSrcFile; 
                liSrcFile.QuadPart = dwBytesTrans;

ovlpRead.Offset += liSrcFile.LowPart;
                ovlpRead.OffsetHigh += liSrcFile.HighPart; 
            } //if

//写操作完成 
            else if (ulCompleteKey == CK_WRITE)
            {
                //写操作完成, 更新目的文件的偏移量
                LARGE_INTEGER liDestFile;  
                liDestFile.QuadPart = dwBytesTrans;  
                ovlpWrite.Offset += liDestFile.LowPart;
                ovlpWrite.OffsetHigh += liDestFile.HighPart;

//当前源文件的偏移量 
                LARGE_INTEGER liTemp;
                liTemp.LowPart = ovlpRead.Offset;
                liTemp.HighPart = ovlpRead.OffsetHigh;
                //当前文件偏移是超过文件大小
                if (liTemp.QuadPart >= liFileSize.QuadPart)
                {
                    break;
                }  
                cout << endl << "*************第 " << ++ j << " 次读写操作完成,开始读文件 ***************"<<endl;
                ReadFile(hSrcFile, lpAddr, sizeMIN, NULL, &ovlpRead); 
                 
            } //else if

} //while

SetFilePointerEx(hDestFile, liFileSize, NULL, FILE_BEGIN);
        SetEndOfFile(hDestFile);

cout << endl << " $$$$$$$$$$$$$$$$$$$$$ 操作完成 $$$$$$$$$$$$$$$$$" <<endl; 
         
    }
    __finally
    {
        cout << endl << "清理资源" <<endl;
        if (INVALID_HANDLE_VALUE != hSrcFile)
            CloseHandle(hSrcFile);
        hSrcFile = INVALID_HANDLE_VALUE;

if(INVALID_HANDLE_VALUE != hDestFile)
            CloseHandle(hDestFile);
        hDestFile = INVALID_HANDLE_VALUE;

if(NULL != lpAddr)
            VirtualFree(lpAddr, 0, MEM_RELEASE | MEM_DECOMMIT);
        lpAddr = NULL; 
    }

cout << endl << endl;
    return 0;
}

void ShowErrMsg(LPCSTR lpMsg){    cout << endl << "Some error happened : " << lpMsg << "\n"; }

版权声明:本文为博主原创文章,未经博主允许不得转载。

GetqueueStatus的更多相关文章

  1. Windows API 函数列表 附帮助手册

    所有Windows API函数列表,为了方便查询,也为了大家查找,所以整理一下贡献出来了. 帮助手册:700多个Windows API的函数手册 免费下载 API之网络函数 API之消息函数 API之 ...

  2. C#封装好的Win32API

    Kernel.cs using System; using System.Runtime.InteropServices; using System.Text; using HANDLE = Syst ...

  3. 英文不好也能快速"记忆" API

    英文不好不要紧,把API函数导入打字练习类软件,即是练习打字速度,提高编程效率:也能短时间记忆API. 坚持每天打一遍,约2小时,连续打两周,会对API有很好的记忆,此方法是结合英文学习方法!以下是W ...

  4. 第12章 纤程(Fiber)

    12.1 纤程对象的介绍 (1)纤程与线程的比较 比较 线程(Thread) 纤程(Fiber) 实现方式 是个内核对象 在用户模式中实现的一种轻量级的线程,是比线程更小的调度单位. 调度方式 由Mi ...

  5. Windows 消息机制详解

    总的来说: MSG包括: 窗口句柄,指示MSG发送的目的窗口 消息标识 lPARAM.wParam 发送时间 发送时的鼠标位置   关于消息队列: Windows系统有一个系统消息队列 每个线程都有一 ...

  6. WaitForMultipleObject与MsgWaitForMultipleObjects用法

    http://blog.csdn.net/byxdaz/article/details/5638680 用户模式的线程同步机制效率高,如果需要考虑线程同步问题,应该首先考虑用户模式的线程同步方法. 但 ...

  7. Chrome中的消息循环

    主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦.主要是线程之间要通信,要切线程,要同步,各种麻烦.我本身的工作经历决定了也没有太多的工作经验,所以chrom ...

  8. DELPHI下API简述(1800个API)

    DELPHI下API简述 http://zero.cnbct.org/show.asp?id=144 auxGetDevCaps API 获取附属设备容量 auxGetNumDevs API 返回附属 ...

  9. API函数

    1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同 ...

随机推荐

  1. Content Provider基础

    1.Content Provider为存储和获取数据提供了统一的接口. 2.Content Provider可以在不同的应用程序之间共享数据. 3.Android为常见的一些数据提供了ContentP ...

  2. PHP 获取header 的自定义参数值

    $.ajax({ type: "GET", url: "default.aspx", beforeSend: function(request) { reque ...

  3. Linux 命令 - mknod

    mknod 创建块设备或者字符设备文件.此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法 mknod [选项] 设备名 设备类 ...

  4. SQL注入的原理及分析

    注入攻击的本质:将用户输入的数据当做代码执行. 有2个限制条件: 1.用户能够控制输入. 2.原本程序要执行的代码,拼接了用户输入的数据后进行执行. 定义:用户输入的数据被当做SQL语句执行. 以下面 ...

  5. Centos7下 Oracle11G自动备份

    1.创建备份目录: [root@Centos ~]# mkdir -p /home/oracle/backup 2.设置目录权限: [root@Centos ~]# chown -R oracle:o ...

  6. Timetable CodeForces - 946D (预处理+背包)

    题意:n天m节课,最多可以逃k节课,每天在学校待的时间为该天上的第一节课到最后一节课持续的时间.问怎样逃课可以使这n天在学校待的时间最短,输出最短的时间. 分析: 1.预处理出每天逃j节课时在学校待的 ...

  7. 基于LAMP实现后台活动发布和前端扫码签到系统

    目的 无论是公司.学校和社会团体,都会举办各式各样的活动,比如运动会.部门会议.项目会议.野炊.团建等.作为团队管理者来讲,当然希望能够把这类活动转移到线上形成完整的系统,类似于电子流的形式.本文以学 ...

  8. IO、阻塞和非阻塞、目录

    系统函数.系统调用 系统函数 open/close函数 函数原型 man 2 open // open, creat - open and possibly create a file or devi ...

  9. 010、MySQL日期时间戳转化为文本日期时间

    #时间戳转化文本时间 SELECT from_unixtime( unix_timestamp( curdate( ) ) ); #时间戳转化文本时间格式化 SELECT from_unixtime( ...

  10. Java 的 String.split 函数,消除空字符串

    代码: String str = "the music made it hard to concentrate"; String delims = "[ ]+" ...