向前进

——On My Way,From Office Boy

导航

wininet 异步+多线程

一、http://topic.csdn.net/t/20050915/17/4273306.html

异步方式并不是什么高深莫测的事物,WinInet   API   更是大家耳熟能详。  
   
  如果你仔细看过   MSDN   和   internet   上关于   WinInet   API   的文章,你会发现尽管在很多篇章中提到了异步方式的使用,但是大部分说明都只说可以使用,而没有说如何使用。尽管如此,还是有一些文章可以给我们很多的提示,我会在后面列出。  
   
  由于网络数据传输经常会消耗一定的时间,因此我们总是把这些可能消耗时间的操作放到一个单独的子线程,以免影响主线程正常的进行。可是当子线程发生长时间阻塞的时候,主线程由于某种原因需要退出,我们通常希望子线程能在主线程退出前正常退出。这时主线程就不得不   wait   子线程,这样就导致主线程也被阻塞了。当然,主线程可以不   wait   子线程而自行退出,还可以使用   TerminateThread   强行终止子线程,但是这样的后果通常是不可预料的,内存泄漏或许是最轻的一种危害了。  
   
  使用异步方式是解决这类问题的正确手段,下面我们根据一个实例来分析一下   WinInet   API   异步方式的使用方法和注意事项。  
   
  我们的例子完成这样的功能:给定一个   URL   (如:http://www.sina.com.cn/),使用   HTTP   协议下载该网页或文件。我们一共创建了三个线程:主线程负责创建下载子线程,并等待子线程返回消息;子线程则使用异步方式的   WinInet   API   完成下载任务,并在各个阶段返回消息给主线程;子线程还会创建一个回调函数线程,其作用我们稍后解释。  
   
  实例代码中涉及到一些线程,消息,事件,错误处理的   API,由于不是我讨论的内容,就不仔细说明了。  
   
  1.   主线程工作流程  
    a.   创建下载子线程  
      m_hMainThread   =   ::CreateThread(NULL,  
        0,  
        AsyncMainThread,  
        this,  
        NULL,  
        &m_dwMainThreadID);  
   
    b.   等待子线程返回消息  
      MSG   msg;  
      while   (1)  
      {  
        ::GetMessage(&msg,   m_hWnd,   0,   0);  
     
        if   (msg.message   ==   WM_ASYNCGETHTTPFILE)  
        {   //子线程发回消息  
          switch(LOWORD(msg.wParam))  
          {  
          case   AGHF_FAIL:  
            {  
            MessageBox(_T("下载行动失败结束!"));  
            return;  
            }  
          case   AGHF_SUCCESS:  
            MessageBox(_T("下载行动成功结束!"));  
            return;  
          case   AGHF_PROCESS:  
            //下载进度通知  
            break;  
          case   AGHF_LENGTH:  
            //获取下载文件尺寸通知  
            break;  
          }  
        }  
     
        DispatchMessage(&msg);  
      }  
     
  2.   下载子线程工作流程  
    a.   使用标记   INTERNET_FLAG_ASYNC   初始化   InternetOpen  
      m_hInternet   =   ::InternetOpen(m_szAgent,  
        INTERNET_OPEN_TYPE_PRECONFIG,  
        NULL,  
        NULL,  
        INTERNET_FLAG_ASYNC);  
      起步并不费劲,也不难理解,MSDN   上说这样设置之后,以后所有的   API   调用都是异步的了。  
      警惕......  
      看起来好像很简单,但是会有无数的陷阱等着我们掉进去。  
   
    b.   设置状态回调函数   InternetSetStatusCallback  
      ::InternetSetStatusCallback(m_hInternet,   AsyncInternetCallback);  
      第一个陷阱就在这里等着你呢,文献[2]中提到使用一个单独的线程来进行这项设置,并解释说如果不这样会有潜在的影响,而在其他文档中却没有这样使用的例子。尽管看起来多余,并且增加了一些复杂度,我们还是先把这种方法写出来再讨论。子线程需要创建一个回调函数线程:  
      //重置回调函数设置成功事件  
      ::ResetEvent(m_hEvent[0]);  
      m_hCallbackThread   =   ::CreateThread(NULL,  
        0,  
        AsyncCallbackThread,  
        this,  
        NULL,  
        &m_dwCallbackThreadID);  
      //等待回调函数设置成功事件  
      ::WaitForSingleObject(m_hEvent[0],   INFINITE);  
      回调函数线程的实现如下:  
      DWORD   WINAPI   CAsyncGetHttpFile::AsyncCallbackThread(LPVOID   lpParameter)  
      {  
        CAsyncGetHttpFile   *   pObj   =   (CAsyncGetHttpFile*)lpParameter;  
       
        ::InternetSetStatusCallback(pObj->m_hInternet,   AsyncInternetCallback);  
       
        //通知子线程回调函数设置成功,子线程可以继续工作  
        ::SetEvent(pObj->m_hEvent[0]);  
         
        //等待用户终止事件或者子线程结束事件  
        //子线程结束前需要设置子线程结束事件,并等待回调线程结束  
        ::WaitForSingleObject(pObj->m_hEvent[2],   INFINITE);  
        return   0;  
      }  
      确实复杂了很多吧,虽然我试验的结果发现两种设置方法都能正确工作,但是确实发现了这两种设置方法产生的一些不同效果,遗憾的是我没有弄清具体的原因。我推荐大家使用后一种方法。  
   
    c.   打断一下子线程的流程,由于回调函数和上一部分的关系如此密切,我们来看看它的实现  
      void   CALLBACK   CAsyncGetHttpFile::AsyncInternetCallback(  
        HINTERNET   hInternet,  
        DWORD   dwContext,  
        DWORD   dwInternetStatus,  
        LPVOID   lpvStatusInformation,  
        DWORD   dwStatusInformationLength)  
      {  
        CAsyncGetHttpFile   *   pObj   =   (CAsyncGetHttpFile*)dwContext;  
        //在我们的应用中,我们只关心下面三个状态  
        switch(dwInternetStatus)  
        {  
        //句柄被创建  
        case   INTERNET_STATUS_HANDLE_CREATED:  
          pObj->m_hFile   =   (HINTERNET)(((LPINTERNET_ASYNC_RESULT)  
            (lpvStatusInformation))->dwResult);  
          break;  
        //句柄被关闭  
        case   INTERNET_STATUS_HANDLE_CLOSING:  
          ::SetEvent(pObj->m_hEvent[1]);  
          break;  
        //一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求  
        case   INTERNET_STATUS_REQUEST_COMPLETE:  
          if   (ERROR_SUCCESS   ==   ((LPINTERNET_ASYNC_RESULT)  
            (lpvStatusInformation))->dwError)  
          {   //设置句柄被创建事件或者读数据成功完成事件  
            ::SetEvent(pObj->m_hEvent[0]);  
          }  
          else  
          {   //如果发生错误,则设置子线程退出事件  
            //这里也是一个陷阱,经常会忽视处理这个错误,  
            ::SetEvent(pObj->m_hEvent[2]);  
          }  
          break;  
        }  
      }  
  d.   继续子线程的流程,使用   InternetOpenUrl   完成连接并获取下载文件头信息  
      //重置句柄被创建事件  
      ::ResetEvent(m_hEvent[0]);  
      m_hFile   =   ::InternetOpenUrl(m_hInternet,  
        m_szUrl,  
        NULL,  
        NULL,  
        INTERNET_FLAG_DONT_CACHE   |   INTERNET_FLAG_RELOAD,  
        (DWORD)this);  
      if   (NULL   ==   m_hFile)  
      {  
        if   (ERROR_IO_PENDING   ==   ::GetLastError())  
        {  
          if   (WaitExitEvent())  
          {  
            return   FALSE;  
          }  
        }  
        else  
        {  
          return   FALSE;  
        }  
      }  
      等我们把   WaitExitEvent   函数的实现列出在来再解释发生的一切:  
      BOOL   CAsyncGetHttpFile::WaitExitEvent()  
      {  
        DWORD   dwRet   =   ::WaitForMultipleObjects(3,   m_hEvent,   FALSE,   INFINITE);  
        switch   (dwRet)  
        {  
        //句柄被创建事件或者读数据请求成功完成事件  
        case   WAIT_OBJECT_0:  
        //句柄被关闭事件  
        case   WAIT_OBJECT_0+1:  
        //用户要求终止子线程事件或者发生错误事件  
        case   WAIT_OBJECT_0+2:  
          break;  
        }  
        return   WAIT_OBJECT_0   !=   dwRet;  
      }  
      在这里我们终于看到异步方式的巨大优势了,InternetOpenUrl   函数要完成域名解析,服务器连接,发送请求,接收返回头信息等任务,异步方式中   InternetOpenUrl   并不等待成功创建了   m_hFile   才返回,我们看到   m_hFile   是可以在回调函数中赋值的。如果   InternetOpenUrl   的返回值为   NULL   并且   GetLastError   返回   ERROR_IO_PENDING,我们使用   WaitForMultipleObjects   来等待请求的成功完成,这样主线程就有机会在这个等待过程中终止子线程的操作。我真是迫不及待的想把主线程如何强行终止子线程的代码列出来了:  
      //设置要求子线程结束事件  
      ::SetEvent(m_hEvent[2]);  
      //等待子线程安全退出  
      ::WaitForSingleObject(m_hMainThread,   INFINITE);  
      //关闭线程句柄  
      ::CloseHandle(m_hMainThread);  
      哈哈,不需要使用   TerminateThread   终止线程,一切都是安全的,可预料的。  
      我们再考虑一种情况,这种情况好得超乎你的想象,InternetOpenUrl   返回了一个非空的   m_hFile   怎么办?呵呵,这说明   InternetOpenUrl   已经成功创建了一个   m_hFile,并且没有发生任何阻塞,都不用等待任何事件,直接继续下一步吧。  
      最后需要说明得是,InternetOpenUrl   的最后一个参数会被作为回调函数的第二个参数使用。并且哪怕在回调函数中不需要这个参数,这个值你也不能设置为   0,否则   InternetOpenUrl   将不会按照异步的方式工作。  
      到这里,我们已经将   WinInet   API   的异步方式使用的关键部分都展示了,你应该可以使用   WinInet   API   的异步方式写出你自己的应用了。不过还是让我们继续完成这个实例的其他部分。  
   
    e.   使用   HttpQueryInfo   分析头信息  
      DWORD   dwStatusSize   =   sizeof(m_dwStatusCode);  
      if   (FALSE   ==   ::HttpQueryInfo(m_hFile,  
        HTTP_QUERY_STATUS_CODE   |   HTTP_QUERY_FLAG_NUMBER,  
        &m_dwStatusCode,  
        &dwStatusSize,  
        NULL))     //获取返回状态码  
      {  
        return   FALSE;  
      }  
      //判断状态码是不是   200  
      if   (HTTP_STATUS_OK   !=   m_dwStatusCode)  
      {  
        return   FALSE;  
      }  
      DWORD   dwLengthSize   =   sizeof(m_dwContentLength);  
      if   (FALSE   ==   ::HttpQueryInfo(m_hFile,  
        HTTP_QUERY_CONTENT_LENGTH   |   HTTP_QUERY_FLAG_NUMBER,  
        &m_dwContentLength,  
        &dwLengthSize,  
        NULL))     //获取返回的Content-Length  
      {  
        return   FALSE;  
      }  
      ...//通知主线程获取文件大小成功  
      需要说明的是   HttpQueryInfo   并不进行网络操作,因此它不需要进行异步操作的处理。  
   
    f.   使用标记   IRF_ASYNC   读数据   InternetReadFileEx  
      //为了向主线程报告进度,我们设置每次读数据最多   1024   字节  
      for   (DWORD   i=0;   i<m_dwContentLength;   )  
      {  
        INTERNET_BUFFERS   i_buf   =   {0};  
        i_buf.dwStructSize   =   sizeof(INTERNET_BUFFERS);  
        i_buf.lpvBuffer   =   new   TCHAR[1024];  
        i_buf.dwBufferLength   =   1024;  
     
        //重置读数据事件  
        ::ResetEvent(m_hEvent[0]);  
        if   (FALSE   ==   ::InternetReadFileEx(m_hFile,  
          &i_buf,  
          IRF_ASYNC,  
          (DWORD)this))  
        {  
          if   (ERROR_IO_PENDING   ==   ::GetLastError())  
          {  
            if   (WaitExitEvent())  
            {  
              delete[]   i_buf.lpvBuffer;  
              return   FALSE;  
            }  
          }  
          else  
          {  
            delete[]   i_buf.lpvBuffer;  
            return   FALSE;  
          }  
        }  
        else  
        {  
          //在网络传输速度快,步长较小的情况下,  
          //InternetReadFileEx   经常会直接返回成功,  
          //因此要判断是否发生了用户要求终止子线程事件。  
          if   (WAIT_OBJECT_0   ==   ::WaitForSingleObject(m_hEvent[2],   0))  
          {  
            ::ResetEvent(m_hEvent[2]);  
            delete[]   i_buf.lpvBuffer;  
            return   FALSE;  
          }  
        }  
        i   +=   i_buf.dwBufferLength;  
        ...//保存数据  
        ...//通知主线程下载进度  
        delete[]   i_buf.lpvBuffer;  
      }  
      这里   InternetReadFileEx   的异步处理方式同   InternetOpenUrl   的处理方式类似,我没有使用   InternetReadFile   因为它没有异步的工作方式。  
   
    g.   最后清理战场,一切都该结束了  
      //关闭   m_hFile  
      ::InternetCloseHandle(m_hFile);  
      //等待句柄被关闭事件或者要求子线程退出事件  
      while   (!WaitExitEvent())  
      {  
        ::ResetEvent(m_hEvent[0]);  
      }  
      //设置子线程退出事件,通知回调线程退出  
      ::SetEvent(m_hEvent[2]);  
      //等待回调线程安全退出  
      ::WaitForSingleObject(m_hCallbackThread,   INFINITE);  
      ::CloseHandle(m_hCallbackThread);  
      //注销回调函数  
      ::InternetSetStatusCallback(m_hInternet,   NULL);  
      ::InternetCloseHandle(m_hInternet);  
      ...//通知主线程子线程成功或者失败退出  
   
   
  实例中,我们建立一个完整的   HTTP   下载程序,并且可以在主线程中对下载过程进行完全的监控。我们使用了   WinInet   API   中的这些函数:  
    InternetOpen  
    InternetSetStatusCallback  
    InternetOpenUrl  
    HttpQueryInfo  
    InternetReadFileEx  
    InternetCloseHandle  
  其中   InternetOpenUrl   和   InternetReadFileEx   函数是按照异步方式工作的,文献[4]中列出了可以按照异步方式工作的   API:  
    FtpCreateDirectory  
    FtpDeleteFile  
    FtpFindFirstFile  
    FtpGetCurrentDirectory  
    FtpGetFile  
    FtpOpenFile  
    FtpPutFile  
    FtpRemoveDirectory  
    FtpRenameFile  
    FtpSetCurrentDirectory  
    GopherFindFirstFile  
    GopherOpenFile  
    HttpEndRequest  
    HttpOpenRequest  
    HttpSendRequestEx  
    InternetConnect  
    InternetOpenUrl  
    InternetReadFileEx  

 

二、

 http://hi.baidu.com/zhaquanmin/blog/item/c9989a2457dad16935a80f38.html

 

基于WININET下HTTP协议访问之同步异步篇
2009-08-02 10:45

      基于WININET下HTTP协议访问,不采用VC下的封装类CInternetSession,因为它的异步操作理论上可行,MSDN中说明实际代码还在完善。

WININET API下以在HTTP协议下下载一个页面的源代码信息为例,先来看看同步的流程:
1)InternetOpen;
2)InternetOpenUrl;
3)HttpQueryInfo;
4)InternetReadFile;
5)InternetCloseHandle。
       在第2步和第4步,程序会一直等待,直到函数返回。如果要设置超时,可以使用InternetSetOption(不过好像没什么用)。在很多时候,这个函数是不合适的。比如用户主动要中断下载,却只能等待函数返回。还有,如果是大文件下载,无法想像一次读取上兆字节的数据,需使用断点续传,虽然也可以使用同步函数InternetSetFilePointer来定位网络文件读取位置,但很多服务器是不支持的。如果在手机上使用,还要考虑诸如移动网关的限制等等。
同步的好处是函数较少,流程清晰,调试也方便。

再采用异步方法,看看异步的总体流程:
1)InternetOpen,需指定是异步;
2)InternetSetStatusCallback,设置回调;
3)InternetOpenUrl,需指定回调参数;
4)WaitForSingObject或WaitForMultipleObjects,接收信号量;
5)HttpQueryInfo;
6)InternetReadFileEx,需指定回调参数;
7)WaitForSingObject或WaitForMultipleObjects,接收信号量;
8)InternetSetStatusCallback,卸载回调;
9)InternetCloseHandle。

    可以看出,异步比同步要复杂了不少,重点在于回调函数。在回调中,系统会及时返回各种系统定义的HTTP消息,我们根据这些消息来设置某些信号量。在WaitForSingObject或WaitForMultipleObjects里,等待这些信号(当然也可以等待用户的取消动作)。当有正确的信号返回时,继续往下的操作。
    异步方式下,InternetOpenUrl可以在header头里设置要读取的范围。比如读0到1024的数据,在header头里加入Range: bytes=0-1024\r\n。这种方式保证了断点续传。要注意的是,如果服务器支持断点续传,此时使用HttpQueryInfo得到的状态码是206,而不是200。

回调函数写法:
    回调函数怎么写啊?看看微软提供的一个例子吧。这个例子是用POST的方式上传数据,比上述下载数据步骤更为麻烦,把InternetOpenUrl这个函数分成更多的函数来处理。有时间再挖挖这个例子。微软这个例子是在一个sendreqexasync.cpp的文件中,在微软的网站上应该可以得到。

源代码如下:

#include<windows.h>
#include<wininet.h>
#include<iostream.h>

DWORD dwNumKSent;
DWORD dwNumKToSend;
DWORD dwNumBytesComplete = 0;
char lpOutBuf[1024];
HANDLE hConnectedEvent, hRequestCompleteEvent;
HINTERNET hInstance, hConnect, hRequest;
char *lpszUrl, *lpszServer;

BOOL bAllDone = FALSE;

void __stdcall Callback(HINTERNET hInternet,
              DWORD dwContext,
              DWORD dwInternetStatus,
              LPVOID lpStatusInfo,
              DWORD dwStatusInfoLen);

void main(int argc, char *argv[])
{
    if (argc != 4)
    {
        cout << "Usage: sendreqexasync <server> <url> <size in kilobytes>" << endl;
        cout << "   Example: sendreqexasync http://www.foo.com/ /postfolder/upload.exe 256" << endl;
        return;
    }

    lpszServer = argv[1];
    lpszUrl = argv[2];
    dwNumKToSend = atoi(argv[3]);

    FillMemory(lpOutBuf, 1024, 'A');
    hConnectedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    hInstance = InternetOpen("sendreqexasync",
                                       INTERNET_OPEN_TYPE_PRECONFIG,
                                       NULL,
                                       NULL,
                                       INTERNET_FLAG_ASYNC);

    if (hInstance == NULL)
    {
        cout << "InternetOpen failed, error " << GetLastError();
        return;
    }

    if (InternetSetStatusCallback(hInstance,
                                  (INTERNET_STATUS_CALLBACK)&Callback) == INTERNET_INVALID_STATUS_CALLBACK)
    {
        cout << "InternetSetStatusCallback failed, error " << GetLastError();
        return;
    }

    hConnect = InternetConnect(hInstance,
                               lpszServer,
                               INTERNET_DEFAULT_HTTP_PORT,
                               NULL,
                               NULL,
                               INTERNET_SERVICE_HTTP,
                               0,
                               1);
    if (hConnect == NULL)
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            cout << "InternetConnect failed, error " << GetLastError();
            return;
        }
        WaitForSingleObject(hConnectedEvent, INFINITE);
    }

    hRequest = HttpOpenRequest(hConnect,
                               "POST",
                               lpszUrl,
                               NULL,
                               NULL,
                               NULL,
                               INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
                               2);
    if (hRequest == NULL)
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            cout << "HttpOpenRequest failed, error " << GetLastError();
            return;
        }
        WaitForSingleObject(hRequestCompleteEvent, INFINITE);
    }

    INTERNET_BUFFERS IntBuff;

    FillMemory(&IntBuff, sizeof(IntBuff), 0);
    IntBuff.dwStructSize= sizeof(IntBuff);
    IntBuff.dwBufferTotal = 1024*dwNumKToSend;
    IntBuff.lpcszHeader = "Content-Type: text/text\r\n";
    IntBuff.dwHeadersLength = lstrlen(IntBuff.lpcszHeader);

    if (!HttpSendRequestEx(hRequest,
                           &IntBuff,
                           NULL,
                           0,
                           2))
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            cout << "HttpSendRequestEx failed, error " << GetLastError();
            return;
        }
        cout << "HttpSendRequestEx called successfully" << endl;
        cout.flush();

        WaitForSingleObject(hRequestCompleteEvent, INFINITE);
    }

    for (dwNumKSent = 0; dwNumKSent < dwNumKToSend; dwNumKSent++)
    {
        DWORD dwBytesWritten;

        if(!InternetWriteFile(hRequest,
                               lpOutBuf,
                               1024,
                               &dwBytesWritten))
        {
            if (GetLastError() != ERROR_IO_PENDING)
            {
                cout << "InternetWriteFile failed, error " << GetLastError();
                return;
            }
            else
            {
                cout << "InternetWriteFile completing asynchronously" << endl;
                cout.flush();
                WaitForSingleObject(hRequestCompleteEvent, INFINITE);
            }
        }
    }

    cout << "Calling HttpEndRequest" << endl;
    cout.flush();
    if (!HttpEndRequest(hRequest, NULL, HSR_INITIATE, 2))
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            cout << "HttpEndRequest called" << endl;
            cout.flush();
            WaitForSingleObject(hRequestCompleteEvent, INFINITE);
        }
        else
        {
            cout << "HttpEndRequest failed, error " << GetLastError() << endl;
            return;
        }
    }


    cout << "------------------- Read the response -------------------" << endl;
    char lpReadBuff[256];

    do
    {
        INTERNET_BUFFERS InetBuff;
        FillMemory(&InetBuff, sizeof(InetBuff), 0);
        InetBuff.dwStructSize = sizeof(InetBuff);
        InetBuff.lpvBuffer = lpReadBuff;
        InetBuff.dwBufferLength = sizeof(lpReadBuff) - 1;
       
        cout << "Calling InternetReadFileEx" << endl;
        cout.flush();

        if (!InternetReadFileEx(hRequest,
                              &InetBuff,
                              0, 2))
        {
            if (GetLastError() == ERROR_IO_PENDING)
            {
                cout << "Waiting for InternetReadFile to complete" << endl;
                cout.flush();
                WaitForSingleObject(hRequestCompleteEvent, INFINITE);
            }
            else
            {
                cout << "InternetReadFileEx failed, error " << GetLastError();
                cout.flush();
                return;
            }
        }

        lpReadBuff[InetBuff.dwBufferLength] = 0;
        cout << lpReadBuff;
        cout.flush();

        if (InetBuff.dwBufferLength == 0)
            bAllDone = TRUE;

    } while (bAllDone == FALSE);

    cout << endl << endl << "------------------- Request Complete ----------------" << endl;

}


/////这是回调函数,重点看下它的写法
void __stdcall Callback(HINTERNET hInternet,
              DWORD dwContext,
              DWORD dwInternetStatus,
              LPVOID lpStatusInfo,
              DWORD dwStatusInfoLen)
{
    cout << "Callback dwInternetStatus: " << dwInternetStatus << " Context: " << dwContext << endl;
    cout.flush();

    switch(dwContext)
    {
    case 1: // Connection handle
        if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED)
        {
            INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
            hConnect = (HINTERNET)pRes->dwResult;
            cout << "Connect handle created" << endl;
            cout.flush();
            SetEvent(hConnectedEvent);
        }
        break;
    case 2: // Request handle
        switch(dwInternetStatus)
        {
        case INTERNET_STATUS_HANDLE_CREATED:
            {
                INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
                hRequest = (HINTERNET)pRes->dwResult;
                cout << "Request handle created" << endl;
                cout.flush();
            }
            break;
        case INTERNET_STATUS_REQUEST_SENT:
            {
                DWORD *lpBytesSent = (DWORD*)lpStatusInfo;
                cout << "Bytes Sent: " << *lpBytesSent << endl;
                dwNumBytesComplete += *lpBytesSent;
            }
            break;
        case INTERNET_STATUS_REQUEST_COMPLETE:
            {
                INTERNET_ASYNC_RESULT *pAsyncRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
                cout << "Function call finished" << endl;
                cout << "dwResult: " << pAsyncRes->dwResult << endl;
                cout << "dwError: " << pAsyncRes->dwError << endl;
                cout.flush();
                SetEvent(hRequestCompleteEvent);
            }
            break;
        case INTERNET_STATUS_RECEIVING_RESPONSE:
            cout << "Receiving Response" << endl;
            cout.flush();
            break;
        case INTERNET_STATUS_RESPONSE_RECEIVED:
            {
                DWORD *dwBytesReceived = (DWORD*)lpStatusInfo;
                cout << "Received " << *dwBytesReceived << endl;
                cout.flush();
            }
        }
    }
}

 

 

三、

http://hi.baidu.com/fagex/blog/item/2999f4ce54c3953eb600c8e8.html

WinINet 异步下载类一实例(h)
2008-11-28 13:06

#include <wininet.h>
#include <mmsystem.h>

class AsyncWinINet
{
public:
   typedef void (*notify_fp)(const StringMap&);
  
   class thread_info
   {
   public:
    thread_info(const String& _url,     //请求下载的地址(in)
     const StringMap& _request_headrs,   //请求头request_headrs(in)
     const notify_fp& _pfp,      //下载进度通知回调函数指针
     const StringMap& _pfp_param,
     String& _response_headrs,     //返回头response_headrs(out)  
     const String& _saved_filename,    //下载内容保存文件名(in)
     String& _response_content,     //返回内容(out)
     size_t _read_content_size)     //控制保存在response_content中内容的长度(in)) :
     : request_headrs(_request_headrs), pfp(_pfp),
     pfp_param(_pfp_param),      //pfp函数传回参数
     response_headrs(_response_headrs), saved_filename(_saved_filename),
     response_content(_response_content), read_content_size(_read_content_size)
    {
     this->response_headrs.clear();
     this->response_content.clear();
     this->url = StringUtil::EncodeURIComponent(_url);
     for(int i = 0; i < 3; ++i)
     {
      this->hEvent[i] = CreateEvent(NULL,TRUE,FALSE,NULL);
     }
    }

    HANDLE hThread;
    DWORD dwThreadID;
    HANDLE hCallbackThread;
    DWORD dwCallbackThreadID;
    HANDLE hEvent[3];
    LPVOID hInternet;
    LPVOID hFile;
    DWORD dwStatusCode;
    DWORD dwContentLength;

    String url;         //请求下载的地址(in)
    const StringMap& request_headrs;   //请求头request_headrs(in)
    const notify_fp& pfp;      //下载进度通知回调函数指针
    const StringMap& pfp_param;     //pfp函数传回参数

    String& response_headrs;     //返回头response_headrs(out)  
    const String& saved_filename;    //下载内容保存文件名(in)
    String& response_content;     //返回内容(out)
    size_t read_content_size;     //控制保存在response_content中内容的长度(in)
   };

   /*******************************************************************************
   * 函数:download
   * 功能:下载,返回WinINet_ERR_CODE值
   *   说明:关于notify_fp 类型说明: 函数的参数为StringMap类型,传回的变量名与变量值
   * 2007-12
   *******************************************************************************/
   static DWORD download(const String& url, //请求下载的地址(in)
    const StringMap& request_headrs,   //请求头request_headrs(in)
    const notify_fp& pfp,      //下载进度通知回调函数指针
    const StringMap& pfp_param,     //pfp函数传回参数
    String& response_headrs,     //返回头response_headrs(out)  
    const String& saved_filename,    //下载内容保存文件名(in)
    String& response_content,     //返回内容(out)
    size_t read_content_size = 0);    //控制保存在response_content中内容的长度(in)

protected:
   static BOOL WaitExitEvent(thread_info *p);
   static DWORD WINAPI AsyncThread(LPVOID lpParameter);
   static DWORD WINAPI AsyncCallbackThread(LPVOID lpParameter);
   static VOID CALLBACK AsyncInternetCallback(HINTERNET hInternet,
    DWORD dwContext,
    DWORD dwInternetStatus,
    LPVOID lpvStatusInformation,
    DWORD dwStatusInformationLength);

};


posted on 2010-03-17 17:24  向前进  阅读(7541)  评论(0编辑  收藏  举报