WiX : Descriptive Markups
Think of WiX as html markups to create the webpages, since you create installation UIs by specifying pushbuttons and texts etc.. Think of WiX a descriptive language that gives non-sequential instructions for its compiler (candle.exe
), it gives the compiler the configuration of your project package and candle
gives you what you want. WiX is not like C, it doesn't explicitly say which goes first and which goes after. It is an instruction to create MSI database.
GUID
Product ID: Each version will have a different ID. If set to "*", then it will be auto-generated.
UpgradeCode: GUID to be shared among the same product of different versions (to notify this is to update/downgrade)
<Product Id="*" UpgradeCode="E052B1DC-F7A6-4B0D-A51A-28FD79571A04" />
To perform a re-install, one can specify it with the Product Id,
msiexec /f {869A369E-6BD5-42e1-B9E9-B3543A46D5F6}
.wxi
, WiX include files, serve as header files, here goes custom defined variables.wxl
, WiX localization file (File Properties: Build Action
must be set to EmbeddedResource
).wxs
, WiX Source files (File Properties: Build Action
set to Compile
)Formatted String
They are special formatted variables to be resolved at installation time. [3]
Format | Condition | Resolved to |
---|---|---|
[PropertyName] or {PropertyName} | Property value if defined, otherwise left untouched | |
[%EnvironmentVariable] | Enviornment variable value | |
[\[] | [ (backslash escapes the following character) | |
[~] | Null | |
[#FileID] | Full path to the file | |
[!FileID] | in Registry or IniFile table column | Full short path to the file |
otherwise | [#FileID] | |
[$ComponentID] | Install directory of the component | |
Registry Table | Action state | |
[?ComponentID] | Installed state of the component | |
[&FeatureID] | Action state of the feature | |
[!FeatureID] | Installed state of the feature |
Dynamic XML Configuration
For applications that use XML files for configuration, installer can dynamically change xml file contents at run-time. WiX can do this via the extension WixUtilExtension
. Here is how, first Add Reference to WixUtilExtension.dll
from the solution explorer. Next in your file where components go, include util
namespace.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
In your component where you defined your xml file, you can set an attribute to a certain node:
<Component Id="xmlconfig" Guid="{SOME-GUID}" Directory="SOMEDIR"> <CreateFolder Directory="SOMEDIR" /> <File Id="configuration.xml" Name="configuration.xml" ShortName="CONFIG~1.XML" Source="configuration.xml" /> <!--XML CONFIG--> <util:XmlFile Id="SomeXmlEntry" File="[SOMEDIR]configuration.xml" ElementPath="rootnode/somenode[\[]@name='SomeName'[\]]" Action="setValue" Name="SomeAttribute" Value="[SomeProperty]"/> </Component>
Notice that square brackets in ElementPath
is escaped. It is to protect XPath from being evaluated as properties. The above does the following change to configuration.xml
(if [SomeProperty]
evaluates to false
at run-time):
<rootnode> <somenode name="SomeName" SomeAttribute="false"/> </rootnode>
VB.NET Custom Actions (Managed Code)
To involve customized custom actions other than standard custom actions, one can create a VB custom action project within in your solution. And in your main setup project, add a reference to the newly created VB custom action project.
Public Class CustomActions <CustomAction()> _ Public Shared Function CustomActionFoo(ByVal session As Session) As ActionResult MsgBox("Hello, I am CustomActionFoo!") Return ActionResult.Success End Function End Class
To note that <CustomAction()> _
tells the public function to be a custom action entry point.
In order to use custom actions, you need to declare there is CustomActionFoo entry in your CustomActionDLL file. And your CustomActionDLL
file is refered as a binary entry, where CustomActions
is your custom action project name.
<Binary Id="CustomActionDLL" SourceFile="$(var.CustomActions.TargetDir)CustomActions.CA.dll" /> <CustomAction Id="CustomActionFoo" DllEntry="CustomActionFoo" BinaryKey="CustomActionDLL" Execute="deferred" />
To put declared custom action to work, you need to put it inside InstallExecuteSequence
or InstallUISequence
.
<InstallExecuteSequence> ... <Custom Action="CustomActionFoo" Sequence="6632"/> <InstallFinalize Sequence="6700"/> ... </InstallExecuteSequence>
Run Windows console commands
It is possible to run windows commands like in other form of VB project. It is done through WScript.Shell
. For instance, we can create a function StartNotepad
to be called by custom actions.
Public Shared Sub StartNotepad() Dim ShellObj As Object = CreateObject("WScript.Shell") ShellObj.Run("notepad", 1, True) End Sub
UI
Your typical UI element will be filled with these children. Each of them has an important role in presenting a live UI.
- UIText : information text for installation events
- ProgressText : information text to show what action is being processed
- TextStyle (Property to define DefaultUIFont) : defines font and size
- InstallUISequence : defines the sequence of dialogs and actions
- RadioButtonGroup (Property attached to it) : Radio buttons to be used in Dialog
- Dialog (Control, Subscribe inside) : defines how a dialog interface looks like
- Publish : defines events attached to buttons (switch from dialog to dialog or do some action etc.)
- Error : messages to display upon some error code
According to ICE20 validation, the following dialogs FilesInUse
and FatalError
(yes, with exact same Ids) are required and must have such form:
<Dialog Id="FilesInUse" Width="374" Height="266" Title="!(loc.ProductName)"> <Control Id="Exit" Type="PushButton" X="301" Y="243" Width="66" Height="17" Text="Exit" /> <Control Id="Ignore" Type="PushButton" X="230" Y="243" Width="66" Height="17" Text="Ignore" /> <Control Id="Retry" Type="PushButton" X="164" Y="243" Width="66" Height="17" Text="Retry" /> <Control Id="List" Type="ListBox" X="21" Y="87" Width="331" Height="135" Property="FileInUseProcess" /> </Dialog> <Dialog Id="FatalError" Width="270" Height="110" Title="!(loc.ErrorDlg_Title)" ErrorDialog="yes"> <Control Id="ErrorText" Type="Text" TabSkip="no" Transparent="yes" X="50" Y="15" Width="200" Height="50" Text="!(loc.IDS__IsErrorDlg_ErrorText)" /> <Control Id="ErrorIcon" Type="Icon" FixedSize="yes" X="15" Y="15" Width="24" Height="24" Text="IconBinary" /> <Control Id="A" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="Abort" /> <Control Id="C" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="Cancel" /> <Control Id="I" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="Ignore" /> <Control Id="N" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="No" /> <Control Id="O" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="OK" /> <Control Id="R" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="Retry" /> <Control Id="Y" Type="PushButton" TabSkip="yes" X="192" Y="80" Width="66" Height="17" Text="Yes" /> </Dialog>
Notice that I used some localization variables, just some substitutions of text. Further, three dialogs (Ids are free to choose) have to be defined to provide an interface in three necessary situations.
<InstallUISequence> <Show Dialog="SetupError" OnExit="error"/> <Show Dialog="SetupSuccess" OnExit="success"/> <Show Dialog="SetupInterrupted" OnExit="suspend"/> </InstallUISequence>
Windows Installer Technology
Any .msi
package goes installed with Windows Installer, whose command line tool is msiexec.exe
. The following installs the package and logs verbosely about most of the installation information to a text file.
msiexec /i MyApplication.msi /l*v MyLogFile.txt
If you have Windows SDK installed, you will get a set of package debugging tools, so called "Windows Installer Development Tools". Although Orca.exe
has to be installed manually. [1] To read log files, Wilogutl.exe
is the recommended way.
Each .msi file is a database, that is, tables of information to be used by msiexec
. Orca is the tool to spy into this database.
Keypath is a tag within a component, it can be a file, a directory, a registry key, or an ODBC data source. It is need to verify if the component is healthy or broken. If the the keypath resource is missing, then the whole component will be re-installed during a repair operation.
InstallUISequence: dialogues to gather information from user (interactive installation) InstallExecuteSequence: copying files and updating registry
Windows environment variables (check the output of set
command)
C:\Users\Foo> set
Some notable environment variables:
ALLUSERSPROFILE APPDATA CLASSPATH HOMEPATH OS Path PATHEXT SystemDrive SystemRoot TEMP TMP windir
Properties can be passed in the command line, it can be a value or a (even empty) string,
msiexec /i myInstaller.msi PROPERTY1=100 PROPERTY2="my value" PROPERTY3=""
Properties have scope, if all uppercase, then it is a public property. One can view the full list of predefined properties by Windows Installer at MSDN.[2]
Transform files end with .mst, it is used along with .msi main package. It comes into play when the main package is compiled and you still want to alter some database tables/rows. It is applied as
msiexec/i MyApplication.msi TRANSFORMS=custom.mst
Install from command line with verbose logging:
msiexec /i MyApplication.msi /l*v log.txt
Uninstall from command line with verbose logging:
msiexec /x MyApplication.msi /l*v log.txt
Preprocessor Variables
Prepare a variables.wxi
file where your predefined variables are written.
<?xml version="1.0" encoding="utf-8"?> <Include> <?define ProductName = "Foobar"?> <?define ProductManufacturer = "Foobar Company"?> </Include>
In Product.wxs
, include the above file inside Wix
Element.
<?include variables.wxi?>
It then can be used as
$(var.ProductName)
Localization
Prepare a file en-us.wxl
in your project, change the project property: cultures to build: en-us
.
<?xml version="1.0" encoding="utf-8"?> <WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization"> <String Id="COMPANY_NAME"><![CDATA[your company name]]></String> </WixLocalization>
To refer this string, use the following in your wxs source code.
!(loc.COMPANY_NAME)
Troubleshooting
Disallowing uninstallation of component
Uninstallation doesn't remove registy entries, components etc. Log file says:
Disallowing uninstallation of component: {COMPONENT-SOME GUID} since another client exists
This is mostly due to a previous unsuccessful installation/uninstallation, so that the components with the same GUID are still in the system. It is really rare that some other package might use the same GUID as yours.
To resolve the issue, one can restore the system to an older rollback point. Normally, package developers use VMWare of HyperV for testing so that a disastrous install/uninstall will not be fatal.
Footnotes
- For the version of Windows SDK for Windows 7 and .NET Framework 4.
- Property Reference (Windows)
- Formatted (Windows)
No comments:
Post a Comment