zoukankan      html  css  js  c++  java
  • 用Application Updater Block生成一个自我更新的WinForms 应用

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

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

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

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

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

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

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

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

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

    转自:http://www.cnblogs.com/bigtall/archive/2004/12/09/74781.aspx
  • 相关阅读:
    1、应用设置之TAB页
    HDUOJ 水果
    Problem G 宝石合成 (内蒙古14年省赛)
    ORA-09925: Unable to create audit trail file汇总
    浅拷贝(在进行当中一个对象的运算时开辟新的空间)
    PHP图像操作类
    如何制作Jar包并在android中调用jar包
    android中正确导入第三方jar包
    Android 数据加密算法 Des,Base64详解
    Android Zip文件解压缩代码
  • 原文地址:https://www.cnblogs.com/asyuras/p/448631.html
Copyright © 2011-2022 走看看