[WM]建立一個可以自我更新的應用程式

參考資料:Creating Self-Updating Applications With the .NET Compact Framework


在這篇,筆者參考資料來源中所提的相關資料來測試一下自動更新應用程式的功能;那麼,要怎麼去做這樣的功能呢?

參考資料:Creating Self-Updating Applications With the .NET Compact Framework
在這篇,筆者參考資料來源中所提的相關資料來測試一下自動更新應用程式的功能;那麼,要怎麼去做這樣的功能呢?
以本篇來說,作法是在Server端建立一個Web Service,提供給Device(Client)端呼叫,Device端提交目前自己的版本號碼給Web Service,之後Web Service會回應是否有新版本可以提供;而有新版本的時候,Device端再進行下載、安裝的動作;接下來先來看看Service端的動作 首先要先新增一個Web Service的專案,預設專案建立完成後,會產生Service.asmx的檔案,這個檔案就是我們要撰寫相關程式碼的地方,這邊筆者是利用XML檔案來作版本的比較,所以準備了一個XML檔案,像是下面這樣

  
其中CurrentVersion是表示目前Server上的版本號,name這邊放的是產品名稱;而Service的程式碼也很簡單只有提供一個方法可以叫用,程式碼會長的話像下面這樣
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO
Imports System.Xml

 _
 _
 _
Public Class Service
    Inherits System.Web.Services.WebService

    Public Class ServiceResult
        Public IsNewVersionAvailable As Boolean
        Public CurrentVersion As Integer
        Public UpdateLink As String
    End Class

     _
    Public Function CheckProduct(ByVal ProductName As String, ByVal Version As Integer) As ServiceResult
        Dim result As New ServiceResult
        ''檢察更新檔是否存在
        If Not File.Exists(Server.MapPath("Products\" & ProductName & ".xml")) Then
            ''回應不需要更新
            result.IsNewVersionAvailable = False
            result.CurrentVersion = 0
            result.UpdateLink = ""
            Return result
        End If
        ''檢查Server版本以及Client版本
        ''載入xml檔案
        Dim xmlDoc As New XmlDocument
        xmlDoc.Load(Server.MapPath("Products\" & ProductName & ".xml"))
        Dim product As XmlNode = xmlDoc.SelectSingleNode("product")
        ''判斷產品名稱是否相同
        If product.Attributes("name").Value.ToUpper <> ProductName.ToUpper Then
            ''回應不需要更新
            result.IsNewVersionAvailable = False
            result.CurrentVersion = 0
            result.UpdateLink = ""
            Return result
        End If
        Dim curVersion As XmlNode = product.SelectSingleNode("CurrentVersion")
        If CInt(curVersion.Attributes("version").Value) <= Version Then
            ''回應不需要更新
            result.IsNewVersionAvailable = False
            result.CurrentVersion = 0
            result.UpdateLink = ""
            Return result
        Else
            ''更新作業
            result.IsNewVersionAvailable = True
            result.CurrentVersion = curVersion.Attributes("version").Value
            result.UpdateLink = curVersion.Attributes("link").Value
            Return result
        End If
    End Function
End Class
準備好Service之後,要將Service部署到IIS上面,部署這部分就是在IIS上面建立網站/虛擬目錄,之後將相關的Service檔案複製上去,而以IIS7來說要將其轉換為"應用程式",這部分我想就不在這邊說明了。部署完成之後可以利用IE來測試看看Service是不是有正常運作,直接在IE上輸入Service位置,例如
如果運作正常會看到如下畫面

如果想直接利用IE來測試動作的話,可以點method的名稱,之後可以看到"叫用"的畫面

到這邊Service的部分就大致完成了,接下來要來看看Device上面的部分;首先建立完裝置應用程式的專案後,要先加入剛剛完成Web參考,而在Client端呼叫Service時會利用類似下面的方式先建立執行個體
    Dim svcClient As New UpdateService.Service
之後呼叫相關的method
        result = svcClient.CheckProduct("SelfUpdateDemo", CInt(txtVersion.Text)
這邊示會將目前Device上的版本號傳到Service端,而Service端判斷有沒有新版本之後傳回來,而我這邊是利用簡單的方式傳一個號碼過去,實際使用時,可以將exe檔的相關版本號讀出來去進行比對的動作。
而Device端完整的程式碼會像下面這樣
Imports System.Net

Public Class Form1
    ''web service
    Dim svcClient As New UpdateService.Service

    Dim request As Net.HttpWebRequest
    Dim response As Net.HttpWebResponse
    ''緩衝收取到的資料
    Dim btyBuffer(1023) As Byte
    ''儲存cab檔案的總長度
    Dim intFileSize As Integer = 0
    ''總共已接收的bytes數量
    Dim intTotalReceivedBytes As Integer = 0
    ''用來寫入cab檔案的資料流
    Dim fsCabFile As IO.FileStream
    ''設定progessbar最大值的委派
    Delegate Sub deSetProgessbarMaxValue(ByVal MaxValue As Integer)
    ''更新progessbar value的委派
    Delegate Sub deSetProgessbar(ByVal Value As Integer)
    ''動作完成的委派
    Delegate Sub deDownloadComplete()

    ''' 
    ''' 設定progessbar的最大值
    ''' 
    ''' 
    ''' 
    Private Sub SetProgessbarMaxValue(ByVal Value As Integer)
        ProgressBar1.Maximum = Value
    End Sub

    ''' 
    ''' 設定progessbar目前進度
    ''' 
    ''' 
    ''' 
    Private Sub SetProgress(ByVal Value As Integer)
        ProgressBar1.Value = Value
    End Sub

    ''' 
    ''' 下載完新版的cab檔案後要進行的動作
    ''' 
    ''' 
    Private Sub DownLoadComplete()
        ''回復游標圖示
        Cursor.Current = Cursors.Default
        ''
        MessageBox.Show("download complete")
        ''關閉應用程式,開始重新安裝應用程式
        Me.Close()
        ''執行cab檔案安裝作業
        Process.Start(GetAppPath() & "\tmp.cab", "")
    End Sub

    ''' 
    ''' 取得執行檔目前所在位置
    ''' 
    ''' 
    ''' 
    Public Function GetAppPath() As String
        Return System.IO.Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly.GetName.CodeBase.ToString)
    End Function

    ''' 
    ''' 檢查更新作業
    ''' 
    ''' 
    ''' 
    ''' 
    Private Sub btnCheck_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCheck.Click
        Dim result As New UpdateService.ServiceResult
        result = svcClient.CheckProduct("SelfUpdateDemo", CInt(txtVersion.Text))
        If result.IsNewVersionAvailable Then
            MessageBox.Show("new version:" & result.CurrentVersion)
            ''下載更新
            request = HttpWebRequest.Create(result.UpdateLink)
            ''非同步取得回應
            request.BeginGetResponse(New AsyncCallback(AddressOf Responsed), Nothing)
            ''設定滑鼠遊標為等待中
            Cursor.Current = Cursors.WaitCursor
        Else
            MessageBox.Show("current vserion is last version")
        End If
        result = Nothing
    End Sub

    ''' 
    ''' 取得回應時引發的非同步處理
    ''' 
    ''' 
    ''' 
    Private Sub Responsed(ByVal ar As IAsyncResult)
        Try
            response = request.EndGetResponse(ar)
        Catch ex As Exception
            ''error handler
            Exit Sub
        End Try
        ''取得檔案的總長度
        intFIleSize = response.ContentLength
        ''設定progessbar的最大值
        ProgressBar1.Invoke(New deSetProgessbarMaxValue(AddressOf SetProgessbarMaxValue), New Object() {intFIleSize})
        ''檢查暫存檔案是否存在
        If IO.File.Exists(GetAppPath() & "\tmp.cab") Then
            IO.File.Delete(GetAppPath() & "\tmp.cab")
        End If
        ''建立用來寫入檔案的flestream
        fsCabFile = New IO.FileStream(GetAppPath() & "\tmp.cab", IO.FileMode.Create)
        ''開始對server要求資料
        response.GetResponseStream.BeginRead(btyBuffer, 0, btyBuffer.Length, New AsyncCallback(AddressOf DataRecevied), Me)
    End Sub

    ''' 
    ''' 從遠端取得資料時的處理
    ''' 
    ''' 
    ''' 
    Private Sub DataRecevied(ByVal ar As IAsyncResult)
        Dim Readbytes As Integer
        Readbytes = response.GetResponseStream.EndRead(ar)
        ''將接收到的資料實際寫入到檔案中
        fsCabFile.Write(btyBuffer, 0, Readbytes)
        ''累加收取到的bytes總數
        intTotalReceivedBytes += Readbytes
        ''更新progessbar
        ProgressBar1.Invoke(New deSetProgessbar(AddressOf SetProgress), New Object() {intTotalReceivedBytes})
        If Readbytes > 0 Then
            ''產生下一輪取得資料的非同步要求
            response.GetResponseStream.BeginRead(btyBuffer, 0, btyBuffer.Length, New AsyncCallback(AddressOf DataRecevied), Me)
        Else
            ''關閉資料流
            fsCabFile.Flush()
            fsCabFile.Close()
            fsCabFile.Dispose()
            ''下載完畢
            Me.Invoke(New deDownloadComplete(AddressOf DownLoadComplete))
        End If
    End Sub
End Class
當更新按鈕按下時就會呼叫Service,詢問是否有新的版本,如果有的話就會開始進行下載的動作,下載完成後再利用process.start去呼叫新下載的cab,開始進行安裝的動作,這邊我擷取幾張畫面
第一步是檢察是否有新版本

第二步是下載檔案

第三步是下載完成後的相關動作

第四步是cab檔案的安裝畫面

利用這樣的方式,就可以達成一個可以自我更新的應用程式了,以上給各位參考看看了。