4

I'm using Wix to code my own MSI installer. I need to run the custom action only before uninstallation of the product, but before any registry values or files are removed. I did the following (just to try):

<Property Id='CALC'>Calc.exe</Property>
<CustomAction Id='BeforeUninstall01' Property='CALC' ExeCommand='' Return='check' />

<InstallExecuteSequence>
  <Custom Action='BeforeUninstall01' After='InstallInitialize'>Installed</Custom>
</InstallExecuteSequence>

It works if I choose to uninstall from the Control Panel, but if I run my MSI instead (while it's already installed) the BeforeUninstall01 custom action is triggered anyway, which it shouldn't.

Any idea how to change this condition?

ahmd0
  • 16,633
  • 33
  • 137
  • 233

2 Answers2

7

You choose the condition "Installed".

Given your code the wanted condition using the built-in property "REMOVE" will result in:

<InstallExecuteSequence>
   <Custom Action='BeforeUninstall01' After='InstallInitialize'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>

This also allows you (even if it is not necessary) to uninstall a single feature, but not the whole product without that your custom action (ca) is triggered. In other words, the ca is triggered always and only, if

Yours condition starts the ca always, but not for first install (incl. repair, update, uninstall, modify, patch, etc. This is not, what you need, indeed.

The condition of Reubz is slightly different, this would start always but not for first install and not during a Major Upgrade, which is not a real improvement here.

Concerning sequencing: If your ca really needs elevated rights then you have to run the custom action "deferred" with system rights and change your given ca definition to (if not, let it):

<CustomAction Id='BeforeUninstall01' Property='CALC' ExeCommand='' Execute="deferred" Impersonate="no" Return="check" />

(I am not a WiX wizard, only I know MSI quite well, so I have not checked any part of your WiX code, only the issues.)

Philm
  • 3,448
  • 1
  • 29
  • 28
  • Thanks, it works, but it created a new issue. In my actual code I need to run a method from my DLL. I use the same approach to invoking a custom action, but when DLL is called elevated `MsiGetProperty` doesn't retrieve any properties from a C++ code. Or, they are rather empty strings. This stops being an issue if I remove `Execute="deferred"` but then it is not run elevated. You see where I'm going with it -- sounds like a catch-22... – ahmd0 Jul 11 '13 at 22:55
  • Yes, this is really annoying. In every advance project someone traps into this. It is explained a bit in the following link. Either you store the properties in a registry or settings file before InstallInitialize and parsing it in your deferred custom action independently from msi. Or you have to follow the documented method and store everything in one property named "CustomActionData". I have not used it in WiX but I am sure there is something which is not too complicated, once you have got the point. – Philm Jul 12 '13 at 20:08
  • Take a look here or google for it, you will find the same answer maybe somewhere better explained: http://msdn.microsoft.com/en-us/library/windows/desktop/aa370543(v=vs.85).aspx – Philm Jul 12 '13 at 20:12
  • Thanks for your help. Yes, I ended up using `CustomActionData` approach but it is very cumbersome to pack all the parameters into it. The reason I needed to do it was to pass the installation context into the custom action, i.e. clean installation, reinstallation or repair, change or uninstallation, which by themselves are a "handful" to manage. – ahmd0 Jul 12 '13 at 20:17
  • Plus in some cases my custom action was called elevated even without showing an end-user UAC prompt. I'm not sure if it's a bug in Windows, or what. I for sure be aware of those MSI packages for my own use... – ahmd0 Jul 12 '13 at 20:19
  • A MSI installed first does always (only) shows an UAC prompt if not started by a process which already is elevated, e.g. a service or a setup.exe bootstrapper. If the MSI is already installed, there maybe situations where a second elevation is not necessary. I am not sure about this. For patch installs, this works for some environments without UAC (even if not a LUA patch), maybe there are other update situations, I am not aware of. But I can't remember such a situation. – Philm Jul 12 '13 at 20:28
  • No, I was starting my MSI from Windows Explorer running under a non-admin Windows account and UAC was not disabled. Indeed it was reinstallation, and it goes straight into my custom handler without the UAC prompt. One doesn't have to be a rocket scientist to see how this could be exploited -- a malicious MSI package can spoof its upgrade ID to the one of, say, MS Office, in which case it may not even require elevation to run a custom action elevated. I hope MS check MSI package file hash before omitting UAC prompt.... Ahhh. Oh, well.... – ahmd0 Jul 12 '13 at 20:37
  • I don't think that MSI has a failure here. It is too good tested for years. But besides, every installed service can start without UAC. Moreoever, UAC is no security gain really, it just raises the challenge a bit. I've heard of, it can be circumvented not too difficult. Mainly it shall lower the infection rates for beginners. And it is a mind changer. – Philm Jul 12 '13 at 21:13
0

try an additional condition to check that the product is not being upgraded

<Custom Action='BeforeUninstall01' After='InstallInitialize'>Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
Riv
  • 1,849
  • 1
  • 16
  • 16
  • You know I just tried it and it not only starts up the `Calc.exe` but it does so even before the UAC elevation prompt comes up. Maybe I should be calling it some time later than `InstallInitialize`? – ahmd0 Jul 11 '13 at 07:35