Basically Visual

April/May 1998

by Peter G. Aitken (c)1998

Originally published in Visual Developer magazine


Programming for the Internet

While the early hype over the Internet has died down a bit, there’s no doubt that Internet/Web connectivity is becoming an integral part of just about all aspects of computing. Programming for the Internet is an important skill for almost any Visual Basic programmer. Does this means that you have a whole new list of API calls to learn? Fortunately, no.

In keeping with the true spirit of Visual Basic, Microsoft has encapsulated the most commonly needed internet functionality in the Internet Transfer Control. Specifically, this control provides capabilities for Hypertext Transfer Protocol (HTTP) and File Transfer Protocol (FTP) transfers. You don’t have quite the flexibility that you would have if you were programming directly, making the required Winsock and other calls, but for most programming needs the Internet Transfer Control provides all you need.

That’s the good news. The bad news is that the Internet Transfer Control is an ornery critter. As originally released, with Visual Basic 5, it was essentially unusable, at least in my experience. The updated version, available with Service Pack 3 (see sidebar) is much, much better although it is still occasionally cranky. If you plan to do any programming with this control, I strongly advise you to install Service Pack 3 before starting, and you’ll save yourself a lot of hassles.

Note that the Internet Transfer Control is an "invisible" control, much like Visual Basic’s Timer control. It is not seen while the program is executing, simply existing behind the scenes doing its job. In this column I will try to provide a clear and concise introduction to using the Internet Transfer Control. There's a lot more information you'll need to make full use of this control, so be sure to refer to the Help system and to Books On Line as well.

Microsoft releases updates to its development products in the form of service packs. Unfortunately, the service packs are designed to update all of Microsoft’s development products and you cannot get the update for just Visual Basic. As a result the service pack is a huge download, 9 separate files totaling over 90 megabytes. When you decompress and run the service pack installation routine, it then detects which of Microsoft’s Visual Studio development tools you have installed on your system and automatically updates them. This huge download is unmanageable unless you have a direct connection to the Internet. Fortunately it can be ordered on CD as well. Point your browser at http://www.microsoft.com/vstudio/sp/default.htm for download and ordering information.

Quick and Easy (but Buggy)

The simplest way to use the Internet Transfer Control is with the OpenURL method. Here's how it works (in this and other examples we will assume there is an Internet Transfer Control named Inet1 on the form):

Inet1.OpenURL(URLName, DataType)

URLName is the full URL that you want to open; it can be either an FTP or an HTTP protocol server. The DataType argument specifies whether you are retrieving text or binary data. Possible values are icString (the default, value = 0) and icByteArray (value = 1). The method’s return value is the complete data returned by the site. The data is returned synchronously, meaning that program execution pauses until the data has been retrieved. This is an important point, as we will see later.

The data returned by the OpenURL method should be put in a string variable or a byte array (or the equivalents) depending on the setting of the DataType argument. Here are a few examples. The following code retrieves the text contents of the default file INDEX.HTML at the indicated site and displays it in a Text Box:

Text1.Text = Inet1.OpenURL("http://www.pgacon.com", icString)

If you want to retrieve a specific file, specify it in the URL:

Text1.Text = Inet1.OpenURL("http://www.pgacon.com/books.htm", icString)

To obtain the directory of an FTP site (the directory is an FTP site's "default" file):

Text1.Text = Inet1.OpenURL("ftp://ftp.microsoft.com", icString)

To download a file from an FTP site, you need to use binary mode unless you are sure it is a text file:

Dim buf() as Byte

buf() = Inet1.OpenURL("ftp://ftp.microsoft.com/anyfile.exe" , _

icByteData)

Then you can save the downloaded file to disk as follows:

Open "c:\programs\anyfile.exe" For Binary as #1

Put #1,, buf()

Close #1

HTTP servers require no log-on, of course, but FTP servers do. Even if the server permits unrestricted access via anonymous log-on, your program must send a user name and a password. What is sent by the Internet Transfer Control is determined by its Username and Password properties. The value of the Username property is sent to the remote server as the username for log-on. If this property is blank (Null or ""), then "anonymous" is sent. Things are a bit more complicated with the Password property. It is explained in the following table.

Value of Username property Value of Password property Password sent to server
Null or "" Null or "" User’s e-mail address
Non-null string Null or "" ""
Null Non-null string Error
Non-null string Non-null string Password property

There is one illegal combination – you cannot have a non-Null Password property combined with a Null Username property. Note that leaving both of these properties blank results in the standard anonymous FTP log-on: a username of "anonymous" and a password consisting of your e-mail address.

While the OpenURL method is convenient and easy to use, I suggest that you stay away from it. Why is this? There are several reasons for my opinion:

These other methods do not return the downloaded data in the same way that OpenURL does. Rather, the data is placed in the Internet Transfer Control's internal buffer. We need to see how you retrieve data from this buffer before getting to the details of the methods. First, we need to look at the Internet Transfer Control's single event, which is essential for retrieving data from the buffer.

What State Am I In?

The Internet Transfer Control's one event occurs whenever the state of the control changes, reflecting a change in the connection status of the control, the receipt of some data, or some other communications event that your program may need to know about (including, as we will see, errors). The associated event procedure has the following declaration:

Inet1_StateChanged(ByVal State As Integer)

An integer value identifying the new state of the control is passed as an argument to the event procedure. The possible values, the defined constants, and their meanings are described in the following table.

Constant Value Description
icNone 0 No state to report.
icResolvingHost 1 The control is looking up the IP address of the specified host computer.
icHostResolved 2 The control successfully found the IP address of the specified host computer.
icConnecting 3 The control is connecting to the host computer.
icConnected 4 The control successfully connected to the host computer.
icRequesting 5 The control is sending a request to the host computer.
icRequestSent 6 The control successfully sent the request.
icReceivingResponse 7 The control is receiving a response from the host computer.
icResponseReceived 8 The control successfully received a response from the host computer.
icDisconnecting 9 The control is disconnecting from the host computer.
icDisconnected 10 The control successfully disconnected from the host computer.
icError 11 An error occurred in communicating with the host computer.
icResponseCompleted 12 The request has completed and all data has been received.

Using the StateChanged event is an essential part of using the Internet Transfer Control's advanced methods - that is, methods other than OpenURL. We'll see how soon. You can also use this event to provide status messages to the user, giving information on what tasks the control is doing or has completed. Listing 1 shows the code to do this. In this example I am using a Text Box control named txtStatus to display the status messages. You could use various other methods to display the messages equally well, such as a status bar.

You should note that the control state identified by the number 11, icError, indicates that an error has occurred. But what error? Lots of things can go wrong when trying to transfer information over the Internet. Fortunately the Internet Transfer Control has two properties, ResponseCode and ResponseInfo, that provide a numerical code and a text description of the most recent error. You can see that the code in Listing 1 uses these properties to provide the user with a description of any error that occurs.

Listing 1. Using the StateChanged event to provide status messages to the user.

Private Sub Inet1_StateChanged(ByVal State As Integer)

Select Case State

Case icResolvingHost ' 1

txtStatus.Text = "Looking up IP address of host computer"

Case icHostResolved ' 2

txtStatus.Text = "IP address found"

Case icConnecting ' 3

txtStatus.Text = "Connecting to host computer"

Case icConnected ' 4

txtStatus.Text = "Connected"

Case icRequesting ' 5

txtStatus.Text = "Sending a request to host computer"

Case icRequestSent ' 6

txtStatus.Text = "Request sent"

Case icReceivingResponse ' 7

txtStatus.Text = "Receiving a response from host computer"

Case icResponseReceived ' 8

txtStatus.Text = "Response received"

Case icDisconnecting ' 9

txtStatus.Text = "Disconnecting from host computer"

Case icDisconnected ' 10

txtStatus.Text = "Disconnected"

Case icError ' 11

txtStatus.Text = "Error " & Inet1.ResponseCode & _

" " & Inet1.ResponseInfo

Case icResponseCompleted ' 12

txtStatus.Text = "Request completed - all data received"

End Select

End Sub

You should be aware that StateChanged events are generate by the OpenURL method even though, given the fact that this method operates synchronously, the events are not really needed. Problems can arise in a program that uses OpenURL as well as the other asynchronous methods because the StateChanged event will be fired, and the associated code executed, at times when it is not needed and may in fact cause mischief. The solution I use for this potential problem is to maintain a global flag that indicates when OpenURL is active:

Dim UsingOpenURL As Boolean

UsingOpenURL = True

buf = Inet1.OpenURL(…)

UsingOpenURL = False

Then in the StateChanged event procedure:

 

Private Sub Inet1_StateChanged(ByVal State As Integer)

If UsingOpenURL Then Exit Sub

End Sub

Getting Chunks

Now let's get back to the problem of retrieving data from the control's buffer. This is done with the GetChunk method. The syntax is as follows:

Inet1.GetChunk( size [,datatype] )

The size argument specifies how many bytes of data to retrieve; it is a type Long. The optional datatype argument specifies whether the data is text or binary. You use the same values for this argument as with the OpenURL method: icString (the default, value = 0) and icByteArray (value = 1). If you try to retrieve more bytes of data than are present in the buffer, you'll simply get whatever is there. If you retrieve only part of the data in the buffer, the remainder of the data stays in the buffer ready to be retrieved with the next call to GetChunk.

Remember, however, that we are dealing here with asynchronous data retrieval. This means that when you execute one of the data retrieval methods that places data in the Internet Transfer Control's buffer, program execution continues while the control waits for the data. How then do you know when the data has been received and is waiting in the buffer to be retrieved with GetChunk?

The answer lies in the StateChanged event. Two of the possible events, as described earlier, can signal the successful receipt of data: icResponseReceived (value = 8) and icResponseCompleted (value = 12). The technique, then, is to place the calls to GetChunk in the StateChanged event procedure, executing them if and only if one of the above states has occurred. To allow for the possibility that it will take more then one call to GetChunk to retrieve all the data in the buffer, we can use a loop. Here's the code, which displays the full buffer contents in a Rich Text Box control named RTB1:

Listing 2. Using GetChunk in the StateChanged event procedure to retrieve data from the Internet Transfer Control buffer.

Private Sub Inet1_StateChanged(ByVal State As Integer)

Dim s1, s2

Select Case State

' Other cases not shown.

Case icResponseCompleted

s1 = ""

s2 = ""

Do

s1 = Inet1.GetChunk(512, icString)

s2 = s2 & s1

Loop Until s1 = ""

RTB1.Text = s2

End Select

End Sub

You should be aware that the icResponseReceived state does not always mean that there is data in the buffer. Some operations, such as handshaking with an FTP site, result in this state without any data being placed in the buffer. If you wait for icResponseCompleted you can be sure that any and all data has been received.

The Execute Method

Perhaps the most important method you'll use with the Internet Transfer Control is Execute. With this method you can send a variety of commands to the remote computer. The syntax is:

Inet1.Execute (url, operation, data, requestHeaders)

The url argument specifies the remote computer that the command will be sent to. This argument is optional; if it is omitted, the URL specified in the control's URL property is used. The operation argument specifies the command to be sent, as explained below. The remaining two arguments are used only with certain commands.

The commands that you can send with the Execute method depend on the connection protocol, FTP or HTTP. For an HTTP connection there are 4 available commands:

GET Retrieve data from the server.

HEAD Retrieve header only from the server

POST Sends additional data required to complete a request.

PUT Uploads a file to the server.

When using Execute with GET or HEAD, the data returned by the server is placed in the Internet Transfer Control's internal buffer. You retrieve the data using the GetChunk method as was explained earlier. Here are some examples of using Execute with GET:

To retrieve the entire default Microsoft home page:

Inet.Execute "http://www.microsoft.com" , "GET"

To retrieve the "books" page from my Web site:

Inet1.Execute "http://www.pgacon.com/books.htm", "GET"

To retrieve the headers from Microsoft's home page:

Inet.Execute "http://www.microsoft.com" , "HEAD"

The GET command can be used to submit data to CGI scripts, for example submitting queries to web search engines. You need to know the proper syntax, of course. For example, the following command searches the Yahoo site for references to "bananas."

Inet1.Execute "http://search.yahoo.com/bin/search?p=banana" , "GET"

What about using the GET with the FTP protocol? It is a bit more involved than HTTP, primarily because there are so many more FTP commands. I do not have the space to cover FTP in this column, but you can find some information in Visual Basic's documentation.