关于Winnet的若干整理

来源:互联网 发布:什么时候用递归算法 编辑:程序博客网 时间:2024/05/16 03:12

转自:http://blog.csdn.net/huzy204/archive/2007/12/12/1932109.aspx

 

很久之前用VC做了一个基于http/https的项目,当时把用wininet开发http/https程序的各种问题都搞得十分清楚。由于当时没有总结,以至于现在又出现相同的问题,又得重新查资料,但还好代码在那里。一些问题看看代码也就明白。

后来由于工作变迁,离开了原来的公司。那部分代码现在也没有了。所以又得从头来过。

现在又做一个http的项目,将一些问题总结一下,为以后再遇到类似的问题节省时间。

1. 几个wininet函数。

URLDownloadToFile:给一个文件的url,就可以把文件下载下来,需要差数可以只有两个,url和文件保存的路径。该函数封装了http,ftp,gppher协议的函数,只要是正确的url都可以下载。

URLDownloadToFile拆分为三个函数:InternetOpen,InternetOpenUrl和InternetReadFile,InternetOpenUrl也是一个封装好了的函数,它也不管具体的协议内容。InternetOpenUrl返回一个HINTERNET句柄,该句柄作为HttpQueryInfo的入参,可以得到相关的信息,如文件的长度,文件修改的日期等。该句柄同时作为InternetReadFile的入参,InternetReadFile将文件下载到一个申请好的缓冲区中。

如果是http协议,InternetOpenUrl可以分解为InternetConnect,HttpOpenRequest,HttpSendRequest。InternetConnect负责连接服务器,HttpOpenRequest去创建一个请求句柄并且把参数存储在句柄中。HttpSendRequest把请求参数送到HTTP服务器。

几个可能会遇到,并且比较郁闷的问题。

1. HttpOpenRequest的问题
HttpOpenRequest的时候如果代码像下面这样写,一般不会出现什么问题。

HINTERNET hRequest = HttpOpenRequest(hConnect,
    "GET",
    pszLoctionFilePath,
    HTTP_VERSION,
    NULL, 
    (const char **)p,
    0,
    1);

倒数第二个参数:IN DWORD dwFlags,msdn上说明:dwFlags Internet flag values. Can be any of the following values: ……
看上去如果是0的话,应该没有什么问题。但我的问题就出在这里。
参数为0的话,就会在第二次访问同一个url(不同的url不会有问题)的时候,HttpQueryInfo会失败, GetLastError()为12150:Header Not Found。这个时候如果打开IE选项,general->delete->delete files。就好了。我用程序删除缓冲区里的全部文件,没有用。非得手动点击一下delete files。这个问题产生的原因还没有找到,如果有谁遇到过同样的问题,麻烦告诉我一声。

倒数第三个参数,我的写法是:
char   szHead[] = "Accept: */*/r/n/r/n";
char **p = new char*[2];*p = szHead;*(p+1) = NULL;
之后倒数第三个参数为:(const char **)p,这样写不会有错。
如果写成(const char **)&szHead,程序不会报错,但debug调试的话,会有First-chance exception in HttpAndFtpTest.exe (KERNEL32.DLL): 0xC0000005: Access Violation.的警告,这是因为强制把一个1维数组变成2维数组,它的第二个数组没有'/0'结尾所导致的访问冲突。

2. 122错误

HttpOpenRequest之后报122:The data area passed to a system call is too small. 错误,原因没有找到。但程序不会有错误,也没有警告。

3. 各个过程花费时间

InternetOpen,InternetConnect,HttpOpenRequest,HttpQueryInfo基本上不花时间。HttpSendRequest和InternetReadFile,占用整个下载过程的绝大部分时间。

4. IE请求对应的程序的写法

http://www.abc.com/123/edf.asp?Key=login&login=2&password=1
对应的IE请求

// 连接服务器
HINTERNET hConnect = InternetConnect(hSession, 
    ServerName, 
    INTERNET_DEFAULT_HTTP_PORT, 
    NULL, 
    NULL, 
    INTERNET_SERVICE_HTTP, 
    0, 
    1);
ServerName为www.abc.com

// 创建一个请求
HINTERNET hRequest = HttpOpenRequest(hConnect,
    Method,
    FormAction,
    HTTP_VERSION,
    NULL, 
    (const char**)&accept,
    0, 
    1);
Method为"GET"
FormAction为/123/edf.asp
BOOL bSeccuss = HttpSendRequest( hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata))
frmdata为Key=login&login=2&password=1。

5. 删除IE缓存的函数

void   ClearInternetCache()   
{   
DWORD   dwNeeded   =   0;   
FindFirstUrlCacheEntry(NULL,   NULL,   &dwNeeded);  

if   (GetLastError()   ==   ERROR_INSUFFICIENT_BUFFER)   
{   
 unsigned   char   *buffer   =   new   unsigned   char[dwNeeded];   
 try   
 {   
  LPINTERNET_CACHE_ENTRY_INFO   lpicei   =   
  reinterpret_cast<LPINTERNET_CACHE_ENTRY_INFO>(buffer);   
  HANDLE   HFind   =     FindFirstUrlCacheEntry(NULL,   lpicei,   &dwNeeded);   
  DeleteUrlCacheEntry(lpicei->lpszSourceUrlName);  

  bool   no_more_files   =   false;   
  while   (!no_more_files)   
  {   
   if   (FindNextUrlCacheEntry(HFind,   lpicei,   &dwNeeded))   
   {   
    DeleteUrlCacheEntry(lpicei->lpszSourceUrlName);   
   }   
   else   switch   (GetLastError())   
   {   
    case   ERROR_INSUFFICIENT_BUFFER:   
    {   
     delete   []   buffer;   
     buffer   =   new   unsigned   char[dwNeeded];   
     lpicei   =   reinterpret_cast<LPINTERNET_CACHE_ENTRY_INFO>(buffer);   
     break;   
    }   
    default:   
    {   
     no_more_files   =   true;   
     break;   
    }   
   }   
  }
  FindCloseUrlCache(HFind);
 }   
 catch   (...)   
 {   
  delete   []   buffer;   
 }   
 delete   []   buffer;                   
}   
}

6. http和https的请求

InternetConnect 的第三个参数,INTERNET_DEFAULT_HTTP_PORT改为INTERNET_DEFAULT_HTTPS_PORT
HttpOpenRequest 的第七个参数 多了一个INTERNET_FLAG_SECURE 选项

还有一个自动安装证书的代码以前做过,现在找不到了。 

7. 更好的InternetOpen方法

DWORD dwFlags = 1;
InternetGetConnectedState(&dwFlags, 0);
if(!(dwFlags & INTERNET_CONNECTION_PROXY))
 *hSession = InternetOpenA("MyAgent", INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, NULL, NULL, 0);
else
 *hSession = InternetOpenA("MyAgent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (*hSession)
 return TRUE;
else
 return FALSE;

8. 下载文件的函数
UINT InternetGetFile (HINTERNET IN hConnect, // Handle from InternetOpen()
                 LPCSTR szFileName)
{
 FILE * pFile;
 if  ( !(pFile = fopen (szFileName, "wb" ) ) )
 {
  return INTERNET_ERROR_FILEOPEN;
 }
 VOID* szTemp[16384];
 DWORD dwSize;
 while (TRUE)
 {
  // Keep coping in 16 KB chunks, while file has any data left.
  // Note: bigger buffer will greatly improve performance.
  if (!InternetReadFile (hConnect, szTemp, 16384,  &dwSize) )
  {
   fclose (pFile);
   return INTERNET_ERROR_READFILE;
  }
  if (!dwSize)
   break;  // Condition of dwSize=0 indicate EOF. Stop.
  else
   fwrite(szTemp, sizeof (char), dwSize , pFile);
 }   // do
 fflush (pFile);
 fclose (pFile);
 return 0;
}

9. 设置internet session

如果要设置internet session及其他参数,可以调用InternetSetOption。 

原创粉丝点击