使用Updater Application Block实现自动更新特性

2009年3月7日星期六

使用Updater Application Block实现自动更新特性

由于有了Smart Client技术,我们可以很好的将胖客户端和瘦客户端应用的优点结合在一起,适应医院使用实际情况的需要。Smart Client可以自动灵活地进行升级和更新从而简化了系统的维护;Smart Client可以充分的利用本地的计算资源,可以将HIS处理的载荷合理的分配给系统中的每一台计算机,提高了系统的响应和性能。

微软公司提供的Microsoft Application Block为开发具有智能更新功能的.NET应用提供了极大的便利。在HIS Demo中我们重用并扩展了Updater Application Block (UAB)等应用程序模块,实现了符合HIS应用实际需求的自动更新等功能。

使用UAB可以实现对.NET应用智能更新支持,UAB为应用提供了下载,验证和后置处理机制。通过UAB提供的接口,我们可以轻易对UAB根据自己需要进行扩展。在HIS Demo中,我们使用BITS下载机制,保证系统的运作效率。UAB的工作流程如下图所示:




UAB主要有四个模块组成Updater, Downloader, Validate以及Post Processor组成。Updater负责整个更新工作的管理;Downloader实现文件的下载,UAB中采用BITS (Background Intelligence Transfer Service)作为Downloader,UAB提供了IDownloader 接口,实现这个接口,开发者可以开发基于任意协议的下载器;Validator完成对下载文件的校验,UAB中提供KeyValidator和RSAValidator两个类,分别来实现对对称和非对称加密文件的校验;Post Processor则提供完成文件更新后需要进行的各种操作。

Q 如何在应用中实现基本的自动更新功能?

HIS Demo中将自动更新的主要功能封装在SelfUpdater类中(源文件HISDEMO \HISCLINIC \SelfUpdater.cs)

在SelfUpdater类中通过InitUpdater()方法对Updater进行初始化,主要工作包括实例化ApplicationUpdateManager,为更新过程中主要的事件UpdaterAvailable和FilesValidated等事件指定事件处理器



/***********************************************************

private void InitUpdater()

{

updater=new ApplicationUpdateManager();

updater.UpdateAvailable += new UpdaterActionEventHandler(updater_UpdateAvailable);

updater.FilesValidated += new UpdaterActionEventHandler(updater_FilesValidated);

}

/***********************************************************

UpdaterAvailable事件当UAB发现服务器上有新的应用版本时发生,这时通常会询问用户是否要需要更新,如果用户选择需要则开始下载

/***********************************************************

///

///当判定存在更新文件时相应的事件方法

///


///

///

private void updater_UpdateAvailable(object sender, UpdaterActionEventArgs e)

{

// Show a message allowing the user to determine if they would like to download the update

// and perform the upgrade

string message = String.Format(

"更新提示:现在服务器上的最新版本是 {0} , 需要更新吗?",

e.ServerInformation.AvailableVersion ) ;

DialogResult dialog ;

if(MsgBoxOwner!=null)

dialog = MessageBox.Show(MsgBoxOwner, message, "更新提示",

MessageBoxButtons.YesNo );

else

dialog = MessageBox.Show( message, "更新提示", MessageBoxButtons.YesNo );

UpdaterArgs args=new UpdaterArgs();



// The user has indicated they don't want to upgrade

if( DialogResult.No == dialog )

{

// stop the updater for this app

updater.StopUpdater( e.ApplicationName );

args.IsRunning=false;

}

Else

{

args.IsRunning=true;

}

//

if(UpdaterStateCallBack!=null)

UpdaterStateCallBack(this,args);

}

/***********************************************************

FilesValidated事件当更新文件成功下载并通过校验后发生,这时时通常会询问用户是否要需要运新更新后的程序,如果用户选择需要是则需要停止当前应用并启动更新后的应用

/***********************************************************

///

/// 当更新文件下载完毕时相应的事件方法

///


///

///

private void updater_FilesValidated(object sender, UpdaterActionEventArgs e)

{

// Ask user if they want to use the new version of the application

DialogResult dialog = MessageBox.Show(

"需要打开新的应用程序吗?",

"打开新版本?", MessageBoxButtons.YesNo );



if( DialogResult.Yes == dialog )

{

// Load this applications configuration file to read the

// UAB information and obtain the basedir

XmlDocument doc = new XmlDocument();

doc.Load( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile );

string baseDir = doc.SelectSingleNode(

"configuration/appUpdater/UpdaterConfiguration/application/client/baseDir"

).InnerText;

// Figure out the path to AppStart.exe which we will chain to

string newDir = Path.Combine( baseDir, "AppStart.exe" );

// Launch AppStart.exe which will launch the new version

ProcessStartInfo process = new ProcessStartInfo( newDir );

process.WorkingDirectory = Path.Combine( newDir ,

e.ServerInformation.AvailableVersion );

Process.Start( process );

StopUpdate();

Environment.Exit( 0 );

}

}

/**********************************************************

使用自动更新是在主表单中实例化SelfUpdater类即可。

/***********************************************************

public frmMainForm()

{

//

// Windows 窗体设计器支持所必需的

//

InitializeComponent();

//Init update obeject

updater=new SelfUpdater();

……

}

/***********************************************************

Q 如何产生和配置清单文件(Manifest.xml)?

清单文件在整个更新过程非常重要。在更新的过程中这个清单会被Downloader从服务器上下载到本地,由Updater进行解析并判断是否有更新。如果有更新,Downloader会根据清单的内容,从服务器上下载相应的文件,Validator会根据清单中的签名对清单进行校验。下面是清单文件的例子,可以看出清单中的信息包括清单本身的签名,可用的应用版本号和位置,文件列表和签名,后处理器的名称等。

注意:如果采用了RSA加密签名,则不可以手动改变此清单。此清单应由相应工具产生并用私有密钥进行签名。



1.0.0.0

http://localhost/HISClinicWebDeploy/1.0.0.0/











.......











清单可用采用UAB自带的工具ManifestUtility.exe产生,如下图所示:




Q 如何产生和配置App.Config文件?

App.Config文件决定了在客户运行的应用版本和位置,其内容如下。其中最关键的appFolderName节点,此路径应为程序的安装路径,此文件应该在客户端初次安装时生成并置于应用的根目录下。















D:\PROGRA~1\Winarray\HISDemo\HISClient\2.0.0.0

HISClinic.exe

2.0.0.0

2004-12-01T01:05:01.6490969+08:00







Q 如何产生和配置HISDemo.exe.config文件?

HISDemo.exe.config文件主要说明了应用使用UAB的情况和配置,对于使用RSA加密签名的文件,公钥存于此文件中。另外,在本应用中Sql的连接字符串也存于此文件中。如Sql服务器配置有改动,只需要改写此文件中的相应信息即可。

































oslBz4MuFil9QKi3zkuqgsl8U7ppPQgCShDvb1D5dQTthlyo+dZ7wG7YEysxgm4P1ofr29muQIdjYRwhn7afvH1VgVahOLxVWsQm2rqNWbEZ9OV3UMFTf0P3erFiqxatDMiK65ayLmHXp0YM0zwxpm+ekQ8jm3Zg9ac0HePZsIk=

AQAB











D:\PROGRA~1\Winarray\HISDemo\HISClient D:\PROGRA~1\Winarray\HISDemo\HISClient\AppStart.exe.config

D:\PROGRA~1\Winarray\HISDemo\HISClient\DownloadFiles





http://localhost/HISClinicWebDeploy/ServerManifest.xml D:\PROGRA~1\Winarray\HISDemo\HISClient\ServerManifest.xml

60000



















Q 如何将安装路径写入配置文件?

为了能使UAB正常的工作,在配置文件HISClinic.exe.config中需要指明程序安装的路径。由于客户端可能安装在任意的路径下,为此在应用的文件安装或下载以后需要再将安装路径写入配置文件,确保应用的正常工作。

在应用程序的文件完成拷贝和下载后,需要运行如下代码,实现下列任务:通过Reflection.Assembly取得当前运行路径以及更改HISClinic.exe.config配置

参考代码: HISClinic\HISDemo\PostProcessor\Processor.cs

/***********************************************************

public class Processor:IPostProcessor

{

public Processor()

{

//

// TODO: 在此处添加构造函数逻辑

//

}

///

/// 实现IPostprocessor.Run接口方法

/// 更改下载程序配置信息

///


public void Run()

{

String p=Path.GetDirectoryName

(System.Reflection.Assembly.GetExecutingAssembly().Location);

DirectoryInfo dir=new DirectoryInfo(p);

Config.ConfigFile config=new

Config.ConfigFile(Path.Combine(p,@"HISClinic.exe.config"));

config.ChangePath("HISClinic",dir.Parent.FullName);

}

public void Dispose(){}

}

/***********************************************************

Q 如何设定更新检测的时间?

应用程序如需要设定更新检测时间,可以通过设置HISClinic.exe.config的polling节点属性以及设定UAB的UpdaterConfiguration.Instance来实现

参考代码: HISClinic\HISDemo\HIClinic\SelfUpdater.CS;

HISClinic\HISDemo\ChangePath\Config.CS;



/***********************************************************

///

/// 设置Polling间隔

///


public int Interval

{

set

{

SavePollingIntoConfigFile(value);

}

get

{

return GetPollingInterval();

}

}

///

/// 保存更新间隔,单位(秒)

///


///

private void SavePollingIntoConfigFile(int interval)

{

Config.ConfigFile configfile=new

Config.ConfigFile(System.Reflection.Assembly.

GetExecutingAssembly().Location+".config");

//修改配置文件

configfile.UpdateInterval=interval;

//修改UAB实例

UpdaterConfiguration.Instance.Polling.Value=interval.ToString();

UpdaterConfiguration.Instance.Polling.Type=PollingType.Seconds;

}

///

/// 获取更新间隔,单位(秒)

///


///

private int GetPollingInterval()

{

Config.ConfigFile configfile=new

Config.ConfigFile(System.Reflection.Assembly.GetExecutingAssembly().Loca

tion+".config");

return configfile.UpdateInterval;

}

/***********************************************************

Q 如何实现强制的手动即时更新?

应用程序如果需要通过界面控制实现强制的手动即时更新,可以通过以下方式实现。首先调用ApplicationUpdateManager.StopUpdater()停止更新,然后重新实例化ApplicationUpdateManager,并通过调用StartUpdater()实现更新

参考代码: HISClinic\HISDemo\HIClinic\selfUpdater.CS

/***********************************************************

///

/// 重新开始更新

///


public void ReStartUpdate()

{

updater=null;

InitUpdater();

updater.StartUpdater();

}

/***********************************************************

Q 如何设定更新日志路径以及设定是否需要保留更新日志?

默认情况下,UAB会将更新日志存放在应用安装的根目录下,并且每次运行运用都会产生一个新的日志。这种机制会在客户端留下大量的日志文件。如果需要设定更新日志路径以及设定是否需要保留更新日志,需要更改UAB配置文件和对应实例以及EMAB配置文件

参考代码: HISClinic\HISDemo\HIClinic\SelfUpdater.CS;

HISClinic\HISDemo\ChangePath\Config.CS;

/***********************************************************

///

/// 设置日志路径

///


///

public void SetLogPath(string path)

{

XmlDocument doc=new XmlDocument();

doc.Load(configfilepath);

XmlNodeList nodes=doc.GetElementsByTagName("logListener");

if(path.Length>0)

{//设置UAB日志标志

XmlNode node=null;

if(nodes.Count>0) node=nodes[0];

else

{

node=doc.CreateElement("logListener");

XmlAttribute attr=doc.CreateAttribute("logPath");

node.Attributes.Append(attr);

doc.SelectSingleNode("configuration/appUpdater/UpdaterConfigurat

ion").AppendChild(node);

}

node.Attributes["logPath"].Value=path;

}

else

{//设置EMAB日志标志

XmlNode node=doc.SelectSingleNode

("configuration/appUpdater/UpdaterConfiguration/logListener");

if(node!=null)

node.ParentNode.RemoveChild(node);

}



SetEMABLogPulish(path.Length>0,doc);

doc.Save(configfilepath);

}

/***********************************************************

Q 如何实现使用任意协议(http, ftp…)进行下载?

UAB采用Windows自带的BITS服务进行文件的下载,如需要采用任意协议或是自定义的下载器进行下载,开发自定义的下载器,并实现IDownloader接口即可

如何删除客户端操作系统上不必要的较早版本的文件备份

默认情况下UAB会保留较早版本的文件备份,可以通过实现IPostprocessor接口,开发简单代码,在应用更新成功后,对不必要文件进行删除。

Q 如何在应用安装的同时自动的导入数据库?

将要安装的数据库文件.mdf拷贝到[数据库安装路径]通过OSQL命令调用如下的T-SQL 脚本。

EXECUTE sp_attach_db @dbname = N'',

@filename1 = N'',

@filename2 = N''

GO

文件的拷贝和OSQL命令的调用可以通过安装程序进行,从而实现数据库的自动安装。

0 评论:

发表评论