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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (81投票s)

2005 年 9 月 5 日

CPOL

4分钟阅读

viewsIcon

832864

downloadIcon

18168

一篇关于通过 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`。
© . All rights reserved.