原文:Wix打包系列(三)自定义Action(Custom Action)

3.1 关于Action

我们已经知道如何生成具有标准安装界面的安装程序了,Windows Installer按照我们的界面设置使用标准的安装步骤进行安装,它的安装过程是由一系列标准的Action组成,通过这些Action来完成对计算机的安装配置;如果我们想自定义安装步骤或者在安装过程中执行自定义的操作,就需要使用自定义的Action。当然,使用Custom Action之前,我们应该先了解一下msi中这些标准的Action。

首先,我们通过Orca工具(参考Windows Installer SDK)打开Sample.msi文件,可以看到使用wix标准安装界面生成的安装程序包含哪些Action,如下图,我们可以看到这些Action的名称和安装顺序,关于Orca以及msi数据库的表和字段的含义,本文不做介绍,有兴趣可以查看msi sdk

这里我们主要关注InstallExecuteSequence 和InstallUISequence表,他们之间的区别是InstallUISequence只有在内置UI的等级是Full UI或者reduced UI时才会起作用,在basic UI 或者silent方式安装时不起作用;关于UI等级 在msi sdk中UILevel Property章节有详细说明。我们定义的Custom Action也可能会包含在这些   Action序列中,通过Sequence来确定执行顺序;当然也可能不包含在Action序列中,当这个Action是由按钮触发的时候。

3.2 Custom Action

    下面我们来看看怎么使用Custom Action,我们在代码中Product标签下加入如下代码:

<Property Id='NOTEPAD' Value='Notepad.exe'/>
<CustomAction Id='LaunchFile' Property='NOTEPAD' ExeCommand='[INSTALLDIR]Manual.pdf' Return='asyncNoWait' />
<InstallExecuteSequence>
<Custom Action='LaunchFile' After='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
    添加以上代码的作用是安装结束后使用记事本打开安装目录下的Manual.pdf文件,代码很好理解CustomAction标记指示要执行的动作,而InstallExecuteSequence指示Action执行的顺序,在InstallFinalize之后执行,NOT Installed是安装的条件,指的是只有在首次安装的时候才会运行此Action,维护模式时不执行Action。
   wix中使用属性来区分Action的种类,对于不同种类的Action需要使用不同的属性组合;而msi中使用Type来区分,具体可参考http://msdn.microsoft.com/library/aa368062.aspx
   假如我们要安装后直接运行应用程序,CustomAction可以做如下调整:
<CustomAction Id='LaunchFile'  FileKey='filFoobarEXE' ExeCommand='' Return='asyncNoWait' />
3.3 Control Action
    3.2中的LaunchFile动作在InstallExecuteSequence序列中的InstallFinalize动作之后执行,即便InstallFinalize已经在InstallExecuteSequence的最后了,但是LaunchFile仍然会在完成界面弹出之前执行;一般我们都想在结束界面点击完成按钮之后执行LaunchFile动作,这时我们就需要用到按钮的事件了,把CustomAction动作绑定到按钮的事件中,这样点击结束按钮就可以执行LaunchFile了,操作步骤如下:
    首先,同样添加CustomAction:
<CustomAction Id='LaunchFile'  FileKey='filFoobarEXE' ExeCommand='' Return='asyncNoWait' />

然后,我们可以查到WixUI_Mondo模式的结束窗口是ExitDialog,需要给ExitDialog界面的完成按钮添加事件:

<UI Id="MyWixUI_Mondo">
<UIRef Id="WixUI_Mondo" />
<UIRef Id="WixUI_ErrorProgressText" />

<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchFile" Order="1">1</Publish>
</UI>
    Publish是用来定义按钮的事件,Control是按钮的标识;Event属性是事件类型,这里我们只介绍DoAction类型;Value的值就是CustomAction的标识;一个按钮可以定义多个Publish事件,Order的值是设置事件执行的顺序,ExitDialog本身没有定义其他事件,这里可以定义为1;如果不知道默认界面的按钮有哪些事件,可以用Orca工具打开msi文件,通过查看ControlEvent表可以知道所有按钮的事件和顺序。
    另外要注意的是:Publish标签必须定义在Control或者UI的下级,因此这里我们定义了一个自定义的UI标签MyWixUI_Mondo。
    关于不同Type的Action应用还有很多,感兴趣的可以看wix和msi sdk的文档,下面我们主要介绍如何编写托管的Custom Action。
   

3.4 编写托管的Custom Action

比如我们要在安装之前检查某个数据库连接是否存在,上面的CustomAction设置则不能满足我们的要求,这时我们就需要自己写一个Action 的DLL。这里我们只介绍如何使用c#生成Action ,对于使用VB和c++语言则不做介绍。

如果我们在安装wix之前安装了vs2005或者vs2008,我们就可以很方便的在vs开发环境下生成Action ,首先在解决方案资源管理器中右键解决方案名称,然后选择添加项目,在添加新项目对话框中项目类型选择wix,模板选择c# Custom Action Project,如图:

下面我们在SampleCustomAction类中添加一个连接数据库的方法,代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;

namespace SampleCustomAction
{
public class CustomActions
{
[CustomAction]
public static ActionResult ConnectDataBase(Session session)
{
session.Log("Begin ConnectDataBase");

// 数据库连接串
string connectionStr = session["CONNECTIONSTRING"];

// 连接数据库方法
//if (DBUtil.ConnectDB(connectionStr))
session["CONNECTSUCCESS"] = "1";
//else
session["CONNECTSUCCESS"] = "0";

return ActionResult.Success;
}
}
}

这里方法实现部分省去了,读者可以自己去实现,数据库连接串CONNECTIONSTRING是在wix源文件中定义的:

<Property Id="CONNECTIONSTRING" Value="data source=(local);user=sa;password=123;initial catalog=master;Persist Security Info=;" />
    然后我们编译SampleCustomAction,在输出目录下会生成2个dll,要注意只有*.CA.dll才是我们需要的,也就是SampleCustomAction.CA.dll,将SampleCustomAction.CA.dll复制到wix源文件所在目录,在Sample.wxs文件中添加如下行:
<Binary Id='ConnectDBClass' SourceFile='$(var.Version)/SampleCustomAction.CA.dll' />

<CustomAction Id='ConnectDB' BinaryKey='ConnectDBClass' DllEntry='ConnectDataBase' />
<CustomAction Id='ConnectError' Error='数据库连接失败' />

<InstallExecuteSequence>
<Custom Action='ConnectDB' After='CostFinalize' />
<Custom Action='ConnectError' After='ConnectDB'><![CDATA[CONNECTSUCCESS <> "1" AND NOT Installed]]></Custom>
</InstallExecuteSequence>
     第1行的Binary 就是我们编写的dll文件,第3行是CustomAction的入口,BinaryKey值是第1行定义的CustomAction的dll标识,DllEntry值是dll中CustomAction的入口函数,第4行是另外一种Type类型的CustomAction,它弹出错误对话框,然后终止安装程序;InstallExecuteSequence中定义Action的执行顺序和执行条件,第8行中条件表明只有CONNECTSUCCESS= "0" AND NOT Installed时此Action才会执行,也就是连接数据库失败并且首次安装时执行。
    注意到这里CustomAction的BinaryKey属性表明CustomAction文件被打包进安装文件中以二进制形式存在,不会被安装到目标计算机硬盘上;如果CustomAction文件是安装到目标计算机硬盘上的文件,则应使用FileKey属性,并添加相应的File标记。
    编译源文件,运行生成的安装包,安装过程中会弹出错误对话框“数据库连接失败”;托管的Action调试起来不方便,可以自己写一个辅助的程序调试,也可以通过session.Log方法记录安装日志,通过以下命令行方式运行安装程序可以生成安装日志:
msiexec /i 1.0.0/Sample.msi /l*v Sample.msi.log

打开安装日志,我们可以看到Action执行的日志:

...
...
操作结束 15:24:05: CostFinalize。返回值 1。
MSI (s) (E0:BC) [15:24:05:218]: Doing action: ConnectDB
操作 15:24:05: ConnectDB。
操作开始 15:24:05: ConnectDB。
MSI (s) (E0:F4) [15:24:05:218]: Invoking remote custom action. DLL: C:/WINDOWS/Installer/MSI1A1.tmp, Entrypoint: ConnectDataBase
MSI (s) (E0:04) [15:24:05:218]: Generating random cookie.
MSI (s) (E0:04) [15:24:05:250]: Created Custom Action Server with PID 4108 (0x100C).
MSI (s) (E0:24) [15:24:05:281]: Running as a service.
MSI (s) (E0:24) [15:24:05:281]: Hello, I'm your 32bit Impersonated custom action server.
SFXCA: Extracting custom action to temporary directory: C:/WINDOWS/Installer/MSI1A1.tmp-/
SFXCA: Binding to CLR version v2.0.50727
Calling custom action SampleCustomAction!SampleCustomAction.CustomActions.ConnectDataBase
Begin ConnectDataBase
MSI (s) (E0!F4) [15:24:05:687]: PROPERTY CHANGE: Adding CONNECTSUCCESS property. Its value is '1'.
MSI (s) (E0!F4) [15:24:05:687]: PROPERTY CHANGE: Modifying CONNECTSUCCESS property. Its current value is '1'. Its new value: '0'.
操作结束 15:24:05: ConnectDB。返回值 1。
MSI (s) (E0:BC) [15:24:05:796]: Doing action: ConnectError
操作 15:24:05: ConnectError。
操作开始 15:24:05: ConnectError。
数据库连接不存在
MSI (s) (E0:BC) [15:24:06:703]: 产品: Foobar 1.0 -- 数据库连接不存在

操作结束 15:24:06: ConnectError。返回值 3。
操作结束 15:24:06: INSTALL。返回值 3。
...
...
    这里CONNECTSUCCESS 属性在wxs源文件中并没有定义,因此在执行到“session["CONNECTSUCCESS"] = "1";”时,会添加该属性,如果wxs源文件中已定义,该处就会修改该属性值。
 
    如果没有vs编译环境,也可以使用wix提供的MakeSfxCA工具编译生成dll,使用示例如下,具体可以参考wix文档和DTF文档:
   1: csc.exe /target:library /reference:path/Microsoft.Deployment.WindowsInstaller.dll /out:CheckPID.dll CheckPID.cs

   2: MakeSfxCA.exe path/CheckPIDPackage.dll path/sfxca.dll path/CheckPID.dll path/CustomAction.config path/Microsoft.Deployment.WindowsInstaller.dll


    到这里我们已经知道如何创建自定义的Action了,下一节我们将介绍如何自定义对话框,在安装程序标准UI的基础上定义我们自己的对话框和UI流程
   

最新文章

  1. Java数据结构——平衡二叉树的平衡因子(转自牛客网)
  2. SQL Server存储(6/8) :理解DCM页
  3. dotnetGen 系列终于开源了
  4. Query Designer中的特征限制(Characteristic Restrictions)、缺省值(Default Values)、自由特性(Free Characteristics)
  5. JSON 数据解析
  6. 【CodeForces 651A】Joysticks 模拟
  7. clipToBounds
  8. Word快捷键
  9. mac磁盘满解决方案
  10. HDU 3920 Clear All of Them I(DP + 状态压缩 + 贪心)
  11. jquery上传插件uploadify 报错http error 302 解决方法之一
  12. 增强iOS应用程序性能的提示和技巧(25个)
  13. Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLIC
  14. Android Studio帮助文档的安装及智能提示设置
  15. mysql Unknown error 1054
  16. js开发打印证书功能
  17. Python&#160;再谈变量作用域与变量引用
  18. 【SQL】如何使用SQL like 方法和SQL [charlist] 通配符(SQL like的拓展)
  19. 推荐一个 MYSQL 的命令行的客户端 MYCLI
  20. WBS功能分解及甘特图

热门文章

  1. Swift - 手机摇晃的监测和响应
  2. 深入浅出Hadoop实战开发(HDFS实战图片、MapReduce、HBase实战微博、Hive应用)
  3. hdu 4812 D Tree(树的点分治)
  4. Java中的throw和throws的差别
  5. 【环境配置】配置sdk
  6. shell telnet 路由器
  7. ADOConnection数据库连接池
  8. c#1所搭建的核心基础之值类型和引用类型
  9. 深入学习微框架:Spring Boot
  10. poj 3211 Washing Clothes(背包)