1. WinInet Introduction
(Refer to: http://z3.invisionfree.com/Everything_Here/ar/t2.htm)
This tutorial guides you through basics of WinInet including how to use WinInet with HTTP, FTP, and Gopher protocols with real world examples.
There are five chapters in this tutorial.
* Introduction to WinInet.
* Working with Common WinInet APIs.
* WinInet APIs for HTTP and FTP with sample examples.
* WinInet MFC classes with sample examples. (*not completed)
* Advanced WinInet and Security Issues.(*not completed)
Prerequisites: Windows API, MFC, OOP, basics of the Internet such as URL, protocols, HTTP, FTP, TCP/IP etc.
Internet Applications
Today, the web programming is a basic need for a developer. Starting from a manufacturing to engineering, document to e-commerce, every business is moving towards the Internet. Why wouldn't be? It's easy to access, anywhere, anytime. You don't have to carry data with you. Just upload your data on one web server and you can access anywhere in the world.
Let's see a typical Internet application. Basically, there are three parts of an Internet application: the client (GUI interface), Server (Dlls, ASP, Database, or CGI), and communication between the client and the server (protocol).
Here is a typical Internet application model:
Internet Protocols
A protocol is a set of rules, which is required to communicate between two computers. Both computers must understand and implement these rules to talk to each other.
Application-level Internet Protocols
The Client and the server applications communicate one another via application-level protocols. All application-level protocols are built on top of TCP/IP, which consists of lower-level protocols that provide the application-level protocol with a mechanism for reliable data transmission between computers. Every protocol has a port number, which is used to decide what protocol is used for connection. Here is a list of port numbers for various standard protocols:
Clients
It's a GUI Windows application with ability to communicate to server with the help of protocols. Clients use APIs to communicate with the servers.
Windows provides two kind of network APIs, WinInet and WinSock. The WinInet is an application-level API and supports only HTTP, FTP, and Gopher protocols. While WinSock is set of low-level APIs and supports most of the protocols described above.
Server Components
Server components typically sit on the web server to extend its functionality. Each component performs a specific task such as reading/writing to the database, performing calculations etc. These components are ASP, ISAPI dlls, or CGI scripts.
WinInet Vs. WinSock
1. WinInet provides a higher level-programming interface, which is easy to use while WinSock, is a low level interface, which is hard to implement. To implement WinSock, you should have some knowledge of Windows Sockets and TCP/IP. While WinInet hides this all from developers and does every thing under the hood.
2. WinInet provides support for only three protocols - HTTP, FTP, and Gopher while WinSock let you work with most of the protocols.
3. WinInet supports build-in-caching which improves downloading performance.
4. WinInet provides easy connections and has support for proxy servers.
5. WinInet provides more security over WinSock.
Basically choosing between WinInet and WinSock is pretty easy. If your application needs to access HTTP, FTP, or Gopher protocols then WinInet is better choice, whille WinSock is for rest.
What is WinInet?
WinInet is high level, easy-to-use API to work with HTTP, FTP, and Gopher protocols. If you use WinInet, you don't have to worry about learning protocol specifications such as TCP/IP or Windows Sockets.
When to use WinInet?
Using HTTP, FTP, and gopher
1. Download web pages, images, sounds, and video files.
2. Execute server files such as CGI scripts, ISAPI dlls, or ASP scripts.
3. Access remote database and file systems.
4. Perform web searches.
5. Download and Upload files between computers.
As you will walk through this tutorial, you will see how easy is working with WinInet and Internet protocols.
2. Working with Common WinInet APIs
(InternetOpen,InternetConnect,InternetOpenUrl的区别与关系)
This article has two parts. First part is the introduction to WinInet APIs required using InternetOpenUrl and second part is sample code. Sample code is a dialog base VC++/MFC application that is attached as Url.zip.
Before talking about InternetOpenUrl, we need to look into InternetOpen API.
-InternetOpen-
This function is root of all WinInet functions, and must be called before any WinInet function. The InternetOpen function initializes WinInet environment and prepares to call other WinInet functions. Basically this function starts a new Internet session. The WinInet is based on hierarchy so next level function will require the handle returned by InternetOpen. Here is syntax of InternetOpen function:
CODE |
HINTERNET InternetOpen( IN LPCSTR lpszAgent, IN WORD dwAccessType, IN LPCSTR lpszProxyName, IN LPCSTR lpszProxyBypass, IN WORD dwFlags ); |
Where, lpszAgent - String containing the name of the application. dwAccessType specifies how this internet session should attempt to access the Internet.
lpszProxyName and lpszProxyBypass parameters are valid only you use INTERNET_OPEN_TYPE_PROXY as second parameter. lpszProxyName is the name of proxy server, and lpszProxyBypass is list of proxy servers. You can specify more than one proxy in lpszProxyNameparameter. lpszProxyBypass parameter allows to specify an IP address to bypass a proxy server. The fourth parameter, dwFlags, allows controlling the Internet session behavior. Value of this parameter is either INTERNET_FLAG_OFFLINE, puts Internet session in offline mode, or INTERNET_FLAG_ASYNC, which foreces that all operations should be asynchronous. Here is an example,
CODE |
HINTERNET hInternet = InternetOpen( "TestApp", INTERNET_OPEN_TYPE_PROXY, "prxy.server.com", 158.55.255.251, INTERNET_FLAG_ASYNC ); |
We will see this in more details in our example later.
After initializing WinInet, there are two ways to work with Internet. Either URL related requirement, or protocol related requirement. InternetConnect function is followed by protocol related functions and InternetOpenUrl is related to URLs.
Here is when to choose what?
InternetOpenUrl - Download a web page, downoad an image via HTTP, download a file via FTP, or download a file via Gopher.This function is especially useful when the application does not need to access the particulars of a protocol, but only requires the data corresponding to a URL.
InternetConnect
+
HttpOpenRequest
+
HttpSendRequest
.)
InternetConnect - HTTP POST/GET headers to send and retrieve data, work with FTP such as create, rename, delete directories or upload/download file, use gopher locators.
InternetConnect
InternetConnect is responsible for starting new HTTP, FTP, or Gopher session after establishing a connection using InternetOpen. A single application can have multiple InternetConnect objects, depends on the requirement of the application.
CODE |
HINTERNET InternetConnect(IN HINTERNET hInternetSession, IN LPCSTR lpszServerName,IN INTERNET_PORT nServerPort, IN LPCSTR lpszUsername, IN LPCSTR lpszPassword,IN DWORD dwService, IN DWORD dwFlags, IN DWORD dwContext); |
Opens an FTP, Gopher, or HTTP session for a given site.
InternetCloseHandle
CODE |
BOOL InternetCloseHandle (IN HINTERNET hInet ); |
This function closes opened Internet connection and terminates any pending operations on the handle and discards any outstanding data.
Here is an example of InterrnetConnect:
CODE |
HINTERNET InternetOpen( IN LPCSTR lpszAgent, IN WORD dwAccessType, IN LPCSTR lpszProxyName, IN LPCSTR lpszProxyBypass, IN WORD dwFlags ); |
Working with URLs
InternetOpenUrl is the main function required to work with URLs after InternetOpen. Other functions are helper functions.
InternetOpenUrl
CODE |
HINTERNET InternetOpenUrl(IN HINTERNET hInternetSession, IN LPCSTR lpszUrl, IN LPCSTR lpszHeaders, IN DWORD dwHeadersLength, IN DWORD dwFlags, IN DWORD dwContext ); |
This is a general function that an application can use to retrieve data over any of the protocols that the Win32 Internet functions support. This function is particularly useful when the application does not need to access the particulars of a protocol, but only requires the data corresponding to a URL. The InternetOpenUrl function parses the URL string, establishes a connection to the server, and prepares to download the data identified by the URL. The application can then use InternetReadFile (for files) or InternetFindNextFile (for directories) to retrieve the URL data. It is not necessary to call InternetConnect before InternetOpenUrl.
Sample: Using InternetOpenUrl to download a URL
Downloading a web page's contents is pretty simple using InternetOpenUrl. Here are few simple steps:
* Create a dialog based application with two buttons and two edit boxes. Set content edit box's as multiline.

* Add two variables using Class wizard, m_strURL and m_strContents, for each edit boxes.
* Include <wininet.h> in your stdafx.h and link to wininet.lib in your project settings. WinInet.lib is in your Lib directory of Visual Studio.
* And Add a handler for Download button using Class Wizard and add this piece of code:
CODE |
void COpenUrlDlg::OnOk() { UpdateData(TRUE); if ( m_strURL.IsEmpty() ) return; HINTERNET hINet, hFile; hINet = InternetOpen("InetURL/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); if ( !hINet ) { AfxMessageBox("InternetOpen Failed"); return; } hFile = InternetOpenUrl( hINet, m_strURL, NULL, 0, 0, 0 ); if ( hFile ) { CHAR buffer[1024]; DWORD dwRead; while ( InternetReadFile( hFile, buffer, 1023, &dwRead ) ) { if ( dwRead == 0 ) break; buffer[dwRead] = 0; m_strURL += buffer; } InternetCloseHandle( hFile ); } InternetCloseHandle( hINet ); UpdateData(FALSE);} |
* Now run your program, type URL with http and hit Download. You will see source code of the URL you have entered.
* Don't forget to include wininet.h #include <wininet.h> in your stdafx.h.
Other URL functions
- InternetCreateUrl- This function constructs a URL from the various components contained in the URL_COMPONENTS structure, required as a input parameter of InternetCreateUrl.
- InternetCrackUrl- Breaks a URL into more than one.
- InternetCanonicalizeUrl- Canonicalizing is a process of converting a URL that may contain unsafe characters into an accepted format. This function accepts your URL and returned a good URL.
- InternetCombineUrl- Combines base and relative URLs.
This chapter covers most used WinInet APIs with sample code. I don't see any use of Gopher protocol these days, so I will skip that part for now.
WinInet HTTP APIs
Here is WinInet HTTP APIs hierarchy. To use an Http API, you have to go through the heirarchy.
(!!!Picture cannot be shown)
Before using any HTTP functions, you must be aware of InternetConnect funtion. Here is syntax for InternetConnect .
CODE |
HINTERNET InternetConnect(IN HINTERNET hInternetSession, IN LPCSTR lpszServerName, IN INTERNET_PORT nServerPort, IN LPCSTR lpszUsername, IN LPCSTR lpszPassword, IN DWORD dwService, IN DWORD dwFlags, IN DWORD dwContext ); |
Where hInternetSession is handle retured by InternetOpen. Parameter lpszServerName is name of the server (a host name or host IP). The next parameter, nServerPort allows you to specify a port number. Here are default values for this parameter:
nServerPort
INTERNET_DEFAULT_FTP_PORT Uses the default port for FTP server, port 21.
INTERNET_DEFAULT_GOPHER_PORT Uses the default port for Gopher server, port 70.
INTERNET_DEFAULT_HTTP_PORT Uses the default port for HTTP server, port 80.
INTERNET_DEFAULT_HTTPS_PORT Uses the default port for FTP server or HTTPS, port 443.
INTERNET_DEFAULT_SOCKS_PORT Uses the default port for Socks firewall servers, port 1080.
INTERNET_INVALID_PORT_NUMBER Uses the default port for the service specified by dwService
Parameter lpszUsername and lpszPassword are UserID and passwords. Next parameter, dwService has three values:
dwService
INTERNET_SERVICE_FTP FTP service.
INTERNET_SERVICE_GOPHER Gopher service.
INTERNET_SERVICE_HTTP HTTP service.
Here is an example:
CODE |
HINTERNET hConnection = InternetConnect( hSession, "www.dotnetheaven.com", INTERNET_DEFAULT_HTTP_POST, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 ); |
Here is a list of HTTP APIs:
HTTP APIs
HttpOpenRequest Opens an HTTP request. You need to pass Internet connection as an input parameter to this function.
HTTPQueryInfo Queries information about a request.
InternetErrorDlg Displays predefined dialog for common Internet errors.
HttpAddRequestHeaders Adds HTTP request headers to the HTTP request handle.
HttpSendRequest Sends actual request to the server. This request may be GET or POST.
HttpOpenRequest
This is the first HTTP function, which should be called after InternetConnect. To open an HTTP connection, InternetConnect must pass its third parameter value as INTERNET_DEFAULT_HTTP_PORT. This function sends a request to get or retrieve data depends on the method. Here is syntax:
CODE |
HINTERNET HttpOpenRequest(IN HINTERNET hHttpSession,IN LPCSTR lpszVerb, IN LPCSTR lpszObjectName,IN LPCSTR lpszVersion,IN LPCSTR lpszReferer, IN LPCSTR FAR * lpszAcceptTypes,IN DWORD dwFlags,IN DWORD dwContext); |
First line of every HTTP request is a combination of three elements: Method, URI, and Protocol Version. Three parameters lpszVerb, lpszObjectName, and lpszVersion of HttpOpenRequest make first line of HTTP request.
Parameters Description
hHttpSession Handle to the HTTP session returned by InternetConnect.
lpszVerb Address of string containing HTTP method. If you pass NULL, default method is GET.
lpszObjectName URI
lpszVersion Protocol version. If you pass NULL, default is HTTP/1.0.
lpszReferer
lpszAcceptTypes
dwFlags
dwContext HTTP service.
Here is how to use this function:
HINTERNET hRequest = HttpOpenRequest( hConnection, "GET", "", NULL, NULL, INTERNET_FLAG_RELOAD, 0 );
We will see all the APIs in out example. For more details, you can see MSDN.
HttpSendRequest
Another important HTTP function is HttpSendRequest. This function actually sends a request to Http server. This request may be to post or to get data from the server. It depends on the HttpOpenRequest handle.
Syntax:
BOOL HttpSendRequest( IN HINTERNET hHttpRequest, IN LPCSTR lpszHeaders, IN DWORD dwHeadersLength, IN LPVOID lpOptional, DWORD dwOptionalLength);
Parameters Description
hHttpRequest Handle of HttpOpenReqest
lpszHeaders NULL
dwHeaderLength 0
lpOptional NULL
dwOptionLength 0
Here is how to use this function:
HttpSendRequest( hData, NULL, 0, NULL, 0);
Example: Now let's see how to use all these functions in our example. This example shows how to download contents of a page using HTTP API functions.
CODE |
HINTERNET hINet, hConnection, hData; hData = HttpOpenRequest( hConnection, "GET", "/uicsa/index.htm", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 ); |
HttpAddRequestHeaders
You can add, replace or remove headers to your HTTP request by using this function. Add, replace, or remove depends on dwModifiers parameter. Ok, here is the syntax:
BOOL HttpAddRequestHeaders( IN HINTERNET hHttpRequest, IN LPCSTR lpszHeaders, IN DWORD dwHeadersLength, IN DWORD dwModifiers);
Parameters Description
hHttpRequest Handle of http request.
lpszHeaders Header you want to add.
dwHeadersLength Length of the header.
dwModifiers what kind of request is it? Add, replace or remove.
Ok, here is an example:
char* chHead = "Accept: image/*\r\n" ;
HttpQueryInfo
This function allows you to retrieve information about a give HTTP request. Here is the syntax:
BOOL HttpQueryInfo(IN HINTERNET hHttpRequest, IN DWORD dwInfoLevel, IN LPVOID lpvBuffer, IN LPDWORD lpdwBufferLength,IN OUT LPDWORD lpdwIndex);
Parameters Description
hHttpRequest Handle of http request.
dwInfoLevel Type of information you are interested in.
lpvBuffer Buffer.
lpdwBufferLength Buffer size
lpdwIndex Index
Ok, here is an example:
CHAR chBuff[1024];
WinInet FTP APIs
Here is a list of ftp functions. All FTP functions are easy to understand and use. There is nothing to explain. I have sample examples for most of the functions.
-Functions:-
FtpCreateDirectory Creates a new directory.
FtpDeleteFile Deletes a file.
FtpFindFirstFile Searches the specified directory of the given FTP session. File and directory entries are returned to the application in the WIN32_FIND_DATA structure.
FtpGetCurrentDirectory Retrieves the current directory for the specified FTP session.
FtpGetFile Retrieves a file from the FTP server and stores it under the specified file name, creating a new local file in the process.
FtpGetFileSize Retrieves the file size of the requested FTP resource.
FtpOpenFile Initiates access to a remote file on an FTP server for reading or writing.
FtpPutFile Stores a file on the FTP server.
FtpRemoveDirectory Removes the specified directory.
FtpRenameFile Renames a file.
FtpSetCurrentDirectory Changes to a different working directory.
--------------------------------------------------------------
FtpCreateDirectory
This function creates a directory on the FTP server.
Syntax:
CODE |
BOOL FtpCreateDirectory(IN HINTERNET hConnect, IN LPCTSTR lpszDirectory ); |
Sample:
CODE |
if (!FtpCreateDirectory(g_hConnection, "NewDir")) DoSomething(); |
--------------------------------------------------------------
FtpDeleteFile
This function deletes a file from the FTP server. Nothing to explain here

CODE |
BOOL FtpDeleteFile( IN HINTERNET hConnect, IN LPCTSTR lpszFileName); |
Sample:
CODE |
if (!FtpDeleteFile(g_hConnection, strFileName)) DoSomething(); |
--------------------------------------------------------------
FtpFindFirstFile
This function finds a file on the FTP server and help InternetFindNextFile to find files on the server.
Syntax:
CODE |
HINTERNET FtpFindFirstFile(IN HINTERNET hConnect,IN LPCTSTR lpszSearchFile, OUT LPWIN32_FIND_DATA lpFindFileData, IN DWORD dwFlags, IN DWORD_PTR dwContext ); |
Sample:
CODE |
CString strFileName = "mcb.asp"; WIN32_FIND_DATA FindFileData; HINTERNET hFindFile=NULL; m_RemoteList.ResetContent(); if (hFindFile) InternetCloseHandle(hFindFile); hFindFile = FtpFindFirstFile(g_hConnection, szFileName, &FindFileData, INTERNET_FLAG_RELOAD, 0); if (hFindFile) { if ( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) strFileName.Format("%s <DIR>", FindFileData.cFileName); else strFileName = FindFileData.cFileName; m_RemoteList.AddString(strFileName); while(InternetFindNextFile(hFindFile, &FindFileData)) { if ( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) strFileName.Format("%s <DIR>", FindFileData.cFileName); else strFileName = FindFileData.cFileName; m_RemoteList.AddString(strFileName); } InternetCloseHandle(hFindFile); } |
--------------------------------------------------------------
FtpGetCurrentDirectory
This function returns the current active directory of the FTP server. All FTP function applies to current active directory. So this function is plays an important role.
Syntax:
CODE |
BOOL FtpGetCurrentDirectory(IN HINTERNET hConnect,OUT LPTSTR lpszCurrentDirectory, IN OUT LPDWORD lpdwCurrentDirectory); |
Sample:
CODE |
char szDir[255]; DWORD dwLen = 255; //get the current remote directory if (!FtpGetCurrentDirectory(g_hConnection, szDir, &dwLen)) DoSomething(); |
--------------------------------------------------------------
FtpGetFile
This function retrieves a file from the FTP server and stores on the local system. Here is the syntax:
CODE |
BOOL FtpGetFile(IN HINTERNET hConnect, IN LPCTSTR lpszRemoteFile, IN LPCTSTR lpszNewFile, IN BOOL fFailIfExists,IN DWORD dwFlagsAndAttributes,IN DWORD dwFlags,IN DWORD_PTR dwContext); |
--------------------------------------------------------------
FtpGetFileSize
Retrieves the file size of the requested FTP resource.
Syntax:
CODE |
DWORD FtpGetFileSize(IN HINTERNET hFile, OUT LPDWORD lpdwFileSizeHigh); |
--------------------------------------------------------------
FtpOpenFile
Initiates access to a remote file on an FTP server for reading or writing.
Syntax:
CODE |
HINTERNET FtpOpenFile (IN HINTERNET hConnect,IN LPCTSTR lpszFileName,IN DWORD dwAccess, IN DWORD dwFlags,IN DWORD_PTR dwContext); |
--------------------------------------------------------------
FtpPutFile
Send a local file on the FTP server.
Syntax:
CODE |
BOOL FtpPutFile(IN HINTERNET hConnect,IN LPCTSTR lpszLocalFile,IN LPCTSTR lpszNewRemoteFile, IN DWORD dwFlags,IN DWORD_PTR dwContext); DWORD dwFlags; if (m_lMode == MODE_ASCII) dwFlags = FTP_TRANSFER_TYPE_ASCII; else dwFlags = FTP_TRANSFER_TYPE_BINARY; BOOL bRet = FtpPutFile(g_hConnection, szLocalFile, szRemoteFile, dwFlags, 0); |
--------------------------------------------------------------
FtpRemoveDirectory
Removes the specified directory on the FTP server.
Syntax:
CODE |
BOOL FtpRemoveDirectory(IN HINTERNET hConnect,IN LPCTSTR lpszDirectory) |
--------------------------------------------------------------
FtpRenameFile
Renames a file stored on the FTP server.
Syntax:
CODE |
BOOL FtpRenameFile(IN HINTERNET hConnect,IN LPCTSTR lpszExisting,IN LPCTSTR lpszNew); |
--------------------------------------------------------------
FtpSetCurrentDirectory
Change to a different working directory on the FTP server.
Syntax:
CODE |
BOOL FtpSetCurrentDirectory(IN HINTERNET hConnect,IN LPCTSTR lpszDirectory); |
有则返回 true,无则 false
HINTERNET hNet = ::InternetOpen(
"MSDN SurfBear", // 1 LPCTSTR lpszCallerName
PRE_CONFIG_INTERNET_ACCESS, // 2 DWORD dwAccessType
"", // 3 LPCTSTR lpszProxyName
INTERNET_INVALID_PORT_NUMBER, // 4 INTERNET_PORT nProxyPort
0 // 5 DWORD dwFlags
) ;
InternetOpen返回一个类型为HINTERNET的句柄。其他的Win32网络函数把这个句柄当作一个参数。
第二个参数dwAccessType指定访问类型。
最后一个参数dwFlags,设置额外的选择。
HINTERNET hUrlFile = ::InternetOpenUrl(
hNet, // 1 HINTERNET hInternetSession
"http://www.microsoft.com", // 2 LPCTSTR lpszUrl
NULL, // 3 LPCTSTR lpszHeaders
0, // 4 DWORD dwHeadersLength
INTERNET_FLAG_RELOAD, // 5 DWORD dwFlags
0 // 6 DWORD dwContext
) ;
InternetOpenUrl返回一个HINTERNET,它被传递给在这个URL(统一资源定位)上操作的函数。
InternetOpenUrl 的第一个参数hInternetSession是从InternetOpen返回的句柄。第二个参数lpszUrl是需要的资源的URL。下面两个参数 lpszHeaders和HeaderLength用来向服务器传送额外的信息。使用这些参数要求具有正在使用的特定协议的知识。
DwFlag是一个可以用几种方式修改InternetOpenUrl行为的标志,InternetOpenUrl的行为包括关闭、隐藏,使原始数据可用和用存在的连接取代开辟一个新的连接。
最后一个参数dwContext是一个 DWORD上下文值。如果有一个值已经被指定,它将被送到状态回调函数。如果这个值是0,信息将不会被送到状态回调函数。
DWORDdwBytesRead=0;
hUrlFile, // 1 HINTERNET hFile
buffer, // 2 LPVOID lpBuffer
sizeof(buffer), // 3 DWORD dwNumberOfBytesToRead
&dwBytesRead // 4 LPDWORD lpdwNumberOfBytesRead
);
pEditCtrl->tSetWindowText(buffer);
剩 下的InternetReadFile的三个参数也非常的明白直接。Inbuffer是指向保留数据的缓冲区的一个无返回值指 针,dwNumberOfByteToRead以字节为单位指定缓冲区的尺寸。最后一个参数,lpdwNumberOfBytesRead是一个指向包含 读入缓冲区字节数的变量的指针。如果返回值是TRUE,而且lpdwNumberOfBytesRead指向0,则文件已经读到了文件的末尾。
为了显示缓冲区,向缓冲区添加一个0并把它送到编辑器控制。
这样,InternetOpen、InternetOpenUrl和InternetReadFile一起创建了Internet浏览器的基础。他们使从Internet上读取文件就象从你的本地硬盘驱动器上读取文件一样容易。
警告:不是所有web页都支持得到页尺寸。(例如:www.toystory.com和www.movielink.com不支持这个功能)另外,TCP/IP能传递的数据也比要求的要少。所以,你的应用程序应该处理着两种情况并且围绕InternetReadFile循环直到结果为TRUE同时*lpdwNumberOfBytesRead为0。
使用HttpOpenRequest,HttpSendRequest和HttpQueryInfo去打开文件http://www.microsoft.com/msdn/msdninfo的代码显示如下,错误检测已经被删除。
//Open Internet session.
HINTERNEThSession=::InternetOpen("MSDNSurfBear",PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0);
//Connect to www.microsoft.com.
HINTERNEThConnect=::InternetConnect(hSession, "www.microsoft.com",INTERNET_INVALID_PORT_NUMBER, "","",INTERNET_SERVICE_HTTP, 0, 0);
//Request the file /MSDN/MSDNINFO/fromtheserver.
HINTERNEThHttpFile=::HttpOpenRequest(hConnect, "GET","/MSDN/MSDNINFO/",HTTP_VERSION, NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);
//Send the request.
BOOLbSendRequest=::HttpSendRequest(hHttpFile,NULL,0,0,0);
//Get the length of the file.
charbufQuery[32];
DWORDdwLengthBufQuery=sizeof(bufQuery);
BOOLbQuery=::HttpQueryInfo(hHttpFile, HTTP_QUERY_CONTENT_LENGTH, bufQuery, &dwLengthBufQuery);
//Convert length from ASCII string to a DWORD.
DWORDdwFileSize=(DWORD)atol(bufQuery);
//Allocate a buffer for the file.
char*buffer=newchar[dwFileSize+1];
//Read the file into the buffer.
DWORDdwBytesRead;
BOOLbRead=::InternetReadFile(hHttpFile, buffer, dwFileSize+1, &dwBytesRead); //Putazeroontheendofthebuffer.
buffer[dwBytesRead]=0;
//Close all of the Internet handles.
::InternetCloseHandle(hHttpFile);
::InternetCloseHandle(hConnect);
::InternetCloseHandle(hSession);
//Display the file in an edit control.
pEditCtrl->tSetWindowText(buffer);
HINTERNET hConnect = ::InternetConnect(
hSession, //1 HINTERNET hInternetSession
"www.microsoft.com", //2 LPCTSTR lpszServerName
INTERNET_INVALID_PORT_NUMBER,//3 INTERNET_PORT nServerPort
"", //4 LPCTSTR lpszUsername
"", //5 LPCTSTR lpszPassword
INTERNET_SERVICE_HTTP, //6 DWORD dwService
0, //7 DWORD dwFlags
O //8 DWORD dwContext
) ;
第六个参数dwService决定服务类型(HTTP,FTP或Gopher)。第二个参数(设置成 www.microsoft.com )提供了服务器的地址。第一个参数hInternetSession是从InternetOpen返回的句柄。第四个、第五个参数提供一个用户姓名和密码 。这七个参数没有控制任何标志影响HTTP操作。最后一个参数为状态回调函数提供前后关系的信息。
hConnect, // 1 HINTERNET hHttpSession
"GET", // 2 LPCTSTR lpszVerb
"/MSDN/MSDNINFO/", // 3 LPCTSTR lpszObjectName
HTTP_VERSION, // 4 LPCTSTR lpszVersion
NULL, // 5 LPCTSTR lpszReferer
0, // 6 LPCTSTR FAR * lplpszAcceptTypes
INTERNET_FLAG_DONT_CACHE, // 7 DWORD dwFlags
0 // 8 DWORD dwContext
) ;
HttpOpenRequest的第一个参数是由InternetConnet返回的HINTERNET。HttpOpenRequest的第七和第八个参数执行与InternetConnect中有相同名字的参数一样的功能。
第 二个参数(“GET”)指定想要得到由第三个参数(“/MSDN/MSDNINFO/”)命名的对象。HTTP版已经传递第四个参数;现在,它肯定是 HTTP VERSION。因为“GET”是最流行的动词类型,HttpOpenRequest将为这个参数接收一个空指针。
第五个参数lpszReferer是一个网点的地址。这个值可以为空。第六个参数执行一个程序接收的文件类型列表。把空值传递给HttpOpenRequest即通知了服务器只有文本文件可以被接收。
6: HttpSendRequest
除了传送请求外,HttpSendRequest允许传送额外的HTTP标题给服务器。关于HTTP标题的信息可以在 http://www.w3.org/ 上的最新的说明上找到。
BOOL bSendRequest = ::HttpSendRequest(
hHttpFile, // 1 HINTERNET hHttpRequest
NULL, // 2 LPCTSTR lpszHeaders
0, // 3 DWORD dwHeadersLength
0, // 4 LPVOID lpOptional
0 // 5 DWORD dwOptionalLength
);
为了得到关于文件的信息,在调用HttpSendRequest后使用HttpQueryInfo函数:
BOOL bQuery = ::HttpQueryInfo(
hHttpFile, // 1 HINTERNET hHttpRequest
HTTP_QUERY_CONTENT_LENGTH, // 2 DWORD dwInfoLevel
bufQuery, // 3 LPVOID lpvBuffer
&dwLengthBufQuery // 4 LPDWORD lpdwBufferLength
) ;
查询的结构是字符串或lpvBuffer中的字符串列表。HTTP_QUERY_CONTENT_LENGTH查询得到文件的长度。可以使用HttpQueryInfo查询大范围的信息。
Win32网络函数使从FTP,Gopher和HTTP服务器上读取信息就想从你的硬盘驱动器上读取信息一样容易。仅仅使用4个函数棗InternetOpen,InternetOpenUrl,InternetReadFile和InternetCloseHandle和很少的HTTP知识,你就可以写一个简单的网络浏览器。把 这个简单的浏览器变成一个工业性质的浏览器将要花费很多工作,包括一些对HTTP的了解,对如何显示HTML文件的了解和以及使用多线程方式的能力。Win32网络函数将开发者从与TCP/IP,WindowsSockets和HTTP编程有关的大多数烦闷工作中解脱出来.
InternetOpenA |
Purpose: new IESession(h) 1.// an async session? if (dwFlags == INTERNET_FLAG_ASYNC) { if (hasSession()) { IESession & sess = StartSession(0); return( sess.Handle() ); } } 不是异步的话, if (!hasSession()) { if (h != NULL) { try { IESession & sess = StartSession(h); std::wstring uaWide = Win::StrUtil::toString(lpszAgent); sess.setUA(uaWide);
} catch (IE::Exception & ex) { log_error_winex(ex); } } }
2. IE::IESession & StartSession(HINTERNET h): currentSession.reset(new IE::IESession(h)); |
InternetOpen is the first WinINet function called by an application. It tells the Internet DLL to initialize internal data structures and prepare for future calls from the application. When the application finishes using the Internet functions, it should call InternetCloseHandle to free the handle and any associated resources. The application can make any number of calls to InternetOpen, though a single call is normally sufficient. The application might need to define separate behaviors for each InternetOpen instance, such as different proxy servers configured for each. |
InternetOpenW |
|
|
InternetConnectA |
1. pEvtMgr->fireConnecting( lpszServerName, nPort, lpszUserName, lpszPassword);
2. CALL_PASSTHRU( InternetConnectA,)
3. IEConnectRequest & conn = sess.StartConnectRequest( h, dwContext, servername, nPort, lpszUserName, lpszPassword, (dwFlags & INTERNET_FLAG_SECURE) ); Purpose: new IEConnectRequest(rid, h, dwContext, lpszServerName, nServerPort, lpszUserName, lpszPassword, isSecure);
|
For FTP sites, InternetConnect actually establishes a connection with the server; for others, such as Gopher, the actual connection is not established until the application requests a specific transaction. FUNCTION: IESession::StartConnectRequest |
InternetConnectW |
|
|
HttpOpenRequestA |
1. if (!sess.findRequest(h, pReq)) { sess.StartRequest(conn, h, verbStr.c_str(), objectNameStr.c_str(), versionStr.c_str(), referrerStr.c_str(), acceptTypes.get(), (dwFlags & INTERNET_FLAG_SECURE), startTime, dwContext ); } IEConnectRequest & conn 是由InternetConnectA 函数 new出的IEConnectRequest。 2. StartRequest Purpose: IERequest * pReq = new IERequest( rid, conn, h, lpszVerb, lpszObjectName, lpszVersion, lpszReferrer, lpszAcceptTypes, isSecure, startTime);
|
HttpOpenRequest creates a new HTTP request handle and stores the specified parameters in that handle. An HTTP request handle holds a request to be sent to an HTTP server and contains all RFC822/MIME/HTTP headers to be sent as part of the request. FUNCTION: IESession::StartRequest |
HttpOpenRequestW |
|
|
InternetOpenUrlA |
|
|
InternetOpenUrlW |
|
|
HttpSendRequestA |
|
|
HttpSendRequestW |
|
|
InternetQueryDataAvailable |
1. InternetQueryDataAvailable 2. loop call InternetReadFile until length ==0. Then again: 1. InternetQueryDataAvailable 2. loop call InternetReadFile until length ==0.
|
|
InternetReadFile |
|
|
codeproject上有一篇老美写的关于HTTP异步的文章:http://www.codeproject.com/Articles/822/Using-WinInet-HTTP-functions-in-Full-Asynchronous。
我做HTTP异步的时候,也是参考了这篇文章,受益匪浅。今天特地翻译出来,与大家共飨。
WinInet HTTP的异步方式使用
绪论
如果你曾经深入MSDN研究过WinInet API,你会注意到可使用异步方式且该方式是被推崇的。
当你决定使用该方式时,你却找不到如何使用异步的说明。网上也没有任何例子。研究了很长时间,也做了很多试验,我最终决定着手来填补一份(非官方)空缺的文档。
为什么异步方式是最好的?因为它能够正确的处理超时。而在IE5.5下WinInet缺少此功能。
如果你试图使用TerminateThread或CloseHandle函数来处理超时(这些函数在MSDN文档中有介绍),你将落入各种各样的陷阱中。
以下条件中异步测试成功:单处理器和多处理器的WinNT4系统下的IE4.01SP3, IE5.0, IE5.01,IE5.5SP1,压力环境(12小时不间断地在多处理器NT服务器下运行15个并发实例)。
原理
使用WinInet函数的异步方式,你必须按照正确的顺序:
1.使用INTERNET_FLAG_ASYNC打开任务。
2.使用InternetSetStatusCallback设置回调。
3.使用InternetOpenUrl打开连接。
4.如果InternetOpenUrl返回NULL且GetLastError的值是ERROR_IO_PENDING:
1)等待回调函数返回INTERNET_STATUS_HANDLE_CREATED通知,保存连接句柄;
2)等待回调函数返回INTERNET_STATUS_REQUEST_COMPLETE通知。
5.解析header里的content-length字段,创建一个INTERNET_BUFFERS结构:
1)dwStructSize = sizeof(INTERNET_BUFFERS);
2)lpvBuffer = 你申请的缓冲;
3)dwBufferLength = 缓冲长度。
6.使用InternetReadFileEx函数,参数为IRF_ASYNC,异步读取剩余的数据。不要使用InternetReadFile,因为它是同步的。
7.如果InternetReadFileEx返回FALSE且GetLastError的值为ERROR_IO_PENDING:等待回到函数返回INTERNET_STATUS_REQUEST_COMPLETE 通知。
警告:INTERNET_BUFFERS结果的成员是会被修改的(dwBufferLength和缓冲区)。
8.如果dwBufferLength不为0,移动lpvBuffer的指针dwBufferLength个长度,重复第6步。
9.使用InternetCloseHandle关闭连接,等待INTERNET_STATUS_HANDLE_CLOSING和特定的INTERNET_STATUS_REQUEST_COMPLETE通知。
之后,你可以开始一个新的连接过程或者关闭任务句柄。但是在关闭之前,你应该卸载回调函数。
细节
在原理中,让我们看看关键点的一些代码:
1&2:使用INTERNET_FLAG_ASYNC打开任务,设置回调
m_Session = InternetOpen(AGENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, INTERNET_FLAG_ASYNC);
InternetSetStatusCallback( m_Session,
(INTERNET_STATUS_CALLBACK)InternetCallbackFunc );
关键点在最后一个参数上: INTERNET_FLAG_ASYNC. 在文档里对这个参数的解释是 Makes only asynchronous requests on handles descended from the handle returned from this function. 写得有些晦涩. 实际效果就是如果在InternetOpen中设置为INTERNET_FLAG_ASYNC, 那么在InternetOpenUrl时就会立刻返回, 如果为NULL就必须有具体的返回值才肯RETRUN.(refer: 使用InternetOpenUrl挂起的一个解决方案, http://blog.csdn.net/li_guotao/article/details/3915102)
3&4:使用InternetOpenUrl打开连接,等待INTERNET_STATUS_REQUEST_COMPLETE通知
使用lParam发送一个任务表示到你的回调。我总是用this指针来传递我的class。这里假设你知道如何处理回调。
InternetOpenUrl( m_Session, uurl, NULL, 0,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE |
INTERNET_FLAG_NO_CACHE_WRITE, (LPARAM)this );
回调会收到一堆的消息。这是收到的dwInternetStatus值的顺序:
[openUrl] InternetStatus: 60 INTERNET_STATUS_HANDLE_CREATED
**此时你应该保存HINTERNET句柄,如下代码:
INTERNET_ASYNC_RESULT* res = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
m_hHttpFile = (HINTERNET)(res->dwResult);
[openUrl] InternetStatus: 10
[openUrl] InternetStatus: 11
[openUrl] InternetStatus: 20
[openUrl] InternetStatus: 21
[openUrl] InternetStatus: 30
[openUrl] InternetStatus: 31
[openUrl] InternetStatus: 40
[openUrl] InternetStatus: 41
[openUrl] InternetStatus: 100 INTERNET_STATUS_REQUEST_COMPLETE
5:解析content-length,创建INTERNET_BUFFERS结构
一旦你得到了句柄,调用HttpQueryInfo(使用HTTP_QUERY_CONTENT_LENGTH标记)得到接收数据的大小。如果HTTP头里没有content-length参数,函数会失败。
创建INTERNET_BUFFERS结构。
INTERNET_BUFFERS ib = { sizeof(INTERNET_BUFFERS) };
ib.lpvBuffer = 你申请的缓冲
ib.dwBufferLength = 缓冲长度
dwBufferTotal供你自己使用,永远不会被WinInet该变(据我所知)。我用它来存储收到数据的总长度。
6&7&8:循环读取剩余数据
调用InternetReadFileEx(使用IRF_ASYNC标记)异步读取剩余数据。不要使用InternetReadFile,因为它是同步函数。
你必须循环调用InternetReadFileEx,直到ib.dwBufferLength为0。在每次循环前你必须调整lpvBuffer指针位置,重置ib.dwBufferLength。
BOOL bOk = InternetReadFileEx( m_hHttpFile, &ib, IRF_ASYNC, (LPARAM)this );
if(!bOk && GetLastError()==ERROR_IO_PENDING)
等待...
while( bOk && ib.dwBufferLength!=0 )
{
(调整ib值)
bOk = InternetReadFileEx( m_hHttpFile, &ib, IRF_ASYNC, (LPARAM)this );
if(!bOk && GetLastError()==ERROR_IO_PENDING)
等待...
}
你的回调函数会收到这些消息:
[connect] InternetStatus: 40 (receiving response)
[connect] InternetStatus: 41 (response received)
[connect] InternetStatus: 50
[connect] InternetStatus: 51
还可能
[connect] InternetStatus: 100 INTERNET_STATUS_REQUEST_COMPLETE
最后一个只有在GetLastError值为ERROR_IO_PENDING才会得到。如果你用dwBufferTotal存储了数据总大小(按字节),把你的字符缓冲最后一位置置为0(如果是字符的话)。
buf[ib.dwBufferTotal] = 0;
9:关闭连接句柄
InternetCloseHandle( m_httpFile );
关闭时,回调会收到这个消息:
[connect] InternetStatus: 70 INTERNET_STATUS_HANDLE_CLOSING
大多数错误例子中,连接都会出乎意料的关闭。此时,你会收到70在100(INTERNET_STATUS_REQUEST_COMPLETE)之后。
此情形在整个过程都可能会发生。
10:关闭m_Session句柄前
你应该卸载回调:
InternetSetStatusCallback( m_Session, NULL );
以上将帮助那些使用WinInet异步方式的人。(之后是老美希望大家购买他的代码,略过...)
6. 使用HTTP族函数(而不是InternetOpenUrl)来进行异步上传文件的一个例子
(refer: 异步http windows版本, http://hohosoft.com/?p=690)
#include<windows.h> #include<wininet.h> #include<iostream> using namespace std; 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 www.foo.com /postfolder/upload.exe 256" << endl; // return; // } lpszServer = "www.qq.com"; lpszUrl = "www.qq.com"; dwNumKToSend = 50; 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, // new header 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(); } } } }
7. 使用WinINet和WinHTTP实现Http访问
(refer: http://www.cppblog.com/kesalin/archive/2007/11/30/37567.aspx)
这篇文章的主要价值在于,很清晰的对比了WinInet中的API对应于WinHTTP中的相应的API。
Http访问有两种方式,GET和POST,就编程来说GET方式相对简单点,它不用向服务器提交数据,在这个例程中我使用POST方式,提交数据value1与value2,并从服务器得到他们的和(value1 + value2)。
为实现Http访问,微软提供了二套API:WinINet, WinHTTP。WinHTTP比WinINet更加安全和健壮,可以这么认为WinHTTP是WinINet的升级版本。这两套API包含了很多相似的函数与宏定义,呵呵,详细对比请查阅msdn中的文章“Porting WinINet Applications to WinHTTP”,在线MSDN连接:http://msdn2.microsoft.com/en-us/library/aa384068.aspx。在这个例程中,通过一个宏的设置来决定是使用WinHttp还是WinINet。代码如下:
#define USE_WINHTTP //Comment this line to user wininet.
略......