Updater Application Block实践

Step #1 Install the Application Blocks 安装自动更新组件

Download the Updater Application Block from Microsoft.

Step #2 Add the Code and References to Your Project: 添加代码和引用到工程

Add the following projects to the solution containing your Winforms project :

把以下工程添加到你的解决方案

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

They should be located in the following folder, if you've accepted the defaults 默认的安装位置在

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

Reference the following projects from within your Winforms Project 在工程中添加引用

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

Add the following namespaces to your form's .cs file.

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

using Microsoft.ApplicationBlocks.ApplicationUpdater; 工程要引用其中的类

Then add the application updater code located here to your code. You'll need to call InitializeAutoUpdate() from your MainForm's Initialize method.把以下代码添加到工程首页。

Step #3 Create your Application's Deployment Directory Structure and Configure AppStart.exe 创建发布目录并配置配置文件

Create a folder for your client installation. For example purposes, we'll use the following directory: 创建客户端安装目录,如:

C:/Program Files/YourApp/1.0.0.0/

Now copy the AppStart.exe and AppStart.exe.config into the root directory like so: 把以下两个文件拷贝到安装的根目录(C:/Program Files/YourApp)

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

Note: You can find these two files in the “C:/Program Files/Microsoft Application Blocks for .NET/Updater/Code/CS/Microsoft.ApplicationBlocks.Updater/AppStart/bin/Debug“ directory.

 

Step #4 Modify the AppStart.exe.config File 修改配置文件AppStart.exe.config

Appstart.exe.config将负责启动你的程序。

Modify this config file to match the current version like so: 按照你程序的当前版本配置此文件:


<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: Create Your Public and Private Keys 创建公匙和私匙

Run the ManifestUtility "C:/Program Files/Microsoft Application Blocks for .NET/Updater/Code/CS/Microsoft.ApplicationBlocks.Updater/ManifestUtility/bin/Debug/ManifestUtility.exe"

Choose “File..Generate Keys” You will be prompted to save the following keys: PublicKey.xml and PrivateKey.xml You'll be using these keys in the next steps.

From what I can tell it's best to create these keys once, because you'll need to reference these RSA public and private keys in a couple of places. You'll want to keep your keys in a safe place, because you'll need them when pushing new updates.

此工具生成成对的公匙和私匙。

Step #6 Create Your IIS Virtual Directory

Create a directory on your WebServer to house your updates. You will end up with two things in this directory

1) a ServerManifest.xml file which will contain the latest version information, and

2) your new application directory. Within this directory, create a directory to house your new application version. So, for our example, we'll create the directories, C:/Inetpub/AppUpdates and C:/Inetpub/AppUpdates/1.0.0.1

Use IIS Manager to create a virtual directory pointing to this physical directory. Remember your URL, as you will need it in an upcoming step. You will have to enable directory browsing for this virtual directory.

创建IIS的虚拟目录,在此存放你的新程序版本,例如:

C:/Inetpub/AppUpdates 包括你新版本程序的信息的配置文件将放在此目录

C:/Inetpub/AppUpdates/1.0.0.1 新版本的程序放在此目录

Step #7. Configure Your Version 1.0.0.0 App.config File 配置你程序的配置文件(你的程序名.config即完整程序名加.config例如:App01.exe.config 程序将默认寻找与其同名的.config配置文件,{DLL没有此项功能})

Here, we'll need to add a few things. First, we need to add a configSections element to define our appUpdater section:


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

Next we need to add the Version key to our appsettings key, we're first going to set our local versoin to 1.0.0.0, so that we can test the auto-update to version 1.0.0.1

<appSettings>
<add key="VERSION" value="1.0.0.0" /> 注意版本号一定要与程序的设定的版本号匹配
</appSettings>

Finally, add the appUpdater section to your config file. I've put brackets where you need to edit the values. You can just copy the <RSAKeyValue> element from the PublicKey.xml file that you created in the previous step. 把刚才生成的公匙填到配置文件的相应位置,(以下[]中)。

<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"> 此处是你程序的名称,如App01,无需后缀
<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 Deploy Version 1.0.0.0 发布版本1.0.0.0


Version your app. You do this by setting the version attribute of your app's AssemblyInfo.cs file. 修改你程序的版本号(在AssemblyInfo.cs中)

[assembly: AssemblyVersion("1.0.0.0")]

Build the Application and copy version 1.0.0.0 to your program file's 1.0.0.0 directory. “C:/Program Files/YourApp/1.0.0.0“将版本1.0.0.0拷贝到你的客户端安装目录(注意,在版本号下1.0.0.0)

At this point, you should be able to run AppStart.exe. The update process should fail, since we haven't deployed the ServerManifest XML file which tells our app that there's a new version available. You can inspect the log files that should be created in your C:/Program Files/YourApp/ directory.

Step #9 Buld Version 1.0.0.1 创建新版本1.0.0.1

将新版本拷贝到你的发布目录,在step #6创建的C:/Inetpub/AppUpdates/1.0.0.1下

注意同时拷贝到发布目录的还应该有:配置文件 App01.exe.config

因为可能找不到微软的组件,以下几个文件也一同拷贝,(否则升级时可能会报错system.io.filenotfoundException的错误,找不到相应的文件)

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

注意以上文件都在版本号的目录下,C:/Inetpub/AppUpdates/1.0.0.1

Step #10 Create Your Server Manifest File 生成服务端的升级说明文件

This should be the last step. Any changes you make to the .config files from this point on will require repeating this step. To do this:

Launch the ManifestUtility program again. 再次用工具 ManifestUtility (step #5中的工具)
Choose the 1.0.0.1 directory from the “Update files folder“ chooser. 选择新版本程序的目录(注意是带版本号的目录)
Enter the URL of the update location. 新版本的HTTP的址(注意带版本号的目录)
Enter version 1.0.0.1 新的程序版本号
Open your PrivateKey.xml file created earlier. Key处添加你刚才生成的私匙
Choose the validator class 选择校验类为RSA的“Microsoft.ApplicationBlocks.ApplicationUpdater.Validators.RSAValidator”
Click CreateManifest, and save the ServerManifest.xml file to your virtual server directory. 生成新版本说明文件,存放在虚拟目录下(注意是不带版本号的,C:/Inetpub/AppUpdates/)
生成的ServerManifest.xml文件中说明了要升级的文件的内容,1.0.0.1下的内容都将下载。

step 2 的代码
#region Auto-Update Stuff

private ApplicationUpdateManager _updater = null;
private Thread _updaterThread = null;
private const int UPDATERTHREAD_JOIN_TIMEOUT = 3 * 1000;

private delegate void MarshalEventDelegate( object sender, UpdaterActionEventArgs e );

private void InitializeAutoUpdate()
{
// hook ProcessExit for a chance to clean up when closed peremptorily
AppDomain.CurrentDomain.ProcessExit +=new EventHandler(CurrentDomain_ProcessExit);

// make an Updater for use in-process with us
_updater = new ApplicationUpdateManager();

// hook Updater events
_updater.DownloadStarted +=new UpdaterActionEventHandler( OnUpdaterDownloadStarted );
_updater.FilesValidated +=new UpdaterActionEventHandler( OnUpdaterFilesValidated );
_updater.UpdateAvailable +=new UpdaterActionEventHandler( OnUpdaterUpdateAvailable );
_updater.DownloadCompleted +=new UpdaterActionEventHandler(OnUpdaterDownloadCompleted);

// start the updater on a separate thread so that our UI remains responsive
_updaterThread = new Thread( new ThreadStart( _updater.StartUpdater ) );
_updaterThread.Start();

// get version from config, set caption correctly
string version = System.Configuration.ConfigurationSettings.AppSettings["version"];
this.Text = this.Text + String.Format(" v. {0}", version);
}

private void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
StopUpdater();
}


private void StopUpdater()
{
// tell updater to stop
_updater.StopUpdater();
if( null != _updaterThread )
{
// join the updater thread with a suitable timeout
bool isThreadJoined = _updaterThread.Join( UPDATERTHREAD_JOIN_TIMEOUT );
// check if we joined, if we didn't interrupt the thread
if( !isThreadJoined )
{
_updaterThread.Interrupt();
}
_updaterThread = null;
}
}

/// <summary>
/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same
/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread
/// </summary>
/// <param name="sender">marshalled reference to the original event's sender argument</param>
/// <param name="e">marshalled reference to the original event's args</param>
private void OnUpdaterDownloadStartedHandler( object sender, UpdaterActionEventArgs e )
{
Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString());

Debug.WriteLine(String.Format( " DownloadStarted for application '{0}'", e.ApplicationName ));
}


/// <summary>
/// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is
/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.
/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke
/// mechanism.
/// </summary>
/// <param name="sender">event sender in this case ApplicationUpdaterManager</param>
/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param>
private void OnUpdaterDownloadStarted( object sender, UpdaterActionEventArgs e )
{
// using the synchronous "Invoke". This marshals from the eventing thread--which comes from the Updater and should not
// be allowed to enter and "touch" the UI's window thread
// so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI
Debug.WriteLine( String.Format( "[OnUpdaterDownloadStarted]Thread: {0}", Thread.CurrentThread.GetHashCode().ToString()) );
this.Invoke(
new MarshalEventDelegate( this.OnUpdaterDownloadStartedHandler ),
new object[] { sender, e } );
}


/// <summary>
/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same
/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread
/// </summary>
/// <param name="sender">marshalled reference to the original event's sender argument</param>
/// <param name="e">marshalled reference to the original event's args</param>
private void OnUpdaterFilesValidatedHandler( object sender, UpdaterActionEventArgs e )
{
Debug.WriteLine(String.Format("FilesValidated successfully for application '{0}' ", e.ApplicationName));

// ask user to use new app
DialogResult dialog = MessageBox.Show(
"Would you like to stop this application and open the new version?", "Open New Version?", MessageBoxButtons.YesNo );
if( DialogResult.Yes == dialog )
{
StartNewVersion( e.ServerInformation );
}
}

/// <summary>
/// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is
/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.
/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke
/// mechanism.
/// </summary>
/// <param name="sender">event sender in this case ApplicationUpdaterManager</param>
/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param>
private void OnUpdaterFilesValidated( object sender, UpdaterActionEventArgs e )
{
// using the asynchronous "BeginInvoke".
// we don't need/want to block here
this.BeginInvoke(
new MarshalEventDelegate( this.OnUpdaterFilesValidatedHandler ),
new object[] { sender, e } );
}

/// <summary>
/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same
/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread
/// </summary>
/// <param name="sender">marshalled reference to the original event's sender argument</param>
/// <param name="e">marshalled reference to the original event's args</param>
private void OnUpdaterUpdateAvailableHandler( object sender, UpdaterActionEventArgs e )
{
Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString());

string message = String.Format(
"Update available: The new version on the server is {0} and current version is {1} would you like to upgrade?",
e.ServerInformation.AvailableVersion,
System.Configuration.ConfigurationSettings.AppSettings["version"] ) ;

// for update available we actually WANT to block the downloading thread so we can refuse an update
// and reset until next polling cycle;
// NOTE that we don't block the thread _in the UI_, we have it blocked at the marshalling dispatcher "OnUpdaterUpdateAvailable"
DialogResult dialog = MessageBox.Show( message, "Update Available", MessageBoxButtons.YesNo );

if( DialogResult.No == dialog )
{
// if no, stop the updater for this app
_updater.StopUpdater( e.ApplicationName );
Debug.WriteLine("Update Cancelled.");
}
else
{
Debug.WriteLine("Update in progress.");
}
}

/// <summary>
/// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is
/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.
/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke
/// mechanism.
/// </summary>
/// <param name="sender">event sender in this case ApplicationUpdaterManager</param>
/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param>
private void OnUpdaterUpdateAvailable( object sender, UpdaterActionEventArgs e )
{
// using the synchronous "Invoke". This marshals from the eventing thread--which comes from the Updater and should not
// be allowed to enter and "touch" the UI's window thread
// so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI
this.Invoke(
new MarshalEventDelegate( this.OnUpdaterUpdateAvailableHandler ),
new object[] { sender, e } );
}


/// <summary>
/// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same
/// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread
/// </summary>
/// <param name="sender">marshalled reference to the original event's sender argument</param>
/// <param name="e">marshalled reference to the original event's args</param>
private void OnUpdaterDownloadCompletedHandler( object sender, UpdaterActionEventArgs e )
{
Debug.WriteLine("Download Completed.");

}

/// <summary>
/// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is
/// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe.
/// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke
/// mechanism.
/// </summary>
/// <param name="sender">event sender in this case ApplicationUpdaterManager</param>
/// <param name="e">the UpdaterActionEventArgs packaged by Updater, which gives us access to update information</param>
private void OnUpdaterDownloadCompleted( object sender, UpdaterActionEventArgs e )
{
// using the synchronous "Invoke". This marshals from the eventing thread--which comes from the Updater and should not
// be allowed to enter and "touch" the UI's window thread
// so we use Invoke which allows us to block the Updater thread at will while only allowing window thread to update UI
this.Invoke(
new MarshalEventDelegate( this.OnUpdaterDownloadCompletedHandler ),
new object[] { sender, e } );
}


private void StartNewVersion( ServerApplicationInfo server )
{
XmlDocument doc = new XmlDocument();

// load config file to get base dir
doc.Load( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile );

// get the base dir
string baseDir = doc.SelectSingleNode("configuration/appUpdater/UpdaterConfiguration/application/client/baseDir").InnerText;
string newDir = Path.Combine( baseDir, "AppStart.exe" );

ProcessStartInfo process = new ProcessStartInfo( newDir );
process.WorkingDirectory = Path.Combine( newDir , server.AvailableVersion );

// launch new version (actually, launch AppStart.exe which HAS pointer to new version )
Process.Start( process );

// tell updater to stop
CurrentDomain_ProcessExit( null, null );
// leave this app
Environment.Exit( 0 );
}

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值