天天看點

Wix 安裝部署教程(十五) --CustomAction的七種用法

原文: 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"]);      
Wix 安裝部署教程(十五) --CustomAction的七種用法

奇怪Install.InstallLocation方式擷取到的installDir為空(看來書上的有問題),但直接擷取變量,我們發現ARPINSTALLLOCATION和MyDir确實被指派了。

二、設定安裝目錄

 類型35的自定義Action用來設定一個Directory元素的路徑。示例:

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

那這個句子會将ID為DataDir的目錄在XP系統下設定為“C:\Documents and Settings\All Users\Application 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"]);      
Wix 安裝部署教程(十五) --CustomAction的七種用法

結果正确。

三、運作嵌入的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" />      

執行結果:

Wix 安裝部署教程(十五) --CustomAction的七種用法
Wix 安裝部署教程(十五) --CustomAction的七種用法

下面是一些有用的示例:

<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>      
Wix 安裝部署教程(十五) --CustomAction的七種用法

 五、調用動态連結庫中的方法

類型1的自定義Action可以調用dll中的方法,VS提供了下面的模闆。

Wix 安裝部署教程(十五) --CustomAction的七種用法

我們将編寫一個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" />      
Wix 安裝部署教程(十五) --CustomAction的七種用法

 2.就是引用工程,再用變量指向工程的輸出目錄。

Wix 安裝部署教程(十五) --CustomAction的七種用法
<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)後,沒有出現找不到檔案的情況。

Wix 安裝部署教程(十五) --CustomAction的七種用法

 另外需要說明的是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" />      
Wix 安裝部署教程(十五) --CustomAction的七種用法

如果不想運作的程式顯示界面,也就是靜默執行,需要使用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》的翻譯和測試。希望對你有幫助。