.Net部署二三事之一——如何为VS安装文件MSI制作更新补丁MSP

前言

首先需要说明的是,本文使用的工具是 VS IDE 、 Orca.exe ,所以 InstallShield 、 Wise 等其它打包工具不在讨论范围,另外需要 InstallShield 、 Wise 关于制作安装文件和更新补丁比 VS IDE 和 Orca.exe 更复杂。

其次本文的背景是,笔者发现国内关于如何在 VS IDE Setup Project (安装和部署工程)制作的 MSI 基础上制作 MSP 更新补丁的文章少之又少,所以再查询了众多英文文章后,总结了如下经验,大部分内容可能只是 http://www.codeproject.com/KB/install/dotnetpatching.aspx?fid=209224&fr=51&df=90&mpp=25&noise=3&sort=Position&view=Quick#xx0xx 中 How to create installation patches for VS.NET deployment projects 一文的翻译。(如果英文好的朋友可以直接看上文,但笔者也有一些解决按照上文步骤制作 MSP 遇到问题的经验共享)

准备

1、  首先需要下载 Microsoft Platform SDK ,地址: http://www.microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en

2、  下载后,安装 Orca.Msi ( Orca 其实是一个 Microsoft 用于编辑配置 MSI 、 MSP 以及打开制作 MSP 必须的 PCP 文件的工具),在 Microsoft Platform SDK 的安装目录下, C:\Program Files\Microsoft Platform SDK\Bin\

3、  其次,需要有升级前的 MSI 安装文件,以及对应的 VS 的安装部署工程;(注意 codeproject 中一文所有准备文件都放在安装部署工程所在目录,否则套用它提供的 Patch.cmd 将会出错,其实完全不用在安装部署目录下,可以另行建立一个工作目录,但如果读者一开始不熟悉 Orca 工具的情况下,还是按照 codeproject 中一文来设置工作目录,切记所有的目录都不宜太长,最好没有特殊字符和空格。)

4、  在工作目录中,需要有如下子目录: TargetImage 和 UpgradedImage , TargetImage 用于存放升级前的 MSI 安装文件, UpgradedImage 用于存放升级后的 MSI 安装文件;(如果没有生成升级后的安装文件,先不执行该步。)这里需要注意,提醒按照 codeproject 制作 MSP 的读者,由于它在文中用到的 Patch.cmd 与本文有所不用,因此按照 codeproject 制作 MSP 的读者需要建立不用的目录结构,这里就不再赘述。

5、  需要有一些安装文件制作的基本知识,如主版本号: MajorVersion ,次版本号, ProductCode 、 UpgradeCode 等等。

制作步骤

1、  C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\Msi\Patchi ng\ Template.pcp 拷贝到工作目录下,并重新命名为 patch.pcp ;(名字可自定义,但注意在 Patch.cmd 中相应修正)

2、  利用 Orca 打开 patch.pcp ,为 ImageFamilies 表添加一行记录, Family 字段需赋值,可以自定义,该值表示产品所在家族系列加上主版本号,例如 ESRI 将该值赋为 ArcGIS92

3、  为 PatchSequence 表添加一行记录, PatchFamily 设为上表中 Family 的值(也可以不同), Sequence 字段为 1.0.0 (所有 pcp 表中字段设置可参考 http://msdn.microsoft.com/en-us/library/aa370890(VS.85).aspx );

4、  修改 Properties 表中记录, PatchGUID 右键后选取粘贴生成一个新的 GUID (非常重要,每次制作 MSP 都需要重新生成,笔者犯过类似错误,花了半天时间才找到原因), PatchOutputPath 设置需要导出的地址,可设置为 Patch\Patch.msp ,注意这是相对目录,相对于 patch.pcp 文件,另外为该表添加一记录, Name MinimumRequiredMsiVersion Value 200

5、  TargetImages 表添加一记录, Target :可自定义(推荐为 Family 值加上升级前次版本号,如 ArcGIS92MSI1 ,原因是此表可添加多条记录,以后应用将多个不同的目标版本升级到最新版本,后面将详述,该段在 codeproject 中未提到,属于高级应用), MsiPath : TargetImage\setup.msi (相对目录,注意 setup.msi 不用改为读者的 msi 文件名,因为在 Patch.cmd 中将读者的 msi 拷贝为 setup.msi ,可细读 Patch.cmd ), Upgraded :与下面 UpgradeImages 表中该值对应,(推荐为 Family 值加上升级后的次版本号,如 ArcGIS92MSI2 ), Order : 1 (多条记录时需要有大小顺序,后面详述), IgnoreMissingSrcFiles : 0 ;

6、  为 UpgradeImages 表添加一记录, Upgraded :与上表中该值对应, MsiPath : UpgradedImage\setup.msi (相对目录,此处不用改,细读 Patch.cmd ), Family :与 ImageFamlies 表中该值对应。

7、  创建 Patch.cmd 文件,(按照 codeproject 一文的读者参考它的 Patch.cmd 文件)

@SETLOCAL

@set path=%path%;"C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\Msi\Patching"

@set PatchTmp=C:\Tmp1

 

:ok

rmdir /s /q %PatchTmp%

mkdir C:\Tmp1

mkdir C:\Tmp1\TargetImage

mkdir C:\Tmp1\UpgradedImage

mkdir C:\Tmp1\Patch

 

for %%a in ("TargetImage\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\TargetImage\ /L*v C:\Tmp1\TargetImage\setup.log

del C:\Tmp1\setup.msi

 

for %%a in ("UpgradedImage\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\UpgradedImage /L*v C:\Tmp1\UpgradedImage\setup.log

del C:\Tmp1\setup.msi

 

copy patch.pcp C:\Tmp1

set PatchDir=%CD%

C:

cd Tmp1

msimsp -s patch.pcp -p Patch\patch.msp -l Patch\patch.log -f C:\Tmp1\Tmp -d

 

rmdir /s /q C:\Tmp1\TargetImage

rmdir /s /q C:\Tmp1\UpgradedImage

rmdir /s /q C:\Tmp1\Tmp

F:

cd %PatchDir%

 

mkdir Patch

mkdir Patch\%1

copy C:\Tmp1\Patch\*.* Patch\%1\*.*

rmdir /s /q C:\Tmp1

 

:end

Pause

 

注意,定位到 F :该行,改为读者工作目录所在盘符,切记。

 

8、  生成升级后的安装文件 MSI ,修改 VS 安装部署工程属性 Version ,(如升级前为 1.0.0 ,升级后可改为 1.0.1 ,注意改次版本号,关于主版本号和次版本号的升级请参阅 MSDN ,也可见下文。)此时,系统提示是否自动修改 ProductCode 和 UpgradedCode ,注意此处非常关键,一定要记住选 NO 。具体为什么,有一定 ProductCode 和 UpgradedCode 知识的读者肯定了解此处的意义, UpgradedCode 每个产品家族为同一个, ProductCode 只有在主版本号升级时,才需要修改,而 MSP 一般为最小版本号升级所使用,所以,一句话,不要改。(改了,如果有问题,笔者解决不了)

9、  将生成的 MSI 拷贝到工作目录的 UpgradedImage 下,按照 codeproject 一文的读者不用执行该步,因为它文中的 Patch.cmd 将自动读取 Debug 目录下的 MSI ,这就是它文中必须要求读者将所有文件拷贝到安装部署工程目录下的原因。

10、              该步不用执行,每当用户 Build 安装部署工程时,系统会自动更新 PackageCode ,这里如修改 PackageCode ,可使用 Orca 打开 MSI ,在查看菜单下摘要信息中修改。

11、              终于到最后一步,运行 Patch.cmd ,如前后改动不大、并且打包文件较少时,执行时间非常短,如果是改动较大,就稍等几分钟,抽根烟,喝杯咖啡。提醒:在 Properties 表中的 IncludeWholeFilesOnly 字段用于判定当打包文件不同时,提取增量,还是直接采取整个新文件,前者可使得 MSP 瘦身,但 MSP 执行时间较长,后者相反。

 

关于 PatchCMD :

1、  首先设置环境变量 Path 以及临时目录;

2、  如临时目前存在先删除,然后建立目录格式;

3、  拷贝升级前 MSI 到临时目录,并利用 MSIExec 解压;注意: TARGETDIR=C:\Tmp1\TargetImage\ ,一定不要与拷贝后的 MSI 相同目录,因为 MSI 解压后还会生成一个较小的文件名相同的 MSI ,这样会导致 MSIExec 解压出错;(笔者被这步给整得很惨)

4、  拷贝升级后 MSI 到临时目录,并利用 MSIExec 解压;

5、  拷贝 PCP 文件到临时目录,利用 msimsp 生成 MSP ;

6、  将生成 MSP 拷回工作目录,删除临时目录。

 

关于主版本号、次版本号、最小版本号(当版本格式为 1.0.0 时,个人意见如下):

1、  当程序只是局部更新,改动较小,修正少量 Bug ,可选择发布 MSP ,修改最小版本号, ProductCode 不变;

2、  当程序更新大量模块的代码,做了较大改动和调整,但结构保持不变,可修改第二位版本号, ProductCode 改变,重新发布 MSI ;

3、  当程序的架构发生变化,如从面向过程改动到面向对象全组件式架构,修改主版本号, ProductCode 改变,重新发布 MSI 。

 

关于多个不同的目标版本升级到最新版本:

在 TargetImages 中一条记录就对应一个目标版本升级到最新版本,例如 1.0.0 到 1.0.2 , 1.0.1 到 1.0.2 ,这样建立的 MSP 可适用与将 1.0.0 、 1.0.1 升级到 1.0.2 ,如果只有 1.0.1 到 1.0.2 ,就无法将 1.0.0 升级到 1.0.2 。对应的 Patch.cmd 和工作目录结构只需做略微改动即可达到目的。以下 Patch.cmd 可供参考:

@SETLOCAL

@set path=%path%;"C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\Msi\Patching"

@set PatchTmp=C:\Tmp1

 

:ok

rmdir /s /q %PatchTmp%

mkdir C:\Tmp1

mkdir C:\Tmp1\TargetImage

mkdir C:\Tmp1\TargetImage\1

mkdir C:\Tmp1\TargetImage\2

mkdir C:\Tmp1\TargetImage\3

mkdir C:\Tmp1\UpgradedImage

mkdir C:\Tmp1\Patch

 

for %%a in ("TargetImage\1\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\TargetImage\1 /L*v C:\Tmp1\TargetImage\setup.log

del C:\Tmp1\setup.msi

 

for %%a in ("TargetImage\2\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\TargetImage\2 /L*v C:\Tmp1\TargetImage\setup.log

del C:\Tmp1\setup.msi

 

for %%a in ("TargetImage\3\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\TargetImage\3 /L*v C:\Tmp1\TargetImage\setup.log

del C:\Tmp1\setup.msi

 

for %%a in ("UpgradedImage\*.msi") do copy "%%a" C:\Tmp1\setup.msi

msiexec /qb /a C:\Tmp1\setup.msi TARGETDIR=C:\Tmp1\UpgradedImage /L*v C:\Tmp1\UpgradedImage\setup.log

del C:\Tmp1\setup.msi

 

copy patch4.pcp C:\Tmp1

set PatchDir=%CD%

C:

cd Tmp1

msimsp -s patch4.pcp -p Patch\patch4.msp -l Patch\patch4.log -f C:\Tmp1\Tmp -d

 

rmdir /s /q C:\Tmp1\TargetImage\1

rmdir /s /q C:\Tmp1\TargetImage\2

rmdir /s /q C:\Tmp1\TargetImage\3

rmdir /s /q C:\Tmp1\TargetImage

rmdir /s /q C:\Tmp1\UpgradedImage

rmdir /s /q C:\Tmp1\Tmp

F:

cd %PatchDir%

 

mkdir Patch

mkdir Patch\%1

copy C:\Tmp1\Patch\*.* Patch\%1\*.*

rmdir /s /q C:\Tmp1

 

:end

pause

 

 

参考链接:

1、  http://www.codeproject.com/KB/install/dotnetpatching.aspx?fid=209224&fr=1&df=90&mpp=25&noise=3&sort=Position&view=Quick#xx0xx

2、  http://msdn.microsoft.com/en-us/library/aa367816(VS.85).aspx

 

posted on 2010-12-02 11:31  和轩僮  阅读(3585)  评论(0编辑  收藏  举报

导航