Currently I'm trying to move the WixUIBannerBmp, WixUIDialogBmp and WixUILicenseRtf WixVariables and their corresponding binary files to a wixlib. Unfortunately when building it ignores these and uses the defaults.
My Library.wxs:
<Fragment>
<WixVariable Id="WixUILicenseRtf" Value="licence.rtf" />
<WixVariable Id="WixUIBannerBmp" Value="binaries/bannrbmp.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="binaries/dlgbmp.bmp" />
</Fragment>
where the rtf and bmp files are included in the wixlib project and the paths are relative to the Library.wxs file.
Anyone have any ideas why this isn't working?
Thanks
Managed to work this out myself! :)
Firstly the fragment is not automatically included into the main Product.wxs unless something is explicitly referenced. In this case I'm using the ARPPRODUCTICON property. If you don't have anything that you can use you can just add a dummy property that will never be used.
Also the paths to the binaries will then be incorrect as the path will be relative to the Product.wxs file. Therefore you need to use the Preprocessor variable to the current project path.
Product.wxs
<Wix>
<PropertyRef Id="ARPPRODUCTICON" />
</Wix>
Library.wxs
<Fragment>
<WixVariable Id="WixUILicenseRtf" Value="$(var.ProjectDir)\adastra-licence.rtf" />
<WixVariable Id="WixUIBannerBmp" Value="$(var.ProjectDir)\Bitmaps\bannrbmp.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="$(var.ProjectDir)\Bitmaps\dlgbmp.bmp" />
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<Icon Id="icon.ico" SourceFile="$(var.ProjectDir)/App.ico"/>
<UIRef Id="WixUI_Common" />
</Fragment>
Related
I am creating a new WIX installer. The installation is based on the customer's existing database. At the very first installation I have the information of the already installed features only in the database. So I have to query theese features from it and the installer shouldn't execute its activation scripts. Every feature has a property and from a custom action I set theese properties but the conditional SQL script execution is not working. I've created a sample wxs source file. Could you help me what is wrong with it?
<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension' xmlns:sql='http://schemas.microsoft.com/wix/SqlExtension'>
<!-- https://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html -->
<Product Id='BC075295-7BB8-4B82-89AC-3F81681130CC' Name='XXX' UpgradeCode='4AD0BCB8-B1BB-4FE1-ABEE-58E93321AAC5' Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='XXX'>
<Package Id='*' Keywords='Installer' Description="XXX Installer" InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252'/>
<Media Id='1' Cabinet='Andoc.cab' EmbedCab='yes' />
<Binary Id="WixCustomActions" SourceFile="CustomAction.CA.dll" />
<Binary Id="sqlScriptBinaryKey" SourceFile="insert.sql" />
<CustomAction Id="SetExecuteScriptCondition" BinaryKey="WixCustomActions" DllEntry="SetExecuteScriptCondition" Execute="immediate" Return="check" />
<Property Id="EXECUTE_SCRIPT" Value="NO" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="XXX" />
</Directory>
<Component Id="componentSqlScript" Directory="INSTALLDIR" Guid="1af938ef-6788-a0e1-2370-a6c2620c9dCC">
<Condition>EXECUTE_SCRIPT = "YES"</Condition>
<sql:SqlDatabase Id="db" Database="[DATABASE_NAME]" Server="[DATABASE_SERVER]" CreateOnInstall="yes" DropOnUninstall="no" ContinueOnError="no">
<sql:SqlScript Id="SqlScriptId" BinaryKey="sqlScriptBinaryKey" ExecuteOnInstall="yes" ExecuteOnUninstall="no" ContinueOnError="no" />
</sql:SqlDatabase>
<CreateFolder />
</Component>
<Feature Id="feature" Title="xxx" Description="xxx" ConfigurableDirectory="INSTALLDIR" Level="1">
<ComponentRef Id="componentSqlScript" />
</Feature>
<InstallExecuteSequence>
<Custom Action='SetExecuteScriptCondition' Before='InstallInitialize' />
</InstallExecuteSequence>
<UI>
<UIRef Id="WixUI_Mondo" />
</UI>
</Product>
</Wix>
My custom action looks like this.
[CustomAction]
public static ActionResult SetExecuteScriptCondition(Session session)
{
session["EXECUTE_SCRIPT"] = "YES";
return ActionResult.Success;
}
In the log I see that the EXECUTE_SCRIPT property value is 'YES' but the insert.sql script is not executed.
I've found a workaround to solve this problem by creating a subfeature to the sql component and moving the component condition to the feature level. And instead of InstallExecuteSequence I execute my custom action in InstallUISequence.
<Feature Id="feature" Title="xxx" Description="xxx" ConfigurableDirectory="INSTALLDIR" Level="1">
<Feature Id="feature_script" Title="script" Description="script" Display="hidden" Level="1" >
<Condition Level="0">EXECUTE_SCRIPT = "NO"</Condition>
<Condition Level="1">EXECUTE_SCRIPT = "YES"</Condition>
<ComponentRef Id="componentSqlScript" />
</Feature>
</Feature>
<InstallUISequence>
<Custom Action='SetExecuteScriptCondition' Before='LaunchConditions' />
</InstallUISequence>
InstallInitialize is too late, as the condition for your component 'componentSqlScript' is evaluated during costing. That's the reason setting that property didn't appear to work.
You need to schedule your action before 'CostInitialize', sequence it in both UI and Execute sequences (since the UI sequence doesn't necessarily run, and if costing is also preformed in the UI sequence then the execute sequence will be too late). You should add Execute="firstSequence" to your CustomAction element, and you should add Secure="yes" to your Property element, to prevent running code excessively and prevent issues where public property values aren't always set to the execute sequence.
I have used Wix on and off for almost a year. After a little break and now I am back to wix and need to build a wix Msi again but I found a very strange thing that I haven't met before. After I created the msi file and and copy the msi to somewhere to install. During the installation, it shows an error that it can't find the source files from the folder: current msi location\EasyLobby\Cogito. I was wondering why it tried to find the source files from that location. I then found that from the project during the compilation, it always creates \EasyLobby\Cogito under the bin\Debug folder. So if I run the msi right from the ...bin\Debug, it runs OK because the \EasyLobby\Cogito folder is there. \
It seems so strange. The msi file should include all the source files and shouldn't look for source files somewhere else. Here is the product.wxs file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupCogito" Language="1033" Version="2.0.0.0" Manufacturer="Microsoft" UpgradeCode="96cb03c9-6a03-4344-b816-20a0bb9e5df0">
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Media Id="1" Cabinet="Server.cab" EmbedCab="yes" />
<!--<MediaTemplate />-->
<Feature Id="ProductFeature" Title="SetupCogito" Level="1">
<ComponentGroupRef Id="ComponentGroup"/>
<ComponentRef Id="EasyLobbyCogitoShortcut" />
</Feature>
<WixVariable Id="WixUILicenseRtf" Value="Files\LicenseAgmt.rtf"/>
<UIRef Id="CogitoUI_Installdir" />
<Property Id="WIXUI_INSTALLDIR" Value="COGITOFOLDER" />
<Binary Id="banner_bmp" SourceFile="Files\Banner.bmp"/>
<Property Id ="PIDTemplate" >
<![CDATA[&&&-&&&&&&-&&&&-&&&&]]>
</Property>
<Icon Id="ELCogitoConfig.exe" SourceFile="..\CogitoIntegration\ELCogitoConfig.exe" />
</Product>
<Fragment>
<Binary Id="CustomActions"
SourceFile="..\CustomActions\bin\Debug\CustomActions.CA.dll" />
<CustomAction Id="IsValidKeyCode"
BinaryKey="CustomActions"
DllEntry="IsValidKeyCode"
Execute="immediate"
Return="check" />
<InstallExecuteSequence>
<Custom Action="IsValidKeyCode"
Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
</Fragment>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="EasyLobby" >
<Directory Id="COGITOFOLDER" Name="Cogito"/>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="EasyLobby Cogito" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="COGITOFOLDER">
<Component Id="EasyLobbyCogitoShortcut" Guid="{BFE6EB30-0F71-4F92-8D93-84B4EBF41F0E}" >
<File Id="Easy" Source="$(var.SourceDir)\ELCogitoConfig.exe" />
<Shortcut Id="ApplicationStartMenuShortcut" Name="Cogito Configuration" Directory="ApplicationProgramsFolder"
WorkingDirectory='INSTALLDIR' Icon="ELCogitoConfig.exe" IconIndex="0" Advertise="yes"/>
</Component>
</DirectoryRef>
And this is the config for heat in project file:
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>SourceDir= C:\Development\SetupCogito\CogitoIntegration;</DefineConstants>
<LinkerBaseInputPaths>..\CogitoIntegration\</LinkerBaseInputPaths>
</PropertyGroup>
<HeatDirectory OutputFile="CogitoSetup.wxs" Directory="..\CogitoIntegration" PreprocessorVariable="var.SourceDir" DirectoryRefId="COGITOFOLDER" ComponentGroupName="ComponentGroup" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="false" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" />
This line here is your problem:
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
Change Compressed=no to Compressed=yes and it will include all the source files in the finished MSI. If you don't compress it, the finished files are not included in the MSI and you get an error if it can't find them at runtime.
See Package reference.
Here's my setup:
Compiling at .NET 4.0 (this cannot change)
Using WiX 3.7
Using the WiX Extended Bootstrapper Application extension
So I'm creating a bootstrapper installation project and though I've got it working, I feel like there is a much more clean and easy way to do it.
Basically, I need to have the user select which environment they wish to use when they install the product. To do this, I use the WiX Extended Bootstrapper Application extension to allow me to put some radio buttons on the first install screen.
Getting that all setup was fine, but then I realized that I don't know how to easily determine which radio button has been selected. So as I work around I just listed the MSI three times in the bundle and put install conditions on each one.
Is there a way to determine which radio button has been selected with just one property? (For example, when I pass the InstallFolder property to my MSI as seen in the code below.) And if there isn't a way to do this currently, is there a way for me to do this without adding my MSI three times to the bundle?
Here's my code (Notice how I list essentially the same MsiPackage three times):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?include Properties.wxi ?>
<Bundle Name="!(loc.Product.Name)" Version="$(var.Version)" Manufacturer="!(loc.Product.Manufacturer)" UpgradeCode="$(var.UpgradeCode)" Condition="VersionNT >= v5.1">
<BootstrapperApplicationRef Id="WixExtendedBootstrapperApplication.Hyperlink2License">
<bal:WixExtendedBootstrapperApplication SuppressRepair="yes" LicenseUrl="" ThemeFile="$(var.Bundle.ExtTheme.RadioBtns.Path)" LocalizationFile="$(var.Bundle.ExtTheme.RadioBtns.l10n.Path)" />
</BootstrapperApplicationRef>
<WixVariable Id="WixMbaPrereqPackageId" Value="Netfx4Full" />
<Variable Name="InstallFolder" Type="string" Value="$(var.InstallFolder.Value)" />
<Variable Name="RadioButton1" Type="numeric" Value="0" />
<Variable Name="RadioButton2" Type="numeric" Value="0" />
<Variable Name="RadioButton3" Type="numeric" Value="1" />
<Chain>
<!-- Other Package References Here -->
<MsiPackage Id="DBConnections_Dev"
SourceFile="$(var.MSI.Path)"
Visible="no"
Vital="yes"
InstallCondition="RadioButton1 = 1">
<MsiProperty Name="INSTALLFOLDER" Value="[InstallFolder]" />
<MsiProperty Name="SELECTED_ENV" Value="1" />
</MsiPackage>
<MsiPackage Id="DBConnections_Stage"
SourceFile="$(var.MSI.Path)"
Visible="no"
Vital="yes"
InstallCondition="RadioButton2 = 1">
<MsiProperty Name="INSTALLFOLDER" Value="[InstallFolder]" />
<MsiProperty Name="SELECTED_ENV" Value="2" />
</MsiPackage>
<MsiPackage Id="DBConnections_Prod"
SourceFile="$(var.MSI.Path)"
Visible="no"
Vital="yes"
InstallCondition="RadioButton3 = 1">
<MsiProperty Name="INSTALLFOLDER" Value="[InstallFolder]" />
<MsiProperty Name="SELECTED_ENV" Value="3" />
</MsiPackage>
</Chain>
</Bundle>
</Wix>
I also asked this question on the project's CodePlex site and the developer responded as follows (here is a link to the full discussion: http://wixextba.codeplex.com/discussions/432341):
This can now be done using the custom actions feature.
I've tested his response and found it to be correct! This functionality is available as of version 3.7.4791.32058. There is also an example included in the source code demonstrating how this is done. I've posted the pertinent code below:
Needed in Custom Action c++ code:
STDMETHODIMP OnPlanCustomAction()
{
...
if (SUCCEEDED(BalGetNumericVariable(L"RadioButton1", &llValue)) && llValue)
{
m_pEngine->SetVariableNumeric(L"RadioButton", 1);
BalExitOnFailure(hr, "Failed to set variable.");
}
else if (SUCCEEDED(BalGetNumericVariable(L"RadioButton2", &llValue)) && llValue)
{
m_pEngine->SetVariableNumeric(L"RadioButton", 2);
BalExitOnFailure(hr, "Failed to set variable.");
}
else if (SUCCEEDED(BalGetNumericVariable(L"RadioButton3", &llValue)) && llValue)
{
m_pEngine->SetVariableNumeric(L"RadioButton", 3);
BalExitOnFailure(hr, "Failed to set variable.");
}
else if (SUCCEEDED(BalGetNumericVariable(L"RadioButton4", &llValue)) && llValue)
{
m_pEngine->SetVariableNumeric(L"RadioButton", 4);
BalExitOnFailure(hr, "Failed to set variable.");
}
else
{
m_pEngine->SetVariableNumeric(L"RadioButton", 0);
BalExitOnFailure(hr, "Failed to set variable.");
}
...
Needed in WiX XML (from examples that come with downloaded DLL):
<Variable Name="RadioButton1" Type="numeric" Value="0" />
<Variable Name="RadioButton2" Type="numeric" Value="1" />
<Variable Name="RadioButton3" Type="numeric" Value="0" />
<Variable Name="RadioButton4" Type="numeric" Value="0" />
<Chain DisableSystemRestore="yes">
<PackageGroupRef Id="NetFx40Redist" />
<MsiPackage
Id="Setup"
Compressed="yes"
SourceFile="Setup.msi"
Vital="yes">
<MsiProperty Name="APPLICATIONFOLDER" Value="[InstallFolder]" />
<MsiProperty Name="RadioButton" Value="[RadioButton]" />
</MsiPackage>
</Chain>
Check out the links in the text for the original examples from which these code samples were taken. Hope this helps others in the future.
I'm having trouble accomplishing a task with the Wix Toolset. In particular, I have a scenario where I have an MSI that configures an MSM (Configurable Module). The MSI has a custom UI dialogue from which user input is to be used to configure the MSM.
When I try to configure the MSM using a hardcoded value for the address property as shown below it works fine and the MSM is configured correctly. (I believe this configuration happens and build-time as opposed to run-time - the issue may lay there.).
The problem occurs when I use a custom dialogue to set the value of the address property at installation time (i.e run-time). The configurable module still uses the hardcoded value, rather than the users input. Is the problem because merge module configuration is done during build-time only. Is there a way to pass a value to the merge module from the UI of the main MSI?
Here is an over simplified version:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="cf1d176d-2d57-435e-8e7f-abba14de821c" Language="1033">
<Media Id="1" Cabinet="SemanticEvolution.cab" EmbedCab="yes" />
<Property Id="Address" Value="http://127.0.0.1" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Semantic Evolution">
<Merge Id="MergeModule" Language="1033" SourceFile="Module.msm" DiskId="1">
<ConfigurationData Name="EndpointAddressConfiguration" Value="[Address]" />
</Merge>
</Directory>
</Directory>
</Directory>
<Feature Id="SemanticEvolutionFeatures" Title="Semnatic Evolution" Level="1">
<Feature Id="TestFeature" Title="TestFeature" Level="1">
<MergeRef Id="MergeModule" />
</Feature>
</Feature>
<UI Id="CustomWixUI">
<UIRef Id="WixUI_FeatureTree" />
<DialogRef Id="ConfigurationDlg" />
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="ConfigurationDlg">LicenseAccepted = "1"</Publish>
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="ConfigurationDlg">NOT Installed</Publish>
</UI>
</Product>
</Wix>
Here is a snipet of the merge module:
<Configuration Name="EndpointAddressConfiguration" Format="Text" />
<Substitution Table="CustomAction" Row="SetEndpointAddress" Column="Target" Value="[=EndpointAddressConfiguration]" />
<CustomAction Id="SetEndpointAddress" Property="EndpointAddress" Value="[EndpointAddress]" />
<InstallExecuteSequence>
<Custom Action="SetEndpointAddress" Before="LaunchConditions">1</Custom>
</InstallExecuteSequence>
Eventually in the merge module the configured property is used as follows:
<util:XmlFile Id="EndpointAddress" Action="setValue" ElementPath="/configuration/system.serviceModel/client/endpoint/#address" File="[#Se.Gui.exe.config]" Value="[EndpointAddress]/ApiDataService"/>
Remember that public properties must be in UPPERCASE.
You can find the answer here:
http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Passing-properties-to-merge-modules-td5417112.html
To access a property from a merge module you must append the merge module id to the property name. Something like: MyProp.msm_guid
http://msdn.microsoft.com/en-us/library/aa370051(VS.85).aspx
How can I make a major upgrade to an installation set (MSI) built with WiX install into the same folder as the original installation?
The installation is correctly detected as an upgrade, but the directory selection screen is still shown and with the default value (not necessarily the current installation folder).
Do I have to do manual work like saving the installation folder in a registry key upon first installing and then read this key upon upgrade? If so, is there any example?
Or is there some easier way to achieve this in MSI or WiX?
As reference, I my current WiX file is below:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
<Product Id="a2298d1d-ba60-4c4d-92e3-a77413f54a53"
Name="MyCompany Integration Framework 1.0.0"
Language="1033"
Version="1.0.0"
Manufacturer="MyCompany"
UpgradeCode="9071eacc-9b5a-48e3-bb90-8064d2b2c45d">
<!-- Package information -->
<Package Keywords="Installer"
Id="e85e6190-1cd4-49f5-8924-9da5fcb8aee8"
Description="Installs MyCompany Integration Framework 1.0.0"
Comments="Installs MyCompany Integration Framework 1.0.0"
InstallerVersion="100"
Compressed="yes" />
<Upgrade Id='9071eacc-9b5a-48e3-bb90-8064d2b2c45d'>
<UpgradeVersion Property="PATCHFOUND"
OnlyDetect="no"
Minimum="0.0.1"
IncludeMinimum="yes"
Maximum="1.0.0"
IncludeMaximum="yes"/>
</Upgrade>
<!-- Useless but necessary... -->
<Media Id="1" Cabinet="MyCompany.cab" EmbedCab="yes" />
<!-- Precondition: .NET 2 must be installed -->
<Condition Message='This setup requires the .NET Framework 2 or higher.'>
<![CDATA[MsiNetAssemblySupport >= "2.0.50727"]]>
</Condition>
<Directory Id="TARGETDIR"
Name="SourceDir">
<Directory Id="MyCompany"
Name="MyCompany">
<Directory Id="INSTALLDIR"
Name="Integrat"
LongName="MyCompany Integration Framework">
<Component Id="MyCompanyDllComponent"
Guid="4f362043-03a0-472d-a84f-896522ce7d2b"
DiskId="1">
<File Id="MyCompanyIntegrationDll"
Name="IbIntegr.dll"
src="..\Build\MyCompany.Integration.dll"
Vital="yes"
LongName="MyCompany.Integration.dll" />
<File Id="MyCompanyServiceModelDll"
Name="IbSerMod.dll"
src="..\Build\MyCompany.ServiceModel.dll"
Vital="yes"
LongName="MyCompany.ServiceModel.dll" />
</Component>
<!-- More components -->
</Directory>
</Directory>
</Directory>
<Feature Id="MyCompanyProductFeature"
Title='MyCompany Integration Framework'
Description='The complete package'
Display='expand'
Level="1"
InstallDefault='local'
ConfigurableDirectory="INSTALLDIR">
<ComponentRef Id="MyCompanyDllComponent" />
</Feature>
<!-- Task scheduler application. It has to be used as a property -->
<Property Id="finaltaskexe"
Value="MyCompany.Integration.Host.exe" />
<Property Id="WIXUI_INSTALLDIR"
Value="INSTALLDIR" />
<InstallExecuteSequence>
<!-- command must be executed: MyCompany.Integration.Host.exe /INITIALCONFIG parameters.xml -->
<Custom Action='PropertyAssign'
After='InstallFinalize'>NOT Installed AND NOT PATCHFOUND</Custom>
<Custom Action='LaunchFile'
After='InstallFinalize'>NOT Installed AND NOT PATCHFOUND</Custom>
<RemoveExistingProducts Before='CostInitialize' />
</InstallExecuteSequence>
<!-- execute comand -->
<CustomAction Id='PropertyAssign'
Property='PathProperty'
Value='[INSTALLDIR][finaltaskexe]' />
<CustomAction Id='LaunchFile'
Property='PathProperty'
ExeCommand='/INITIALCONFIG "[INSTALLDIR]parameters.xml"'
Return='asyncNoWait' />
<!-- User interface information -->
<UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText" />
</Product>
</Wix>
There's an example in the WiX tutorial: https://www.firegiant.com/wix/tutorial/getting-started/where-to-install/
<Property Id="INSTALLDIR">
<RegistrySearch Id='AcmeFoobarRegistry' Type='raw'
Root='HKLM' Key='Software\Acme\Foobar 1.0' Name='InstallDir' />
</Property>
Of course, you've got to set the registry key as part of the install too. Stick this inside a component that's part of the original install:
<RegistryKey
Key="Software\Software\Acme\Foobar 1.0"
Root="HKLM">
<RegistryValue Id="FoobarRegInstallDir"
Type="string"
Name="InstallDir"
Value="[INSTALLDIR]" />
</RegistryKey>
'Registry' is deprecated. Now that part of code should look like this:
<RegistryKey Id="FoobarRegRoot"
Action="createAndRemoveOnUninstall"
Key="Software\Software\Acme\Foobar 1.0"
Root="HKLM">
<RegistryValue Id="FoobarRegInstallDir"
Type="string"
Name="InstallDir"
Value="[INSTALLDIR]" />
</RegistryKey>
You don't really need to separate RegistryKey from RegistryValue in a simple case like this. Also, using HKMU instead of HKLM takes care of it whether you're doing a machine or user install.
<RegistryValue
Root="HKMU"
Key="Software\[Manufacturer]\[ProductName]"
Name="InstallDir"
Type="string"
Value="[INSTALLDIR]"
KeyPath="yes" />