by Brad Wist
Thanks to the Internet's ever-increasing prominence in our world, we developers are constantly finding new and better ways to take advantage of its capabilities. Frequently, that means finding new ways to perform tasks on the Internet-pushing the limits to do something that hasn't been done before. At other times, we must find alternate paths to take advantage of functionality that's existed for years, such as file transfers using the File Transfer Protocol (FTP).
FTP gives us the ability to send or receive all sorts of files across the Internet. Web browsers use underlying FTP functionality when downloading files. We can employ that same functionality in our Visual Basic applications to transfer files across the Internet or intranet.
To begin, we could pull up the specifications for FTP (ds.internic.net/rfc/rfc959.txt) and start writing massive amounts of code to accomplish our task. Or we could take advantage of the Internet Transfer Control that ships with VB 5. Let's take the second option.
In this article, we'll build a simple application that will allow us to connect to an FTP site and transfer files back and forth. The application we'll design, shown in Figure A, lets the user specify a URL, user name, and password. Drive, directory, and file list boxes provide access to local files, while a separate list box displays remote files.
Figure A: Our sample application looks like this at design time.We'll also use the status bar control that ships with VB to display status information about the connection. To add the status-bar control to your application, choose the Project | Components… menu item, then select Microsoft Windows Common Controls 5.0 and click OK.
The sample application lets users transfer files by dragging them from one list box to the other. Although this feature is optional, it's very user-friendly. The sample code found in Listing A shows you the steps to implement this drag-and-drop feature-pay particular attention to the MouseDown, MouseUp, and DragDrop events. We'll examine how those transfers are performed throughout the rest of this article.
Listing A: Code for core FTP functionality
Private Const ftpDIR As Integer = 0
Private Const ftpPUT As Integer = 1
Private Const ftpGET As Integer = 2
Private Const ftpDEL As Integer = 3
Private iLastFTP As Integer
Private Sub cmdConnect_Click()
On Error GoTo ConnectError
Inet1.URL = txtURL
Inet1.UserName = txtUserName
Inet1.Password = txtPassword
Inet1.Protocol = icFTP
iLastFTP = ftpDIR
Inet1.Execute Inet1.URL, "DIR"
End Sub
Private Sub Inet1_StateChanged(ByVal _
State As Integer)
Select Case State
Case icNone
sbFTP.Panels("status").Text = ""
Case icResolvingHost
sbFTP.Panels("status").Text
= "Resolving Host"
Case icHostResolved
sbFTP.Panels("status").Text _
= "Host Resolved"
Case icConnecting
sbFTP.Panels("status").Text _
= "Connecting..."
Case icConnected
sbFTP.Panels("status").Text _
= "Connected!"
Case icRequesting
sbFTP.Panels("status").Text _
= "Requesting..."
Case icRequestSent
sbFTP.Panels("status").Text _
= "Request Sent"
Case icReceivingResponse
sbFTP.Panels("status").Text _
= "Receiving Response..."
Case icResponseReceived
sbFTP.Panels("status").Text _
= "Response Received!"
Case icDisconnecting
sbFTP.Panels("status").Text _
= "Disconnecting..."
Case icDisconnected
sbFTP.Panels("status").Text _
= "Disconnected"
Case icError
sbFTP.Panels("status").Text _
= "Error! " & Trim(CStr( _
Inet1.ResponseCode)) & _
": " & Inet1.ResponseInfo
Case icResponseCompleted
sbFTP.Panels("status").Text _
= "Response Completed!"
ReactToResponse iLastFTP
End Select
End Sub
Public Function _
ReactToResponse(ByVal _
iLastCommand As Integer) As Long
Select Case iLastCommand
Case ftpDIR
ShowRemoteFileList
Case ftpPUT
MsgBox "File Sent from " & CurDir()
Case ftpGET
MsgBox "File Received "& "in " & CurDir()
Case ftpDEL
End Select
End Function
Public Function ShowRemoteFileList() As Long
Dim sFileList As String
Dim sTemp As String
Dim p As Integer
sTemp = Inet1.GetChunk(1024)
Do While Len(sTemp) > 0
DoEvents
sFileList = sFileList & sTemp
sTemp = Inet1.GetChunk(1024)
Loop
lstRemoteFiles.Clear
Do While sFileList > ""
DoEvents
p = InStr(sFileList, vbCrLf)
If p > 0 Then
lstRemoteFiles.AddItem
Left(sFileList, p - 1)
If Len(sFileList) > (p + 2) Then
sFileList = Mid(sFileList, p + 2)
Else
sFileList = ""
End If
Else
lstRemoteFiles.AddItem sFileList
sFileList = ""
End If
Loop
End Function
Public Function GetFiles(sFileList As String) As Long
Dim sFile As String
Dim sTemp As String
Dim p As Integer
iLastFTP = ftpGET
sTemp = sFileList
Do While sTemp > ""
DoEvents
p = InStr(sTemp, "|")
If p Then
sFile = Left(sTemp, p - 1)
sTemp = Mid(sTemp, p + 1)
Else
sFile = sTemp
sTemp = ""
End If
Inet1.Execute Inet1.URL, "GET " & sFile & _
" " & sFile
'wait until this execution is done
'before going to next file
Do
DoEvents
Loop Until Not _
Inet1.StillExecuting
Loop
iLastFTP = ftpDIR
Inet1.Execute Inet1.URL, "DIR"
End Function
Public Function PutFiles(sFileList As String) As Long
Dim sFile As String
Dim sTemp As String
Dim p As Integer
iLastFTP = ftpPUT
sTemp = sFileList
Do While sTemp > ""
DoEvents
p = InStr(sTemp, "|")
If p Then
sFile = Left(sTemp, p - 1)
sTemp = Mid(sTemp, p + 1)
Else
sFile = sTemp
sTemp = ""
End If
Inet1.Execute Inet1.URL, "PUT" & sFile & _
" " & sFile
'wait until this execution is done
'before going to next file
Do
DoEvents
Loop Until Not Inet1.StillExecuting
Loop
iLastFTP = ftpDIR
Inet1.Execute Inet1.URL, "DIR"
End Function
Private Sub dirLocal_Change()
filLocal.Path = dirLocal.Path
End Sub
Private Sub drvLocal_Change()
dirLocal.Path = drvLocal.Drive
End Sub
Private Sub filLocal_DragDrop(Source _
As Control, X As Single, Y As Single)
'receiving files from FTP site.
Dim I As Integer
Dim sFileList As String
If TypeOf Source Is ListBox Then
For i = 0 _
To Source.ListCount - 1
If Source.Selected(i) Then
sFileList = _
sFileList & _
Source.List(i) & "|"
End If
Next
End If
If Len(sFileList) > 0 Then
'strip off the last pipe
sFileList = Left(sFileList, _
Len(sFileList) - 1)
GetFiles sFileList
End If
End Sub
Private Sub _
filLocal_MouseDown(Button As _
Integer, Shift As Integer, X As _
Single, Y As Single)
filLocal.Drag vbBeginDrag
End Sub
Private Sub filLocal_MouseUp(Button _
As Integer, Shift As Integer, _
X As Single, Y As Single)
filLocal.Drag vbEndDrag
End Sub
Private Sub _
lstRemoteFiles_DragDrop(Source _
As Control, X As Single, Y As Single)
Dim I As Integer
Dim sFileList As String
If TypeOf Source Is FileListBox Then
For i = 0 To Source.ListCount - 1
If Source.Selected(i) Then
sFileList = sFileList & _
Source.List(i) & "|"
End If
Next
End If
If Len(sFileList) > 0 Then
'strip off the last pipe
sFileList = Left(sFileList, _
Len(sFileList) - 1)
PutFiles sFileList
End If
End Sub
Private Sub _
lstRemoteFiles_KeyDown(KeyCode _
As Integer, Shift As Integer)
If KeyCode = vbKeyDelete Then
Inet1.Execute Inet1.URL, "DEL " & _
lstRemoteFiles.List( _
lstRemoteFiles.ListIndex)
Do
DoEvents
Loop While Inet1.StillExecuting
End If
iLastFTP = ftpDIR
Inet1.Execute Inet1.URL, "DIR"
End Sub
Private Sub _
lstRemoteFiles_MouseDown(Button _
As Integer, Shift As Integer, )
X As Single, Y As Single)
lstRemoteFiles.Drag vbBeginDrag
End Sub
Private Sub lstRemoteFiles_MouseUp(Button As _
Integer, Shift As Integer, _
X As Single, Y As Single)
lstRemoteFiles.Drag vbEndDrag
End Sub
Internet Transfer Control
The Internet Transfer Control provides fairly extensive capabilities for transferring data across the Internet, in the form of either Web pages or files. For our purposes, we'll concentrate on file transfers and leave the rest for another article.
The control resides in the MSINET.OCX file. To load the control into your VB toolbox, choose Project | Components…. Next, find the Microsoft Internet Transfer Control 5.0 control, select it by placing an X beside it, then click OK. Now, add the control to your project form. Note that the control will appear as a button and won't be visible at runtime.
You can open the Object Browser (by pressing [b]) to examine all the properties, methods, events, and built-in constants available through this code. In addition to the control's help file, this information makes an excellent reference. For this article, we'll focus on the small set of available properties and methods listed in Table A.
Table A: Selected properties, methods, and events
Properties | Description |
Password | The password you use when connecting with the FTP server. |
StillExecuting | Specifies whether a command is still being processed. |
URL | The URL of the FTP server. |
Username | User name to use to log into the FTP server. |
Execute | Initiates an asynchronous command/connection. |
GetChunk | Reads data from the buffer. |
OpenURL | Initiates a synchronous command/connection. |
StateChangedFires whenever the control state has changed, for example, when a response is received from the FTP server.
Using the controlTo perform FTP transfers, you must follow a few basic steps. First, you define the FTP server you want to attach to. You can specify the FTP site in two ways: using the RemoteHost and RemotePort properties or via the URL property. For simplicity's sake, we'll use the URL property:
Inet1.URL = txtURL
You also must specify the user name and password you'll provide. Many FTP sites allow anonymous connections. In those cases, the user name anonymous will work with any password you like, although most FTP sites ask you to provide your E-mail address, as well. Here's the syntax:
Inet1.UserName = txtUsername
Inet1.Password = txtPassword
Setting the URL property will clear the Username and Password properties. So, be sure to set the URL first, then specify the user name and password.
Since we're going to be dealing strictly with FTP connections, we'll set the Protocol property accordingly, as follows:
Inet1.Protocol = icFTP
We'll want to execute these commands when we make our first connection to the FTP server. We use the cmdConnect command button to establish this connection, so the code will go to the server. At the same time, when we make this first FTP connection, we'll also retrieve the list of files available on the FTP server. We'll see how to do this next.
Execute gets things doneYou'll use the Execute method to send all commands to the FTP site through the control. The syntax of the Execute method is
Inet1.Execute URL, Operation, Data, _
RequestHeaders
However, when performing FTP commands, we'll only use the URL and Operation parameters. The others have no meaning for us-they're used in other processes.
You send all FTP commands in the Operation parameter; they take the syntax command [file1 [file2]]. The help file for the Internet Transfer Control includes a list of valid FTP commands under the Execute page. We'll focus on a few of these commands in the rest of this article.
Asynchronous processingWhen you're using the Execute method, keep in mind that all its operations are asynchronous. This means that when you tell the control to perform an operation, it starts the operation but returns control back to the application. The control will handle all communications back and forth, based on the properties and commands you've given it. When the operation is completed, the control will notify the application.
If you use the OpenURL method, the control makes a synchronous connection and executes the command. However, control doesn't return until the command finishes executing. This more straightforward approach is somewhat simpler to program. Since the asynchronous approach is more flexible-and therefore preferable-we'll use it exclusively here.
Our discussion of the asynchronous approach would be incomplete without mentioning the StillExecuting property. This property identifies when the control is in the middle of performing some operation. If you need to perform an operation that requires several commands, you'll start the first command, loop until the control has stopped processing the command (i.e., StillExecuting is False), then move on to the next operation, as follows:
Inet1.Execute txtURL, "get MyFile.txt"
Do
DoEvents
Loop While Inet1.StillExecuting
In event-driven programming, we want to be able to react when the operation is complete. We'll use the StateChanged event to provide this functionality. Specifically, we'll look for the new State of the control to be icResponseCompleted. It may be useful to set a variable, such as iLastFTP, to store a value signifying which FTP command executed last. Then you can test that variable in the StateChanged event to determine what command completion you're reacting to, with the lines:
Sub Inet1.StateChanged(ByVal State As Integer)
Select Case State
Case icResponseCompleted
'put your code here
End Select
End Sub
Of course, the State parameter can hold a number of other values as well. We show these in the full code listing, found in Listing A. You can also check the help file for all these values. Now, let's build our FTP application.
Creating the sample projectThe first step is to begin a new EXE project in VB5. Build a form similar to that shown in Figure A.
As you can see, the form should include TextBox controls for the target URL, user name, and password. You'll also need to provide a way to display both local and remote files. Our example uses the DirListBox, DriveListBox, and FileListBox controls for the local files, and a standard ListBox control to display the remote files. Finally, you must add a CommandButton to establish the initial connection. After that, our work with the list boxes will be complete. Table B shows the controls to add to the form, as well as some key properties. (You can also download our sample files from www.zdjournals.com/ivb. Click on the Source Code hyperlink.)
Table B: Controls to add to the form
Control | Property | Setting |
Form | Caption | File Transfer |
TextBox | Name | txtURL |
TextBox | Name | txtUserName |
TextBox | Name | txtPassword |
DriveListBox | Name | drvLocal |
DirListBox | Name | dirLocal |
FileListBox | Name | filLocal |
ListBox | Name | lstRemoteFiles |
CommandButton | Name Caption | cmdConnect Connect |
As the name implies, the Connect button will connect to the designated FTP site and retrieve a list of files. To accomplish this, put the following code in the cmdConnect_Click event:
Public Sub cmdConnect_Click()
Inet1.URL = txtURL
Inet1.UserName = txtUserName
Inet1.Password = txtPassword
Inet1.Protocol = icFTP
'Use constant to identify that
'we're getting a directory listing.
'We'll use it in the
'Inet1_StateChanged Event.
iLastFTP = ftpDIR
Inet1.Execute Inet1.URL, "DIR"
End Sub
The first time we use the Execute method, we establish a connection between the user machine and the FTP site.
Executing the Dir command will place a list of files in the control's buffer. To retrieve them from the buffer, we'll use the GetChunk method. GetChunk requires one parameter that specifies the maximum amount of data (in bytes) that we'll retrieve. We specify an amount and keep looping until we've emptied out the buffer. The result is a string of filenames, separated by a carriage return and line feed (vbCrLf). We can then display the list of files however we want. We wrote the function ShowRemoteFileList() in Listing A to load the file list into a list box.
Once we have the list of files, we can let the user upload files to (or download files from) the FTP site. Since downloading files is more common, let's consider this example first. We download files by executing the FTP command Get. The syntax of the command is Get file1 file2, where file1 is the name of the file on the FTP site and file2 is the name you want the file to have locally. File2 can include path information as well. The GetFiles() function in Listing A demonstrates how to issue the command and retrieve the file.
Similarly, if you want to upload a file to the FTP site (assuming you have write privileges at the site), you use the FTP command Put. The syntax of the command is Put file1 file2, where file1 is the local filename (which can include the path) and file2 is the name the file will have on the FTP site. The PutFiles() function in Listing A demonstrates this process.
Please note that you'll have a problem to work around. The FTP Command Line doesn't allow spaces in the filename or path. To solve this problem, you can take one of the following steps:
With a little work, our application can allow the user to select multiple files to transfer in one operation. Of course, the application will need to issue an Execute command for each transfer. Then, we must test the StillExecuting property to determine whether the control has finished executing that command. Once it's complete, we can loop back and send the command again for the second file. We can continue this process for as many files as necessary.
Known bugs and issuesYou should be aware of several issues that exist with the current versions of the control. These issues vary depending on which version you're using.
In the version that ships with VB 5 (version 5.00.3714), the control sends all filenames as uppercase when you're sending or receiving files. If you're hitting an Internet Information Server (IIS) using NT/DOS file settings, case doesn't matter, since the filenames aren't case-sensitive. However, if you're hitting a UNIX server, it's extremely important, since UNIX filenames are case-sensitive. The result is that any files you send will be named in all uppercase, and you won't be able to retrieve files that have lowercase letters in their names.
Fortunately, Microsoft is aware of this conflict (see the Microsoft Knowledge Base article support.microsoft.com/support/kb/articles/Q168/7/66.asp for more information) and has corrected it in Service Pack 2 for Visual Studio. However, the SP2 control (version 5.01.4319) introduces an even worse problem.
In the SP2 version of the control, you can't log in to any server, other than a strictly anonymous server (such as ftp://ftp.microsoft.com). User names and passwords are sent incorrectly to the FTP server. (See the Microsoft Knowledge Base article support.microsoft.com/support/kb/articles/Q173/2/65.asp for more details.)
Finally, Microsoft released Service Pack 3 (http://www.microsoft.com/vstudio/sp) in early December 1997, correcting these problems. So, be sure to upgrade to SP3 before developing your own projects.
ConclusionAs the Internet's importance grows in our daily lives, we must make our applications more Internet-aware. Actually, the Internet offers several solutions to some potential problems-the challenge is to take advantage of the existing capabilities to meet those challenges. If you need to transfer files between two Internet sites, the Internet Transfer Control offers a quick solution. In this article, we've shown you how to use the control in your applications. We've also pointed out a couple of bugs to work around.
Copyright © 1998, ZD
Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD
Inc. Reproduction in whole or in part in any form or medium without
express written permission of ZD Inc. is prohibited. All other product
names and logos are trademarks or registered trademarks of their
respective owners.