我们已经知道如何生成具有标准安装界面的安装程序了,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>
<CustomAction Id='LaunchFile' FileKey='filFoobarEXE' ExeCommand='' Return='asyncNoWait' />
3.2中的LaunchFile动作在InstallExecuteSequence序列中的InstallFinalize动作之后执行,即便InstallFinalize已经在InstallExecuteSequence的最后了,但是LaunchFile仍然会在完成界面弹出之前执行;一般我们都想在结束界面点击完成按钮之后执行LaunchFile动作,这时我们就需要用到按钮的事件了,把CustomAction动作绑定到按钮的事件中,这样点击结束按钮就可以执行LaunchFile了,操作步骤如下:
<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>
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=;" />
<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>
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。 ... ...
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