Using WiX 3.6 I'm trying to create an MSI for silent installation of an in-house product.
One some machines, my installer works fine, but on others the installer fails with an error message indicating the property value hasn't been passed through from the UI phase to the Install phase of the process:
This error comes from a guard clause in my WiX source:
<Condition Message="The property SERVER must be defined on the commandline. Installation will abort.">
<![CDATA[Installed or SERVER]]>
</Condition>
Why is my Guard clause triggering even though I provide the property SERVER on the command line?
The installation commandline:
msiexec /i 'C:\work\Excel.2010(x86).msi' SERVER=fsis-app-server ENVIRONMENT=Production SKIN=Black
According to everything I can find by consultation with Professor Google, all I should need is to make my property public (by using all capitals for the name) and to mark it Secure="yes":
<Property Id="SERVER"
Secure="yes"/>
I've done this, but it's still not working - any ideas what I've missed?
Other details ...
... my installer is configured to run as a MajorUpgrade every time.
... I'm deploying exclusively to Windows 7. On my machine (which does have this problem), I have Windows Installer. V 5.0.7601.17514
... Most machines upgrade fine, but a handful (<10 out of 200) fail with this error. My machine is one (helpful for troubleshooting). The previous version is NOT listed under Programs and Features on this machine.
Update #1
Very interesting, reading the logs. My properties (e.g. SERVER) are not losing their values as the install progresses.
If I'm reading the log right, it seems my error in being shown by the previous MSI, not the one I'm currently running:
MSI (s) (D8:D0) [13:20:19:213]: Product: FSIS Plugin for Excel 2010 (32 bit) v4.7.1047.0 -- The property SERVER must be defined on the commandline. Installation will abort.
The installer I'm using is v4.8.9999.0, so the old version number is my clue as to what's happening ... though this doesn't match the v4.5.0.0 in my original screenshot.
Looks like I have two jobs ahead of me ...
... napalm the old installer to clean up this machine
... alter the installer to prevent recurrence.
Update #2
I've verified that my failure happens on UNinstallation.
<Condition Message="The property SERVER must be defined on the commandline. Installation will abort.">
<![CDATA[Installed or SERVER]]>
</Condition>
Why does this guard clause trigger and abort uninstallation?
I thought the point of having "Installed or" was to allow it to work properly when removing the product.
Related
After testing many of install/uninstall of my product in the machine I've ended up a scenario where I don't have my product installed, at least I cannot see it in the control panel, but when I try to install it again, Wix is trying to Update a product which is not installed.
I've executed the msi with the logs, then I found that FindRelatedProducts is found a GUID and populating the WIX property WIX_UPGRADE_DETECTED.
part of the log:
FindRelatedProducts: Found application: {MY-GUID}
MSI (c) (24:8C) [07:21:36:885]: PROPERTY CHANGE: Adding WIX_UPGRADE_DETECTED property. Its value is '{MY-GUID}'.
As I'm using WIX_UPGRADE_DETECTED to select with dialog (Install or Upgrade) to show, It's showing a upgrade dialog, but no product is installed.
If I test in another machine, doing the same scenario, FindRelatedProducts doesn't find any product, which is the correct case.
I'm suspecting of some entry in the windows Registry(regedit) was not cleaned.
Do you know how Wix detects FindRelatedProducts? Or why Wix is populating the WIX_UPGRADE_DETECTED with no product installed?
Thanks!
Short Answer: The answer below is probably too elaborate, just do an uninstall of what your upgrade setup finds and then try to
install again.
From a command prompt (Windows Key + Tap R +
Type: cmd.exe + Enter) run the following command:
msiexec.exe /x {GUID-FROM-LOG-FILE}
The GUID is (most likely) the one from your log file: WIX_UPGRADE_DETECTED. Then try
installing again.
Failing Uninstall: If the uninstall fails, try running this Microsoft FixIt tool. Sometimes it can sort out setups that don't uninstall properly. Alternative, under the hood fix (not recommended).
UpgradeTable: The first thing I would do is to verify what is in the UpgradeTable in the compiled MSI file that shows the problem. Does the upgrade code there match the upgrade code for your setup? (UpgradeCode entry in the Property Table).
The content of the UpgradeTable determines what existing installations (if any) are detected as related to your new installation. If you configure weird stuff here you could even end up uninstalling competitive products erroneously detected as related to yours - I wouldn't try that :-). Too much paperwork.
Uninstall: Now, how to get rid of the problem installation? You need to get hold of the ProductCode GUID. There are numerous ways to obtain this information. It should be the product GUID you see in your MSI log for WIX_UPGRADE_DETECTED, so try that first:
msiexec.exe /x {GUID}
Here is an answer on uninstalling MSI setups in a general sense (all kinds of different options - have a quick read?): Uninstalling an MSI file from the command line without using msiexec.
ProductCode (GUID): Rob has already mentioned the right MSI API to list installed products, I will just add that I have this answer here that can help: How can I find the product GUID of an installed MSI setup? It lists several options to see what is installed on your box.
VBScript / COM Automation: I will just inline the VBScript option from the first link above (there are several options listed in that linked answer):
' Retrieve all ProductCodes (with ProductName and ProductVersion)
Set fso = CreateObject("Scripting.FileSystemObject")
Set output = fso.CreateTextFile("msiinfo.csv", True, True)
Set installer = CreateObject("WindowsInstaller.Installer")
On Error Resume Next ' we ignore all errors
For Each product In installer.ProductsEx("", "", 7)
productcode = product.ProductCode
name = product.InstallProperty("ProductName")
version=product.InstallProperty("VersionString")
output.writeline (productcode & ", " & name & ", " & version)
Next
output.Close
PowerShell: Throwing in the PowerShell option as well. In some cases this can trigger unexpected self-repair.
get-wmiobject Win32_Product | Format-Table IdentifyingNumber, Name, LocalPackage -AutoSize
The Windows Installer has registration spread about the registry. It provides an API to introspect over that registration. In this case, you want ::MsiEnumRelatedProducts(). It will get you all the products related via that UpgradeCode.
You can then uninstall those products by doing:
msiexec /x {PRODUCT-CODE-GUID}
You should find {PRODUCT-CODE-GUID} is the same as what you redacted as {MY-GUID}.
Note: this has nothing to do with the WiX Toolset. Is 100% Windows Installer behavior (aka: MSI).
FindRelatedProducts is executed by Windows Installer not WiX. Your assertion that the behavior is incorrect is most certainly incorrect. You have a dirty machine and MSI has artifacts indicating that it's installed. Your other machine is clean and does not.
I was able to identify what were the registry entries left on the machine which was causing the errors that I mentioned.
They are in the following location:
- HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\UpgradeCodes\{OTHER-GUID}
- HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products\{OTHER-GUID}
Note that, the {OTHER-GUID} is not my product code, but a generated one, my product code is one of a values present into those registry entries.
Just removing them makes my installer working again.
Tks #stein-Åsmul this command: get-wmiobject Win32_Product is very helpful.
When I'm building my MSI file, and I use a basic condition, the expected happens. For example, let's say that I have this in Setup File:
<Property Id="myProperty" Value="0"/>
<Condition Message="Value of myProperty is [myProperty], and it should be 1.">
<![CDATA[Installed OR myProperty = "1"]]>
</Condition>
If I build this and run the MSI file, it works -- that is, it displays the error message and quits.
Working condition when running MSI
However, if I put the MSI into a Bundle, it doesn't work. That is, when I put just this into my Bootstrapper ("Properties" below is the name of my Setup project -- bad name, I realize):
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)"/>
</Chain>
And then I run the setup file, I get an error. When the installation starts, it checks the condition, gives me the expected message box (same as above), and then I get this error message:
Setup Failed
Looking at the Log, I get three error messages:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
With the exit code:
Exit code: 0x643, restarting: No
I'm such a noob at WiX that I'm not even sure how to go about researching what the problem is -- I can't even ask an intelligent question. Hence, I'm reaching out to you kind folks!
(I'm using WiX 3.10 and Visual Studio 2015)
EDIT:
Thanks for getting back to me! I tried your suggestions:
In the installer file, I made the property public and I made it secure. I left the condition the same, and, since I don't think that I should get the value here as opposed to in the bootstrapper, I left the value of the property out. Here is the code that I made for the property/condition:
<Property Id="MY_PROPERTY" Secure="yes"/>
<Condition Message="MY_PROPERTY is [MY_PROPERTY]. Should be 1">
<![CDATA[Installed OR MY_PROPERTY = "1"]]>
</Condition>
Then, in the boostrapper file, I added a child element of and gave it a value:
<MsiPackage SourceFile="$(var.LCondErrorInstaller.TargetPath)">
<MsiProperty Name="MY_PROPERTY" Value="0"/>
</MsiPackage>
When I ran it, I got almost the same behaviour as I did before, except for one difference -- when I get the error message. This time, I get the pop-up screen with the Message condition and the same error message as I did before (see "Setup Failed" above), except this time I get it happens little later in the installation, making me think that the condition is, in fact, getting triggered in the bootstrapper.
As far as the log files, they look the same (I'm not sure how to get log files of the MSI when running the Burn file, what I do now is just run the Burn file with the flag "/l", like so: > file.exe /l logFile.log).
For clarity, here are the parts of the log file that appear to be important:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
...
Exit code: 0x643, restarting: No
I should have been more specific when I initially asked the question about what kind of behaviour I'm looking for...
I will have more than just that one MSI file in the Burn file. What I want to do is this: when the Burn file installs, if there is a condition in one of the MSI files that is not met, I want that MSI file to simply not be installed, and the rest of the MSIs to be installed. I don't care about the UI.
If there's another way to do this, I'm all ears.
If you have launch conditions in the MSI you can replicate or move those launch conditions into the bootstrapper bundle itself to stop this type of behaviour.
Launch condition failure returns Fatal Error 1603 (0x643 in hex) which is what I would expect to see when the MSI launched by the bootstrapper fails due to launch condition not met.
You should see something like this in the msi's logs
Action ended 17:33:38: LaunchConditions. Return value 3.
MSI (c) (08:4C) [17:33:38:610]: Doing action: FatalError
Action 17:33:38: FatalError.
Action start 17:33:38: FatalError.
...
MSI (c) (08:4C) [17:33:41:188]: MainEngineThread is returning 1603
To elaborate, you would have to change your msi package definition to the following to get it to run properly through the bootstrapper
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)">
<MsiProperty Name="MYPROPERTY" Value="1"/>
</MsiPackage>
</Chain>
Additionally if you want to pass in a property from your bootstrapper to your MSI the property must be a public property which is a property whose name is ALL CAPS.
If you want to use this property somewhere in the Install phase of your msi you must also mark this property as secure.
I created an installer project for Win XP using VS 2013.
While trying to install it, I got an error:
"DIRCA_CheckFX. Return value 3."
I visited this article in StackOverFlow: DIRCA_CHECKFX Return Value 3 - VS 2013 Deployment Project.
I did the Suggestion that I read there - to replace the "dpca.dll" - and it works great !
But only at machines that I didn't install the bad msi before.
While trying to install the new version of msi on a machine that has the bad version, I got an error that this program is already installed.
But while trying to uninstall the program - I got the error of : "DIRCA_CheckFX. Return value 3."
My question Is - How to Unistall the bad Version Of my installer and prevent this error?
thanks.
The root cause of this is the perhaps silly choice to do a check for the .NET Runtime when uninstalling the product. I suppose it's possible that someone uninstalled the .NET FW and that you are running uninstall custom actions that require it, and maybe that happened to you, but it seems unnecessary to me IMO.
If I were to have this problem, there are two solutions:
If you have that exact same MSI file, same version, ProductCode, identical in every way, then open it with Orca and go to the InstallExecuteSequence table. Go to the DIRCA_CheckFX call and set a condition of False, save the MSI file. Then install that MSI file with a command line msiexec /i [path to new MSI] REINSTALL=ALL REINSTALLMODE=vomus and this will do an update in place of the installed product, including the call to DIRCA_CheckFX that is now suppressed. Uninstall should work.
In the absence of the proper MSI file, troll through the Windows\installer folder looking at the cached MSI files. Hovering the mouse over each should get you to your cached MSI for the broken product. Again, edit with Orca as above to suppress the call on DIRCA_CheckFX.
I'm using WiX to generate an MSI that installs a browser plugin on a perUser basis. I have a custom action to install a driver using DPInst (which needs elevated privileges).
The install works fine when UAC is enabled; Windows shows the prompt to elevate. However, if I have UAC disabled and try to install on a regular user account, dpinst.exe will get spawned until the computer freezes. (About a thousand times at last count).
In the <InstallExecuteSequence> I have:
<Custom Action="Install_Drivers" After="InstallFiles">NOT Installed</Custom>
My custom action is:
<CustomAction Id='Install_Drivers' Execute='deferred' Directory='DRIVERS' ExeCommand='"[DRIVERS]DPinst.exe" /SW /SA' Return='ignore' Impersonate='no'/>
I have Return='ignore' because, from what I understand so far, dpinst.exe always returns a non-0 code.
How can I ensure that the custom action fails correctly when UAC is disabled? On a related note, can I show a custom message to the user when the driver installation fails?
Further Information: I'm developing on Windows 7 currently, but targeting WinXP and up.
Edit Taking a look at the log for the installation these seem to be the relevant lines:
Executing op: CacheSizeFlush(,)
Executing op: ActionStart(Name=Install_Drivers,,)
Executing op: CustomActionSchedule(Action=Install_Drivers,ActionType=3170,Source=C:\long_redacted\Drivers\,Target="C:\long_redacted_path\Drivers\DPinst.exe" /SW /SA,)
Disallowing shutdown. Shutdown counter: 0
CustomAction Install_Drivers returned actual error code 1073807364 but will be translated to success due to continue marking
The bit about the shutdown is, I believe, when I logged off stop the installation. (Canceling doesn't seem to have any effect).
Try setting the 'Impersonate=no' attribute on the 'CustomAction' element, like this:
<CustomAction Id='Install_Drivers' Execute='deferred' Directory='DRIVERS' ExeCommand='[DRIVERS]DPinst.exe" /SW /SA' Return='ignore' Impersonate="no" />
Also note: you have a stray double-quote in your ExeCommand
Installing a driver is an inherently per-machine operation. A limited user can't do it. So with UAC disabled, it's not going to work. DPInst apparently doesn't get the hint that it doesn't have permissions and can't get them. Sounds like a bug in DPInst. You should change your installer to be per-machine and add a launch condition on the Privileged property to prevent the installer from starting for limited users without UAC.
I have made a change to a custom action in my msi file to not attempt to stop a windows service if the service is stopped or stop pending. However, after re-caching the msi using msiexec /fv mymsi.msi, when running the major upgrade to the next version (which also has the corrected service stopping custom action) the verbose logging is showing that the old custom action code is still being run even though the cached msi was updated. I even ran a binary comparison of the cached msi against the one that was used in the re-caching.
What am I doing wrong here.
If the old custom action is still running it means one of two things:
The new MSI logic is not correct and is still running the custom action.
The recache/reinstall did not work.
To re-cache/reinstall make sure your MSI is basically identical (same ProductCode/Upgradecode etc) to the old MSI except with the updated custom action. Then do:
msiexec /fv new.msi /l*v log.txt
That will overwrite the old MSI and do a repair using the new MSI (and give you a log file in case anything goes wrong).
In my case it was a cached copy of my MSI in %windir%\Installer and corresponding keys in the system registry (found them by GUID in HKLM and HKCR).