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 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"]);

結果正確。

三、運行嵌入的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》的翻譯和測試。希望對你有幫助。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM