[IE]WinINet API,WinInet的异步使用方法

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:

user posted image

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:

user posted image

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 useINTERNET_OPEN_TYPE_PROXY as second parameter. lpszProxyName is the name of proxy server, andlpszProxyBypass 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. 
(InternetOpenUrl 大概的等价于: 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. TheInternetOpenUrl 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) orInternetFindNextFile (for directories) to retrieve the URL data. It is not necessary to callInternetConnect 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.

user posted image

* 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 theURL_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.
 3. Common HTTP and FTP WinInet APIs
(not done!!)

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 lpszVerblpszObjectName, 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;
CHAR buffer[2048];
CString m_strContents;
DWORD dwRead, dwFlags, dwStatus;
hINet = InternetOpen("InetURL/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
if ( !hINet )
{
AfxMessageBox("InternetOpen Failed");
return;
}
try
{
hConnection = InternetConnect( hINet, "www.dotnetheaven.com", 80, " "," ", INTERNET_SERVICE_HTTP, 0, 0 );
if ( !hConnection )
{
InternetCloseHandle(hINet);
return;
}
// Get data

hData = HttpOpenRequest( hConnection, "GET", "/uicsa/index.htm", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 );
if ( !hData )
{
InternetCloseHandle(hConnection);
InternetCloseHandle(hINet);
return;
}
HttpSendRequest( hData, NULL, 0, NULL, 0);
while( InternetReadFile( hData, buffer, 255, &dwRead ) )
{
if ( dwRead == 0 )
return;
buffer[dwRead] = 0;
m_strContents += buffer;
}
}
catch( CInternetException* e)
{
e->ReportError();
e->Delete();
}
InternetCloseHandle(hConnection);InternetCloseHandle(hINet);
InternetCloseHandle(hData);



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" ;
HttpAddRequestHeaders( hHttpHandle, chHead, -1, HTTP_ADDREQ_FLAG_ADD);

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];
DWORD dwLen 1024;
BOOL bRet;
bRet = HttpQueryInfo( hRequest, HTTP_QUERY_CUSTOM, chBuff, &dwLen, NULL);

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 smile.gif. Here is the syntax:

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

 
--------------------------------------------------------------
 
4. WinInet中的几个网络函数
(refer: 
前者是后者的简化版,我把二者又综合了一下。)
 
这篇技术性文章讨论了如何利用MicrosoftWin32网络函数创建一个网络浏览器。这篇文章的宗旨是让读者了解一些Win32网络函数的作用、能力和使用范围,而不是为这些功能给出一个详细的文档。这篇文章所配合的SurfBear样本应用程序使用Win32网络函数从网络服务器上读取HTML文件,并把它们显示成原始的、没有经过格式化的文本。
4.1 InternetOpenUrl
0: InternetGetConnectedState
BOOL InternetGetConnectedState(OUT LPDWORD lpdwFlags, IN DWORD dwReserved );
判断当前是否有internet连接
有则返回 true,无则 false
若有连接,则lpdwFlags 中会存放着连接方式
1: InternetOpen
初始化WININET.DLL。它在其他的Win32网络函数之前被调用。

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
// 5 DWORD dwFlags
) ;
InternetOpen返回一个类型为HINTERNET的句柄。其他的Win32网络函数把这个句柄当作一个参数。
InternetOpen 的第一个参数lpszCallerName指定正在使用网络函数的应用程序。当HTTP协议使用时,这个名字将变成用户代理。
第二个参数dwAccessType指定访问类型。
NProxyPort参数用在CERN_PROXY_INTERNET_ACCESS中用来指定使用的端口数。使用INTERNET_INVALID_PORT_NUMBER相当于提供却省的端口数。
最后一个参数dwFlags,设置额外的选择。
当结束使用Wein32网络函数时,应该调用InternetCloseHandle释放InternetOpen分配的资源。
2:InternetOpenUrl
一旦你把Win32网络函数初始化了,你就可以使用其他网络函数。下一个要调用的Internet函数是InternetOpenUrl。这个函数连接到一个网络服务器上并且从服务器上读取数据。InternetOpenUrl能对FTP,Gopher或HTTP协议起作用。在这篇文章中,我们只涉及HTTP协议。

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
// 6 DWORD dwContext
) ;
InternetOpenUrl返回一个HINTERNET,它被传递给在这个URL(统一资源定位)上操作的函数。
InternetOpenUrl 的第一个参数hInternetSession是从InternetOpen返回的句柄。第二个参数lpszUrl是需要的资源的URL。下面两个参数 lpszHeaders和HeaderLength用来向服务器传送额外的信息。使用这些参数要求具有正在使用的特定协议的知识。

DwFlag是一个可以用几种方式修改InternetOpenUrl行为的标志,InternetOpenUrl的行为包括关闭、隐藏,使原始数据可用和用存在的连接取代开辟一个新的连接。

最后一个参数dwContext是一个 DWORD上下文值。如果有一个值已经被指定,它将被送到状态回调函数。如果这个值是0,信息将不会被送到状态回调函数。
3: InternetReadFile
你打开一个文件后,就要读它,所以下一个函数是InternetReadFile是符合逻辑的:
charbuffer[10*1024];
DWORDdwBytesRead=0;
 
 
BOOL bRead = ::InternetReadFile(
hUrlFile, // 1 HINTERNET hFile
buffer, // 2 LPVOID lpBuffer
sizeof(buffer),
 // 3 DWORD dwNumberOfBytesToRead
&dwBytesRead // 4 LPDWORD lpdwNumberOfBytesRead
);
 
buffer[dwBytesRead]=0;
pEditCtrl->tSetWindowText(buffer);
 
 
InternetReadFile接收InternetOpenUrl返回的句柄。它也对其他Win32网络函数,例如FtpOpenFile,FopherOpenFile和HttpOpenRequest返回的句柄有影响。

剩 下的InternetReadFile的三个参数也非常的明白直接。Inbuffer是指向保留数据的缓冲区的一个无返回值指 针,dwNumberOfByteToRead以字节为单位指定缓冲区的尺寸。最后一个参数,lpdwNumberOfBytesRead是一个指向包含 读入缓冲区字节数的变量的指针。如果返回值是TRUE,而且lpdwNumberOfBytesRead指向0,则文件已经读到了文件的末尾。
一个真正的web浏览器将在InternetReadFile上循环,不停地从Internet上读入数据块。
为了显示缓冲区,向缓冲区添加一个0并把它送到编辑器控制。
这样,InternetOpen、InternetOpenUrl和InternetReadFile一起创建了Internet浏览器的基础。他们使从Internet上读取文件就象从你的本地硬盘驱动器上读取文件一样容易。
 
4.2 HTTP函数
在一些例子中,InternetOpenUrl太普通了,所以你可能需要其他的Win32网络函数。InternetOpenUrl相当于不同的FTP,GOPHER和HTTP函数的封皮。当使用HTTP时,InternetOpenUrl调用InternetConnect,HttpOpenRequest以及HttpSendRequest,比如说我们想要在下载一个HTML页之前得到它的尺寸以便于我们在缓冲区中为其分配适当的尺寸,HttpQueryInfo将得到web页的大小。

警告:不是所有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);
 
4: InternetConnet
连接到一个HTTP,FTP或Gopher服务器:
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操作。最后一个参数为状态回调函数提供前后关系的信息。
5: HttpOpenRequest
一旦和服务器的连接已经建立,接下来HttpOpenRequest和HttpSenRequest一起工作打开文件。HttpOpenRequest去创建一个请求句柄,把请求参数送到HTTP服务器。
创建一个请求句柄并且把参数存储在句柄中。
HINTERNET hHttpFile = ::HttpOpenRequest(
           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
把请求参数送到HTTP服务器。
除了传送请求外,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
);
7: HttpQueryInfo
为了得到关于文件的信息,在调用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查询大范围的信息。
4.3 WinINet中还有其他协议的函数,比如一系列FTP协议的函数
FtpCreateDirectory
FtpDeleteFile
FtpFindFirstFile
FtpGetCurrentDirectory
FtpGetFile
FtpOpenFile
FtpPutFile
FtpRemoveDirectory
FtpRenameFile
FtpSetCurrentDirectory
4.4 SurfBear样本应用程序
4.5 总结
Win32网络函数使从FTP,Gopher和HTTP服务器上读取信息就想从你的硬盘驱动器上读取信息一样容易。仅仅使用4个函数棗InternetOpen,InternetOpenUrl,InternetReadFile和InternetCloseHandle和很少的HTTP知识,你就可以写一个简单的网络浏览器。把这个简单的浏览器变成一个工业性质的浏览器将要花费很多工作,包括一些对HTTP的了解,对如何显示HTML文件的了解和以及使用多线程方式的能力。Win32网络函数将开发者从与TCP/IP,WindowsSockets和HTTP编程有关的大多数烦闷工作中解脱出来.
4.6 IE-A-G-E-N-T中在各个API中所做的主要的额外操作:

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

 

 

 

 
5. WinInet的异步使用方法
 

codeproject上有一篇老美写的关于HTTP异步的文章:http://www.codeproject.com/Articles/822/Using-WinInet-HTTP-functions-in-Full-Asynchronous

我做HTTP异步的时候,也是参考了这篇文章,受益匪浅。今天特地翻译出来,与大家共飨。

WinInet HTTP的异步方式使用

绪论

如果你曾经深入MSDN研究过WinInet API,你会注意到可使用异步方式且该方式是被推崇的。

当你决定使用该方式时,你却找不到如何使用异步的说明。网上也没有任何例子。研究了很长时间,也做了很多试验,我最终决定着手来填补一份(非官方)空缺的文档。

为什么异步方式是最好的?因为它能够正确的处理超时。而在IE5.5下WinInet缺少此功能。

如果你试图使用TerminateThreadCloseHandle函数来处理超时(这些函数在MSDN文档中有介绍),你将落入各种各样的陷阱中。

以下条件中异步测试成功:单处理器和多处理器的WinNT4系统下的IE4.01SP3, IE5.0, IE5.01,IE5.5SP1,压力环境(12小时不间断地在多处理器NT服务器下运行15个并发实例)。

原理
使用WinInet函数的异步方式,你必须按照正确的顺序:
1.使用INTERNET_FLAG_ASYNC打开任务。
2.使用InternetSetStatusCallback设置回调。
3.使用InternetOpenUrl打开连接。
4.如果InternetOpenUrl返回NULLGetLastError的值是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返回FALSEGetLastError的值为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_CO
MPLETE通知。

之后,你可以开始一个新的连接过程或者关闭任务句柄。但是在关闭之前,你应该卸载回调函数。

细节
在原理中,让我们看看关键点的一些代码:

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. 使用WinINetWinHTTP实现Http访问

(refer: http://www.cppblog.com/kesalin/archive/2007/11/30/37567.aspx

这篇文章的主要价值在于,很清晰的对比了WinInet中的API对应于WinHTTP中的相应的API。

Http访问有两种方式,GETPOST,就编程来说GET方式相对简单点,它不用向服务器提交数据,在这个例程中我使用POST方式,提交数据value1value2,并从服务器得到他们的和(value1 + value2)。

为实现Http访问,微软提供了二APIWinINet, WinHTTPWinHTTPWinINet更加安全和健壮可以这么认为WinHTTPWinINet的升级版本这两套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.

略......

 

posted @ 2013-08-29 16:11 金石开 阅读(...) 评论(...) 编辑 收藏