VB.NET 中的应用程序自动更新






4.76/5 (81投票s)
一篇关于通过 Web 更新 Windows 应用程序的文章。
引言
此代码使应用程序能够在有新版本可用时自行更新。自动更新对用户是透明的,并且每次用户启动程序时都会检查新更新。此代码是为在受控环境(内网)中使用而编写的,由于安全问题,不打算通过 Internet 使用。
背景
我开发客户端/服务器系统已经很久了,软件生命周期中最糟糕的部分是在所有用户的机器上更新它。最糟糕的情况是当你需要进行数据库更改,并且所有用户都必须拥有最新版本,否则系统将崩溃。
如今,许多系统都提供自动更新功能,这使程序员和用户的生活都变得更容易。在互联网上搜索,我找不到任何可以以简单方式用于我的项目的代码,所以我决定编写自己的代码。
我希望自动更新程序具备的几点是:
- 易于实现和使用。
- 对用户透明。
- 能够升级系统以及自动更新程序。
- 一个在不同系统中使用时不需要更改即可编译到库中的代码。
工作原理
在你想让它自动更新的程序中,你只需要在 `Main` 过程中调用 `AutoUpdate` 函数。`AutoUpdate` 函数将与从网站上的文件中读取的版本进行比较。如果程序版本低于读取的版本,程序将下载自动更新程序并启动它,然后该函数返回 `True`,这意味着将运行自动更新,并且当前程序应该关闭。自动更新程序从要更新的程序接收几个参数,执行必要的自动更新,然后启动更新后的系统。
代码
自动更新程序
这是自动更新程序的代码。该程序是一个无窗口的应用程序,只有一个 `Main` 过程。在这里唯一需要更改的是最后的错误消息。
Imports System.IO
Imports System.Net
Module Main
Public Sub Main()
Dim ExeFile As String ' the program that called the auto update
Dim RemoteUri As String ' the web location of the files
Dim Files() As String ' the list of files to be updated
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 CommandLine As String ' the command line passed to the original
' program if is the case
Dim myWebClient As New WebClient ' the web client
Try
' Get the parameters sent by the application
Dim param() As String = Split(Microsoft.VisualBasic.Command(), "|")
ExeFile = param(0)
RemoteUri = param(1)
' the files to be updated should be separeted by "?"
Files = Split(param(2), "?")
Key = param(3)
CommandLine = param(4)
Catch ex As Exception
' if the parameters wasn't right just terminate the program
' this will happen if the program wasn't called by the system
' to be updated
Exit Sub
End Try
Try
' Process each file
For i As Integer = 0 To Files.Length - 1
Try
' try to rename the current file before download the new one
' this is a good procedure since the file can be in use
File.Move(Application.StartupPath & "\" & Files(i), _
Application.StartupPath & "\" & _
Now.TimeOfDay.TotalMilliseconds & ".old")
Catch ex As Exception
End Try
' download the new version
myWebClient.DownloadFile(RemoteUri & Files(i), _
Application.StartupPath & "\" & Files(i))
Next
' Call back the system with the original command line
' with the key at the end
System.Diagnostics.Process.Start(ExeFile, CommandLine & Key)
' do some clean up - delete all .old files (if possible)
' in the current directory
' if some file stays it will be cleaned next time
Dim S As String = Dir(Application.StartupPath & "\*.old")
Do While S <> ""
Try
File.Delete(Application.StartupPath & "\" & S)
Catch ex As Exception
End Try
S = Dir()
Loop
Catch ex As Exception
' something went wrong...
MsgBox("There was a problem runing the Auto Update." & vbCr & _
"Please Contact [contact info]" & vbCr & ex.Message, _
MsgBoxStyle.Critical)
End Try
End Sub
End Module
自动更新类/函数
这是包含 `AutoUpdate` 函数的类,该函数检查是否需要更新。可以直接复制到你的代码中,也可以放在库(DLL)中。如果它在你的代码中,则不需要是类。
此函数通过引用接收一个参数,即传递给程序的命令行,它可以是原始命令行,也可以是自动更新程序传递的命令行。如果它是由自动更新程序发送的,则 `Key` 将从命令行中清除,因此你必须使用返回的参数而不是原始命令行。
在这里,你需要更改 `RemotePath` 变量以指向你的更新 Web 文件夹,以及最后的错误消息。
Public Class AutoUpdate
Public Function AutoUpdate(ByRef CommandLine As String) As Boolean
Dim Key As String = "&**#@!" ' any unique sequence of characters
' the file with the update information
Dim sfile As String = "update.dat"
' the Assembly name
Dim AssemblyName As String = _
System.Reflection.Assembly.GetEntryAssembly.GetName.Name
' here you need to change the web address
Dim RemotePath As String = _
"http://[the web address for the update folder root]/"
' where are the files for a specific system
Dim RemoteUri As String = RemotePath & AssemblyName & "/"
' clean up the command line getting rid of the key
CommandLine = Replace(Microsoft.VisualBasic.Command(), Key, "")
' Verify if was called by the autoupdate
If InStr(Microsoft.VisualBasic.Command(), Key) > 0 Then
Try
' try to delete the AutoUpdate program,
' since it is not needed anymore
System.IO.File.Delete(_
Application.StartupPath & "\autoupdate.exe")
Catch ex As Exception
End Try
' return false means that no update is needed
Return False
Else
' was called by the user
Dim ret As Boolean = False ' Default - no update needed
Try
Dim myWebClient As New System.Net.WebClient 'the webclient
' Download the update info file to the memory,
' read and close the stream
Dim file As New System.IO.StreamReader( _
myWebClient.OpenRead(RemoteUri & sfile))
Dim Contents As String = file.ReadToEnd()
file.Close()
' if something was read
If Contents <> "" Then
' Break the contents
Dim x() As String = Split(Contents, "|")
' the first parameter is the version. if it's
' greater then the current version starts the
' update process
If x(0) > Application.ProductVersion Then
' assembly the parameter to be passed to the auto
' update program
' x(1) is the files that need to be
' updated separated by "?"
Dim arg As String = Application.ExecutablePath & "|" & _
RemoteUri & "|" & x(1) & "|" & Key & "|" & _
Microsoft.VisualBasic.Command()
' Download the auto update program to the application
' path, so you always have the last version runing
myWebClient.DownloadFile(RemotePath & "autoupdate.exe", _
Application.StartupPath & "\autoupdate.exe")
' Call the auto update program with all the parameters
System.Diagnostics.Process.Start( _
Application.StartupPath & "\autoupdate.exe", arg)
' return true - auto update in progress
ret = True
End If
End If
Catch ex As Exception
' if there is an error return true,
' what means that the application
' should be closed
ret = True
' something went wrong...
MsgBox("There was a problem runing the Auto Update." & vbCr & _
"Please Contact [contact info]" & vbCr & ex.Message, _
MsgBoxStyle.Critical)
End Try
Return ret
End If
End Function
End Class
自动更新 Web 文件夹
自动更新 Web 文件夹应该为每个要升级的系统提供一个文件夹。根文件夹是你将在 `RemotePath` 变量中引用的文件夹。`AutoUpdate.exe` 程序应该放在这个文件夹里。每个子文件夹都应该命名为程序集名称(通常是程序名,不带扩展名)。在程序文件夹中,你可以保存要更新的文件以及包含最新文件版本和要更新的文件的 `update.dat` 文件。第一个参数是版本,第二个参数用“|”分隔是要更新的文件,它们用“?”分隔,如下所示:
1.2.1234.5543|MyProgram.exe?file1.txt?file2.cfg
使用代码
好了,现在使用代码非常简单。在你想启用自动更新的应用程序中,只需调用 `AutoUpdate` 函数即可。你必须声明一个 `CommandLine` 变量来存储传递给程序的参数,并在需要时使用它,而不是使用 `Microsoft.VisualBasic.Command()` 函数。
' The variable with the command line (to replace the
' Microsoft.VisualBasic.Command() function)
Public CommandLine As String
Public Sub Main()
' if you are using class create it
Dim MyAutoUpdate As New AutoUpdate
' test if an update is needed and quit the program if so.
If MyAutoUpdate.AutoUpdate(CommandLine) Then Exit Sub
' here goes your regular code in the main sub
End Sub
关注点
这个自动更新的编写意图是始终检查新版本,因为相关的应用程序如果运行旧版本可能会出现问题。它保证了所有使用该应用程序的人都在运行最新版本。这很容易更改为在有可用更新时进行更新,但如果不可用则不崩溃。
这个程序对于小型程序和少量文件来说运行良好。如果你需要下载大文件,我建议你打开一个闪屏并告知用户正在进行某项操作。
限制
- 原始命令行不能包含“|”字符,因为它在自动更新程序中用作分隔符。
- 用户必须有权限在应用程序安装文件夹中写入文件。
- 主程序应使用 `CommandLine` 变量而不是原始命令行,因为它可能附加有 `key`。