タスク1 アクティブなインターネット接続があるか確認する
最初のタスクでは、インターネットへのアクティブな接続があるか確認します。そのために、標準のWebBrowserオブジェクトをフォームに追加し、アップデートサイトに関する詳細を十分に表示できるくらいの大きさにします。WebBrowserを使えば、Internet Explorerに設定された詳細に基づいてインターネットに自動的に接続できるので、ゲートウェイ、プロキシ、ファイアウォールなどの詳細でユーザーを悩まさなくて済みます。また、WebBrowserにはDocument Completedイベントも用意されており、ページの読み込みが完了したとき、このイベントがトリガーされます。
このアップデートをホストするWebサイトには、次の2つの条件が課せられます。
第1に、デフォルトページの名前を常に同じに保たなければなりません。ページの内容は変更してかまいませんが、名前は一定に保ってください。第2に、ページを識別するために固定ページタイトル(Fixed Page Title)を使用します。ページの読み込みが完了した時点で、ドキュメントタイトル(Document Title)を用いて、現在正しいサイトにいることを確認します。
Private WithEvents Timer As Windows.Forms.Timer Private TimeDelay As Integer ' Timeout in seconds Private HttpWait As Boolean ' Wait for Http page to load Private Retry As Boolean ' Retry to load failed connection Private TimeOut As Boolean ' Wait has timed out Private Canceled As Boolean ' Cancel button Clicked Private Closeme As Boolean ' Close the form Private Updated As Boolean ' Files were updated Private Sub WBData_DocumentCompleted(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _ Handles WBData.DocumentCompleted HttpWait = True If WBData.DocumentTitle.ToUpper <> "MY UPDATE SITE" Then _ TimeOut = True End Sub Private Sub Timer_Tick(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Timer.Tick If TimeDelay = 0 Or Canceled Then TimeOut = True Timer.Enabled = False Else TimeDelay -= 1 End If End Sub Private Sub Cancel_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button3.Click Canceled = True End Sub Private Sub StartUpdate() Dim Updaterfile As String Dim Serverfile As String Dim TotalUpdates As Integer Dim LastUpdate As Integer Dim msgresult As MsgBoxResult Me.Show() LblText.Text = "Connecting to Remote Server" & vbCrLf & _ "Please ensure that your internet connection is active" Me.Refresh() WBData.Url = _ New System.Uri("http://" & UpdateSite & "/Default.html") HttpWait = False TimeDelay = TimeOutlen Timer = New Windows.Forms.Timer Timer.Interval = 1000 Timer.Enabled = True While Not (HttpWait Or TimeOut Or Canceled) System.Windows.Forms.Application.DoEvents() .................
ご覧のとおり、ドキュメントの読み込みが完了した時点で、ドキュメントタイトルとプリセットしたタイトルが一致するかを確認しています。タイトルが一致しなければ、アップデートサイトに正しく接続していないことを意味します。また、カウントダウンタイマで接続待ち状態があまり長くならないようにしています。なお、Webページが複雑なほど読み込みに時間がかかるので、アップデート用のWebページはあまり大きくしないようにしてください。
このフォームにはキャンセルボタンも設けてあります。これにより、ユーザーは、インターネット接続が遅い場合や、今はアップデートのことを考えたくない場合に、アップデートをスキップできます。ここからタスク#4(アプリケーションの開始)に飛びましょう。この処理を先に説明するのは、この段階でアップデートチェックを完了せずにアプリケーションを開始しなければならない場合があるからです。つまり、アップデートのキャンセルやタイムアウトが生じた場合に、メインアプリケーションを開始する必要があるということです。
Private Sub AutoUpdater_FormClosing(ByVal sender As Object, _ ByVal e As System.Windows.Forms.FormClosingEventArgs) _ Handles Me.FormClosing If Not Closeme Then e.Cancel = True End If End Sub Private Sub LoadApp() Shell(AppPath & "\" & AppEXE, AppWinStyle.NormalFocus) Closeme = True Me.Close() End Sub
ここでは、Shellコマンドでメインアプリケーションを開始し、アップデータを終了することだけを行っています。このタスクに関して語るべきことはあまりありません。ただし、これをメインアプリケーションの中で行ってもよいのではと考える人はきっと多いはずです。アプリケーションの一切を実行時にバインドするのであれば、確かにそれも可能です。しかし、そのようにしてもプロジェクトのプライマリEXEファイルはアップデートできません。メインアプリケーションのアップデートおよび実行用に別のアプリケーションを使えば、すべてを一気にアップデートしてアプリケーションを開始できるため、アプリケーションをいったん止めて再開するようなことは必要なくなります。
さて、メインアプリケーションの読み込みに対処したので、前に戻って、接続のタイムアウトまたはキャンセルボタンのクリックが生じたときの処理を考えましょう。
........... If TimeOut Then LblText.Text = "Connection to Remote Server Failed" & _ vbCrLf & "Application will continue in 10 seconds" Button1.Enabled = True TimeOut = False TimeDelay = 10 Timer.Interval = 1000 Timer.Enabled = True Retry = False While Not (TimeOut Or Retry Or Canceled) System.Windows.Forms.Application.DoEvents() End While If Retry Then LblText.Text = "Reconnecting to Remote Server" & _ vbCrLf & " Please ensure that your Internet connection _ is active" Button1.Enabled = False TimeOut = False HttpWait = False TimeDelay = TimeOutlen Timer.Interval = 1000 Timer.Enabled = True Else LoadApp() Exit Sub End If End If End While ...............
Whileループ内でタイムアウトの発生をチェックしています。タイムアウトが発生した場合は、ユーザーに再試行のチャンスが与えられます。そこでユーザーが再試行をもう止めるか、再試行タイマが時間切れになったら、メインアプリケーションを開始し、現在の処理を終了します。オリジナルのタイムアウトループに独自の再試行コードを含めることで、サーバに接続するときユーザーに複数の再試行処理を行わせることもできます。
さて、インターネットに正常に接続し、アップデートサイトとつながったとします。つまり、キャンセルボタンはクリックされなかったわけです。ここでは、当該アプリケーションにまだ適用していないアップデートがないか確認する必要があります。結局、これを行う最も簡単な方法は、最初に設定しておいた一意のUpdateID定数を用いてファイルをダウンロードすることでした。
............... If Not Canceled Then LblText.Text = "Connection to Remote Server completed" & _ vbCrLf & " Please wait while checking for new updates" GBLbl.Refresh() Updaterfile = AppPath & "\UpdateLog.xml" Serverfile = "http://" & UpdateSite & "/" & UpdateID & ".XML" Try If File.Exists(Updaterfile) Then File.Delete(Updaterfile) My.Computer.Network.DownloadFile(Serverfile, Updaterfile) ................
ここで、必要なものをすべて取得します。アップデートログファイルはサーバ側に置かれており、ビルトインのHTTPダウンローダを用いてサーバからすぐ取り出すことができます(注:HTTPダウンローダではファイルが必ずしも上書きされないので、以前のファイルがあれば削除する必要があります)。必要な情報の格納にXMLを用いていることにも注目してください。XMLは、データ量の多少にかかわらず、すぐ必要なデータや送信対象のデータを格納する新しい標準となっています。今やOffice 2007でもドキュメントの格納にXML形式が使われています。
アップデートデータのために使用するXML形式は、ごく単純なものです。以下にサンプルを示します。
<?xml version="1.0" encoding="utf-8" ?> <updates total ="1"> <file name="test1"> <server>hexedit/test1.xml</server> <local>test1.dat</local> <update>1</update> </file> </updates>
ビルトインのXMLノード関数を使用して各種のノードを抽出します(AutoUpdateDemo.zipにサンプルのXML関数クラスが含まれています。これについての説明は割愛します)。このクラスは、XMLファイルのノードにアクセスするための簡単なコマンドをいくつか公開しており、各ファイルの必要な情報をオブジェクトとして返します。