I have created a task for MSBuild that is to be used by developers and would like to create an installer to install the task and the associated .targets file into the MSBuild extensions folder in Program Files (usually C:/Program Files/MSBuild on 32-bit XP).
To do this properly, I would like to ask the system for that folder location at installation time - is there a registry key that provides this information or some other installer property? I intend to use WiX for the installation.
Wix itself has a MSBuild task so I think its best to see how they did it, the relvent source is Toolset.wxs (Google Code Search).
Look at the part where they defined the MSBuild Folder:
<DirectoryRef Id="ProgramFilesFolder">
<Directory Id="Dir_MSBuild" Name="MSBuild">
<Directory Id="Dir_MSBuildMS" Name="Microsoft">
<Directory Id="Dir_MSBuildMSWix" Name="WiX">
<Directory Id="Dir_MSBuildMSWix35" Name="v3.5">
<Component Id="WixMSBuildBinaries35"
Guid="2CB1EA5F-2542-4AFF-A05B-FAF576265F89"
Win64="no">
<File Source="WixTasks.dll" Checksum="yes"
KeyPath="yes" Vital="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</DirectoryRef>
This will install the Wix MsBuild task (WixTasks.dll) under \Program Files\MSBuild\Microsoft\Wix
We do exactly this (and more:)
Create a wxi file with something like:
<!-- Product name as you want it to appear in Add/Remove Programs-->
<?if $(var.Platform) = x64 ?>
<!-- Product name as you want it to appear in Add/Remove Programs-->
<?define ProductName = "Custom MSBuild Tasks (64 bit)" ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define ProductName = "Custom MSBuild Tasks" ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
<!-- Directory name used under MSBuild -->
<?define InstallName = "CustomTasks" ?>
And then when you need to define your components:
<Fragment Id="ComponentsFragment">
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="MSBuildFolder" Name="MSBuild">
<Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
... Your custom .Targets and tasks go here
Related
I was trying to give a dynamic file name based on its version for WIX Component file as given below. But I would like to know how the dynamic file name will be picked up in this. Got some reference from some other WXS file but not clear how it is working. Even the file is still not working properly.
Tried this : File - icsharpcode.sharpziplibmarquee_w64.d.wxs
<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:inin='http://www.inin.com/i3WixExtensions'>
<Fragment>
<DirectoryRef Id='IWP_MARQUEE'>
<Component Id='icsharpcode.sharpziplibmarquee_w64.d' Guid='YOUR-GUID' >
<File Name='icsharpcode.sharpziplib-w64r-$(ININExtension.TierVersionEicName).dll' ReadOnly='no' KeyPath='yes' Id='icsharpcode.sharpziplib-w64r.dll' Source="FindFirstInDirs(TIER_ROOT_EIC, pub/gen/bin/w64/ICSharpCode.SharpZipLib-w64r-$(ININExtension.TierVersionEicName).dll)" />
<?ifdef DebugBuild?>
<File Name='icsharpcode.sharpziplib-w64d-$(ININExtension.TierVersionEicName).dll' ReadOnly='no' Id='icsharpcode.sharpziplib-w64d.dll' Source="FindFirstInDirs(TIER_ROOT_EIC, pub/gen/bin/w64/ICSharpCode.SharpZipLib-w64d-$(ININExtension.TierVersionEicName).dll)" />
<?else ?>
<File Name='icsharpcode.sharpziplib-w64r-$(ININExtension.TierVersionEicName).pdb' ReadOnly='no' Id='icsharpcode.sharpziplib-w64r.pdb' Source="FindFirstInDirs(TIER_ROOT_EIC, pub/gen/bin/w64/ICSharpCode.SharpZipLib-w64r-$(ININExtension.TierVersionEicName).pdb)" />
<?endif ?>
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Must read files like ICSharpCode.SharpZipLib-w64r-23-1.dll, ICSharpCode.SharpZipLib-w64d-23-1.dll, ICSharpCode.SharpZipLib-w64r-23-1.pdb. Where 23-1 is 2023R1 (dynamically updating based on release version)
I built a simple MSI installer with Wix 3.11. It includes an EXE and a shortcut, but the shortcut never appears on the desktop. I build the MSI without errors, it otherwise installs just fine, and there are no errors in the MSI log file. I viewed the MSI with Orca and I see the shortcut and desktop directory in their tables. Thoughts? I've tried setting ALLUSERS to 1 as well to no avail. Here is my wxs file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" Name="Imaginary Product" Language="1033"
Version="1.2.3.4" Manufacturer="Company" UpgradeCode="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
<Package Id="*" InstallerVersion='300' Compressed='yes'/>
<Media Id="1" Cabinet="ImaginaryProduct.cab" EmbedCab="yes"/>
<?if $(sys.BUILDARCH)="x64" ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" SourceName="Desktop">
<Component Id="MainExecutableShortcut" Guid="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}">
<Shortcut Id="ImaginaryUIEXEShortcut" Name="Imaginary UI" Description="Runs Imaginary UI" Target="ImaginaryUI.exe" WorkingDirectory="ProductProgramFilesFolder"/>
<RemoveFolder Id="RemoveDesktopFolder" Directory="DesktopFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\ImaginaryUI" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</Directory>
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="ManufacturerProgramFilesFolder" Name="Company">
<Directory Id="ProductProgramFilesFolder" Name="Imaginary Product">
<Component Id="MainExecutable" Guid="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}">
<File Id="ImaginaryUIEXE" Name="ImaginaryUI.exe" Vital="yes" Source=".\bin\$(var.Configuration)\ImaginaryUI.exe" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id='MainApplication' Level='1'>
<ComponentRef Id="MainExecutable" Primary="yes"/>
<ComponentRef Id="MainExecutableShortcut" Primary="yes"/>
</Feature>
</Product>
</Wix>
Update: when the shortcut doesn't specify and icon, I see this in the installer log:
Action start 16:27:50: CreateShortcuts.
MSI (s) (08:34) [16:27:50:522]: Note: 1: 2205 2: 3: Icon
MSI (s) (08:34) [16:27:50:522]: Note: 1: 2228 2: 3: Icon 4: SELECT `Name`, `Data` FROM `Icon`
MSI (s) (08:34) [16:27:50:522]: Note: 1: 2205 2: 3: MsiShortcutProperty
MSI (s) (08:34) [16:27:50:523]: Note: 1: 2205 2: 3: MsiShortcutProperty
Action ended 16:27:50: CreateShortcuts. Return value 1.
When I do specify one, I see even less:
MSI (s) (14:D0) [16:34:59:527]: Doing action: CreateShortcuts
MSI (s) (14:D0) [16:34:59:527]: Note: 1: 2205 2: 3: ActionText
Action start 16:34:59: CreateShortcuts.
MSI (s) (14:D0) [16:34:59:528]: Note: 1: 2205 2: 3: MsiShortcutProperty
MSI (s) (14:D0) [16:34:59:528]: Note: 1: 2205 2: 3: MsiShortcutProperty
Action ended 16:34:59: CreateShortcuts. Return value 1.
So, no indication of failure. The icon just goes into the ether.
I'm not sure if this is your issue but to use the desktop folder you just define it as
<Directory Id="DesktopFolder"/>
I think you are actually putting the shortcut in "C:\Desktop\" (assuming the TARGETDIR is C:). All System Folder Properties should be defined the same way.
Also, remove this from your component definitions
<RemoveFolder Id="RemoveDesktopFolder" Directory="DesktopFolder" On="uninstall"/>
You don't want to remove the Desktop folder, it's not something you created in your install and neither is it something you own. Also, RemoveFolder will not do anything if anything else is present in that folder and basically everyone has at least a Recycle Bin in their desktop folder so it will never do anything.
I'm not sure why the RemoveFolder is used in other Shortcut definitions like the startmenu. It may be due to how windows installer works where it only implicitly removes a folder if components containing Files were present.
I'm pretty sure that Target must be an ID, not the name of the file. So change
Target="ImaginaryUI.exe"
to
Target="[#ImaginaryUIEXE]"
I wrote this in my WiX project file (I simplified my examples):
<?define info = R172 ?>
<?define var1="$(var.info.TargetPath)" ?>
<?define var2="$(var.info.TargetDir)" ?>
I would like the info will be expanded by preprocessor into such variant:
<?define var1="$(var.R172.TargetPath)" ?>
<?define var2="$(var.R172.TargetDir)" ?>
But I get the error:
Undefined preprocessor variable $(var.info.TargetPath).
This variant doesn't work too:
<?define var1="$(var.$(var.info).TargetPath)" ?>
<?define var2="$(var.$(var.info).TargetDir)" ?>
Also I have tried foreach using:
<?foreach info in R172?>
<?define var1="$(var.info.TargetPath)" ?>
<?define var2="$(var.info.TargetDir)" ?>
<?endforeach?>
But I get the same problem.
Can I do the similar substitution somehow?
UPD
I.e. I want to use it for working with referenced projects through the foreach:
<!-- The list of the names of referensed projects -->
<?define ACAD_VERSIONS=R172;R182;R190?>
<?foreach ACAD in ACAD_VERSIONS?>
<Feature Id="Feature.$(var.ACAD)" Title="$(var.ACAD)" Level="1">
<Component Id="cmp$(var.ACAD)" Guid="*" Directory="INSTALLFOLDER">
<File Id="extension.$(var.ACAD).dll" Source="$(var.$(var.ACAD).TargetPath)" KeyPath="yes"/>
</Component>
</Feature>
<?endforeach?>
Unfortunately the current version of WIX (3.10.2) doesn't appear to support what you are requesting.
Looking at the WIX source code, specifically the file src\tools\wix\PreProcessorCore.cs from WIX310-Debug.zip downloadable from here, it doesn't look like WIX supports recursive name substitution. So you can't do $(var.$(var.something)). Take a look at the function PreprocessString(...) to confirm this for yourself.
You might be able to do something with <?undef ...?>. For example:
<?define var1="$(var.item1)" ?>
<?include CommonFeature.wxs ?>
.
<?undef var1 ?>
<?define var1="$(var.item2)" ?>
<?include CommonFeature.wxs ?>
See the related WIXToolset issue: Nested preprocessor variables.
when I get my achitecture type like this:
<Property Id="PLATTFORM">
<RegistrySearch Id="myRegSearchPalttform"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Name="PROCESSOR_ARCHITECTURE"
Type="raw">
</RegistrySearch>
</Property>
and want to check if it is "AMD64" like this:
<?define myPlattform = [PLATTFORM] ?>
<?if $(var.myPlattform) = AMD64 ?>
some stuff
<?else ?>
some stuff
<?endif ?>
it fails.
When I set the value static:
<?define stest = AMD64 ?>
<?if $(var.stest) = AMD64 ?>
it goes in true scope. So why is the value from the registry(there is the value AMD64) not identical with my proof string????
Tanx in advance
<?define myPlattform = [PLATTFORM] ?>
Probably because myPlattform is a preprocessor variable and gets assigned before the PLATTFORM property ever has a value. If you want to conditionally install different components, you can try this way: How to use conditions in features in WiX?
This questions is possibly a duplicate of Is there a way to set a preprocessor variable to the value of a property?.
Update: If your goal is to set an installation location based on architecture, and your architecture is determined by the "PLATTFORM" property using the registry search you specified, then you could try the following:
<Property Id="PLATTFORM">
<RegistrySearch Id="myRegSearchPalttform"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Name="PROCESSOR_ARCHITECTURE"
Type="raw">
</RegistrySearch>
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SomeValue" />
</Directory>
</Directory>
<ComponentGroup Id="ProductComponentGroup">
<Component Id="ProductComponent" Guid="INSERT-GUID-HERE" Directory="INSTALLFOLDER">
<File Id="TestTextFile.txt" Source=".\TestTextFile.txt" KeyPath="yes"/>
</Component>
</ComponentGroup>
<Feature Id="ProductFeature" Level="1">
<ComponentGroupRef Id="ProductComponentGroup"/>
</Feature>
<SetDirectory Id="INSTALLFOLDER" Value="[ProgramFilesFolder]\SomeOtherValue">
PLATTFORM="AMD"
</SetDirectory>
Note: See that I used the SetDirectory element. I generally download the WiX weekly releases and never used that element until testing the above sample. Therefore I'm not sure what version SetDirectory was first introduced.
I have a website that I am installing for one of our internal products, and would like to know if there is a way to set up multiple <WebAddress/> blocks to be conditionally installed along with this one website?
Consider my basic website authoring below:
<Component
Id="WebsiteComp"
Directory="INSTALLDIR"
Guid="{702AF20D-F9F3-45A1-B966-890855904591}"
KeyPath="yes">
<iis:WebAppPool
Id="AppPool"
Name="OurSite"
ManagedPipelineMode="Integrated"
ManagedRuntimeVersion="v4.0"/>
<iis:WebSite
Id="Website"
Description="[WEBSITENAME]"
Directory="INSTALLDIR">
<iis:WebApplication
Id="WebApp"
Name="[WEBSITENAME]"
WebAppPool="AppPool"/>
<!-- if ENV = "DEV" -->
<iis:WebAddress
Id="DevHostHeader"
Header="dev.product.company.com"
Port="80"/>
<!-- if ENV = "QA" -->
<iis:WebAddress
Id="QaHostHeader"
Header="qa.product.company.com"
Port="80"/>
<iis:WebAddress
Id="QaHostHeader"
Header="product.qa1.company.com"
Port="80"/>
<!-- if ENV = "PROD" -->
<iis:WebAddress
Id="ProdHostHeader"
Header="prod.product.com"
Port="80"/>
</iis:WebSite>
</Component>
<Component/> is the most specific element that a condition can applied to. But in order to specify the condition there I have to duplicate all my web site auhtoring for each environment, correct?
Is there a way to author one version of the <WebAppPool/>, <WebSite/>, and <WebApplication/> and then have different versions and quantities (like QA in the above example) of <WebAddress/> inserted/chosen based on the condition of a propertie's value?
I really don't want to get into making multiple versions of the installer for a specific environment.
Thank you,
Zachary
You can approach the problem in a different way.
You can have a single WebAddress element, and the Header attribute will take the value of the property. Like this:
<iis:WebAddress Id="HostHeader" Header="[HOSTHEADER]" Port="80"/>
Now, based on the condition (production, DEV, QA) you set the property to the required value, for instance, qa.product.company.com for QA. Thus, you'll conditionally install the host header you need, and will keep a single WebAddress entry in the sources. Note, that Port attribute can accept the property values as well.
Well, I figured out how to do it without code duplication... pre-processor to the rescue!
Here's a simplified look at my "web services" directory:
ProductName.WebService.wxs
ProductName.DEV.WebAddress.wxi
ProductName.PROD.WebAddress.wx
ProductName.QA1.WebAddress.wxi
ProductName.WebService.wxs is as follows:
<Wix
xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Fragment>
<iis:WebDirProperties .../>
<?foreach EnvID in DEV;PROD;QA1 ?>
<?define FullEnvID = "ProductName.$(var.EnvID)" ?>
<?if $(var.EnvID) = "DEV" ?>
<?define CompGUID = "{DFEAC94A-590E-4E92-9206-E574ABDDBB29}" ?>
<?elseif $(var.EnvID) = "PROD" ?>
<?define CompGUID = "{FEE4FBB1-9894-48F4-8DDC-9FC83F8AD778}" ?>
<?elseif $(var.EnvID) = "QA1" ?>
<?define CompGUID = "{EED17AF6-BF99-4B34-821D-6A8487292111}" ?>
<?endif ?>
<Component
Id="$(var.FullEnvID).WebSvc"
Directory="INSTALLDIR"
Guid="$(var.CompGUID)"
KeyPath="yes">
<Condition><![CDATA[ENV="$(var.EnvID)"]]></Condition>
<iis:WebAppPool
Id="$(var.FullEnvID).WebAppPool"
Name="[WEBSITENAME]"
ManagedPipelineMode="Integrated"
ManagedRuntimeVersion="v4.0"/>
<iis:WebSite
Id="$(var.FullEnvID).Website"
Description="[WEBSITENAME]"
Directory="INSTALLDIR">
<iis:WebApplication
Id="$(var.FullEnvID).WebApplication"
Name="[WEBSITENAME]"
WebAppPool="$(var.FullEnvID).WebAppPool"/>
<?include $(var.FullEnvID).WebAddress.wxi ?>
</iis:WebSite>
</Component>
<?undef CompGUID ?>
<?undef FullEnvID ?>
<?endforeach ?>
</Fragment>
</Wix>
<Condition><![CDATA[ENV="$(var.EnvID)"]]></Condition> determines which web site component is installed
<?include $(var.FullEnvID).WebAddress.wxi ?> slips in just the <iis:WebAddress/> sections as the loop iterates.
Here's what ProductName.DEV.WebAddress.wxi looks like:
<Include
xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<iis:WebAddress
Id="dev.product.company.com"
Header="dev.product.company.com"
Port="80"/>
<iis:WebAddress
Id="product.dev.company.com"
Header="product.dev.company.com"
Port="80"/>
</Include>