荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Jobs (温少), 信区: Visual
标  题: Win32 Internet HTTP Functions in Visual Basic
发信站: BBS 荔园晨风站 (Mon Nov 29 19:02:53 1999), 站内信件



Win32 Internet HTTP Functions in Visual Basic
James Braum
Microsoft Developer Network Technology Group

September 1996

Click to open or copy the files in the VBHTTP sample application for this
technical article.

Abstract
Visual Basic? lets you leverage the power of the Microsoft? Win32? Internet
(WinInet for short) Software Development Kit (SDK) in a number of useful ways.
The WinInet general Internet functions allow you to easily read files from the
Internet. This article is a successor to "Visual Basic and the WinInet SDK,"
which shows you the basics of these general functions. This technical article
lets you dig a little deeper into the WinInet bag of tricks, showing you HTTP
functions that will allow to do such things as determine a file size before
reading it, examine and modify HTTP request headers, and look at HTTP response
headers. A sample Visual Basic application (called VBHTTP) shows request and
response headers that are exchanged between a client and an HTTP server during
a transaction. You can do exciting things with WinInet, so I encourage you to
read on for some ideas and examples of what can be done.

Introduction
The Microsoft Win32 Internet (WinInet) application programming interface (API)
allows you to quickly create Internet-aware applications using a wealth of
functions for working with HTTP, FTP, and Gopher protocols. Without the API,
you would be overwhelmed with having to know the specifics of HTTP, TCP/IP, FTP,
 Windows? Sockets, and so on. Fortunately, the WinInet API makes life
significantly easier. With Visual Basic, you can use the functionality that
WinInet exposes in a number of useful ways. This article sheds some light on
HTTP request and response headers, and how you can use them. A small sample
(VBHTTP) illustrates the headers and shows how you can use this information to
customize your HTTP server requests.

The WinInet API also provides FTP and Gopher functions; however, the WinInet
SDK documentation indicates that these functions are best suited to
asynchronous use, and Visual Basic requires third-party tools to use
asynchronous functions and callbacks. Therefore, this article focuses on HTTP
functions, which can safely be called synchronously. (For a look at how you
would use the FTP functions asynchronously using Visual C++?, please refer to
Robert Coleridge's article titled "Advanced FTP: Teaching Fido to Phetch."

This article begins with some background on the Hypertext Transfer Protocol
(HTTP) and moves into HTTP specifics and the use of request and response
headers.

HTTP Primer
What Is HTTP?
The Hypertext Transfer Protocol (HTTP) has been in use since 1990. HTTP is an
application-level protocol that is fast and non杛esource-intensive. It is the
protocol used to transmit Hypertext Markup Language (HTML) files. It can be
considered a generic protocol that can be used in a variety of ways.

This article is concerned with HTTP version 1.0, which is in common use today.
The next version of HTTP, referred to as HTTP/1.1, is currently being developed
by the World Wide Web Consortium (W3C) and the HTTP working group of the
Internet Engineering Task Force (IETF). Their Web site at http://www.w3.org/
contains current drafts of specifications and more information about HTTP.

HTTP uses a request-and-response model to transfer information. In a simple
case, the user agent (client application) sends a request to the server. The
server responds with a message that contains a success or failure code,
protocol version, server information, and body content, depending on the
request. The user agent can modify the request to provide greater control over
the server. The section of this article titled "WinInet HTTP Functions" looks
at a WinInet function that will allow us to make such modifications.

HTTP Servers
The WinInet HTTP functions work with HTTP servers that recognize requests in
HTTP/1.0. HTTP servers can actually be less complex than a typical user agent
because they have an easier job: they just respond to requests. The client, on
the other hand, must process the information that it has received in response
to a request. In the case of an HTML file, this involves a significant amount
of processing. To display an HTML page, for example, multiple threads may be
spawned to simultaneously load and display inline .GIF and .JPG files.

HTTP Clients
WinInet is useful for creating HTTP clients. (The client is the application
that sends a request to a server.) You can build your own Web browser, create
Web-traversing robots, or write your own Internet-aware application for solving
some particular problem.

Request Headers
Request headers are sent to the server as part of a request message. You can
modify them to develop client applications that have detailed control over the
server. If your client application needs caching capabilities, security, and so
forth, you can append or modify request headers to do this.

Commonly used HTTP/1.0 request headers are:

Authorization


From


If-Modified-Since


User-Agent
Refer to the Internet Draft
(http://www.w3.org/pub/WWW/Protocols/HTTP/1.0/spec.txt), published by the
Internet Engineering Task Force in August 1996, for detailed descriptions of
request headers.

Response Messages and Headers
HTTP servers respond to client requests in the form of a response message. This
message contains a status line, response headers, and entity-header
meta-information about the resource (that is, information about the resource
itself, not about the information that is contained in the resource) identified
in the request message sent by the client. The status line contains the HTTP
version number, a status code, and a reason phrase. For instance, "HTTP/1.0 200
OK" is a typical status line returned in a response message from an HTTP
server. Table 1 contains status codes and reason phrases.

Table 1. Status Codes and Their Meaning

Status Code Meaning
200 OK
201 Created
202 Accepted
204 No Content
301 Moved Permanently
302 Moved Temporarily
304 Not Modified
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable


Two common response headers are:

Location


Server
Entity-header fields that contain meta-information about the file (resource)
specified in the request are also returned with the response message. Common
entity-header fields are:

Allow


Content-Encoding


Content-Length


Content-Type


Expires


Last-Modified
The VBHTTP sample application groups the entity-header fields under the
"Response Headers" tab to make it clearer to read. Instead of differentiating
between response headers and entity-header fields, you can think in terms of
headers returned in response to a request. This oversimplifies the issue
somewhat, but this article is concerned with making things easy.

OK, enough background theory. Let's look at some actual Visual Basic code that
lets us both view and modify our requests.

WinInet HTTP Functions
We will dive in with some sample code that requests a page, then looks at the
content length of that page:

Dim hInternetSession       As Long
Dim hInternetConnect       As Long
Dim hHttpOpenRequest       As Long
Dim sBuffer               As String * 1024
Dim lBufferLength         As Long
lBufferLength = Len(sBuffer)
hInternetSession = InternetOpen(scUserAgent, _
         INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0)
hInternetConnect = InternetConnect(hInternetSession, _
         "www.microsoft.com", INTERNET_DEFAULT_HTTP_PORT, _
         vbNullString, vbNullString, INTERNET_SERVICE_HTTP, 0, 0)
hHttpOpenRequest = HttpOpenRequest(hInternetConnect, "GET", _
         vbNullString,"HTTP/1.0", vbNullString, 0, _
         INTERNET_FLAG_RELOAD, 0)
HttpSendRequest hHttpOpenRequest, vbNullString, 0, 0, 0
HttpQueryInfo hHttpOpenRequest, HTTP_QUERY_CONTENT_LENGTH, _
         ByVal sBuffer, lBufferLength, 0
Debug.Print sBuffer
InternetCloseHandle (hInternetSession)

It doesn't take much code to examine the HTTP response headers before taking
further action in your code. This example opens "www.microsoft.com" using the
access parameters contained in the registry, then sends a request to retrieve
the file. After sending the request, we examine the response from the server.
The buffer contains the length of the file. (To keep things clear, the code
does not check for errors.) Not all servers will provide file length for you.
After examining the response header for content length, you can decide if you
want to retrieve the file, using the InternetReadFile function.

Notice that there is only one call to InternetCloseHandle. This function allows
you to close entire handle subtrees. Closing the root handle will close all
subsequent handles you may have opened up.

This is the type of functionality that the WinInet HTTP functions provide that
you can't get with the Internet functions. You will still use some Internet
functions to open the connection and actually read the file.

The following sections examine these WinInet functions: InternetOpen,
InternetConnect, HttpOpenRequest, HttpAddRequestHeaders, HttpSendRequest,
HttpQueryInfo, InternetReadFile, and InternetCloseHandle.

InternetOpen
InternetOpen initializes our application's use of WinInet functions. A long
handle is returned. Below are the two constants you can use to call the
function, as well as the declaration you will need:

Public Const scUserAgent = "my wininet app"
Public Const INTERNET_OPEN_TYPE_PRECONFIG = 0
Public Declare Function InternetOpen Lib "wininet.dll" Alias _
   "InternetOpenA" (ByVal sAgent As String, ByVal lAccessType _
   As Long, ByVal sProxyName As String, ByVal sProxyBypass As _
   String, ByVal lFlags As Long) As Long

The Win32 Internet functions do not currently provide support for Unicode.
However, support will be provided in future versions. Therefore, all the
functions are aliased to call the ANSI version.

InternetConnect
InternetConnect returns a handle to an HTTP session. The two constants tell the
function to listen on port 80, the default port that HTTP servers listen to,
before establishing the connection. Here are the declaration and two constants
that you will need:

Public Declare Function InternetConnect Lib "wininet.dll" Alias _
   "InternetConnectA" (ByVal  InternetSession As Long, _
   ByVal sServerName As String, ByVal nServerPort As Integer, _
   ByVal sUsername As String, ByVal sPassword As String, _
   ByVal lService As Long, ByVal lFlags As Long, _
   ByVal lContext As Long) As Long
Public Const INTERNET_DEFAULT_HTTP_PORT = 80
Public Const INTERNET_SERVICE_HTTP = 3

HttpOpenRequest
HttpOpenRequest returns an HTTP request handle. Here are the declaration and a
constant you will need:

Public Declare Function HttpOpenRequest Lib "wininet.dll" Alias _
      "HttpOpenRequestA" (ByVal hHttpSession As Long, ByVal sVerb As _
      String, ByVal sObjectName As String, ByVal sVersion As String, _
      ByVal sReferer As String, ByVal something As Long, ByVal lFlags _
      As Long, ByVal lContext As Long) As Long
Public Const INTERNET_FLAG_RELOAD = &H80000000

HttpOpenRequest takes eight parameters (described below) and returns the HTTP
request handle, if successful. The handle holds the request until you send it
with HttpSendRequest, which stores the HTTP headers to be sent as part of the
request.

hHttpSession is the handle returned from a previous call to InternetConnect.


sVerb is a string that contains a method, or verb, to use for the request. Two
common HTTP 1.0 verbs (methods) are GET and POST. These verbs are
case-sensitive. GET retrieves a file. POST tells the server to accept some
information that you are passing to it梖or example, when you are submitting a
response to a form. If the sVerb parameter is NULL, GET will be used.
If the If-Modified-Since header field is used, the GET becomes a conditional
get. It will only retrieve the file if the file has been modified since a
particular date specified in the header field. You can structure your requests
to only retrieve the file if it has been changed since some predetermined point
in time. An example of this will be illustrated below.

sObjectName is the object we are interested in. This is usually a file, but it
can also be a search specifier or an executable.


sVersion is the HTTP version. Leaving this NULL is equivalent to specifying
HTTP/1.0.


sReferer is a string that contains a URL of a document containing a URL in the
sObjectName. This parameter can be left NULL to specify no "referer" [sic].


sAcceptTypes is a string containing a list of valid acceptance types. Leaving
this NULL will indicate that only text documents will be accepted.


lFlags is an action flag. Use the INTERNET_FLAG_RELOAD flag to instruct the
function to retrieve data from the server even if it is locally cached.


lContext is an application-defined value. For the simple examples presented
here, just use 0.
HttpAddRequestHeaders
The HttpAddRequestHeaders function allows you to add or modify headers before
sending them to the server. Here are the declaration and three constants you
need:

Public Declare Function HttpAddRequestHeaders Lib "wininet.dll" Alias _
      "HttpAddRequestHeadersA" (ByVal hHttpRequest As Long, _
      ByVal sHeaders As String, ByVal lHeadersLength As Long, ByVal _
      lModifiers As Long) As Integer
Public Const HTTP_ADDREQ_FLAG_ADD_IF_NEW = &H10000000
Public Const HTTP_ADDREQ_FLAG_ADD = &H20000000
Public Const HTTP_ADDREQ_FLAG_REPLACE = &H80000000

hHttpRequest is the handle returned from the call to HttpOpenRequest. This is
contrary to the WinInet SDK documentation, which indicates that this is the
handle returned from the call to InternetConnect. So please note that you
should pass the handle returned from the HttpOpenRequest call, or the function
call will return NULL.


The sHeaders parameter contains the headers you want to append to the request.
Each header must be terminated with a carriage return/line feed (CR/LF) pair.
You can send more than one header with this string. A common header that you
can send is the "If-Modified-Since" header, which will add conditional behavior
to the GET verb specified in the HttpOpenRequest function. After opening a
request, you can append a header that tells the server not to return the
contents of the file in the body of the message if the file has not been
modified since a certain date (see note below). This is how a Web browser could
manage caching pages: Before sending the request, the client looks to see if it
already has a copy of the page. If it does, it checks the time that it was
loaded into the cache directory. The browser then appends the appropriate
request header. If the file has not been updated since the last time it was
cached on the client's machine, the server will respond with status code 304,
which indicates "not modified." The browser then displays the page by reading
it from the cache instead of bringing it across the wire.
Note   All HTTP/1.0 dates are represented as Greenwich Mean Time (GMT). An HTTP
Date in augmented Backus-Naur Form (BNF) is:

HTTP-date      = rfc1123-date | rfc850-date | asctime-date
rfc1123-date   = wkday "," SP date1 SP time SP "GMT"
rfc850-date    = weekday "," SP date2 SP time SP "GMT"
asctime-date   = wkday SP date3 SP time SP 4DIGIT
date1          = 2DIGIT SP month SP 4DIGIT
                 ; day month year (e.g., 02 Jun 1982)
date2          = 2DIGIT "-" month "-" 2DIGIT
                 ; day-month-year (e.g., 02-Jun-82)
date3          = month SP ( 2DIGIT | ( SP 1DIGIT ))
                 ; month day (e.g., Jun  2)
time           = 2DIGIT ":" 2DIGIT ":" 2DIGIT
                 ; 00:00:00 - 23:59:59
wkday          = "Mon" | "Tue" | "Wed"
               | "Thu" | "Fri" | "Sat" | "Sun"
weekday        = "Monday" | "Tuesday" | "Wednesday"
               | "Thursday" | "Friday" | "Saturday" | "Sunday"
month          = "Jan" | "Feb" | "Mar" | "Apr"
               | "May" | "Jun" | "Jul" | "Aug"
               | "Sep" | "Oct" | "Nov" | "Dec"

(See also: http://www.w3.org/)

lHeadersLength is the length of the sHeaders string.


lModifiers are several different flags you can use to modify the behavior of
the function. The constants are defined above. You can add the flags together
to get the desired action. Here are three flags that you can use:
HTTP_ADDREQ_FLAG_REPLACE is used to remove existing headers or overwrite them
with new ones. If the header already exists and the value you are specifying is
empty, that header will be removed. If you are supplying a new value to an
existing header, this new value will replace the old value.


HTTP_ADDREQ_FLAG_ADD adds the header to the request message if it does not
already exist.


HTTP_ADDREQ_FLAG_ADD_IF_NEW adds a header. If a header already exists, however,
the function will return an error.
There are three other flags that can be used if you are interested in
coalescing headers. Refer to the WinInet SDK documentation
(http://www.microsoft.com/intdev/sdk/docs/wininet) if this interests you.

HttpSendRequest
After you have opened your request and added request headers, if any, you are
ready to send your request to the server. The HttpSendRequest function does
exactly what you would expect: It sends your request. It also lets you append
additional request headers before you send it, so you do not have to make an
additional call to HttpAddRequestHeaders. (I would, however, recommend using
HttpAddRequestHeaders so you can explicitly check the return value to ensure
that the request headers were successfully appended or modified.) After the
request is sent, the status line, response headers, and any entity header
meta-information are read. You can interrogate these with the HttpQueryInfo
function, which is discussed below.

The declaration for HttpSendRequest is:

Public Declare Function HttpSendRequest Lib "wininet.dll" Alias _
      "HttpSendRequestA" (ByVal hHttpRequest As Long, ByVal sHeaders _
      As String, ByVal lHeadersLength As Long, sOptional As Any, _
      ByVal  lOptionalLength As Long) As Integer

The function will return TRUE if the request was successfully sent.

hHttpRequest is the handle returned from HttpOpenRequest.


sHeaders and lHeadersLength are two parameters that can be used for appending
additional headers to the request, instead of making a separate call to
HttpAddRequestHeaders.


sOptional and lOptionalLength are parameters for sending additional data along
with the request. This is not used for GET requests, so we leave these two
parameters NULL in our sample application.
HttpQueryInfo
This function lets you look at the status line, response headers, and entity
header meta-information returned from the request sent by HttpSendRequest.

The declaration is:

Public Declare Function HttpQueryInfo Lib "wininet.dll" _
   Alias "HttpQueryInfoA" (ByVal hHttpRequest As Long, _
   ByVal lInfoLevel As Long, ByRef sBuffer As Any, _
   ByRef lBufferLength As Long, ByRef lIndex As Long) As Integer
Public Const HTTP_QUERY_FLAG_REQUEST_HEADERS = &H80000000

The function returns TRUE if successful.

As usual, hHttpRequest is the handle returned from the call to HttpOpenRequest.


lInfoLevel is the response header or entity-header metainformation we are
interested in interrogating, along with a request modifier flag. If you do not
include an optional flag, the response headers will be queried. The sample
application contains a number of constants you can use for querying these
attributes. HTTP_QUERY_CONTENT_LENGTH is one such constant that can be used to
get metainformation about the resource identified in the request.


sBuffer is the buffer into which the results are copied. Notice that the
declaration specifies that the parameter will be passed by reference. When you
actually call the function, you can call it by value with the ByVal keyword in
front of the variable in the function call.


lBufferlength is the length of the buffer. When the function successfully
returns strings, this value will be set to the actual length of the response
that was copied into the buffer. If the function fails, this parameter
indicates the size that the buffer must actually be in order to receive the
string.


lIndex is used when you have multiple headers with the same name. Leave this
set to 0 unless you have some need for using multiple headers.
InternetReadFile
InternetReadFile is the function that will start reading the file once you have
sent the request and decided to actually retrieve the body content.

Public Declare Function InternetReadFile Lib "wininet.dll" _
      (ByVal hFile  As Long, ByVal sBuffer As String, _
      ByVal lNumberOfBytesToRead As Long, lNumberOfBytesRead As Long) _
      As Integer

hFile is the handle returned from the call to HttpOpenRequest.


sBuffer is the buffer.


lNumberOfBytesToRead is the number of bytes returned; lNumberOfBytesRead is the
actual number of bytes that were read.
InternetCloseHandle
The InternetCloseHandle function closes handles and frees resources associated
with WinInet functions. The function returns TRUE if the handle is successfully
closed. This is the declaration:

Public Declare Function InternetCloseHandle Lib "wininet.dll" _
   (ByVal hInet As Long) As Integer

hInet is the handle you want to close.
VBHTTP Sample Application
This sample application lets you examine the request and response headers that
are exchanged between the client and the HTTP server during a typical
transaction. The following screen shot, from our sample, is what you will see
when you run it (Figure 1).



Figure 1. Response view

The screen contains results sent back from the request sent to
www.microsoft.com. We are looking at the status message, response headers, and
entity header meta-information about the resource content.

From this we can determine that the content length is 10,447 bytes, the status
code is 200 (which means "OK"), and the content expires on Wednesday, September
11, 1996, at 16:19:02 GMT. With this, your Internet-aware application can
determine if the content is still valid, verify that the content type is
correct, and set up a progress meter so that when the resource is read the
progress can be displayed.

Let's take a look at the request headers that were sent to the server (Figure 2)
.



Figure 2. Request view

We can see that GET is the request method, "http sample" is the user agent
(this is typically more useful product information), and the Pragma is
"No-Cache," which tells the server to send the page regardless of whether or
not the client has it cached. You can also see that we sent a cookie?"
MC1=GUID=3986c31df6ce11cfbcee0000f84a13db"梩o the server.

These are just to give you an idea of what information is available with simple
calls to the WinInet functions.

How Is This Useful to Me?
You can add a request header instructing the server to send the content of the
resource only if it was updated after a certain time. This way the application
only retrieves the content if it has changed since the last time it was
retrieved.

For instance, a call to the WinInet HttpAddRequestHeaders function adds a
request header that indicates to the server that we are only interested in
seeing the content of the resource specified in the HttpOpenRequest call if
that resource has been modified since August 24, 1996. The following example
shows how this request header is added. You would insert the following code
just after you open your HTTP request using the handle returned from that call.

Dim sHeaders    As String
Dim lLength    As Long
Dim iRetVal    As Integer

sHeaders = "If-Modified-Since: Sat, 24 Aug 1996 17:38:52 GMT" & vbCrLf
lLength = Len(sHeaders)
iRetVal = HttpAddRequestHeaders(hInternetConnect, sHeaders, lLength, _
            HTTP_ADDREQ_FLAG_ADD Or HTTP_ADDREQ_FLAG_REPLACE)

Check the return value to make sure that it successfully added the request.

If the file has not been modified since the date specified, the status code in
the response message will be 304, "Not Modified." You can check for this, then
opt to not call the InternetReadFile function to retrieve the contents.

DLL Version
It is easy to check which version of the WinInet DLL you are using. Add these
declarations to your project:

Public Declare Function InternetQueryOption Lib "wininet.dll" _
      Alias "InternetQueryOptionA" (ByVal hInternet As Long, _
      ByVal lOption As Long, ByRef sBuffer As Any, ByRef lBufferLength _
      As Long) As Integer
Public Const INTERNET_OPTION_VERSION = 40
Public Type tWinInetDLLVersion
    lMajorVersion As Long
    lMinorVersion As Long
End Type

InternetQueryOption is a WinInet function that lets you check option settings
on a specified handle. INTERNET_OPTION_VERSION will return the version of the
DLL that you are currently using.

This is how you call the function:

dim vDllVersion As tWinInetDLLVersion
dim iRetVal    As Integer
iRetVal = InternetQueryOption (hInternetSession, _
         INTERNET_OPTION_VERSION, vDllVersion, Len(vDllVersion))
Debug.Print vDllVersion.lMajorVersion
Debug.Print vDllVersion.lMinorVersion

You can use this function to check for the length of time before a connection
attempt will time out, or to return the parent handle of the connection, and so
forth.

Conclusion
As you can see, the WinInet HTTP functions provide you with more flexibility
than the general WinInet Internet functions. Using these functions will allow
you to build slick Internet-aware applications that have more sophisticated
control over the requests they send to the server.

? 1999 Microsoft Corporation. All rights reserved. Terms of use.

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.11.111]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店