We've now covered enough of the FileSwapper code to examine the main form, which acts as the hub of the application.
When the main form first loads, it reads the registry, updates the configuration window with the retrieved settings, starts the other threads, and then logs in, as shown here:
Private Sub SwapperClient_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Me.Show() Me.Refresh() ' Read the registry. Global.Settings.Load() txtSharePath.Text = Global.Settings.SharePath txtPort.Text = Global.Settings.Port chkMP3Only.Checked = Global.Settings.ShareMP3Only txtUploads.Text = Global.Settings.MaxUploadThreads txtDownloads.Text = Global.Settings.MaxDownloadThreads ' Create the search, download, and upload objects. ' They will create their own threads. App.SearchThread = New Search(lstSearchResults) App.DownwnloadThread = New FileDownloadQueue(lstDownloads) App.UploadThread = New FileServer(lstUploads) App.UploadThread.StartWaitForRequest() ' Start the login process. Global.Identity.Port = Global.Settings.Port DoLogin() End Sub
The login is actually a multiple step procedure. First, the peer information is submitted with the App.Login() method. Next, the file catalog is created and submitted with the App.PublishFiles() method. Finally, the timer is enabled to automatically update the login information as required.
While the peer is sending data to the discovery web service, the mouse pointer is changed to an hourglass, and the text in the status bar panel is updated to reflect what's taking place.
Private Sub DoLogin() Me.Cursor = Cursors.WaitCursor ' Log in. pnlState.Text = "Trying to log in." App.Login() If Not Global.LoggedIn Then pnlState.Text = "Not logged in." Return End If ' Submit list of files. pnlState.Text = "Sending file information..." If App.PublishFiles() Then pnlState.Text = "File list published to server." Else pnlState.Text = "Could not publish file list." End If ' Refresh login information every five minutes. tmrRefreshRegistration.Start() Me.Cursor = Cursors.Default End Sub
The timer fires every 300,000 milliseconds (every five minutes) to update the login information:
Private Sub tmRefreshRegistration_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles tmrRefreshRegistration.Tick App.RefreshLogin() End Sub
Currently, no steps are taken to refresh the published file list, although you can add this functionality easily using a timer, or by monitoring the file system for changes.
When the form closes, the client is automatically logged out, and the threads are terminated:
Private Sub SwapperClient_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Closed App.Logout() App.DownwnloadThread.Abort() App.SearchThread.Abort() App.UploadThread.Abort() End Sub
In this case, the code is actually not aborting the thread directly. Instead, it's calling a custom Abort() method that's provided in each of the threaded classes. The code in this method then terminates processing in the most reasonable manner, as you'll see later in this chapter.
To be even more cautious, the FileSwapper also traps the Application.UnhandledException event. This event fires if an exception is about to terminate your application (typically because it isn't handled with a Catch block). You won't be able to stop the application from ending, but you'll be able to perform some last minute cleanup such as attempting to log out of the discovery service, or logging information about the error.
Public Sub UnhandledException(ByVal sender As Object, _ ByVal e As UnhandledExceptionEventArgs) ' Log the error. Trace.Write(e.ExceptionObject.ToString()) ' Log out of the discovery service. App.Logout() End Sub
This event handler is coded inside the main form and attached shortly after a login:
Private Sub SwapperClient_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' (Other code omitted.) DoLogin() AddHandler AppDomain.CurrentDomain.UnhandledException, _ AddressOf UnhandledException End Sub
The FileSwapper application includes a configuration window (see Figure 9-5) that allows the registry settings to be configured by the user. This window doesn't perform any validation, although you could add this code easily.
When the user clicks the Update() button, these settings are saved in the registry. The peer then logs out and logs back in to the discovery service.
Private Sub cmdUpdate_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdUpdate.Click Global.Settings.Port = Val(txtPort.Text) Global.Settings.SharePath = txtSharePath.Text Global.Settings.ShareMP3Only = chkMP3Only.Checked Global.Settings.MaxDownloadThreads = Val(txtDownloads.Text) Global.Settings.MaxUploadThreads = Val(txtUploads.Text) Global.Settings.Save() ' Log back in. App.Logout() Global.Identity.Port = Global.Settings.Port DoLogin() End Sub