Wednesday, 18 July 2012

WIX Registry Key Exists Custom Action

The RegistrySearch Element in WIX has a bug where by you cannot check the existence of a Key with out a value. ClickOnce installers with a custom bootstrapper manifest also inherits this issue as I believe that it is a problem in the underlying Windows Installer.
For a deployment component that I am currently building for an application I have a requirement to check if the .Net Platform Update 1 is installed as per the instructions here.

The key in question is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.0.1, which has no default value.

After taking a look around on the internet I could not find a code snippet that was flexible and reusable. Most where simple hard coded examples on how to work around the problem. So I decided to build my own Custom Action and add it to an assembly of custom actions that I share and reuse between deployment components.

The Custom Action

The code is pretty straight forward. A key name, as specified by the session property REGKEY, is opened from the registry hive as specified by REGHIVE session property. The session property REGKEYEXISTS is used to store the result of the check.

Using the Custom Action

To use the custom action in an installer first embed the custom action as a Binary Element.
< SourceFile="$(var.CustomActions.TargetDir)\Installer.CustomActions.CA.dll" CustomActions?>Then define the Custom Action and sequence it for execution, in this case it is before LaunchConditions.

<CustomAction Id="CheckRegistryKeyExists" BinaryKey="CustomActions" DllEntry="CheckRegistryKeyExists" Execute="immediate" Return="check" />
  <Custom Action="CheckRegistryKeyExists" Before="LaunchConditions">NOT Installed</Custom>

Then specify the required properties and setup the condition and message.
<Property Id="REGHIVE">HKLM</Property>
<Property Id="REGKEYEXISTS">0</Property>
<Condition Message='!(loc.Update401Missing)'>Installed OR REGKEYEXISTS="1"</Condition>

When the custom action is run with logging msiexec /i Workflow.Designer.Installer.msi /lv out.txt the following output is displayed to aid with debugging.

Action start 14:38:41: CheckRegistryKeyExists.
MSI (c) (A0:FC) [14:38:41:385]: Invoking remote custom action. DLL: C:\MSIA543.tmp, Entrypoint: CheckRegistryKeyExists
MSI (c) (A0:24) [14:38:41:387]: Cloaking enabled.
MSI (c) (A0:24) [14:38:41:387]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (A0:24) [14:38:41:387]: Connected to service for CA interface.
SFXCA: Extracting custom action to temporary directory: C:\MSIA543.tmp-\
SFXCA: Binding to CLR version v4.0.30319
Calling custom action Emex.Installer.CustomActions!Emex.Installer.CustomActions.CustomActions.CheckRegistryKeyExists
CheckRegistryKeyExists: Begin
CheckRegistryKeyExists: Looking up key SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.0.1 in Hive HKLM
MSI (c) (A0!F4) [14:39:09:498]: PROPERTY CHANGE: Modifying REGKEYEXISTS property. Its current value is '0'. Its new value: '1'.
CheckRegistryKeyExists: End


The statement Debugger.Launch() statement launches and attaches the debugger to the installer when the Custom Action is executed. In the above code it is surrounded with the #if DEBUG to prevent the debugger being launched in release mode.

When the debug version of the installer is launched the following dialog is displayed. You can attach the VS project that contains the source for the Custom Action and simply step through the code as you normally would.


Tuesday, 17 July 2012

WIX Light.exe LGHT0204: ICE03: Error

Recently I have been developing a  set of deployment components for the various system components of a workflow designer and execution subsystem. One of the deployment components required is to deploy a workflow and domain model designer to an end users PC.
Since there can be a wide variance as to what .net runtime components an end user has installed on their PC it was therefore a requirement to perform some up front checks as a prerequisite to an install on a given target PC.
The application has three required components that must be deployed to  a candidate client PC, these are as follows:

  • .Net Framework 4.0
  • Multi-Targeting Pack for Microsoft .NET Framework 4.0.1 (KB2495638)
  • WCF Data Services 5.0 for OData V3

A WIX installer was created, the Conditions.wxi contains obviously enough the prerequisite conditions for the deployment. The Product.wxs contains all of the assembly references for the application files. The Strings_en-us.wxl obviously contains localised strings for the installer messages etc..

 So the conditions where defined as shown in the following snippet:

<Property Id="UPDATE401">
    <RegistrySearch Id="Update401x32" Type="raw" Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.0.1" Win64="no" />
<Property Id="UPDATE401X64">     <RegistrySearch Id="Update401x64" Type="raw" Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.0.1" Win64="yes" /> </Property>
<Property Id="ODATA">     <RegistrySearch Id="ODataRegistry32" Type="raw" Root="HKLM" Key="SOFTWARE\Microsoft\Microsoft WCF Data Services\5.0" Name="Version" Win64="no" /> </Property>
<Property Id="ODATAX64">     <RegistrySearch Id="ODataRegistry64" Type="raw" Root="HKLM" Key="SOFTWARE\Microsoft\Microsoft WCF Data Services\5.0" Name="Version" Win64="yes" /> </Property>
<Condition Message='!(loc.Update401Missing)'><![CDATA[(UPDATE401 <> "" OR UPDATE401X64 <> "") OR Installed]]></Condition>
<Condition Message='!(loc.ODataMissing)'><![CDATA[(ODATA <> "" OR ODATAX64 <> "") OR Installed]]></Condition>

The corresponding message strings are shown in the following snippet:
<String Id="ODataMissing" Overridable="yes">WCF Data Services 5.0 for OData V3 missing [] </String>
<String Id="Update401Missing" Overridable="yes">Multi-Targeting Pack for Microsoft .NET Framework 4.0.1 (KB2495638) []</String>

In some deployments the end user will install the application I decided to add references to the download pages for the .Net framework components that this application requires to be deployed to the PC before the application will execute on the PC. In larger client environments the application and the dependant components will be  deployed using automated deployment systems.

On compiling the WIX application the following errors where reported.

Emex.Workflow.Designer.Installer\Conditions.wxi(20,0): error LGHT0204: ICE03: Invalid format string; Table: LaunchCondition, Column: Description, Key(s): (UPDATE401 <> "" OR UPDATE401X64 <> "") OR Installed
Emex.Workflow.Designer.Installer\Conditions.wxi(22,0): error LGHT0204: ICE03: Invalid format string; Table: LaunchCondition, Column: Description, Key(s): (ODATA <> "" OR ODATAX64 <> "") OR Installed

At first this looks like the error is due to the condition expression being incorrect. But this is not the case. The problem is actually the message strings, the [ ] delimiters that I had used around the links, for no obvious reason, where the cause of the problem. To resolve the errors it was simply a case of escaping the delimiters as per the rules here.
<String Id="ODataMissing" Overridable="yes">WCF Data Services 5.0 for OData V3 missing [\[]]</String>