As part of an WiX-based installer I have the following component with a User element (besides other components with File, ServiceInstall and ServiceControl elements):
<Component Id="cmpServiceUser" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
<util:User
Id="ServiceUser"
Name="[ProductName]"
Password="[ServiceUserPassword]"
PasswordNeverExpires="yes"
LogonAsService="yes"
CreateUser="yes"
UpdateIfExists="yes"
RemoveOnUninstall="yes"
/>
</Component>
I have decided to go the only-major-upgrades way and placed the following element inside the Product:
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
/>
The first installation of the product works as expected. During a major upgrade, I would have expected the user to be first removed (during RemoveExistingProducts) and later being created again. But it behaves different: The user is really removed nearly at the end of the installation - after the service for example has been restarted again (Process Explorer can still tell me the service's user name but not the SID any more - as the service's user was removed after the service was started).
After many hours of dissecting various installation logs I have turned to peek into the sources of the UtilExtension and probably found an explanation in scauser.cpp (slightly reformatted for better SO-readability):
// Schedule the removal because the user exists and we don't have any flags set
// that say, don't remove the user on uninstall.
//
// Note: We can't rollback the removal of a user which is why RemoveUser is a
// commit CustomAction.
So I guess this late removal of the user really belongs to the RemoveExistingProducts action in this case.
Thanks for reading this far - now the question: Is it possible to use util:User#RemoveOnUninstall in collaboration with a major upgrade strategy?
Related
A simple msi created using WiX. Trying to install a product. On some computers it will fail, without an error, and the installer log will contain these rows:
PROPERTY CHANGE: Adding WIX_UPGRADE_DETECTED property. Its value is '{7C9...0A0}'
PROPERTY CHANGE: Adding MIGRATE property. Its value is '{7C9...0A0}'
Product Code from property table after transforms: '{68F...FAE}'
Product not registered: beginning first-time install
Doing action: INSTALL
Doing action: InstallValidate
Feature: ProductFeature; Installed: Absent; Request: Null; Action: Null
MIGRATE = {7C9...0A0};{A9B...BCE}
Installation completed successfully.
Somehow, Windows Installer decides that there is nothing to do (Request: Null), even though it acknowledges that this is the first time install. That computer had previous version of the product installed. How do I force the installer to install?
Here is the relevant WiX code:
<Product Id="*"
Name="..."
Language="1033"
Version="..."
Manufacturer="..."
UpgradeCode="8D8...196">
<Package Id="*"
InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
InstallPrivileges="elevated"
Platform="$(var.Platform)" />
<Property Id="REINSTALLMODE" Value="amus" />
<MajorUpgrade AllowDowngrades="yes" IgnoreRemoveFailure="yes" AllowSameVersionUpgrades="no"/>
<Feature Id="ProductFeature" Title="..." Level="1" Absent="disallow" AllowAdvertise="no" InstallDefault="local" TypicalDefault="install">
<Condition Level="1">True</Condition>
Full installer log: https://www.dropbox.com/s/abnryavh203a82w/acme_installer_log.txt?dl=0
You did not set MigrateFeatures in your MajorUpgrade element. The documentation says the default value is Yes.
http://wixtoolset.org/documentation/manual/v3/xsd/wix/majorupgrade.html
The log shows you are doing an upgrade, therefore the only features that are going to be installed is the same set that are already installed.
It's not clear how many features there are in your product, but if you always want all features to be installed during an upgrade set MigrateFeatures to No.
Either way, it would be better to supply the entire log to see what's going on and if there is another explanation.
This got too long for a comment, adding as an answer. Just a couple of things:
You definitely should not set REINSTALLMODE="amus" for any MSI package.
If you include merge modules in this setup you could downgrade shared files - even if you otherwise don't write to shared locations yourself.
The best would be the default "omus", but I guess "emus" is also ok (Reinstall if the file is missing, or is an equal or older version).
It certainly looks like the MigrateFeatureStates action is at least related to the problem seen:
MigrateFeatureStates: based on existing product, setting feature 'ProductFeature' to 'Absent' state.
Your INSTALLLEVEL property looks fine.
You might want to open the compiled MSI and verify that there are no entries in the Condition table (which can change feature selection states). There should be nothing there, but perhaps just verify.
I suppose you could try to set the Absent attribute for the WiX Feature element to "disallow", recompile and see if this changes things. If this is your only feature I would make this change in addition to using Phils suggestion to set MigrateFeatureStates to "No". Why allow your only feature to be removed?
It is a little odd how this problem has occurred. Perhaps you ran modify on your MSI and simply selected the feature to be uninstalled in the old installation on the machine? Sounds unlikely - especially if it is your only feature.
Did you install several earlier versions of your product on the machine in question, or was there only one prior version?
Again, are there entries in the "Condition table" in the compiled MSI? (Check using Orca).
Did you change the feature name in your newest package?
In any event you should be able to "fix" the broken install by 1) simply running your existing setup and adding ADDLOCAL=ALL to your msiexec.exe install command - I think that will override the MigrateFeatureStates action (not a real fix, just a workaround). Or 2) you can disable MigrateFeatureStates and recompile the setup - that should fix things permanently, and 3) if I were you I would set the Absent attribute for the WiX Feature element to "disallow" so that your (only?) feature can't be removed.
Phil, if you see this, do you know what the MIGRATE property seen in the log is about? It looks like some WiX specific stuff - unless it is something from this MSI's upgrade table:
MSI (c) (04:24) [13:01:38:751]: Doing action: FindRelatedProducts
MSI (c) (04:24) [13:01:38:751]: Note: 1: 2205 2: 3: ActionText
Action 13:01:38: FindRelatedProducts. Searching for related applications Found application: [1]
Action start 13:01:38: FindRelatedProducts.
FindRelatedProducts:
MSI (c) (04:24) [13:01:38:751]: PROPERTY CHANGE: Adding WIX_UPGRADE_DETECTED property. Its value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'.
MSI (c) (04:24) [13:01:38:751]: PROPERTY CHANGE: Adding MIGRATE property. Its value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'.
FindRelatedProducts:
MSI (c) (04:24) [13:01:38:752]: PROPERTY CHANGE: Modifying WIX_UPGRADE_DETECTED property. Its current value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'. Its new value: '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}'.
MSI (c) (04:24) [13:01:38:752]: PROPERTY CHANGE: Modifying MIGRATE property. Its current value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'. Its new value: '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}'.
Action ended 13:01:38: FindRelatedProducts. Return value 1.
tandrasz: as far as I can see the two GUIDs that are found by FindRelatedProducts ({7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}), are different from your new setup's product code ({68F5E655-2E2D-492E-B445-BE1650AF5FAE}). This makes it look like there are two "related products" found by FindRelatedProducts on the machine in question. Are two older versions of the same MSI installed, or do you have a "suite" of MSI files that all share the same upgrade code?. If you have two "unrelated" packages installed, perhaps MigrateFeatureStates finds no way to migrate the feature states between those two and the new package you are installing?
This question already has an answer here:
UPGRADINGPRODUCTCODE condition not working in wixui_install.wxs in library
(1 answer)
Closed 7 years ago.
I have an application that needs to know if it has been upgraded. So I thought of writing a registry key with the installer, which then could be read from the application. I tried to achieve this by adding the following component to the installer:
<Component Id ="Registry_IsUpgraded">
<Condition>Installed AND UPGRADINGPRODUCTCODE</Condition>
<RegistryValue Id="RegKey_IsUpgraded" Root="HKLM"
Key="SOFTWARE\Microsoft\Office\Excel\AddIns\websmsExcelAddIn"
Name="IsUpgraded" Value="yes"
Type="string" />
</Component>
However, registry key is not written during the upgrade. In the msi log I found this line:
Component: Registry_IsUpgraded; Installed: Absent; Request: Local; Action: Null
I suppose Action: Null means that the component will not be installed, since Action for other Components that are installed during the upgrade is Local.
But why is this and why is my registry key not written? Is there maybe a better/easier way to tell my application that it has been upgraded?
Installed and UPGRADINGPRODUCTCODE is always going to evaluate to false because during a major upgrade (indicated by UPGRADINGPRODUCTCODE) the new product is by definition not yet Installed.
I assume your application needs to do something on first run after each upgrade, yes? If so, just write the ProductCode property to the registry value and then in your application code trigger an event when it detects the value has changed. This will simplify your installer requirements.
I'm using wix installer (version 3), I have an msi of version 1.99 and another msi of version 2.00. My app has the ability to do import and export of the DB by calling it with some arguments. I'm trying to perform a major upgrade, and trying to call custom actions before and after the upgrade.
Now, the custom action code works just fine. The problem is, that the code that should run BEFORE the old version is removed, is running AFTER it is removed, and thus cannot activate the app and produce the backup files.
In short: How do I time the custom actions to do their work before the removal of the old version?
This is how I call them:
<CustomAction Id="doExport"
Return="check"
Execute="immediate"
BinaryKey="ImportExportBinary"
DllEntry="BeforeInstall" />
<CustomAction Id="doImport"
Return="check"
Execute="immediate"
BinaryKey="ImportExportBinary"
DllEntry="AfterInstall" />
<InstallExecuteSequence>
<Custom Action="doExport" Before="InstallInitialize"> NOT Installed</Custom>
<Custom Action="doImport" After="InstallFinalize"> NOT Installed</Custom>
</InstallExecuteSequence>
EDIT:
Here is the major upgrade code:
<MajorUpgrade AllowDowngrades="no"
Schedule="afterInstallFinalize"
DowngradeErrorMessage='Cannot downgrade!'
AllowSameVersionUpgrades='yes' ></MajorUpgrade>
I've tried playing a bit with "Execute" attribute of the CustomAction element, without any results.
First, do your upgrade creating a verbose log to make sure your custom actions are working and being called. You've marked them immediate, so they run before anything changes on the system and so will be called before the old product is removed. When you say "the code works just fine" you probably mean when you run it from your interactive account. But that's not happening. Your code is running out of an msiexec.exe process, the working directory is not what you expect, your code may not be looking in the right place for the files, it will not be elevated and so may not be able to do what it thinks it can. There are many opportunities for your code to not work as expected.
If you ever marked them as deferred I can see why doExport might not work. Without seeing your MajorUpgrade element I can't be sure, but the default scheduling for RemoveExistingProducts is afterInstallValidate. Your custom action is before InstallInitialize, so the actual sequence in the MSI file could easily be InstallValidate, RemoveExistingProducts, doExport, InstallInitialize.
and RemoveExistingProducts that does the uninstall of the old version is before your custom actions.
So if you want to use execute deferred, try Before="RemoveExistingProducts" on your doExport, or use Schedule in your MajorUpgrade to afterInstallInitialize and keep your doExport before InstallInitialize.
I tryied the same thing, and it took me long to find a way for it to work.
You can use
<Custom Action="doExport" Before="RemoveExistingProducts"></Custom>
The option RemoveExistingProducts refers to the action of uninstalling the existing version of your product, so you have to execute the action before the existing version is removed.
I did not find this option in this list: https://learn.microsoft.com/en-us/windows/win32/msi/suggested-installexecutesequence
But it seems to work, and executes before the list of options described in the link.
About the options which should go inside the <Custom></Custom> tag, I am not sure, but I hope it helps to start the code at the right moment.
After review a lot of posts in this site finally I decide to put mine.
I am preparing an MSI file with Wix.
I need to check if a particular version of an enterprise product is installed, before to install my system. I have the GUID of that product (which is the same for all versions), but I need to check if 1.10.0 version is installed.
Any idea, please.
Thanks in advance.
PD: I am newbie in Wix, so at this moment I am just using the wxs file created by default with the Setup Project.
Clarifying: I don't want to upgrade the software that I am installing, I need to check another program and version which my installer depends.
To do it properly, you probably want to use a custom action. Inside the custom action, use the MsiGetProductInfo function.
A way of doing it in pure-WiX would be to modify the example found here: How do I compare registry versions in WiX?
First create a RegistrySearch element:
<Property Id="PRODUCTVERSION">
<RegistrySearch Id="ProductVersionSearch" Root="HKLM" Key="software\Microsoft\Windows\Current Version\Uninstall\[PRODUCTCODE]" Name="DisplayVersion" Type="raw" />
</Property>
Then use a Condition element:
<Condition Message="Product version 1.10.0 must be installed">
<![CDATA[PRODUCTVERSION AND PRODUCTVERSION = "1.10.0"]]>
</Condition>
This would search for exactly version 1.10.0, so may not be what you want if you're looking for something like "v1.10.0 or newer"... But should get you started.
Perhaps try the proposed solution in this post: WiX Installer: getting version of the product being upgraded
It involves using the Upgrade table to identify the installed product, and a custom action using VBScript to determine the version.
If you want to do something like create an error message or fail the install if that version is present you can have multiple upgrade entries. Have one that has something like this, bad syntax...
<Property Id="VERSION110INSTALLED" Secure="yes" />
<Upgrade Id="YOUR_GUID">
<UpgradeVersion
Minimum="1.10.0" Maximum="1.10.0"
Property="VERSION110SINSTALLED"
IncludeMinimum="yes" IncludeMaximum="yes" OnlyDetect="yes" />
</Upgrade>
Then you have that property set if version 1.10.0 is present, and if you want to produce an error message condition it on VERSION110SINSTALLED, and sequence it after FindRelatedProducts.
I am writing an installer that also contains MSI package built using WIX. I check the return code after running msiexec and skip some error codes. For example user should't receive error when return code is 1638 (product has already installed)
But when the older version of product was installed the return code is 1603 (unknown error). I also want to skip this situation (user already get newer version)
Wix code is
<Upgrade Id="<GUID>">
<UpgradeVersion Minimum="$(var.product_version)" OnlyDetect="yes" Property="NEWERFOUND" />
<UpgradeVersion Minimum="0.1.0" IncludeMinimum="yes" Maximum="$(var.product_version)" IncludeMaximum="no" Property="SELFFOUND" />
</Upgrade>
<MajorUpgrade DowngradeErrorMessage=You have installed newer version $(var.product_name)." />
The question is how I can return another code than 1603 when SELFFOUND or NEWERFOUND properties are activated
Update: I didn't want to influence the return code, I want to get appropriate code instead the general fatal error
You can't control the return code of msiexec.exe. Its return codes are documented and the list is comprehensive. The service determines the exit code based on the status of installation.
Update: The only thing I can suggest is to use database functions directly:
MsiOpenPackage, and
MsiDoAction("FindRelatedProducts") to run FindRelatedProducts action which will evaluate SELFFOUND and NEWERFOUND properties by processing Upgrade table.
Then analyze the properties with MsiGetProperty.
Close the package with MsiCloseHandle.
Using the obtained values of SELFFOUND and NEWERFOUND, you can decide whether you need to install the product or not. If yes, you can run msiexec.exe or use MsiInstallProduct.
Disclamer: I have never tried to do anything like this, and I am not sure it will work. It should, yet it does not look easy.