Introduction
Fixing bugs and adding new features is a part of developing software applications. From my experience, sending application updates to users is also a critical part of developing applications, especially when the user has no advanced knowledge of computers. In the Internet era today, the software developer must make application deployment and updating easier and often makes automatic application updates to do this.
Background
I already searched through the internet on this topic, but not much seems suited to my needs. So, I tried to create one myself. Before starting development, I tried Updater Application Block (UAB) from Microsoft through Brendan Tompkins, Application Auto Update in VB.NET by Eduardo Oliveira and Microsoft Application Blocks - Application Updater, Using the Manifest Utility by Les Smith. My thanks go out to all of them. There are certain features of an Auto Update Application that I need:
- User must login and specify the URL of the Update Server
- Application automatic re-run after checking update
- Update process transparent to the user
- Easy for developer to upload the update files
How it works
After the user logs in and specifies the Server URL, we must call AutoUpdate.exe with the ProcessStartInfo
class and set ProcessStartInfo.Arguments
with the required command line
string. After ProcessStartInfo
starts, we must exit the main application. AutoUpdate.exe will delay several seconds before running, waiting for the main application to close. After that, AutoUpdate.exe will check updates and copy update files from the server to the client computer using the WebClient
class based on the Manifest file that we created. At the end of update process, AutoUpdate.exe will re-run the main application.
Using the code
The Auto Update program
The Auto Update program consists of only a form and a module. Form
is used for the user interface when Auto Update downloads the update files. Module
is filling with sub Main
and sub ProcessUpdate
.
Imports System.Net
Imports System.xml
Imports System.IO
Module Module1
Friend myForm As Form1
Friend ExeFile As String
Dim RemoteUri As String
Dim Key As String
Dim ManifestFile As String
Dim UserID As String
Dim CommandLine As String
Sub Main()
Try
Dim param() As String =
Split(Microsoft.VisualBasic.Command(), "|")
ExeFile = param(0)
RemoteUri = param(1)
ManifestFile = param(2)
UserID = param(3)
CommandLine = Microsoft.VisualBasic.Command()
If ExeFile = "" Or RemoteUri = "" Or
ManifestFile = "" Then Environment.Exit(1)
myForm = New Form1
myForm.Label1.Text =
"Checking for application update, please wait!..."
Application.DoEvents()
Application.Run(myForm)
Catch ex As Exception
MsgBox(ex.ToString)
Application.Exit()
End Try
End Sub
Sub ProcessUpdate()
Dim myWebClient As New WebClient
Try
myForm.Label2.Text = "download manifest..."
Application.DoEvents()
myWebClient.DownloadFile(RemoteUri & ManifestFile, ManifestFile)
myForm.Label2.Text = "download manifest done"
Application.DoEvents()
Catch ex As Exception
MessageBox.Show(ex.ToString)
Exit Sub
End Try
Try
Dim m_xmld As XmlDocument
Dim m_nodelist As XmlNodeList
Dim m_node As XmlNode
m_xmld = New XmlDocument
m_xmld.Load(Application.StartupPath & "\" & ManifestFile)
'Get the list of name nodes
m_nodelist = m_xmld.SelectNodes("/update/name")
'Init progressbar
InitProgress(m_nodelist.Count)
'Loop through the nodes
For Each m_node In m_nodelist
'Get the file Attribute Value
Dim fileAttribute =
m_node.Attributes.GetNamedItem("file").Value
'Get the fileName Element Value
Dim fileNameValue = m_node.ChildNodes.Item(0).InnerText
'Get the fileVersion Element Value
Dim fileVersionValue = m_node.ChildNodes.Item(1).InnerText
'Get the fileLastModified Value
Dim fileLastModiValue = m_node.ChildNodes.Item(2).InnerText
'Temp file name
Dim TempFileName As String =
Application.StartupPath & "\" &
Now.TimeOfDay.TotalMilliseconds
Dim isToUpgrade As Boolean = False
Dim RealFileName As String =
Application.StartupPath & "\" & fileNameValue
Dim LastModified As Date = CDate(fileLastModiValue)
Dim FileExists As Boolean = File.Exists(RealFileName)
'If file not exist then download file
If Not FileExists Then
isToUpgrade = True
ElseIf fileVersionValue <> "" Then
'verify the file version
Dim fv As FileVersionInfo =
FileVersionInfo.GetVersionInfo(RealFileName)
isToUpgrade =
(GetVersion(fileVersionValue) >
GetVersion(fv.FileMajorPart &
"." & fv.FileMinorPart & "." & fv.FileBuildPart &
"." & fv.FilePrivatePart))
'check if version not upgrade then check last modified
If Not isToUpgrade Then
isToUpgrade =
(LastModified >
File.GetLastWriteTimeUtc(RealFileName))
End If
Else
'check last modified file
isToUpgrade =
(LastModified >
File.GetLastWriteTimeUtc(RealFileName))
End If
'Download upgrade file
If isToUpgrade Then
myForm.Label2.Text =
"Update file " & fileNameValue & "..."
Application.DoEvents()
' Download file and name it with temporary name
myWebClient.DownloadFile(RemoteUri & fileNameValue,
TempFileName)
' Rename temporary file to real file name
File.Copy(TempFileName, RealFileName, True)
' Set Last modified
File.SetLastWriteTimeUtc(RealFileName, LastModified)
' Delete temporary file
File.Delete(TempFileName)
End If
IncrementProgress()
Next
'Delete server manifest file
File.Delete(Application.StartupPath & "\" & ManifestFile)
myForm.Label1.Text = "Application update complete!"
Application.DoEvents()
Dim startInfo As New ProcessStartInfo(Application.StartupPath &
"\" & ExeFile)
startInfo.Arguments = CommandLine
Process.Start(startInfo)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Main application
In the main application, we must check if this is first run or a re-run of the application from the Auto Update program. We check this by retrieving the parameter
length.
Module Module1
Public g_login As String
Public isUpdate As Boolean = False
Sub Main()
Dim param() As String = Split(Microsoft.VisualBasic.Command(), "|")
If param.Length > 1 Then isUpdate = True
Dim frmLogin As New Form1
frmLogin.ShowDialog()
End Sub
End Module
In the login form, when the user clicks the OK button, we must add this code:
Private Sub btnLogin_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnLogin.Click
Dim MainForm As New Form2
If txtUSER_ID.Text.Trim = "" Then
MsgBox("Please enter your User ID!", MsgBoxStyle.Information)
txtUSER_ID.Focus()
Exit Sub
ElseIf txtPASS.Text.Trim = "" Then
MsgBox("Please enter your Password!", MsgBoxStyle.Information)
txtPASS.Focus()
Exit Sub
ElseIf txtSERVER_URL.Text.Trim = "" Then
MsgBox("Please enter Server URL!", MsgBoxStyle.Information)
txtSERVER_URL.Focus()
Exit Sub
End If
If txtUSER_ID.Text.Trim = "User" And txtPASS.Text.Trim = "123" Then
Me.Hide()
If Not isUpdate Then
Dim startInfo As New ProcessStartInfo(Application.StartupPath &
"\" & "AutoUpdate.exe")
Dim cmdLine As String
cmdLine = "MainApp.exe|" & txtSERVER_URL.Text.Trim &
"|ServerManifest.xml|" & txtUSER_ID.Text.Trim
startInfo.Arguments = cmdLine
Process.Start(startInfo)
Environment.Exit(1)
Else
MainForm.ShowDialog()
End If
Else
MsgBox("Invalid Password!", MsgBoxStyle.Information)
txtPASS.Focus()
Exit Sub
End If
End Sub
Manifest files utility
This program will create a manifest file that fills with the list of update files in the selected folder we must choose. The manifest file is formatted in XML.
Private Sub Button2_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button2.Click
If txtPath.Text = "" Then
Exit Sub
End If
Try
xmlDoc.RemoveAll()
lblStatus.Text = "Readind directory structure..."
Application.DoEvents()
xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction("xml",
"version='1.0' encoding='UTF-8'"))
createRootNode()
With SaveFileDialog1
.AddExtension = True
.FileName = "ServerManifest.xml"
.DefaultExt = ".xml"
.Filter = "xml files (*.xml)|*.xml"
.FilterIndex = 1
.RestoreDirectory = True
If .ShowDialog =
Forms.DialogResult.OK Then xmlDoc.Save(.FileName)
End With
lblStatus.Text = "The XML document of manifest was created."
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Private Sub createRootNode()
Try
oElmntRoot = xmlDoc.CreateElement("update")
xmlDoc.AppendChild(oElmntRoot)
loopNodes(oElmntRoot, txtPath.Text)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Private Sub loopNodes(ByVal oElmntParent As XmlElement,
ByVal strPath As String)
Dim ofs As New DirectoryInfo(strPath & "\")
'Files Loop ------------------------------------------------------
Dim oFile As FileInfo
For Each oFile In ofs.GetFiles
lblStatus.Text = "Reading file " & oFile.Name
Application.DoEvents()
If oFile.Name <> "ServerManifest.xml" And _
Not oFile.Name.EndsWith(".pdb") And _
Not oFile.Name.EndsWith(".config") Then
Dim oElmntLeaf1 As XmlElement 'Manipulates the files nodes
Dim oElmntFileName As XmlElement
Dim oElmntFileVersion As XmlElement
Dim oElmntFileModified As XmlElement
oElmntLeaf1 = xmlDoc.CreateElement("name")
oElmntLeaf1.SetAttribute("file", oFile.Name)
oElmntParent.AppendChild(oElmntLeaf1)
oElmntFileName = xmlDoc.CreateElement("filename")
oElmntFileName.InnerText = oFile.Name
oElmntLeaf1.AppendChild(oElmntFileName)
Dim fv As FileVersionInfo =
FileVersionInfo.GetVersionInfo(oFile.FullName)
oElmntFileVersion = xmlDoc.CreateElement("fileversion")
oElmntFileVersion.InnerText = fv.FileVersion
oElmntLeaf1.AppendChild(oElmntFileVersion)
oElmntFileModified = xmlDoc.CreateElement("filelastmodified")
oElmntFileModified.InnerText = oFile.LastWriteTimeUtc
oElmntLeaf1.AppendChild(oElmntFileModified)
End If
Next
End Sub
History
- 22 June, 2007 -- Original version posted