用Application Updater Block生成一个自我更新的WinForms 应用

本文详细介绍如何利用.NET Framework中的ApplicationUpdaterBlock实现应用程序的自我更新功能。从安装组件到生成公钥私钥,再到配置IIS及创建更新文件,一步步指导读者完成整个更新流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

bigtall

在过去的两个星期里, 我一直在做我的第一个真正的.net WinForm应用的开发.  这是一个很有趣的过程,我一直在疯了似的学习东西.  其中之一就是我要允许应用程序能够用微软的Application Updater Block进行自我更新。  当它正常工作的那一刻,让我有一种很大的成就感,同时我也意识到微软没有提供那种按步骤顺序的例子。  Duncan Mackenzie 有一个 很好的blog文章 可以做一个开始,但是这个例子是VB做的并且没有提供RSA公钥和私钥的细节情况,所以我决定说一下我的工作过程。  希望能对你有用!

Step #1 Install the Application Blocks

Download the Updater Application Block from Microsoft .

Run the MSI Installer.

Step #2 在项目中加入代码和引用:

把下列工程加入到你的WinForm工程所在的解决方案:

Microsoft.ApplicationBlocks.ApplicationUpdater
Microsoft.ApplicationBlocks.ApplicationUpdater.Interfaces
Microsoft.ApplicationBlocks.ExceptionManagement
Microsoft.ApplicationBlocks.ExceptionManagement.Interfaces

如果你选择默认安装的话,它们的位置可能是:

C:\Program Files\Microsoft Application Blocks for .NET\Updater\Code\CS\Microsoft.ApplicationBlocks.Updater

在你的WinForm工程中引用下列工程

Microsoft.ApplicationBlocks.ApplicationUpdater
Microsoft.ApplicationBlocks.ApplicationUpdater.Interfaces
Microsoft.ApplicationBlocks.ExceptionManagement

把下列命名空间加入到你Form的.cs文件中

using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Xml;

然后 添加这个位置的应用程序更新代码到你的代码中.  你需要从你的MainForm初始化方法中调用 InitializeAutoUpdate()。

Step #3 生成你应用程序的发布目录结构并配置 AppStart.exe

生成一个用于客户端程序安装的目录.  本例子中,我们用如下的目录:

C:\Program Files\YourApp\1.0.0.0\

现在复制 AppStart.exeAppStart.exe.config 到类似如下的根目录中

 C:\Program Files\YourApp\AppStart.exe
 C:\Program Files\YourApp\AppStart.exe.config

说明: 这两个文件你可以在如下目录中找到 “C:\Program Files\Microsoft Application Blocks for .NET\Updater\Code\CS\Microsoft.ApplicationBlocks.Updater\AppStart\bin\Debug“  

Step #4 修改 AppStart.exe.config 文件

AppStart.exe 会启动你的应用程序,如果更新文件下载完成之后还有可能要重启.  它需要知道启动你最新的程序的目录位置. 
修改配置文件以配合当前的版本:

<appStart>
 
<ClientApplicationInfo>
   
<appFolderName>C:\Program Files\YourApp\1.0.0.0</appFolderName>
   
<appExeName>YourAppName.exe</appExeName>
   
<installedVersion>1.0.0.0</installedVersion>
   
<lastUpdated>2004-06-10T15:33:17.3745836-04:00</lastUpdated>
 
</ClientApplicationInfo>
</appStart>

Step #5: 生成你的公钥和私钥

运行 "C:\Program Files\Microsoft Application Blocks for .NET\Updater\Code\CS\Microsoft.ApplicationBlocks.Updater\ManifestUtility\bin\Debug\ManifestUtility.exe"

选择 “File..Generate Keys”  会提示你是否需要保存: PublicKey.xml 和 PrivateKey.xml  这两个密钥接下来就会用到. 

我这里要提醒大家,这些密钥只要生成一次就可以了, 因为下面几个地方需要引用到RSA公钥和私钥.  你需要把这些密钥存放在一个安全的地方,因为在发布一个新的更新的时候会用到它

Step #6 创建IIS 虚拟目录

在你的Web服务器上生成一个目录来存放你的更新文件.  在这两个目录中要放两样东西 1)  ServerManifest.xml 文件,包含最后版本的一些信息;2) 你的新程序的目录. 在这个目录里,生成一个目录来存放你的新版本程序.  在我们的例子中,我们用这两个目录, C:\Inetpub\AppUpdates C:\Inetpub\AppUpdates\1.0.0.1

用 IIS 管理器生成一个虚拟目录指向刚才的实际目录.  记下你的 URL, 在上传步骤中我们需要用到它.  你必须要打开虚拟目录的“目录浏览”选项.

Step #7. 配置你的版本 1.0.0.0 的App.config 文件

这里,我们会需要往里添加一些新东西.  首先, 我们需要加入一个configSections 元素来定义我们的 appUpdater 节:

<configSections>
 
<section name="appUpdater" type="Microsoft.ApplicationBlocks.ApplicationUpdater.UpdaterSectionHandler,Microsoft.ApplicationBlocks.ApplicationUpdater" />
</configSections>

接下来,我们需要添加一个 Version 键到我们的 appsettings 中, 我们首先设置我们的本地版本为 1.0.0.0, 这样我们就可以测试自动更新到版本 1.0.0.1

<appSettings>
 
<add key="VERSION" value="1.0.0.0" />
</appSettings>

最后,, 加入 appUpdater 节到你的配置文件中.  我这里用一对方括号把你要修改的值包含起来.  你可以直接从你上一步生成的 PublicKey.xml文件中复制 <RSAKeyValue> 元素.

<xmlFile> 元素必须要指向你在Step #6创建的虚拟目录的 URL .

<appUpdater>
  <UpdaterConfiguration>
   <polling type="Seconds" value="120" />
  
<logListener logPath="C:\Program Files\YourApp\UpdaterLog.txt" />
   <downloader type="Microsoft.ApplicationBlocks.ApplicationUpdater.Downloaders.BITSDownloader"
assembly="Microsoft.ApplicationBlocks.ApplicationUpdater,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
 
  <validator type="Microsoft.ApplicationBlocks.ApplicationUpdater.Validators.RSAValidator" assembly="Microsoft.ApplicationBlocks.ApplicationUpdater,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
 
<key>
  
<RSAKeyValue>
  
<Modulus>[YOUR MODULUS KEY]</Modulus>
 
<Exponent>[YOUR EXPONENET]</Exponent>
 
</RSAKeyValue>
 
</key>
 
</validator> 
  <application name="[YOUR APP NAME]" useValidation="true">
   
<client>
     
<baseDir>C:\Program Files\YourApp</baseDir>
     
<xmlFile>C:\Program Files\YourApp\AppStart.exe.config</xmlFile>
     
<tempDir>C:\Program Files\YourApp\temp</tempDir>
   
</client>
    <server>
     
<xmlFile>http://[YOUR URL]/ServerManifest.xml</xmlFile>
    
<xmlFileDest>C:\Program Files\YourApp\ServerManifest.xml</xmlFileDest>
    
<maxWaitXmlFile>60000</maxWaitXmlFile>
   
</server>
  </application>
 </UpdaterConfiguration>
 
</appUpdater>

Step #8 发布版本 1.0.0.0

设置应用程序版本号.  可以通过设置在 AssemblyInfo.cs 文件中的版本属性来设置版本号.

[assembly: AssemblyVersion("1.0.0.0")]

编译应用程序并复制 1.0.0.0 版程序到你程序的 1.0.0.0 目录中. “C:\Program Files\YourApp\1.0.0.0“

这里,你需要运行一下 AppStart.exe.  更新过程会失败,因为我们并没有把发布 ServerManifest XML 文件来指示应用程序新版本是否可用.  你可以检查日志文件,位置在 C:\Program Files\YourApp\ 目录中.

Step #9 构建版本 1.0.0.1 

这是最有趣的部分.  首先, 通过更新应用程序的 AssemblyInfo.cs 和 App.config 文件内容来生成修订版本 1.0.0.1 .  编译程序, 然后复制文件到step #6生成的Web服务器目录中. 

Step #10 生成服务器的清单文件

这个是最后一步.  如果你对本步骤中的.config文件作了任何修改的话,都必须把本步骤重来一遍.  做法如下:

  1. 再次运行 ManifestUtility 程序. 
  2. 在 “Update files folder“ 选择器中选择 1.0.0.1 目录 . 
  3. 输入更新位置的 URL . 
  4. 输入新版本号 1.0.0.1
  5. 打开之前生成的 PrivateKey.xml 文件.
  6. 选择验证类 “Microsoft.ApplicationBlocks.ApplicationUpdater.Validators.RSAValidator”
  7. 鼠标点击 CreateManifest, 并保存 ServerManifest.xml 文件到你的虚拟服务器目录中.

就这些!  Pheeew!  从你的 C:\Program Files\YourApp\ 目录中运行你的 AppStart.exe .  你的程序就会被装入, 当你的程序运行的时候,你就会得到一个提示 “新版本可用” .  新版本会下载到目录 C:\Program Files\YourApp\1.0.0.1 中, 然后程序会自动重启.  如果有任何问题, 记得检查一下日志文件.  这些日志在诊断问题的时候会很有用的.

-Brendan

posted on Thursday, June 10, 2004 11:25 AM
附录:文中步骤 #2 包含的代码如下:
  1 None.gif      
  2 None.gif private  ApplicationUpdateManager _updater  =   null
  3 None.gif private  Thread _updaterThread  =   null
  4 None.gif private   const   int  UPDATERTHREAD_JOIN_TIMEOUT  =   3   *   1000
  5 None.gif 
  6 None.gif private   delegate   void  MarshalEventDelegate(  object  sender, UpdaterActionEventArgs e ); 
  7 None.gif 
  8 None.gif private   void  InitializeAutoUpdate() 
  9 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 10InBlock.gif    //  hook ProcessExit for a chance to clean up when closed peremptorily 
 11InBlock.gif    AppDomain.CurrentDomain.ProcessExit +=new EventHandler(CurrentDomain_ProcessExit); 
 12InBlock.gif 
 13InBlock.gif    //  make an Updater for use in-process with us 
 14InBlock.gif    _updater = new ApplicationUpdateManager(); 
 15InBlock.gif 
 16InBlock.gif    //  hook Updater events 
 17InBlock.gif    _updater.DownloadStarted +=new UpdaterActionEventHandler( OnUpdaterDownloadStarted ); 
 18InBlock.gif    _updater.FilesValidated +=new UpdaterActionEventHandler( OnUpdaterFilesValidated ); 
 19InBlock.gif    _updater.UpdateAvailable +=new UpdaterActionEventHandler( OnUpdaterUpdateAvailable ); 
 20InBlock.gif    _updater.DownloadCompleted +=new UpdaterActionEventHandler(OnUpdaterDownloadCompleted); 
 21InBlock.gif 
 22InBlock.gif    //  start the updater on a separate thread so that our UI remains responsive 
 23InBlock.gif    _updaterThread = new Thread( new ThreadStart( _updater.StartUpdater ) ); 
 24InBlock.gif    _updaterThread.Start(); 
 25InBlock.gif 
 26InBlock.gif    //  get version from config, set caption correctly 
 27InBlock.gif    string version = System.Configuration.ConfigurationSettings.AppSettings["version"]; 
 28InBlock.gif    this.Text = this.Text + String.Format(" v. {0}", version); 
 29ExpandedBlockEnd.gif}
 
 30 None.gif 
 31 None.gif private   void  CurrentDomain_ProcessExit( object  sender, EventArgs e) 
 32 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 33InBlock.gif    StopUpdater(); 
 34ExpandedBlockEnd.gif}
 
 35 None.gif 
 36 None.gif 
 37 None.gif private   void  StopUpdater() 
 38 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 39InBlock.gif    //  tell updater to stop 
 40InBlock.gif    _updater.StopUpdater(); 
 41InBlock.gif    ifnull != _updaterThread ) 
 42ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif
 43InBlock.gif        //  join the updater thread with a suitable timeout 
 44InBlock.gif        bool isThreadJoined = _updaterThread.Join( UPDATERTHREAD_JOIN_TIMEOUT ); 
 45InBlock.gif        //  check if we joined, if we didn't interrupt the thread 
 46InBlock.gif        if!isThreadJoined ) 
 47ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{     
 48InBlock.gif            _updaterThread.Interrupt(); 
 49ExpandedSubBlockEnd.gif        }
 
 50InBlock.gif        _updaterThread = null
 51ExpandedSubBlockEnd.gif    }
 
 52ExpandedBlockEnd.gif}
 
 53 None.gif 
 54 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
 55InBlock.gif/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM.  It takes the same  
 56InBlock.gif/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread 
 57InBlock.gif/// </summary> 
 58InBlock.gif/// <param name="sender">marshalled reference to the original event's sender argument</param> 
 59ExpandedBlockEnd.gif/// <param name="e">marshalled reference to the original event's args</param> 

 60 None.gif private   void  OnUpdaterDownloadStartedHandler(  object  sender, UpdaterActionEventArgs e )  
 61 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 62InBlock.gif    Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString()); 
 63InBlock.gif 
 64InBlock.gif    Debug.WriteLine(String.Format( "  DownloadStarted for application '{0}'", e.ApplicationName )); 
 65ExpandedBlockEnd.gif}
 
 66 None.gif 
 67 None.gif 
 68 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
 69InBlock.gif/// Event handler for Updater event.  This event is fired by the originating thread from "inside" the Updater.  While it is 
 70InBlock.gif/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.   
 71InBlock.gif/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke 
 72InBlock.gif/// mechanism. 
 73InBlock.gif/// </summary> 
 74InBlock.gif/// <param name="sender">event sender in this case ApplicationUpdaterManager</param> 
 75ExpandedBlockEnd.gif/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param> 

 76 None.gif private   void  OnUpdaterDownloadStarted(  object  sender, UpdaterActionEventArgs e ) 
 77 ExpandedBlockStart.gifContractedBlock.gif dot.gif {  
 78InBlock.gif    //  using the synchronous "Invoke".  This marshals from the eventing thread--which comes from the Updater and should not 
 79InBlock.gif    //  be allowed to enter and "touch" the UI's window thread 
 80InBlock.gif    //  so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI 
 81InBlock.gif    Debug.WriteLine( String.Format( "[OnUpdaterDownloadStarted]Thread: {0}", Thread.CurrentThread.GetHashCode().ToString()) ); 
 82InBlock.gif    this.Invoke(  
 83InBlock.gif            new MarshalEventDelegate( this.OnUpdaterDownloadStartedHandler ),  
 84ExpandedSubBlockStart.gifContractedSubBlock.gif            new object[] dot.gif{ sender, e } ); 
 85ExpandedBlockEnd.gif}
 
 86 None.gif 
 87 None.gif 
 88 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
 89InBlock.gif/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM.  It takes the same  
 90InBlock.gif/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread 
 91InBlock.gif/// </summary> 
 92InBlock.gif/// <param name="sender">marshalled reference to the original event's sender argument</param> 
 93ExpandedBlockEnd.gif/// <param name="e">marshalled reference to the original event's args</param> 

 94 None.gif private   void  OnUpdaterFilesValidatedHandler(  object  sender, UpdaterActionEventArgs e ) 
 95 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 96InBlock.gif    Debug.WriteLine(String.Format("FilesValidated successfully for application '{0}' ", e.ApplicationName)); 
 97InBlock.gif     
 98InBlock.gif    //  ask user to use new app 
 99InBlock.gif    DialogResult dialog = MessageBox.Show(  
100InBlock.gif            "Would you like to stop this application and open the new version?""Open New Version?", MessageBoxButtons.YesNo ); 
101InBlock.gif    if( DialogResult.Yes == dialog ) 
102ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif
103InBlock.gif        StartNewVersion( e.ServerInformation ); 
104ExpandedSubBlockEnd.gif    }
 
105ExpandedBlockEnd.gif}
 
106 None.gif 
107 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
108InBlock.gif/// Event handler for Updater event.  This event is fired by the originating thread from "inside" the Updater.  While it is 
109InBlock.gif/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.   
110InBlock.gif/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke 
111InBlock.gif/// mechanism. 
112InBlock.gif/// </summary> 
113InBlock.gif/// <param name="sender">event sender in this case ApplicationUpdaterManager</param> 
114ExpandedBlockEnd.gif/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param> 

115 None.gif private   void  OnUpdaterFilesValidated(  object  sender, UpdaterActionEventArgs e ) 
116 ExpandedBlockStart.gifContractedBlock.gif dot.gif
117InBlock.gif    //  using the asynchronous "BeginInvoke".   
118InBlock.gif    //  we don't need/want to block here 
119InBlock.gif    this.BeginInvoke(  
120InBlock.gif            new MarshalEventDelegate( this.OnUpdaterFilesValidatedHandler ), 
121ExpandedSubBlockStart.gifContractedSubBlock.gif            new object[] dot.gif{ sender, e } ); 
122ExpandedBlockEnd.gif}
 
123 None.gif 
124 None.gif 
125 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
126InBlock.gif/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM.  It takes the same  
127InBlock.gif/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread 
128InBlock.gif/// </summary> 
129InBlock.gif/// <param name="sender">marshalled reference to the original event's sender argument</param> 
130ExpandedBlockEnd.gif/// <param name="e">marshalled reference to the original event's args</param> 

131 None.gif private   void  OnUpdaterUpdateAvailableHandler(  object  sender, UpdaterActionEventArgs e ) 
132 ExpandedBlockStart.gifContractedBlock.gif dot.gif {     
133InBlock.gif    Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString()); 
134InBlock.gif 
135InBlock.gif    string message = String.Format(  
136InBlock.gif        "Update available:  The new version on the server is {0} and current version is {1} would you like to upgrade?",  
137InBlock.gif        e.ServerInformation.AvailableVersion,   
138InBlock.gif        System.Configuration.ConfigurationSettings.AppSettings["version"] ) ; 
139InBlock.gif 
140InBlock.gif    //  for update available we actually WANT to block the downloading thread so we can refuse an update 
141InBlock.gif    //  and reset until next polling cycle; 
142InBlock.gif    //  NOTE that we don't block the thread _in the UI_, we have it blocked at the marshalling dispatcher "OnUpdaterUpdateAvailable" 
143InBlock.gif    DialogResult dialog = MessageBox.Show( message, "Update Available", MessageBoxButtons.YesNo ); 
144InBlock.gif 
145InBlock.gif    if( DialogResult.No == dialog ) 
146ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif
147InBlock.gif        //  if no, stop the updater for this app 
148InBlock.gif        _updater.StopUpdater( e.ApplicationName ); 
149InBlock.gif        Debug.WriteLine("Update Cancelled."); 
150ExpandedSubBlockEnd.gif    }
 
151InBlock.gif    else 
152ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif
153InBlock.gif        Debug.WriteLine("Update in progress."); 
154ExpandedSubBlockEnd.gif    }
 
155ExpandedBlockEnd.gif}
 
156 None.gif 
157 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
158InBlock.gif/// Event handler for Updater event.  This event is fired by the originating thread from "inside" the Updater.  While it is 
159InBlock.gif/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.   
160InBlock.gif/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke 
161InBlock.gif/// mechanism. 
162InBlock.gif/// </summary> 
163InBlock.gif/// <param name="sender">event sender in this case ApplicationUpdaterManager</param> 
164ExpandedBlockEnd.gif/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param> 

165 None.gif private   void  OnUpdaterUpdateAvailable(  object  sender, UpdaterActionEventArgs e ) 
166 ExpandedBlockStart.gifContractedBlock.gif dot.gif
167InBlock.gif    //  using the synchronous "Invoke".  This marshals from the eventing thread--which comes from the Updater and should not 
168InBlock.gif    //  be allowed to enter and "touch" the UI's window thread 
169InBlock.gif    //  so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI 
170InBlock.gif    this.Invoke(  
171InBlock.gif        new MarshalEventDelegate( this.OnUpdaterUpdateAvailableHandler ),  
172ExpandedSubBlockStart.gifContractedSubBlock.gif        new object[] dot.gif{ sender, e } ); 
173ExpandedBlockEnd.gif}
 
174 None.gif 
175 None.gif     
176 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
177InBlock.gif/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM.  It takes the same  
178InBlock.gif/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread 
179InBlock.gif/// </summary> 
180InBlock.gif/// <param name="sender">marshalled reference to the original event's sender argument</param> 
181ExpandedBlockEnd.gif/// <param name="e">marshalled reference to the original event's args</param> 

182 None.gif private   void  OnUpdaterDownloadCompletedHandler(  object  sender, UpdaterActionEventArgs e ) 
183 ExpandedBlockStart.gifContractedBlock.gif dot.gif
184InBlock.gif    Debug.WriteLine("Download Completed."); 
185InBlock.gif 
186ExpandedBlockEnd.gif}
 
187 None.gif 
188 ExpandedBlockStart.gifContractedBlock.gif /**/ /**/ /**/ /// <summary> 
189InBlock.gif/// Event handler for Updater event.  This event is fired by the originating thread from "inside" the Updater.  While it is 
190InBlock.gif/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.   
191InBlock.gif/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke 
192InBlock.gif/// mechanism. 
193InBlock.gif/// </summary> 
194InBlock.gif/// <param name="sender">event sender in this case ApplicationUpdaterManager</param> 
195ExpandedBlockEnd.gif/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param> 

196 None.gif private   void  OnUpdaterDownloadCompleted(  object  sender, UpdaterActionEventArgs e ) 
197 ExpandedBlockStart.gifContractedBlock.gif dot.gif
198InBlock.gif    //  using the synchronous "Invoke".  This marshals from the eventing thread--which comes from the Updater and should not 
199InBlock.gif    //  be allowed to enter and "touch" the UI's window thread 
200InBlock.gif    //  so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI 
201InBlock.gif    this.Invoke(  
202InBlock.gif        new MarshalEventDelegate( this.OnUpdaterDownloadCompletedHandler ),  
203ExpandedSubBlockStart.gifContractedSubBlock.gif        new object[] dot.gif{ sender, e } ); 
204ExpandedBlockEnd.gif}
 
205 None.gif 
206 None.gif 
207 None.gif private   void  StartNewVersion( ServerApplicationInfo server ) 
208 ExpandedBlockStart.gifContractedBlock.gif dot.gif
209InBlock.gif    XmlDocument doc = new XmlDocument(); 
210InBlock.gif 
211InBlock.gif    //  load config file to get base dir 
212InBlock.gif    doc.Load( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile ); 
213InBlock.gif 
214InBlock.gif    //  get the base dir 
215InBlock.gif    string baseDir = doc.SelectSingleNode("configuration/appUpdater/UpdaterConfiguration/application/client/baseDir").InnerText; 
216InBlock.gif    string newDir = Path.Combine( baseDir, "AppStart.exe" ); 
217InBlock.gif 
218InBlock.gif    ProcessStartInfo process = new ProcessStartInfo( newDir ); 
219InBlock.gif    process.WorkingDirectory = Path.Combine( newDir , server.AvailableVersion ); 
220InBlock.gif 
221InBlock.gif    //  launch new version (actually, launch AppStart.exe which HAS pointer to new version ) 
222InBlock.gif    Process.Start( process ); 
223InBlock.gif 
224InBlock.gif    //  tell updater to stop 
225InBlock.gif    CurrentDomain_ProcessExit( nullnull ); 
226InBlock.gif    //  leave this app 
227InBlock.gif    Environment.Exit( 0 ); 
228ExpandedBlockEnd.gif}
 
229 None.gif 
230 None.gif

转自: http://www.cnblogs.com/bigtall/archive/2004/12/09/74781.aspx

转载于:https://www.cnblogs.com/asyuras/archive/2006/07/12/448631.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值