Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Application Auto Update

0.00/5 (No votes)
22 Jun 2007 1  
Auto update features for windows application

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    ' the form interface of update process

    Friend ExeFile As String  ' the main program that called the auto update

    Dim RemoteUri As String   ' the url location of the files

    Dim Key As String         ' the key used by the program when called back 

    ' to know that the program was launched by the 

    ' Auto Update program

    Dim ManifestFile As String  ' the manifest file name

    Dim UserID As String        ' the User ID

    Dim CommandLine As String   ' the command line passed to the original 


    Sub Main()
        Try
            ' Get the parameters sent by the 

            ' application should be separated by "|"

            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 Parameter omitted then application exit

            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
        ' Download manifest file

        Try
            ' get the update file content in manifest file

            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
            'Create the XML Document

            m_xmld = New XmlDocument
            'Load the Xml file

            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()
        ' Get the parameters sent by the application

        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

    'Check is valid login

    If txtUSER_ID.Text.Trim = "User" And txtPASS.Text.Trim = "123" Then
        Me.Hide()
        'Check if first run then call auto update

        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() 'Clears the xmlDoc for multiple process, 

                           ' because xmlDoc is a class level object

        lblStatus.Text = "Readind directory structure..."
        Application.DoEvents()

        xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction("xml", 
            "version='1.0' encoding='UTF-8'")) 'First Chid: Header

        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
        '            xmlDoc.Save("ServerManifest.xml")


        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") 'Second Child: update

        xmlDoc.AppendChild(oElmntRoot) 'Add the element to the xml document


        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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here