WiX is an odd beast.
I have this condition in an installer:
<Property Id="APPCMDVERSION">
<DirectorySearch Id="APPCMDVersion" Path="[SystemFolder]\inetsrv">
<FileSearch Name="appcmd.exe" MinVersion="7.0" />
</DirectorySearch>
</Property>
<Condition Message="This module requires the IIS appcmd.exe command line tool in order to enable the module.">
<![CDATA[Installed OR APPCMDVERSION]]>
</Condition>
Which as far as I can tell is a verbatim "copy" of the example given in the docs. Yet it doesn't work.
<Property Id="USER32VERSION">
<DirectorySearch Id="SystemFolderDriverVersion" Path="[SystemFolder]">
<FileSearch Name="user32.dll" MinVersion="6.0.6001.1750"/>
</DirectorySearch>
</Property>
What am I missing?
Try taking out the \ after [SystemFolder] ...
Related
I need to check service executable version before proceed my installation.
I have read full path for registered service, from registry:
<Property Id="SOME_SERVICE_PATH">
<RegistrySearch Id="FindServicePath" Type="raw" Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\TARGET_SERVICE" Name="ImagePath" />
</Property>
After that I tried to perform file search like below:
<Property Id="TARGET_SERVICE_UNSUPPORTED">
<DirectorySearch Id="ServiceParticularVersionSearch" Path="[SOME_SERVICE_PATH]">
<FileSearch Name="Service.exe" MaxVersion="2.5.0.1" />
</DirectorySearch>
</Property>
And this not ganed me results.
I suppose the problem is in the value I passed to DirectorySearch Path property.
According to the Wix documentaion Path should be initialized with
"Path on the user's system. Either absolute, or relative to containing directories"
Unfortunately, there is no place in the registry I can read service installation directory. This is 3d party component.
Are there any solutions here? important moment - Its forbidden to use custom actions in our project
As it turned out, the answer was in Type parameter of RegistrySearch element. If I assign "file" to it, I will be able to apply FileSearch on path I read and check executable version.
Another solution here is to use remark from RegistrySearch Element documentation
file
The registry value contains the path to a file. To return the full file path you must add a FileSearch element as a child of this element; otherwise, the parent directory of the file path is returned.
I mean "otherwise, the parent directory of the file path is returned"
Solution 1
<Property Id="TARGET_SERVICE_UNSUPPORTED">
<RegistrySearch Id="FindServicePath" Type="file" Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\TARGET_SERVICE" Name="ImagePath" />
<FileSearch Name="Service.exe" MaxVersion="2.5.0.1" />
</DirectorySearch>
</Property>
Solution 2
<Property Id="SOME_SERVICE_PATH">
<RegistrySearch Id="FindServicePath" Type="file" Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\TARGET_SERVICE" Name="ImagePath" />
</Property>
<Property Id="TARGET_SERVICE_UNSUPPORTED">
<DirectorySearch Id="ServiceParticularVersionSearch" Path="[SOME_SERVICE_PATH]">
<FileSearch Name="Service.exe" MaxVersion="2.5.0.1" />
</DirectorySearch>
</Property>
In my installation I need to check presence of 64-bit entry at first.
And read its value if it is present in 64-bit part of registry.
If entry is absent then I need to try to read this entry from 32-bit registry part(Wow6432Node).
I need to read it directly from wxs file or from custom action on VBScript.
Is it possible to do?
If you're running a 64bit MSI you can set two AppSearch/RegLocator entries using the style:
<Property Id="MY_32BIT_REG">
<RegistrySearch Id="my32bitreg"
Root="HKLM"
Key="SOFTWARE\My Company"
Name="foo"
Type="raw"
Win64="no" />
</Property>
<Property Id="MY_64BIT_REG">
<RegistrySearch Id="my64bitreg"
Root="HKLM"
Key="SOFTWARE\My Company"
Name="foo"
Type="raw"
Win64="yes" />
</Property>
These entries will check the appropriate "HKLM\SOFTWARE\My Company" and "HKLM\SOFTWARE\Wow6432Node\My Company" registry hives.
I have a WiX installer that install a bunch of extension files to the app directory for another app. To ensure that the files end up in the right place, I use a bunch of nested DirectorySearch to find the app directory.
The app is usually installed in a path following this pattern:
\Program Files (x86)\CompanyName\ProductName\[version]\[environment]\[optional intermediate folder]\AppFolderName
In other words, common installation folders include:
\Program Files (x86)\CompanyName\ProductName\1.0\Prod\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.1\Prod\OptionalFolderName\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.2\Test\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.2\Test\OptionalFolderName\AppFolderName
To handle this, I'm using a nested set of DirectorySearch elements to assign the path to a Property. The following works if the optional folder (OptionalFolderName) is present:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="OptionalIntermediateFolder" Path="OptionalFolderName">
<DirectorySearch Id="AppFolder" Path="AppFolderName" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
However, since OptionalFolderName is optional, I want to use a DirectorySearch to determine if the app folder is directly under the environment folder or one level down. In an attempt to do this, I changed the search tree to:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
The later version however does not work if the optional folder is present, but it does work if it is not present. In other words, it is as if one of the the Depth attributes is isgnored; my guess is that this is because I am using the Depth attribute twice at different levels within the tree.
Any suggestions on how I can work around this?
Update - added log snippet extract from msiexec /i [msiname] /l*v [logfile]:
Action 14:38:47: AppSearch. Searching for installed applications
Action start 14:38:47: AppSearch.
AppSearch: Property: SOMEAPPFOLDER, Signature: AppFolder
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1322 2:
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1322 2:
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1324 2: [environmentname] 3: 1
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1325 2: CompanyName
Action ended 14:38:47: AppSearch. Return value 1.
MSI (c) (00:90) [14:38:47:066]: Doing action: LaunchConditions
MSI (c) (00:90) [14:38:47:067]: Note: 1: 2205 2: 3: ActionText
Action 14:38:47: LaunchConditions. Evaluating launch conditions
Action start 14:38:47: LaunchConditions.
MSI (c) (00:A8) [14:38:47:069]: Font created. Charset: Req=0, Ret=0, Font: Req=MS Shell Dlg, Ret=MS Shell Dlg
Couldn't find the AppFolderName app folder for [environment/version].
MSI (c) (00:90) [14:38:48:543]: Note: 1: 2205 2: 3: Error
MSI (c) (00:90) [14:38:48:543]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1709
MSI (c) (00:90) [14:38:48:543]: Product: [productname] -- Couldn't find the AppFolderName app folder for [environment/version].
Action ended 14:38:48: LaunchConditions. Return value 3.
Behavior of Depth when FileSearch is used
Depth works differently when FileSearch is an immediate descendant of DirectorySearch.
In the normal case, Depth specifies the maximum number of folder levels above the specified path. However, if FileSearch is an immediate descendant, Depth specifies the maximum number of folder levels to look for the file below the specified path.
In other words, there's no way to specify the depth for a directory that is an immediate parent of a file.
Example
Let's look at the code you've pasted:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
In the above, the first use of Depth allows two directory levels between ProductFolder and EnvironmentFolder, but the second use of Depth refers to the number of levels between AppFolder and AppNameExe.
Workaround
The following xml uses two searches to workaround the problem:
<!-- Locate the parent directory first -->
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="1">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="1" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
<!-- Now, look for the file in the above directory -->
<Property Id="APPFILEEXISTS">
<DirectorySearch Id="AppFolder" Path="SOMEAPPFOLDER">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0"
MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</Property>
The second depth is used to search the file two levels below AppFolder, not to search AppFolder two levels below EnvironmentFolder.
You can do a first folder search:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<!-- <FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" /> -->
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
And use it as reference to verify that the app file exists:
<Property Id="APPEXISTS">
<DirectorySearchRef Id="AppFolder" Parent="EnvironmentFolder" Path="AppFolderName">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearchRef>
</Property>
My program will be installed to a path in registry, which has two different values for single user and all users.
So I'd like to have something like:
<Property Id="MYINSTALLDIR">
if single user, then <RegistrySearch Id='MyRegistry' Type='raw' Root='HKCU' Key='Software\MyApp\Foo' Name='InstallDir' />
else if ALLUSERS, then <RegistrySearch Id='MyRegistry' Type='raw' Root='HKLM' Key='Software\MyApp\Foo' Name='InstallDir' />
</Property>
Is this possible?
Perform the two registry searches to two different properties and then use a SetProperty custom action to assign one of the two properties to the real property based on which one has data and which one has a higher priority ( use conditions to drive the execution ).
Finally, it is working now...
With following snippet in wxs file, ALLUSER=1 or 2 can be passed to msiexec to enable HKLM registry search.
<Property Id="INSTALLDIR1">
<RegistrySearch Id='RegistryCU' Type='raw' Root='HKCU' Key='Software\Foo' Name='InstallDir' />
</Property>
<Property Id="INSTALLDIR2">
<RegistrySearch Id='RegistryLM' Type='raw' Root='HKLM' Key='Software\Foo' Name='InstallDir' />
</Property>
<CustomAction Id="PerUserInstall" Property="InstallDir" Value="[INSTALLDIR1]" Execute="immediate" />
<CustomAction Id="PerMachineInstall" Property="InstallDir" Value="[INSTALLDIR2]" Execute="immediate" />
<InstallExecuteSequence>
<Custom Action="PerUserInstall" After="AppSearch">ALLUSERS="" OR (ALLUSERS=2 AND (NOT Privileged))</Custom>
<Custom Action="PerMachineInstall" After="AppSearch">ALLUSERS=1 OR (ALLUSERS=2 AND Privileged)</Custom>
</InstallExecuteSequence>
In my case, both HKCU and HKLM contain values and they have same priority, so the only way to do is to set property of ALLUSER in command line.
I need to be able to copy a file that exists on the target machines hard-drive based on a registry setting that holds the folder path.
I have been trying to get this going for a day or two and am having difficulty, can anyone help?
Thanks,
B
Try something along these lines:
<Component Id="MyComponent" Guid="E5FF53DE-1739-42c4-BE37-60F810C9CD69">
<Condition>MYTESTDIR</Condition>
<CopyFile Id="fileToCopy.datCopy" SourceName="[MYTESTDIR]fileToCopy.dat" DestinationProperty="WEBSERVICEBINFOLDER" />
</Component>
You can populate MYTESTDIR with a value from the registry using a RegistrySearch.
You can first search your registry for the file as follows:
<Property Id="PROPERTYNAME" Secure="yes">
<RegistrySearch Id="SomeID"
Root="HKLM"
Type="raw"
Key="SOFTWARE\SomeFolder\SomeSubFolder"
Win64="yes"
Name="InstallPath">
<DirectorySearch Id="REQUIREDDIRECTORY" AssignToProperty="yes" Depth="1" Path="THEEXPECTEDPATH">
</DirectorySearch>
</RegistrySearch>
</Property>
Then use a Custom Action to set the file name
<CustomAction Id="SETFILE"
Property="FILE"
Value="[PROPERTYNAME]file.extension" />
and then copy file as described by the previous answer...
<CopyFile Id="fileToCopy.datCopy" SourceName="[FILE]" DestinationProperty="[YOURDESTINATION]" />