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.
- Check for an active Internet connection.
- Check whether there are updates available for this application.
- Download the files to their respective locations.
- Start the updated application.
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) ................