65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义应用程序自动更新

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.64/5 (10投票s)

2007年6月22日

CPOL

2分钟阅读

viewsIcon

172469

downloadIcon

3545

Windows 应用程序的自动更新功能。

引言

修复错误和添加新功能是开发软件应用程序的一部分。根据我的经验,将应用程序更新发送给用户也是开发应用程序的关键部分,特别是当用户不具备高级计算机知识时。在当今互联网时代,软件开发者必须使应用程序部署和更新更容易,并且经常采用自动应用程序更新来实现这一点。

背景

我已经搜索过互联网上的相关主题,但似乎没有太多适合我的需求。因此,我尝试自己创建一个。在开始开发之前,我尝试了 Brendan Tompkins 的 Microsoft 的 Updater Application Block (UAB), Eduardo Oliveira 的 VB.NET 中的应用程序自动更新,以及 Les Smith 的 Microsoft Application Blocks - 应用程序更新程序,使用清单实用工具。感谢他们所有人。我需要自动更新应用程序的某些功能

  • 用户必须登录并指定更新服务器的 URL
  • 应用程序在检查更新后自动重新运行
  • 更新过程对用户透明
  • 开发者可以轻松上传更新文件

工作原理

在用户登录并指定服务器 URL 后,我们必须使用 ProcessStartInfo 类调用 AutoUpdate.exe,并将 ProcessStartInfo.Arguments 设置为所需的 命令行字符串。在 ProcessStartInfo 启动后,我们必须退出主应用程序。AutoUpdate.exe 会延迟几秒钟再运行,等待主应用程序关闭。之后,AutoUpdate.exe 将检查更新,并使用我们创建的 Manifest 文件,通过 WebClient 类将更新文件从服务器复制到客户端计算机。在更新过程结束时,AutoUpdate.exe 将重新运行主应用程序。

使用代码

自动更新程序

自动更新程序仅包含一个窗体和一个模块。窗体用于自动更新下载更新文件时的用户界面。模块包含 sub Mainsub 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

主应用程序

在主应用程序中,我们必须检查这是首次运行还是从自动更新程序重新运行的应用程序。我们通过检索 参数的长度来检查这一点。

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

在登录窗体中,当用户单击“确定”按钮时,我们必须添加此代码

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 文件实用工具

该程序将创建一个 Manifest 文件,其中包含我们选择的文件夹中更新文件的列表。Manifest 文件以 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

历史

  • 2007 年 6 月 22 日 -- 发布原始版本
© . All rights reserved.