WinInet 编程 参考

来源:互联网 发布:淘宝提供虚假资质证明 编辑:程序博客网 时间:2024/05/29 04:47

http://www.codeproject.com/KB/IP/asyncwininet.aspx#

http://support.microsoft.com/kb/839873

 

 

do not ask me why.i dont know

 

FILE: AsyncWinINet.h

 


#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);

};


FILE: AsyncWinINet.cpp

 


 

DWORD AsyncWinINet::download(const string &url, const map &request_headrs,
        const AsyncWinINet::notify_fp &pfp, const map &pfp_param, string &response_headrs,
        const string &saved_filename, string &response_content, size_t read_content_size)
{
 thread_info info(url, request_headrs, pfp,
  pfp_param, response_headrs, saved_filename,
  response_content, read_content_size);

 info.hThread = CreateThread(NULL,
  0,
  AsyncWinINet::AsyncThread,
  &info,
  NULL,
  &info.dwThreadID);

 WaitForSingleObject(info.hThread, INFINITE); //等待子线程安全退出
 CloseHandle(info.hThread);//关闭线程句柄

 return TRUE;
}

//---------------------------------------------------------------------
DWORD WINAPI AsyncWinINet::AsyncThread(LPVOID lpParameter)
{
 thread_info* p = (thread_info*)lpParameter;

 //a. 使用标记 INTERNET_FLAG_ASYNC 初始化 InternetOpen
 string user_agent("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler ; .NET CLR 2.0.50727)");
 map iheadrs(p->request_headrs.begin(), p->request_headrs.end());
 map::iterator it = iheadrs.find("User-Agent");
 if(it == iheadrs.end()) iheadrs["User-Agent"] = user_agent;
 else user_agent = it->second;

 p->hInternet = InternetOpen(user_agent.c_str(),
  INTERNET_OPEN_TYPE_PRECONFIG,
  NULL,
  NULL,
  INTERNET_FLAG_ASYNC);

 //ResetEvent(p->hEvent[0]);
 //p->hCallbackThread = CreateThread(NULL,
 // 0,
 // AsyncWinINet::AsyncCallbackThread,
 // p,
 // NULL,
 // &p->dwCallbackThreadID);
 //WaitForSingleObject(p->hEvent[0], INFINITE);//等待回调函数设置成功事件
 InternetSetStatusCallback(p->hInternet, AsyncWinINet::AsyncInternetCallback);

 string sheadrs;
 for(it = iheadrs.begin(); it != iheadrs.end(); ++it)
 {
  sheadrs += it->first + ":" + it->second;
  if(it->second.find(StringUtil::enter) == string::npos) { sheadrs += StringUtil::enter; }
 }
 sheadrs += StringUtil::enter;

 DWORD start_time = timeGetTime();
 ResetEvent(p->hEvent[0]); //重置句柄被创建事件
 p->hFile = InternetOpenUrl(p->hInternet, p->url.c_str(), sheadrs.c_str(), sheadrs.length(),
  INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, (DWORD)p);

 FILE *fp = fopen(p->saved_filename.c_str(), "w+");
 while(true)
 {
  if (NULL == p->hFile)
  {
   DWORD dwError = ::GetLastError();
   if (ERROR_IO_PENDING == dwError || ERROR_SUCCESS == dwError)
   {
    if (WaitExitEvent(p)) { break; }
   }
   else break;
  }

  //读取返回文件头
  DWORD dwLength = 0;
  LPVOID lpOutBuffer = NULL;
  while(true) //读取response_headrs数据
  {
   if(!HttpQueryInfo(p->hFile, HTTP_QUERY_RAW_HEADERS_CRLF,
    lpOutBuffer, &dwLength, NULL))
   {
    DWORD err_code = GetLastError();
    if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break;
    else if(err_code == ERROR_INSUFFICIENT_BUFFER)
    {
     lpOutBuffer = new char[dwLength];
     continue;
    }
    else break;
   }
   break;
  }
  if(lpOutBuffer != NULL)
  {
   p->response_headrs.append((char*)lpOutBuffer,dwLength);
   delete [] lpOutBuffer;
  }

  //e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
  DWORD dwStatusSize = sizeof(p->dwStatusCode);
  if (FALSE == HttpQueryInfo(p->hFile, //获取返回状态码
   HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
   &p->dwStatusCode, &dwStatusSize, NULL)) { break; }

  //判断状态码是不是 200
  if (HTTP_STATUS_OK != p->dwStatusCode) break;

  map msgMap(p->pfp_param.begin(), p->pfp_param.end());
  msgMap["url"] = p->url;

  //获取返回的Content-Length
  //DWORD dwLengthSize = sizeof(p->dwContentLength);
  //if (FALSE == HttpQueryInfo(p->hFile,
  //HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  //&p->dwContentLength, &dwLengthSize, NULL)) { p->dwContentLength = 0; }

  //f. 使用标记 IRF_ASYNC 读数据 InternetReadFileEx
  //为了向主线程报告进度,我们设置每次读数据最多 1024 字节

  char lpvBuffer[1024];
  p->dwContentLength = 0; //Content-Length: 202749
  while(true)
  {
   INTERNET_BUFFERS i_buf = {0};
   i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
   i_buf.lpvBuffer = lpvBuffer;
   i_buf.dwBufferLength = 1024;

   //重置读数据事件
   ResetEvent(p->hEvent[0]);
   if (FALSE == InternetReadFileEx(p->hFile, &i_buf, IRF_ASYNC, (DWORD)p))
   {
    if (ERROR_IO_PENDING == ::GetLastError())
    {
     if (WaitExitEvent(p)) break;
    }
    else break;
   }
   else
   {
    //在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
    //因此要判断是否发生了用户要求终止子线程事件。
    if (WAIT_OBJECT_0 == WaitForSingleObject(p->hEvent[2], 0))
    {
     ResetEvent(p->hEvent[2]);
     break;
    }
   }

   if(i_buf.dwBufferLength == 0)
   {
    DWORD time = timeGetTime() - start_time;
    if(time != 0)
    {
     Real speed = (Real)p->dwContentLength;
     speed /= ((Real)time)/1000.0f;
     speed /= 1024.0f;
     msgMap["speed"] = StringUtil::toString((DWORD)speed);
    }
    if(p->pfp) p->pfp(msgMap);
    break;
   }
   if(fp)
   {
    fwrite(i_buf.lpvBuffer, sizeof(char), i_buf.dwBufferLength, fp);
   }
   if(p->read_content_size > p->response_content.size())
   {
    p->response_content.append((char*)i_buf.lpvBuffer, i_buf.dwBufferLength);
   }
   p->dwContentLength += i_buf.dwBufferLength;
  }
  break;
 }

 if(fp)
 {
  fflush(fp); fclose(fp); fp = NULL;
 }

 if(p->hFile)
 {
  InternetCloseHandle(p->hFile);//关闭 m_hFile
  while (!WaitExitEvent(p)) //等待句柄被关闭事件或者要求子线程退出事件
  {
   ResetEvent(p->hEvent[0]);
  }
 }

 //设置子线程退出事件,通知回调线程退出
 SetEvent(p->hEvent[2]);

 //等待回调线程安全退出
 //WaitForSingleObject(p->hCallbackThread, INFINITE);
 //CloseHandle(p->hCallbackThread);

 //注销回调函数
 InternetSetStatusCallback(p->hInternet, NULL);
 InternetCloseHandle(p->hInternet);

 return TRUE;
}

//------------------------------------------------------------------------------------
DWORD WINAPI AsyncWinINet::AsyncCallbackThread(LPVOID lpParameter)
{
 thread_info *p = (thread_info*)lpParameter;
 InternetSetStatusCallback(p->hInternet, AsyncWinINet::AsyncInternetCallback);

 //通知子线程回调函数设置成功,子线程可以继续工作
 SetEvent(p->hEvent[0]);

 //等待用户终止事件或者子线程结束事件
 //子线程结束前需要设置子线程结束事件,并等待回调线程结束
 WaitForSingleObject(p->hEvent[2], INFINITE);

 return 0;
}

//----------------------------------------------------------------------------
VOID CALLBACK AsyncWinINet::AsyncInternetCallback(HINTERNET hInternet,
              DWORD dwContext,
              DWORD dwInternetStatus,
              LPVOID lpvStatusInformation,
              DWORD dwStatusInformationLength)
{
 thread_info* p = (thread_info*)dwContext;

 //在我们的应用中,我们只关心下面三个状态
 switch(dwInternetStatus)
 {
  //句柄被创建
 case INTERNET_STATUS_HANDLE_CREATED:
  p->hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
   (lpvStatusInformation))->dwResult);
  break;

  //句柄被关闭
 case INTERNET_STATUS_HANDLE_CLOSING:
  SetEvent(p->hEvent[1]);
  break;

  //一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求
 case INTERNET_STATUS_REQUEST_COMPLETE:
  if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
   (lpvStatusInformation))->dwError)
  {
   //设置句柄被创建事件或者读数据成功完成事件
   SetEvent(p->hEvent[0]);
  }
  else
  {
   //如果发生错误,则设置子线程退出事件 这里也是一个陷阱,经常会忽视处理这个错误,
   SetEvent(p->hEvent[2]);
  }
  break;

 case INTERNET_STATUS_CONNECTION_CLOSED:
  SetEvent(p->hEvent[2]);
  break;

 }
}

//--------------------------------------------------------------------
BOOL AsyncWinINet::WaitExitEvent(thread_info *p)
{
 DWORD dwRet = WaitForMultipleObjects(3, p->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;
}


 

//A Example: Main.cpp

 

#include <iostream>
#include <cassert>
#include <Windows.h>
#include <wininet.h>
#include <tchar.h>

 

using namespace std;

 

DWORD dwNumKSent;
DWORD dwNumKToSend;
DWORD dwNumBytesComplete = 0;
char lpOutBuf[1024];
HANDLE hConnectedEvent, hRequestCompleteEvent;
HINTERNET m_Session, hConnect, hRequest, hFile;
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: TestLib <server> <url> <size in kilobytes>" << endl;
  cout << "   Example: TestLib 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);

 // 打开一个Internet会话
 m_Session = InternetOpen("sendreqexasync",
  INTERNET_OPEN_TYPE_PRECONFIG,
  NULL,
  NULL,
  INTERNET_FLAG_ASYNC);

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

 // 设置回调函数
 if (InternetSetStatusCallback(m_Session,
  (INTERNET_STATUS_CALLBACK)&Callback) == INTERNET_INVALID_STATUS_CALLBACK)
 {
  cout << "InternetSetStatusCallback failed, error " << GetLastError();
  return;
 }

 // 开一个远程文件,包括连接和请求
 hConnect = InternetOpenUrl(m_Session,
  "http://192.168.1.103/myServer/RemoteConfig.xml",
  NULL,
  0,
  INTERNET_FLAG_RELOAD |
  INTERNET_FLAG_PRAGMA_NOCACHE |
  INTERNET_FLAG_NO_CACHE_WRITE,
  1);
 if (hConnect == NULL)
 {
  if (GetLastError() != ERROR_IO_PENDING)
  {
   cout << "InternetConnect failed, error " << GetLastError();
   return;
  }
  WaitForSingleObject(hConnectedEvent, INFINITE);
 }

 // 创建一个可读写文件
 HANDLE fp = CreateFile("test.xml",GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,
  NULL,/*CREATE_NEW|*/OPEN_ALWAYS,
  FILE_ATTRIBUTE_NORMAL,0);

 cout << "------------------- Read the response -------------------" << endl;
 while(true) //读取response_headrs数据
 {
  //WaitForSingleObject(hRequestCompleteEvent,INFINITE);
  //读取返回文件头
  DWORD dwLength = 0;
  LPVOID lpOutBuffer = NULL;
  while(true)
  {
   if(!HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF,
    lpOutBuffer, &dwLength, NULL))
   {
    DWORD err_code = GetLastError();
    if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break;
    else if(err_code == ERROR_INSUFFICIENT_BUFFER)
    {
     lpOutBuffer = new char[dwLength];
     continue;
    }
    else break;
   }
   break;  
  }
  //e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
  DWORD dwStatusSize = sizeof(DWORD);
  DWORD dwServerBackState = 0;
  if (FALSE == HttpQueryInfo(hConnect, //获取返回状态码
   HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
   &dwServerBackState, &dwStatusSize, NULL)) { break; }

  //判断状态码是不是 200
  if (HTTP_STATUS_OK != dwServerBackState) break; 

  // 循环读入数据
  int dwContentLength = 0;
  DWORD dwFilewrite = 0;
  while(true)
  {
   INTERNET_BUFFERS i_buf = {0};
   i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
   i_buf.lpvBuffer = lpOutBuf;
   i_buf.dwBufferLength = 1024;

   //重置读数据事件
   ResetEvent(hConnectedEvent);
   if (FALSE == InternetReadFileEx(hConnect, &i_buf, IRF_ASYNC, 2))
   {
    if (ERROR_IO_PENDING == ::GetLastError())
    {
     WaitForSingleObject(hConnectedEvent, INFINITE);
    }
    else break;
   }
   else
   {
    //在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
    //因此要判断是否发生了用户要求终止子线程事件。
    if (WAIT_OBJECT_0 == WaitForSingleObject(hConnectedEvent, 0))
    {
     ResetEvent(hConnectedEvent);
     if(0==WriteFile(fp,lpOutBuf,i_buf.dwBufferLength,&dwFilewrite,NULL))
      cout << "WriteFile failed, error " << GetLastError() <<endl;
     CloseHandle(fp);
     fp = NULL;
     exit(0);
    }
   }

   if(fp)
   {
    if(0==WriteFile(fp,lpOutBuf,i_buf.dwBufferLength,&dwFilewrite,NULL))
     cout << "WriteFile failed, error " << GetLastError() <<endl;
   }
   dwContentLength += i_buf.dwBufferLength;
  }
 }

 cout << endl << endl << "------------------- Request Complete ----------------" << endl;
 system("pause");
}

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);
  }
  if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
  {
   cout << "Function call finished" << endl;
   cout.flush();
   SetEvent(hConnectedEvent);
  }
  if (dwInternetStatus == INTERNET_STATUS_CONNECTION_CLOSED)
  {
   cout << "Connect handle closed" << 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();
   //SetEvent(hRequestCompleteEvent);
   break;
  case INTERNET_STATUS_RESPONSE_RECEIVED:
   {
    DWORD *dwBytesReceived = (DWORD*)lpStatusInfo;
    cout << "Received " << *dwBytesReceived << endl;
    cout.flush();
   }

  }

 }

}

原创粉丝点击