In WiX DirectorySearch can be used to determine if a specific directory exists on the target computer. But I don't understand if there's a consistent way to determine that a directory does not exist.
For example:
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id='InstallDir' Type='directory'
Root='HKLM' Key='Software\Company\Product\Install' Name='InstallPath'/>
</Property>
<Property Id='IS_INSTALLED' Secure='yes'>
<DirectorySearch Id='IsInstalled' Path='[INSTALLDIR]' />
</Property>
When both the registry key and the directory exist, the IS_INSTALLED property is set to the path returned by DirectorySearch.
When the directory does not exist, IS_INSTALLED appears to be set to "C:\".
Is a condition like:
<Condition>NOT (IS_INSTALLED = "C:\")</Condition>
a reliable way to detect that the directory was found? Is there a better way?
Answer: Here is WiX code based on mrnxs answer that I accepted
<Property Id="PRODUCT_IS_INSTALLED" Secure="yes">
<RegistrySearch Id='RegistrySearch1' Type='directory'
Root='HKLM' Key='Software\Company\Product\Version\Install' Name='Path'>
<DirectorySearch Id='DirectorySearch1' Path='[PRODUCT_IS_INSTALLED]'/>
</RegistrySearch>
</Property>
<CustomAction Id='SET_INSTALLDIR'
Property='INSTALLDIR'
Value='[PRODUCT_IS_INSTALLED]'/>
<InstallExecuteSequence>
<Custom Action='SET_INSTALLDIR' After='AppSearch'></Custom>
</InstallExecuteSequence>
Usually this happens when the property is used as a property-based folder. In this case the CostFinalize action automatically sets the property to a valid path (for example "C:\") so the folder can be used by Windows Installer.
Since this path is generated automatically, you cannot be sure that it will be "C:\" on all your client machines, so you shouldn't use this value in your condition. Instead, you can try this:
use a custom property for your folder
use a type 51 custom action (property set with formatted text) to set this property to a valid default path (for example "[ProgramFilesFolder]MyCompany\MyProduct")
use another property for the search
use another type 51 custom action to set the folder property to your search property
For example, if your search is IS_INSTALLED your folder can use IS_INSTALLED_PATH. IS_INSTALLED_PATH can be set to a default path and after AppSearch action you can set it to IS_INSTALLED if the search found something.
This way you can use for conditioning:
IS_INSTALLED
or
NOT IS_INSTALLED
Understaing AppSearch's RegLocator and DrLocator patterns can be a little tricky. I reccomend ignoring the condition for a moment and logging the install to verify that AppSearch is properly setting the properties you want. Fix the problems you find on that end first. When it works the property will be set to the value of the registry or the path to the directory.
Then you should be able to use:
<Condition>IS_INSTALLED/> <!-- it's not important what the value is, just that it exists -->
<Condition>Not IS_INSTALLED/>
Btw, I would avoid using the property INSTALLDIR. In my installers ( InstallShield ) that has special meaning as the main focal point of the installation.
Another approach could be this, in this you can continue the Install Sequence if you want to set the InstallDir to anywhere else, if the SystemDir and RegisteryDir is not same
<Property Id="RegisteryDir" Secure="yes">
<RegistrySearch Id='InstallDir' Type='directory'
Root='HKLM' Key='Software\Company\Product\Install' Name='InstallPath'/>
</Property>
<Property Id='SystemDir' Secure='yes'>
<DirectorySearch Id='IsInstalled' Path='[RegisteryDir]' />
</Property>
<CustomAction Id="SET_INSTALL_DIR" Property="INSTALLDIR" Value="[SystemDir] />
<InstallExecuteSequence>
<Custom Action='SET_INSTALLDIR' After='AppSearch'>
SystemDir AND SystemDir=RegisteryDir
</Custom>
</InstallExecuteSequence>
Related
Hi I would like to perform the following CustomAction when installing my program:
<!--Include the custom action for overwrite Client.config-->
<CustomAction Id="SetCmdLineParams" Property="QtExecCA" Value='if exist "[CURRENTDIRECTORY]\Client.config" ("xcopy" /Y "[CURRENTDIRECTORY]\Client.config" "[INSTALLFOLDER]")' Execute="immediate" />
<CustomAction Id="QtExecCA" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/>
<!--Include the InstallExecuteSequence for overwrite Client.config-->
<InstallExecuteSequence>
<Custom Action="SetCmdLineParams" After="CostFinalize"/>
<Custom Action="QtExecCA" Before="InstallFinalize" />
</InstallExecuteSequence>
Unfortunately this doesn't work because: CAQuietExec: Command string must begin with quoted application name.
But if I quote "if exist", then the command does not work. What can I do now?
if exist is a feature of cmd.exe. You'd need to say cmd /c first or create a .bat file and call that.
Honestly though, this is really fragile code. For one CURRENTDIR isn't always going to be what you think it is. You should write a C++ or C# custom action that uses the OriginalDatabase property to get where the MSI is running from and copy the config file from there.
Another approach I've used very successfully in the past is to write a utility that can transform a seed MSI by embedding a user provided config file into it. Now the deployment story is simplified.
I try to search a file in my project. The problem is I use the var "SourceDir" and it's work fine in install with UI but no with silent install.
I found it's because SourceDir is not setted before I try to used it in silent mode.
That's why I want to set my property after the "ResolveSource" action
<Property Id='CUSTOMCONFIGFILEEXISTS'>
<DirectorySearch Id='DirSearch' Path='[SOURCEDIR]' Depth='0'>
<FileSearch Id='FileSearch' Name='EasyFolderApplicationDesktopToolbar.exe.config'/>
</DirectorySearch>
</Property>
...
<Component Id="CustomMainExecutableConfig" Guid="A952C40B-0274-4EA8-8A48-0216395455CF" Directory="INSTALLDIR" NeverOverwrite="yes">
<Condition>CUSTOMCONFIGFILEEXISTS</Condition>
<CopyFile Id="CustomEasyFolderApplicationDesktopToolbarCONFIG" SourceProperty="CUSTOMCONFIGFILEEXISTS" DestinationProperty="INSTALLDIR" />
<!--<CopyFile Id="CustomEasyFolderApplicationDesktopToolbarCONFIG" SourceProperty="CONFIGFILEEXISTS" DestinationProperty="INSTALLDIR" />-->
</Component>
I already try this, with no result :
<Property Id='CUSTOMCONFIGFILEEXISTS'>
<DirectorySearch Id='DirSearch' Path='[Temp]' Depth='0'>
<FileSearch Id='FileSearch' Name='EasyFolderApplicationDesktopToolbar.exe.config'/>
</DirectorySearch>
</Property>
<CustomAction Id='SET_CUSTOMCONFIGFILEEXISTS'
Property='Temp'
Value='[SourceDir]'/>
...
<InstallExecuteSequence>
<ResolveSource After="CostInitialize" ></ResolveSource>
<Custom Action='SET_CUSTOMCONFIGFILEEXISTS' After='ResolveSource'></Custom>
<Custom Action="AlreadyUpdated" After="FindRelatedProducts">SELFFOUND</Custom>
<Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>
I already see this How do I use the SourceDir MSI property in WiX?
But I don't know how to do...
Can you help me ?
That search is ultimately an AppSearch, and in a WiX-built MSI that's the first thing that runs, so your 'CUSTOMCONFIGFILEEXISTS' property has already been processed by the search, so it's false by the time that component results in the component not being installed and the copyfile not being done. So the reason for the failure is not ResolveSource - it's because 'CUSTOMCONFIGFILEEXISTS' is being set false and you are conditioning the component and the copyfile on that false condition.
IMO you are over-thinking this. Don't bother with a search. Just pick a component relevant to the file you want to copy (don't invent a transitional component for it with a condition). Add the CopyFile to that component with [SourceDir] as the source. If the file is there it will be copied, if not then it won't be.
Don't add an unconditional ResolveSource action because it will happen every time the an installer action takes place (repair, removing features, patches, uninstall) and that is generally unnecessary. In any case I am certain you do not need a ResolveSource. There is an implicit ResolveSource at first install or it wouldn't even know where the MSI was! Just do the copyfile as I suggested.
We have an installer created using WIX. As part of this install we would like to show the currently selected installation path. I thought this would be much easier than it is, apparently. I have tried using the a public property "INSTALLDIR" (I know we're not using Installshield, this value is a directory ID.)
<Directory Id="INSTALLDIR" Name="AcmeInc">
I can also see where INSTALLDIR gets set when running the install
MSI(EC:6C) Dir (target): Key: INSTALLDIR , Object: C:\Program Files\AcmeInc\
but when I try to show this on the UI using a Text attribute I get "...\." which doesn't even look to be a relative path.
I know there has got to be something simple I'm missing here.
Assuming you're using WiX 3.5 and the MajorUpgrade element - the following should work (I usually use APPLICATIONFOLDER instead of INSTALLDIR - but they should be interchangeable).
First, let's set ARPINSTALLOCATION as described on http://robmensching.com/blog/posts/2011/1/14/ARPINSTALLLOCATION-and-how-to-set-it-with-the-WiX-toolset
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" />
Now lets set the selected installation folder to the previous installation folder, if one previously existed that is.
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id="FindInstallLocation"
Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[WIX_UPGRADE_DETECTED]"
Name="InstallLocation"
Type="raw"
Win64="yes" />
</Property>
And during the UI sequence, we want this value to be set 'early'
<InstallUISequence>
<AppSearch After="FindRelatedProducts"/>
</InstallUISequence>
I have the following:
<WixVariable Id="WixUIBannerBmp" Value="FirstBanner.jpg" />
I want to be able to set the value of this WixVariable using a property that gets passed in when the installer is kicked off:
msiexec.exe /i MyInstaller.msi /l* install.log MYPROPERTY=SomeValue
So, based on the the value of MYPROPERTY, I set the value of the WixVariable to one of two (or more) values.
My initial stab at this proved unsuccessful:
<Property Id="BANNERLOCATION" Value="FirstBanner.jpg" />
<WixVariable Id="WixUIBannerBmp" Value="[BANNERLOCATION]" />
I get the following build error:
error LGHT0103: The system cannot find the file '[BANNERLOCATION]'.
Is what I am trying to achieve possible? This is simply branding the install based on a passed-in property, after all ...
No, what you are trying to accomplish is not supported by the Windows Installer. Sorry.
We need to modify the Startup type of our Windows service from "Automatic" to "Automatic Delayed Start". How do I do this?
My code is like this:
<ServiceInstall
Id="WinServiceInstall"
Name="ServiceManager"
DisplayName="ServiceManager"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Vital ='yes'
Description ='Monitoring and running the jobs'
Account="[SERVICEACCOUNT]"
Password="[SERVICEPASSWORD]">
<util:ServiceConfig
FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType ="restart"
cRestartServiceDelayInSeconds ="10" />
</ServiceInstall>
And how do I set the Restart service time? I would like to set Restart service after 2 minutes if failed.
Place a ServiceConfig element within the ServiceInstall element.
<ServiceConfig DelayedAutoStart="yes" OnInstall="yes" OnReinstall ="yes" />
If you need to support really old versions of Windows, then you will need to set it with a reg-value, because MSI < 5.0 doesn't recognize this option with ServiceConfig. Otherwise, you should use the ServiceConfig method.
<RegistryValue Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\[ServiceName]"
Type="integer" Name="DelayedAutostart" Value="1"/>
I put this in the same component as the ServiceInstall, and everything seems to work fine. I imagine you could do the same thing for the service restart time.