zoukankan      html  css  js  c++  java
  • Wix 安装部署教程(十五) --CustomAction的七种用法

          在WIX中,CustomAction用来在安装过程中执行自定义行为。比如注册、修改文件、触发其他可执行文件等。这一节主要是介绍一下CustomAction的7种用法。 在此之前要了解InstallExecuteSequence,它是一个Action的执行序列。 Installer会按照默认顺序来执行这些Action。通过字面意思也大概知道这些Action的目的。这些方法不是每次一都执行,分安装和卸载。如果CustomAction没有指定,很可能会安装卸载的时候都会执行一次。

    • AppSearch
    • LaunchConditions
    • ValidateProductId
    • CostInitialize
    • FileCost
    • CostFinalize
    • InstallValidate
    • InstallInitialize
    • ProcessComponents
    • UnpublishFeatures
    • RemoveRegistryValues
    • RemoveShortcuts
    • RemoveFiles
    • InstallFiles
    • CreateShortcuts
    • WriteRegistryValues
    • RegisterUser
    • RegisterProduct
    • PublishFeatures
    • PublishProduct
    • InstallFinalize

      一、设置变量      

    Custom Action本身是分很多类型,这里是用到的是Type 51 Custom Action。用来设置变量的值。详情请看 Custom Action Types

    <CustomAction Id="rememberInstallDir"  Property="ARPINSTALLLOCATION"  Value="[INSTALLLOCATION]" />

     这个例子用内置的ARPINSTALLLOCATION变量保存了安装路径,value是用方括号包括目录ID。同样的方式你可以设置你的自定义变量。然后加入执行序列。

    <InstallExecuteSequence>
    <Custom Action="rememberInstallDir"  After="InstallValidate"/>
    </InstallExecuteSequence>

    我们可以获取这些变量,比如在卸载的时候通过C#程序获取变量的值,稍后会介绍。

    ProductInstallation install =new ProductInstallation(session["ProductCode"]);
    string installDir = install.InstallLocation;

    还有第二种方法,Custom Action的简便方式:使用SetProperty 元素 ,例如在InstallInitialize 执行完成后我们将自定义的变量MyDir 的值设置成123.

    <SetProperty Id="MyDir"  Value="123" After="InstallInitialize" Sequence="execute" />

    记录日志:

    session.Log("installDir:"+installDir);
    session.Log("ARPINSTALLLOCATION:" + session["ARPINSTALLLOCATION"]);
    session.Log("MyDir:" + session["MyDir"]);

    奇怪Install.InstallLocation方式获取到的installDir为空(看来书上的有问题),但直接获取变量,我们发现ARPINSTALLLOCATIONMyDir确实被赋值了。

    二、设置安装目录

     类型35的自定义Action用来设置一个Directory元素的路径。示例:

    <CustomAction Id="SetAppDataDir" Directory="DataDir" Value="[CommonAppDataFolder]MyProduct" />

    那这个句子会将ID为DataDir的目录在XP系统下设置为“C:Documents and SettingsAll UsersApplication Data”,WIN7下的 C: ProgramData  然后这个Action需要在InstallFiles之前执行。

    同样这也有第二种方法设置Directory的值。

    <SetDirectory Id="DataDir" Value="[CommonAppDataFolder]MyProduct"  Sequence="execute" />

    加入序列:

    <InstallExecuteSequence>
    <Custom Action="SetAppDataDir" Before="InstallFiles" />
    </InstallExecuteSequence>

    记录日志:(Directory 本质也是Property)

    session.Log("DataDir:" + session["DataDir"]);

    结果正确。

    三、运行嵌入的VBScript和JScript 

    类型37和类型38的Custom Action可以执行嵌入的脚本。通过Script属性来指定是vbscript还是jscript. 下面的示例是弹出两个提示框。

       <CustomAction Id="testVBScript" Script="vbscript" Execute="immediate" >
          <![CDATA[
            msgbox "this is embedded code..."
            msgbox "myProperty: " & Session.Property("myProperty")
            ]]>
        </CustomAction>

    注意到可以通过Session.Property的方式获取到已经存在的变量。 这个session对象控制着安装进程,它打开了包含安装表格和数据的的数据库。从而可以通过这个上下文对象可以获取和修改安装的变量和参数。更多信息可以参考 Session Object

    加入序列

    <InstallUISequence>
    <Custom Action="testVBScript" After="LaunchConditions" />
    </InstallUISequence>
    <Property Id="myProperty" Value="2" />

    执行结果:

      

    下面是一些有用的示例:

    <CustomAction Id="testVBScript" Script="vbscript"
    Execute="immediate" >
    <![CDATA[
    ' 写入一条安装日志
    Dim rec
    Set rec = Session.Installer.CreateRecord(0)
    rec.StringData(0) = "My log message"
    Session.Message &H04000000, rec
    '改变安装level
    Session.SetInstallLevel(1000)
    ' 获取Product元素的属性。
    Dim productName
    productName = Session.ProductProperty("ProductName")
    ' 改变Directory元素的路径
    Dim installFolder
    installFolder = Session.TargetPath("INSTALLFOLDER")
    ]]>
    </CustomAction>

    四、调用外部的脚本文件(VBScript / JScript)

     类型5(JScript)和类型6(VBScript)的自定义Action可以调用外部脚本文件的函数。我们定义一个myScript.vbs的文件,它包含一个myFunction的函数:

    Function myFunction()
    If Session.Property("myProperty") = "2" Then
    msgbox "Property is 1. Returning success!"
    myFunction = 1
    Exit Function
    End If
    msgbox "Property not 1. Returning failure."
    myFunction = 3
    Exit Function
    End Function

    返回1意味着调用成功,返回3意味着调用失败,而且会终止安装。另外我们需要用一个Binary元素来保存我们的vbs文件。

    <Binary Id="myScriptVBS" SourceFile="myScript.vbs" />

    然后通过我们的CustomAction和的BinaryKey和VBScriptCall(或者JScriptCall如果是JScript文件)来调用函数:

    <CustomAction Id="myScript_CA" BinaryKey="myScriptVBS" VBScriptCall="myFunction" Execute="immediate" Return="check" />

    加入序列:

    <InstallUISequence>
    <Custom Action="myScript_CA" After="LaunchConditions" />
    </InstallUISequence>

     五、调用动态链接库中的方法

    类型1的自定义Action可以调用dll中的方法,VS提供了下面的模板。

    我们将编写一个C#的dll,技术上讲,类型1的Custom Action需要非托管的C/C++的dll,Windows Installer 本身不支持.Net下的自定义Action。不过我们使用的模板会将我们的托管代码编译成c/c++的dll。使用.Net的时候要注意.Net的版本,当然也有C++的Custom Action 供选择。

    创建一个C# Custom Action Project 之后,我们会得到一个引用了Microsoft.Deployment.WindowsInstaller 的源文件。这个命名空间可以让我们获取properties ,features 和 components 。

    using Microsoft.Deployment.WindowsInstaller;
    namespace CustomAction
    {
        public class CustomActions
        {
            [CustomAction]
            public static ActionResult CustomAction1(Session session)
            {
                session.Log("Begin CustomAction1");
    
                return ActionResult.Success;
            }
        }
    }

    ActionResult 用来说明custom action 的调用是成功还是失败。单个的.Net 程序集之前只能定义16个CustomAction。wix3.6以后提高到128个。如果超过了这个,你只能再定义一个程序集了(没有测试过...)。这些都是来自Deployment Tools Foundation(DTF)的支持,DTF是一个让我们写.Net代码的类库,并且支持低级的Windows Installer技术。它提供了很多有用的类。 工程编译之后,我们会得到两个文件,一个是.dll 另外一个是 .CA.dll ,只有后者的才能被MSI识别。

    有两种方法引用这个dll。

     1.直接复制到Wix 工程目录,然后用Binary 元素引用它。

    <Binary Id="myCustomActionsDLL"  SourceFile="CustomAction.CA.dll" />

     2.就是引用工程,再用变量指向工程的输出目录。

     

       <Binary Id="myCustomActionsDLL"  SourceFile="$(var.CustomAction.TargetDir)CustomAction.CA.dll" />

      示例:在安装结束后,修改应用程序中的配置参数

     public class CustomActions
        {
            [CustomAction]
            public static ActionResult SetDevLanguage(Session session)
            {
                Record record = new Record(0);
                record[0] = "开始执行!";
                session.Message(InstallMessage.Info, record);
                session.Log("Begin CustomAction  SetDevLanguage");
    
                string xmlDoc = Path.Combine(session["INSTALLFOLDER"], "DVStudio.exe.config");
                session.Log("安装目录:" + session["INSTALLDIR"]);
                session.Log("配置文件:" + xmlDoc);
                if (File.Exists(xmlDoc))
                {
                    session.Log("config文件存在!");
                    session.Log("安装包语言是" + session["ProductLanguage"]);
                    string strContent = File.ReadAllText(xmlDoc);
                    strContent = Regex.Replace(strContent, "zh-CHT", lanDictionary[session["ProductLanguage"]]);
                    session.Log("语言修改为" + lanDictionary[session["ProductLanguage"]]);
                    File.WriteAllText(xmlDoc, strContent);
                }
                else
                {
                    session.Log("config文件没有找到!");
                }
                return ActionResult.Success;
            }
    
            private static Dictionary<string, string> lanDictionary = new Dictionary<string, string>()
            {
                {"2052","zh-CN"},
                {"1028","zh-CHT"},
                {"1033","en-US"},
            };
        }

    定义Custom Action 并加入序列

        <CustomAction Id="CA_myCustomAction" BinaryKey="myCustomActionsDLL" DllEntry="SetDevLanguage" Execute="immediate" Return="check" />
          <Custom Action="CA_myCustomAction" After="InstallFiles"  Overridable="yes"  />

    测试发现,after InstallFiles有的时候并不能检测到config文件,可能是安装文件和执行Action存在一定的延时,修改为InstallFinalize(最后一个Action)后,没有出现找不到文件的情况。

     另外需要说明的是Session.Message 类型为Info的时候只是记录到日志,修改为Warning会弹出提示框。

    六、触发可执行文件

    有两种方法可以通过custom Action运行一个可执行文件(.exe)。类型2的Custom Action是用Binary元素来保存文件。类似上面的脚本文件的做法。所有这些Binary中的文件都不会拷贝到用户的电脑上去。我们创建一个exe并放到工程目录下。

    <Binary Id="myProgramEXE" SourceFile="$(sys.SOURCEFILEDIR)Demo.exe" />

    这里的$(sys.SOURCEFILEDIR)是WIX定义的系统变量,代表的是工程目录。

    定义一个CustomAction:

     <CustomAction Id="myProgramEXE_CA" BinaryKey="myProgramEXE" Impersonate="yes" Execute="deferred" ExeCommand="" Return="check" />

    CustomAction的Impersonate属性告诉Installer是否模拟用户去触发这个Exe,它的默认值是no,意味着以LocalSystem 用户(功能强大的内置账户,拥有改变用户电脑参数的权限)去执行。如果你不需要,就设置为Yes,以当前用户的身份去运行。而ExeCommand属性可以传递命令行参数到可执行文件。即使是内容为空,也需要定义它。

    然后加入序列:

    <InstallExecuteSequence>
    <Custom Action="myProgramEXE_CA" Before="InstallFinalize" />
    </InstallExecuteSequence>

    另外一种方式是类型18的Custom Action是直接调用的安装了的文件。

    <DirectoryRef Id="INSTALLLOCATION">
    <Component Id="CMP_MainAppEXE" Guid="7AB5216B-2DB5-4A8A-9293-F6711FFAAA83">
    <File Id="mainAppEXE" Source="Demo.exe"  KeyPath="yes" />
    </Component>
    </DirectoryRef>

    而Custom action 通过ID获取到文件

    <CustomAction Id="RunMainApp" FileKey="mainAppEXE" ExeCommand="" Execute="commit" Return="ignore" />

    如果不想运行的程序显示界面,也就是静默执行,需要使用WixUtilExtension下的QtEexc action .WIX文档中也有这么一节: Quiet Execution Custom Action 

    另外类型34的Custom action 可以通过目录获取文件,并传递了参数。

    <CustomAction Id="RunMainApp" Directory="INSTALLLOCATION" ExeCommand="[INSTALLLOCATION]Main_App.exe –myArg 123" Execute="commit"  Return="ignore" />

    七、发送错误终止安装

     类型19的自定义action使用Error属性,来发送一个错误并终止安装。

    <Property Id="myProperty" Value="0" />
    <CustomAction Id="ErrorCA" Error="Ends the installation!" />
    <InstallUISequence>
    <Custom Action="ErrorCA" Before="ExecuteAction">
    <![CDATA[
    myProperty <> 1
    ]]>
    </Custom>
    </InstallUISequence>

    在Custom元素内部有一个条件表达式,当myproperty不为1的时候触发。(注意到这个action是在InstallUISequence中触发的)

    小结:CustomAction给我们提供了可以自己编程的窗口,可以去触发bat,修改配置文件,触发exe。文章大部分内容是来自《Packtpub.WiX.3.6.A.Developers.Guide.to.Windows.Installer.XML.Dec.2012》的翻译和测试。希望对你有帮助。

  • 相关阅读:
    编写一个ComputerAverage抽象类,类中有一个抽象方法求平均分average,可以有参数。定义 Gymnastics 类和 School 类,它们都是 ComputerAverage 的子类。Gymnastics 类中计算选手的平均成绩的方法是去掉一个最低分,去掉一个最高分,然后求平均分;School 中计算平均分的方法是所有科目的分数之和除以总科目数。 要求:定义ComputerAv
    创建一个接口Shape,其中有抽象方法area,类Circle 、Rectangle实现area方法计算其面积并返回。又有Star实现Shape的area方法,其返回值是0,Star类另有一返回值boolean型方法isStar;在main方法里创建一个Vector,根据随机数的不同向其中加入Shape的不同子类对象(如是1,生成Circle对象;如是2,生成Rectangle对象;如是3,生成S
    学校中有老师和学生两类人,而在职研究生既是老师又是学生,对学生的管理和对教师的管理在他们身上都有体现。
    定义抽象类Shape,抽象方法为showArea(),求出面积并显示,定义矩形类Rectangle,正方形类Square,圆类 Circle,根据各自的属性,用showArea方法求出各自的面积,在main方法中构造3个对象,调用showArea方法。(体现多态)
    分别编写两个类Point2D,Point3D来表示二维空间和三维空间的点,使之满足下列要求:
    2.编写实现:有一个三角形类Triangle,成员变量有底边x和另一条边y,和两边的夹角a(0<a<180),a为静态成员,成员方法有两个:求面积s(无参数)和修改角度(参数为角度)。 编写实现: 构造函数为 Triangle(int xx,int yy,int aa) 参数分别为x,y,a赋值 在main方法中构造两个对象,求出其面积,然后使用修改角度的方法,修改两边的夹角,再求出面积值。(提示
    定义一个复数(z=x+iy)类Complex,包含: 两个属性:实部x和虚部y 默认构造函数 Complex(),设置x=0,y=0 构造函数:Complex(int i,int j) 显示复数的方法:showComp()将其显示为如: 5+8i或5-8i 的形式。 求两个复数的和的方法:(参数是两个复数类对象,返回值是复数类对象)public Complex addComp(Compl
    定义一个类Point,代表一个点,public属性有x和y,方法有显示点坐标 show(),构造函数有两个参数分别给x,y赋值,在main方法中构造两个对象,再创建一方法(getMiddle)为取两个点构成线段的中点的坐标,参数为2个点对象,调用此方法后得到一个新的点,编写Application,显示该对象的坐标值。
    编写程序读取一组正数,找出它们的最大数,然后计算该数的出现次数,输入是以 0结束。比如:输入 3 5 2 5 5 5 0,程序找出最大数是 5,它出现的次数是 4。
    vim快捷键-02
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/4788449.html
Copyright © 2011-2022 走看看