其他分享
首页 > 其他分享> > 用 WinInet InternetOpen做一个简单的下载器

用 WinInet InternetOpen做一个简单的下载器

作者:互联网

环境: win7 64位 vs2008 ( MFC ASCii)

参考文章:MSDN
      Wininet 错误码
      利用WinINet进行网络程序的编程


写在开头:本文中是用的同步方法 ,
     好处是比较简单,方便 ;
     坏处是 InternetOpenUrl 可能会遇到 卡死不返回的情况;
     更好的方法是使用异步的方式下载。


  下载工作是放在一个线程中的,整体下载流程 InternetOpen - > InternetOpenUrl -> ( HttpQueryInfo可省略) -> InternetReadFile -> InternetCloseHandle

下面正文开始


1. 首先引入库文件

#include <wininet.h>
#pragma comment( lib, "wininet.lib")



2. InternetOpen 初始化
  主要是设置代理和 访问方式 , 这里如果不需要代理的话,前面都可以不用管,最后一个参数 是一个DWORD 的标记符 ,同步的话填0 就可以,具体细节可以参照MSDN

	HINTERNET hSession = InternetOpen(_T("NBDOWNLOAD"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 



3. InternetOpenUrl
函数原型 :

		void InternetOpenUrlA(
				  HINTERNET hInternet,
				  LPCSTR    lpszUrl,
				  LPCSTR    lpszHeaders,
				  DWORD     dwHeadersLength,
				  DWORD     dwFlags,
				  DWORD_PTR dwContext
		);

参数 : hInternet : InternetOpen 返回的 handle
    lpszUrl : 需要访问的url
   lpszHeaders http 请求头 ,可以用Chrome 访问一下,然后复制下来 ,类似这种形式,也可以用NULL ,默认情况下编译器会自动生成http请求头,不过服务器不一定认,还是用浏览器的头比较保险。

	const TCHAR szHeaders[] = _T("Accept: */*\r\nUser-Agent:  Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\r\n");
	

dwHeadersLength 请求头的长度 ,sizeof(szHeaders)
  dwFlags 又是一个DWORD 的标记符,填 INTERNET_FLAG_DONT_CACHE 即可。
  dwContext 异步请求时候传的上下文,这里填 NULL

	HINTERNET hRequest = InternetOpenUrl(hSession, Url, NULL, 0, INTERNET_FLAG_DONT_CACHE, );



4. HttpQueryInfo
  使用这个主要是在下载前先获取文件的总大小,如果不需要可以省略这步(其实可以获取很多种信息,具体可以参照MSDN Query Info Flags

	char szContentLength[128]={0};
	DWORD Totallength = sizeof(szContentLength);
	int ret = HttpQueryInfo(hRequest , HTTP_QUERY_CONTENT_LENGTH,  szContentLength, (LPDWORD)&Totallength,NULL);
	Singlelen = atoi(szContentLength);  //获取到的长度是字符数组形式的,单位是字节,转换成int 最多可以表示2G大小
	

第二个参数 就是上面 Query Info Flags 中列举的可查询的信息 ,第三个参数 是返回数据的数组,第四个参数是数组的大小
最后一个参数 MSDN上是这样说的,不过不太清楚什么场景上会用到multiple headers。

A pointer to a zero-based header index used to enumerate multiple headers with the same name. When calling the function, this parameter is the index of the specified header to return. When the function returns, this parameter is the index of the next header. If the next index cannot be found, ERROR_HTTP_HEADER_NOT_FOUND is returned.



5. InternetReadFile
函数原型:

		BOOLAPI InternetReadFile(
				  HINTERNET hFile,
				  LPVOID    lpBuffer,
				  DWORD     dwNumberOfBytesToRead,
				  LPDWORD   lpdwNumberOfBytesRead
		);

参数:
   hFile : InternetOpenUrl 返回的handle
   lpBuffer : 缓冲区地址
  dwNumberOfBytesToRead : 每次需要读取的字节数 ,一般用 缓冲区大小 -1
   lpdwNumberOfBytesRead : 实际读取的字节数 ,当lpdwNumberOfBytesRead 返回为0 的时候说明已经读取完全部数据了。

	#define  DOWNBLOCK 1024
	ULONG Number = 1; //不能定义为0 ,如果为0 会影响while判断
	byte Temp[DOWNBLOCK]={0}; 
	while(Number){
		BOOL bReadFile = InternetReadFile(hRequest , Temp, DOWNBLOCK - 1, &Number);
		/******写入本地文件 *********/
	}



6. 关闭句柄

	InternetCloseHandle(hRequest);
	InternetCloseHandle(hSession);



7. 以上只是理想情况,事实上,除了InternetCloseHandle()每个步骤都可能失败,可以通过判断函数的返回值来判断函数是否正常,如果返回FALSE 或 NULL 说明函数执行失败了,可以在后面立即调用 GetLastError() 来获取详细的错误信息,错误信息在开头的参考文章里说的很详细,也可能是系统的其他错误,用vs的错误查找工具即可。


另外开头已经说过,InternetOpenUrl 在访问一个无法访问到的网址时,很可能会出现无限等待的情况,更好的方法就是改用异步的方式。



整体参考代码


UINT ThreadDownLoad(LPVOID lpParam){
	//CPingAnDownLoadDlg* pdlg = (CPingAnDownLoadDlg*)lpParam;
	byte Temp[DOWNBLOCK]={0}; 
	const TCHAR szHeaders[] = _T("Accept: */*\r\nUser-Agent:  Mozilla/5.0 (Windows NT 6.1; Win64; x64) 
						AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\r\n");
	ULONG Number = 1;  
	FILE *stream = NULL; 
	/*char szContentLength[128]={0};
	//DWORD Totallength =128;
	int Singlelen = 0; 
	int CurrentLen = 0;
	*/
	HINTERNET AhHttp = NULL;
	HINTERNET hSession = InternetOpen(_T("NBDODNLOAD"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);  
	if (hSession != NULL){  
		hHttp  = InternetOpenUrl(hSession, 需要访问的URL, NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);
		if (hHttp == NULL){
			return 0;
		}
		/* 获取文件大小
		memset(szContentLength,0,sizeof(szContentLength));
		int ret = HttpQueryInfo(hHttp, HTTP_QUERY_CONTENT_LENGTH
								,szContentLength, (LPDWORD)&Totallength,NULL);
		Singlelen = atoi(szContentLength);
		if(Singlelen <= 0){
			InternetCloseHandle(hHttp); 
			hHttp = NULL; 
			return 0;
		}
		*/
		stream = fopen(_T("nbtest.exe"),"wb"); //注意一下打开方式
		if(stream == NULL){
			InternetCloseHandle(hHttp); 
			hHttp = NULL; 
			return 0;
		}
		while (Number > 0){  
			BOOL bReadFile = InternetReadFile(hHttp, Temp, DOWNBLOCK - 1, &Number);  
			if(bReadFile == FALSE){
				DWORD ret = GetLastError();
				InternetCloseHandle(hHttp); 
				hHttp = NULL; 
				break;
			}
			fwrite(Temp, sizeof (char), Number , stream);  
			//CurrentLen += Number;
		}//end while
		
		InternetCloseHandle(hSession);  
		hSession = NULL;
		//PostMessage(pdlg->m_hWnd,WM_DOWNLOAD_FIN,DownState,UpdataNumber);
	}//end if
	return 0;
}

标签:InternetOpen,InternetOpenUrl,Number,szContentLength,hSession,DWORD,NULL,下载,WinIn
来源: https://blog.csdn.net/qq_38777624/article/details/96723100