自定义应用程序自动更新
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 Main
和 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
主应用程序
在主应用程序中,我们必须检查这是首次运行还是从自动更新程序重新运行的应用程序。我们通过检索 参数
的长度来检查这一点。
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 日 -- 发布原始版本