#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. bzoj 4487: [Jsoi2015]染色问题

    先贴一个题解吧,最近懒得要死2333,可能是太弱的原因吧,总是扒题解,(甚至连题解都看不懂了),blog也没更新,GG http://blog.csdn.net/werkeytom_ftd/artic ...

  2. NULL判斷符

    Null 传导运算符 编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在.比如,要读取message.body.user.firstName,安全的写法是写成下面这样. const ...

  3. 如果shell到win上出现乱码怎么办

    如果shell到win上出现这样的乱码怎么办? 如果出现了乱码,不慌一条命令搞定它!!! chcp 65001

  4. main方法

    main函数的分析(python) 对于很多编程语言来说,程序都必须要有一个入口,比如C,C++,以及完全面向对象的编程语言Java,C#等.如果你接触过这些语言,对于程序入口这个概念应该很好理解,C ...

  5. 在命令提示符中运行install adb 包名.apk文件 遇到的问题

    昨天更新了SDK以后遇到一个奇怪的问题,使用eclipse直接运行程序到手机没有问题,但是如果将程序打包之后在命令提示符中执行adb install 包名.apk 就会报错:adb server is ...

  6. JSONPath 表达式的使用

    一.JSONPath使用需要的包 <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactI ...

  7. Python Sphinx使用踩坑记录

    描述 使用 pip 安装sphinx后,按照教程建立了一个新的py文件,如下 # run.py def run(name): """ this is how we run ...

  8. 005、Java中使用文档注释

    01. 代码如下: package TIANPAN; /** * 此处为文档注释 * @author 田攀 微信382477247 */ public class TestDemo { public ...

  9. Ubuntu 14.04 配置 VNC Server

    用putty连接Linux后,如果会话断开,也会终止此会话在Linux执行的任务. 用WinSCP传输文件很方便,目前也只能传输文件. 按照以下步骤以及提示,安装VNC Server, 1.apt-g ...

  10. C++远征--jame_yuan(慕课网)

    int &a=b;引用符 int const a = 3; int *p = &a;     只读 不能 赋给 读写 有默认参数值得参数必须在参数表的最右端 void fun(int ...