Online Updates with VB.NET

Introduction

Programmers often split larger applications into smaller, manageable DLLs. Often, shortly after release one or more of the DLLs show minor problems and require bug fixes. However, it is not always easy to get the bug fixes to all the software users. Fortunately, in today's workplace, almost everyone has some connection to the Internet. You are going to look at using this connection to deliver an update.

As difficult as it sounds, I soon found out after been tasked to write an auto updater for some of our larger applications, that it's reasonably simple. There is simply a short list of tasks to follow, and everything needed is already included in .NET.

  1. Check for an active Internet connection.
  2. Check whether there are updates available for this application.
  3. Download the files to their respective locations.
  4. Start the updated application.

The Code

First, you need to set a few defaults and locations.

'Project name
Public Const AppName As String = "Hex Editor"

' Unique Project ID --
Public Const UpdateID As String = "HexEdit"

' The Application's Root Path "C:\Dir1\Dir2" (No trailing backslash)
Public Const AppPath As String = "C:\Program Files\Hex_edit"

' The Application's Exe "Dir3\Prog.exe" (from the root path.
' No leading Backslash)
Public Const AppEXE As String = "Hex_edit.EXE"

' Other startup defaults
' Last update loaded...
Public Const LastUpdate As Integer = 0
' Wait time to connect to Update Site in seconds
Public Const TimeOutlen As Integer = 60
' Default site to download updates from
Public Const UpdateSite As String = "updates.mysite.com"

Because the Auto Updater is written as a general application, and you want to be able to add it to any project without having to modify large blocks of code, anything that is specifically related to the project is stored in variables.

The above defaults relate to first time usage, and should be stored in a file or the Registry. This I will leave up to you to decide where to store the info.

Your first task is to check whether you have an open connection to the Internet. For that, you add a Standard WebBrowser object to your form. And, make it reasonably large enough to hold relevant details about the update site. By using the WebBrowser, you can automatically use the details set in Internet Explorer to connect to the Internet, and not hassle the user for details about gateways, proxies, and firewalls. The WebBrowser also has a Document Completed event that will trigger when the page has completed loading.

The website that will be hosting the updates will have two requirements:

First, the default page must always stay with the same name. You can alter the page content, but keep the name constant. Second, use a Fixed Page Title to identify the page. Once the page has completed loading, you use the Document Title to verify that you are at the right site.

Private WithEvents Timer As Windows.Forms.Timer
Private TimeDelay As Integer    ' Timeout in seconds
Private HttpWait As Boolean     ' Wait for Http page to load
Private Retry As Boolean        ' Retry to load failed connection
Private TimeOut As Boolean      ' Wait has timed out
Private Canceled As Boolean     ' Cancel button Clicked
Private Closeme As Boolean      ' Close the form
Private Updated As Boolean      ' Files were updated

Private Sub WBData_DocumentCompleted(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _
   Handles WBData.DocumentCompleted
   HttpWait = True
   If WBData.DocumentTitle.ToUpper <> "MY UPDATE SITE" Then _
      TimeOut = True
End Sub

Private Sub Timer_Tick(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles Timer.Tick
   If TimeDelay = 0 Or Canceled Then
      TimeOut = True
      Timer.Enabled = False
   Else
      TimeDelay -= 1
   End If

End Sub

Private Sub Cancel_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles Button3.Click
   Canceled = True
End Sub

Private Sub StartUpdate()
   Dim Updaterfile As String
   Dim Serverfile As String
   Dim TotalUpdates As Integer
   Dim LastUpdate As Integer
   Dim msgresult As MsgBoxResult

   Me.Show()
   LblText.Text = "Connecting to Remote Server" & vbCrLf & _
   "Please ensure that your internet connection is active"
   Me.Refresh()
   WBData.Url = _
      New System.Uri("http://" & UpdateSite & "/Default.html")
   HttpWait = False
   TimeDelay = TimeOutlen
   Timer = New Windows.Forms.Timer
   Timer.Interval = 1000
   Timer.Enabled = True
   While Not (HttpWait Or TimeOut Or Canceled)
      System.Windows.Forms.Application.DoEvents()
.................

As you can see, when the document has completed loading you check whether the document title matches your preset title. If the title does not match, you have not connected successfully to your update site. You also have a countdown timer running to make sure that you don't wait too long for a connection. Just remember: The more complex your web page, the longer to load it. So, keep you update web page small.

You also have a cancel button on your form so that, if the user does have a slow Internet connection, or just does not want to worry about the update right now they can skip the update. From here, quickly jump to Task #4, starting the application. Why jump ahead? At this point, you may need to start the application without ever completing an update check. The update could be cancelled or timed out, so you want to start the main application.

Private Sub AutoUpdater_FormClosing(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.FormClosingEventArgs) _
   Handles Me.FormClosing
   If Not Closeme Then
      e.Cancel = True
   End If
End Sub

Private Sub LoadApp()
   Shell(AppPath & "\" & AppEXE, AppWinStyle.NormalFocus)
   Closeme = True
   Me.Close()
End Sub

Here, you simply use the Shell command to start the main application and exit the updater. Not much more can be said for this task. However, many may ask, "Why not do all this within my main application?" If you late bound just about everything in your application I'm sure you could; however, you still would not really be able to update the primary EXE file of the project. Using a second application to update and run the main application gives you the ability to update just about everything in a single pass and start the application without the need to stop and restart.

Now that loading the Main Application is taken care of, get back to what happens when the Connection times out, or the user clicks Cancel.

...........
   If TimeOut Then
      LblText.Text = "Connection to Remote Server Failed" & _
      vbCrLf & "Application will continue in 10 seconds"
      Button1.Enabled = True
      TimeOut = False
      TimeDelay = 10
      Timer.Interval = 1000
      Timer.Enabled = True
      Retry = False
      While Not (TimeOut Or Retry Or Canceled)
         System.Windows.Forms.Application.DoEvents()
      End While
      If Retry Then
         LblText.Text = "Reconnecting to Remote Server" & _
         vbCrLf & " Please ensure that your Internet connection _
                    is active"
         Button1.Enabled = False
         TimeOut = False
         HttpWait = False
         TimeDelay = TimeOutlen
         Timer.Interval = 1000
         Timer.Enabled = True
      Else
         LoadApp()
         Exit Sub
      End If
   End If
End While
...............

Inside the While Wend loop, you check whether a timeout occurred. If so, you give the user an opportunity to retry; if the user decides against retrying to connect, of the retry timer runs out, you simply open the Main application and exit. By putting your retry code within the original Timeout loop, the user can have multiple retries at connecting to the server.

Now, you are at the point where you've successfully connected to the Internet and contacted your Update site; the user has not clicked Cancel. You want to check whether there are outstanding updates for this application. The easiest method I found to do this was simply by downloading a file, using the Unique UpdateID constant that you set earlier.

...............
If Not Canceled Then
   LblText.Text = "Connection to Remote Server completed" & _
   vbCrLf & " Please wait while checking for new updates"
   GBLbl.Refresh()
   Updaterfile = AppPath & "\UpdateLog.xml"
   Serverfile = "http://" & UpdateSite & "/" & UpdateID & ".XML"
   Try
      If File.Exists(Updaterfile) Then File.Delete(Updaterfile)
      My.Computer.Network.DownloadFile(Serverfile, Updaterfile)
................

Online Updates with VB.NET

Here is where it all gets interesting. The update log file is stored on the site, and by using the Built in HTTP downloader, you can pull the file off the server very quickly. (NOTE: It's necessary to delete any previous copy of the file because the HTTP Downloader does not always overwrite files.) You will also notice that I'm using XML to store the relevant info needed. XML has become the new standard of storing small to large amounts of data that is required quickly, or needs to be transmitted. Even Office 2007 now uses the XML format to store documents.

The XML format that you use for your update data is very simple. A sample is shown below.

<?xml version="1.0" encoding="utf-8" ?>
<updates total ="1">
   <file name="test1">
      <server>hexedit/test1.xml</server>
      <local>test1.dat</local>
      <update>1</update>
   </file>
</updates>

You use the built-in XML Node functions to extract your different nodes. (A sample XML Function Class is included in the download and is a little beyond the scope of this article to explain.) The Class exposes a few simple commands to transpose through the nodes of the XML file and returns an object with the relevant info for each file.

...........
XMLFunction.OpenXMLDoc(Updaterfile)
XMLFunction.XMLSNode("/updates")
TotalUpdates = Val(XMLFunction.GetNodeVal("total"))
If TotalUpdates > LastUpdate Then
.............

Here, you complete Task #2, You check whether there are updates available. Your XML file has several items of data in it and, when adding files to the list, you increment the Update number by one; then, each file that gets updated is given this update number so that the application knows which update each file is attached to. By comparing the Last update in the file and the Update number in the file, you can see whether or not it is necessary to download files.

.............
   FileList = XMLFunction.GetFiles
   For Each ThisFile As XMLFunc.XmlFunctions.FileDetails In FileList
      If ThisFile.Update > LastUpdate Then
         Updaterfile = AppPath & "\" & ThisFile.Local
         Serverfile = "http://" & UpdateSite & "/" _
            & ThisFile.Server
         LblText.Text = "Downloading " & ThisFile.File & _
         " from server" & vbCrLf & " Please wait for update to complete"
         GBLbl.Refresh()
         If File.Exists(Updaterfile) Then File.Delete(Updaterfile)
         My.Computer.Network.DownloadFile(Serverfile, Updaterfile)
      End If
   Next
End If
...............

If you find that there is a new update available, you then complete Task #3, download and install the files. Because each file has a number that relates to a specific update, you can simply loop over each file and check whether the specified update has been loaded. This also allows the software to download multiple updates in a single session. Another advantage is that, if the same file has been included in a number of updates, the developer can always edit the original entry for that file to the latest update number and save bandwidth from users downloading redundant updates.

You also will notice that the server file extension and the target file extension name are different in the XML example. The simple reason is that some Web Hosting servers consider some file extensions as local data, and will prevent a HTTP request from downloading it. This is mostly done for server security, and rather than opening up the server for attacks, you offer the file with a name that is considered safe to download and save it with its original name. Your Server blocked the download of DAT files. This also allows you to store named files for multiple projects on a single server, without stressing that two projects may have the same named file that are not cross compatible.

...............
         If Updated Then
            LblText.Text = "Update succesfully installed." & vbCrLf & _
            "Application will Exit in a few seconds"
            LastUpdate = TotalUpdates
         Else
            LblText.Text = "No Updates to load." & vbCrLf & _
            "Application will Exit in a few seconds"
         End If
      Catch ex As Exception
         LblText.Text = "Error during download" & vbCrLf & _
         "Application will Exit in a few seconds"
      End Try
      TimeOut = False
      TimeDelay = 10
      Timer.Interval = 1000
      Timer.Enabled = True
      Retry = False
      Button3.Enabled = False
      Button4.Enabled = True
      While Not (TimeOut Or Canceled)
         System.Windows.Forms.Application.DoEvents()
      End While
   End If
   LoadApp()
End Sub

All that is left is to inform the user of the final result and start the Main application. Also, do not forget that you need to store the last update on the system, either in a file or the Registry.

Auto updaters can be a lot more advanced than what is shown here; however, the primary idea here is to keep the project as simple as possible, and hence as bug free as possible, because unfortunately this auto updater cannot update itself.

Conclusion

By adding this simple base code to your projects, you can further improve the ability to deliver much-needed updates to your clients/users, and this can add that much-needed value that many people are looking for.



About the Author

Richard Newcombe

Richard Newcombe has been involved in computers since the time of the Commodore 64. Today, he has excelled in programming, and designs. Richard is in his mid 30's and, if or when you looking for him look no further than his computer. Always willing to help and give advice where he can in regard to computer related subjects. At present he is working as a .NET 2008 Software Developer for Syncrony Web Services, South Africa.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • 10 Rules that Make or Break Enterprise App Development Projects In today's app-driven world, application development is a top priority. Even so, 68% of enterprise application delivery projects fail. Designing and building applications that pay for themselves and adapt to future needs is incredibly difficult. Executing one successful project is lucky, but making it a repeatable process and strategic advantage? That's where the money is. With help from our most experienced project leads and software engineers, …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds