昆明小虫

这是最好的时期,也是最坏的时期...这是希望的春天,也是失望的冬天...动起来...时刻准备着...
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

使用Updater Application Block实现自动更新例子

Posted on 2005-10-31 08:55  昆明小虫  阅读(2914)  评论(0编辑  收藏  举报
 

Ø       使用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)作为DownloaderUAB提供了IDownloader 接口,实现这个接口,开发者可以开发基于任意协议的下载器;Validator完成对下载文件的校验,UAB中提供KeyValidatorRSAValidator两个类,分别来实现对对称和非对称加密文件的校验;Post Processor则提供完成文件更新后需要进行的各种操作。

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

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

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

 

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

private void InitUpdater()

{

updater=new ApplicationUpdateManager();

updater.UpdateAvailable += new UpdaterActionEventHandler(updater_UpdateAvailable);

    updater.FilesValidated += new UpdaterActionEventHandler(updater_FilesValidated);

}

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

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

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

/// <summary>

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

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

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

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

/// <summary>

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

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

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加密签名,则不可以手动改变此清单。此清单应由相应工具产生并用私有密钥进行签名。

<ServerApplicationInfo signature="hPPVuZQAo95scW8vozlwho0Qny+eEdfbI4vqBbYyqfKaTYnigY5k83XHvkVyrPlLFGcUqIezGiKWkFq7X49ZryiIWyzWi3LEnltTy3xGBTAMvTekRfik0VFjiHVyQ9Fwz5DTKxhRWqmfJqAHiEM3Bj2JGJxTNvynbRFHjw0+RWM=">

  <availableVersion>1.0.0.0</availableVersion>

<updateLocation>http://localhost/HISClinicWebDeploy/1.0.0.0/</updateLocation>

<files>

  <file name="BLL.dll" signature="eAfVF1W3+k7CWBP67EJddtJd8b02ZE3N3R02QRdGw1VO5iluUEK22aCydC9kxOwEYPo4CWmxW4QMalSSSdv4h4TkPU1QGWB9i7Cs72pmWxMaKBN19WkFIgQr588lf+cCqRCBOdn7NVapXCwK6hLARcEhpWG/EeN3tAf/zSfRr8A=" /> 

  <file name="HISClinic.exe" signature="ojYnuA9kby/hlJOtLlsDglQT30Qn1/i1X3dvZMRjn+fZ0Tr0LjmBISOR0GgHxlHEexOzkyhjgJy3diVbhCbt4ucnxaN+B/ei2oBfW6Xt0wx25ywuGpo9tLuzwaUj9b9eAQ02Sj9eHY2YvKo0pSrOye9yoCYdl/fZhDj2WKhPcs4=" />

  <file name="HISClinic.exe.config" signature="aP0sXIqEC0FeDUHzPQ3EiregL9r8Y5CdtMP0es1rEW0Cx++iPPAJK8t1HBdE+FOGmQ2URwgUFZNzlm8sTw5yIM+mwSdo/MD8irICv2H/v980JbwcUY4vaPdl5+14PJPXRK+XVOk6s4tigw44qHbu8hu/geh7YvYrDVCfYOdbFIo=" />

  <file name="Microsoft.ApplicationBlocks.ApplicationUpdater.dll" signature="evasZb0Vd4k4AHQgZbsxubVMEDQPgxSn9YznyuPzFV87gMyJNNMzYeaAfJhwQWBga1aWO/CfwF1UectXsgQ4N0kziK3Nk4NvJBZzP5dkxh5zHnTvMgPTeyES3knQoKbUvHSyzEfxKYP4PlmJQXtupBYDzUe72C/sEkQR+obq/EY=" />

  .......

  <file name="PostProcessor.dll" signature="KsPcHLW/RJW5ohs3RT4FCyALTqoUDBHknhSQguZ8l0A95746qb5bQbbYjYMuNkGsLJPomsh0fytpld7oXhtaTksaCelRRTOnVXNjXY1Ev7BXD8Ph1GHPWopdQrQqHYRGEorCjGWx2rCpLvpu+xgFcP+jLCllCqJrWhz7wMOvY68=" />

  </files>

  <postProcessor type="PostProcessor.Processor" assembly="PostProcessor, Version=1.0.1796.1348, Culture=neutral, PublicKeyToken=null" name="PostProcessor.dll" />

</ServerApplicationInfo>

 

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

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

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

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

    <section name="appStart" type="Microsoft.ApplicationBlocks.ApplicationUpdater.AppStart.ConfigSectionHandler,AppStart" />

  </configSections>

  <appStart>

    <ClientApplicationInfo>

      <appFolderName>D:\PROGRA~1\Winarray\HISDemo\HISClient\2.0.0.0</appFolderName>

      <appExeName>HISClinic.exe</appExeName>

      <installedVersion>2.0.0.0</installedVersion>

      <lastUpdated>2004-12-01T01:05:01.6490969+08:00</lastUpdated>

    </ClientApplicationInfo>

  </appStart>

</configuration>

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

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

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

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

    <section name="exceptionManagement" type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,Microsoft.ApplicationBlocks.ExceptionManagement" />

  </configSections>

  <appUpdater>

    <UpdaterConfiguration>

      <polling type="Seconds" value="60" />

      <logListener logPath="D:\PROGRA~1\Winarray\HISDemo\HISClient\UpdaterLog.txt" />

      <!--  ****************    BITS DOWNLOADER    ****************  -->

      <downloader type="Microsoft.ApplicationBlocks.ApplicationUpdater.Downloaders.BITSDownloader" assembly="Microsoft.ApplicationBlocks.ApplicationUpdater,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />

      <!--  ****************    THE RSA KEY HASHING VALIDATOR    ****************       -->

      <validator type="Microsoft.ApplicationBlocks.ApplicationUpdater.Validators.RSAValidator" assembly="Microsoft.ApplicationBlocks.ApplicationUpdater,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">

        <key>

          <RSAKeyValue>

            <Modulus>oslBz4MuFil9QKi3zkuqgsl8U7ppPQgCShDvb1D5dQTthlyo+dZ7wG7YEysxgm4P1ofr29muQIdjYRwhn7afvH1VgVahOLxVWsQm2rqNWbEZ9OV3UMFTf0P3erFiqxatDMiK65ayLmHXp0YM0zwxpm+ekQ8jm3Zg9ac0HePZsIk=</Modulus>

            <Exponent>AQAB</Exponent>

          </RSAKeyValue>

        </key>

      </validator>

      <application name="HISClinic" useValidation="true">

        <client>

          <baseDir>D:\PROGRA~1\Winarray\HISDemo\HISClient</baseDir>          <xmlFile>D:\PROGRA~1\Winarray\HISDemo\HISClient\AppStart.exe.config</xmlFile>

          <tempDir>D:\PROGRA~1\Winarray\HISDemo\HISClient\DownloadFiles</tempDir>

        </client>

        <server>

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

          <maxWaitXmlFile>60000</maxWaitXmlFile>

        </server>

      </application>

    </UpdaterConfiguration>

  </appUpdater>

</configuration>

 <exceptionManagement mode="on" />

  <appSettings>

    <add key="DBConnectionString" value="uid=sa;pwd=;data source=(local);initial catalog=HisClinic" />

  </appSettings>

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

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

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

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

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

public class Processor:IPostProcessor

{

public Processor()

    {

        //

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

        //

    }

    /// <summary>

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

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

    /// </summary>

    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.configpolling节点属性以及设定UABUpdaterConfiguration.Instance来实现

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

                   HISClinic\HISDemo\ChangePath\Config.CS;

 

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

/// <summary>

/// 设置Polling间隔

/// </summary>

public int Interval

{

set

{

        SavePollingIntoConfigFile(value);

    }

    get

{

        return GetPollingInterval();

    }

}

/// <summary>

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

/// </summary>

/// <param name="interval"></param>

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;

}

/// <summary>

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

/// </summary>

/// <returns></returns>

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

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

/// <summary>

/// 重新开始更新

/// </summary>

public void ReStartUpdate()

{

    updater=null;

    InitUpdater();

    updater.StartUpdater();

}

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

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

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

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

HISClinic\HISDemo\ChangePath\Config.CS;

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

/// <summary>

/// 设置日志路径

/// </summary>

/// <param name="path"></param>

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'<database_name, sysname, test_db>',

                   @filename1 = N'<filename1, nvarchar(260), [数据库安装路径]\*.mdf>',

                   @filename2 = N'<filename2, nvarchar(260), [数据库安装路径]\*.mdf >'

GO

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

下载例子