Uninstalling MSI-Package always gives reboot messa

2019-08-17 04:22发布

问题:

I'm just scripting something for some new software. Therefore i have to remove some older software. I'm doing this whit a PS script. This works almost fine. The elevated rights are working, the software got removed, but there is always a message from the first uninstalling concerning a reboot. This message must only be acknowledged by pressing 'OK'.

Now how can I force the message not coming up?

This is what I have in the PS-Script:

start-process msiexec.exe -Wait -ArgumentList '/x "file.msi" /passive /norestart'

I tried also the /quiet flag, but than i see nothing (d'uh) and the software is still there.

That i have to do a restart is logical, but i just don't want to see the message. I'm not sure if it is a message from MSIEXEC or from the MSI-file itself. Google could not find a thing.

回答1:

There are perhaps two issues here:

  1. That message does not seem to be the standard Windows Installer request for a reboot. That implies it's coming from a custom action, so it's just some code that doesn't care about any MSI options to control reboots. If you didn't author the MSI and you can't change it then there isn't anything you can do about it.

  2. If you do a silent uninstall of an installed product then you won't get told if it fails, because silent means no UI. In particular, if the uninstall requires elevation then it will fail if admin privilege is required anywhere. On the other hand it might be failing for some other reason. What happens if you try the uninstall normally with some UI or progress bar showing? Also, if you add logging to your uniunstall you may see an error. Add something like /l*vx [path to log file]



回答2:

UPDATE: After the fact thought: check if this dialog is actually showing from the InstallUISequence? If it is only inserted into the InstallUISequence and not present in the InstallExecuteSequence, then it will never run when you trigger silent install / uninstall for your setup since the whole InstallUISequence is skipped in this installation mode.

Add QN to the msiexec.exe command line to run silently. See samples below. If you get lucky, this could solve your whole problem without any of the minor upgrades or hacks mentioned below at all. So check it. Do add REBOOT=ReallySuppress to all silent msiexec.exe command lines as well - to prevent standard MSI features from triggering a sudden reboot.


REBOOT=ReallySuppress

There is a property called REBOOT in MSI files that can be set to REBOOT=ReallySuppress on the msiexec.exe command line in order to suppress standard MSI scheduled reboots. This is especially important for silent installs which could otherwise trigger a spontaneous reboot without warning (for very badly authored MSI files with design errors - I always disable ScheduleReboot and ForceReboot when making packages for corporate use - rebooting knowledge workers' PCs without warning can wipe out a lot of work):

msiexec.exe /x {11111111-1111-1111-1111-1111111111GH} REBOOT=ReallySuppress

I would also use the old-style msiexec.exe switches: /x, /i, etc... rather than the "convenience functions": /passive, /norestart, etc... Just my two cents.

For reference:

  • The msiexec.exe command line
  • Technet version of the msiexec.exe command line

Non-standard Reboot

However, the dialog you posted seems non-standard. It actually looks like a custom dialog shown from a custom action in the setup itself - these will conform to no MSI standard and you must deal with them on a case-by-case basis.

I guess it is a safe assumption that the software you seek to uninstall is already "in the wild" and installed on a number of workstations that you are trying to remove it from? Given that this is a correct assumption, how many computers are we talking about for cleanup? Dozens? Hundreds? Thousands? If the MSI was not live, you could have tuned it to remove the dialog with a transform before deployment. Just mentioning this for the future. Technically you can do this by setting the condition for the custom action to 0 (for false - then the custom action will never run).


Digression: There is a terrible "hack" that I have never tried that I would only use for a single computer cleanup as a support person: How to apply a Msi transform at uninstall? I wouldn't use it (unless I was cleaning up a single PC - such as a dev box).

It is also possible to directly edit the cached MSI (at least make a backup first): I screwed up, how can I uninstall my program?. This is not recommended either, unless you just have a couple of boxes to manually clean up. Also, set the custom action to not run by modifying its condition, do not delete the custom action.


Cleanup

So I guess we are left with a couple of options:

  1. Find a PUBLIC property used in the custom action condition that we can hard code to 0 or undefine (set it equal to an empty string) on the msiexec.exe command line preventing the custom action from running. Note that there might not be a condition to override on the command line - it depends on the setup design.

    • This might work, but isn't super reliable if that property is set inside the MSI itself as well via AppSearch, custom actions or some other mechanism (MSI features a lot of "moving parts" that interact in ways that can make a grown man cry). This could happen intermittently as well - in other words it might not happen on all systems. Suddenly it just blows up. Apply suitable paranoia accordingly. Just my two cents.
    • The property can also have been made by the vendor to specifically allow the reboot dialog to be suppressed - in this case you can use it, but I would be very surprised if a vendor has done this. Showing such a custom reboot dialog shows lacking knowledge of how MSI should be used. But see the last bullet point on ISSCHEDULEREBOOT below...
    • The condition must evaluate to false. So if DOREBOOT is specified as a condition it has to be set equal to an empty string on the command line. This "undefines" it - the property doesn't exist in the MSI session. In other words setting DOREBOOT=0 will not yield a false condition! (if the custom action is conditioned with only DOREBOOT - then there is no value specified in the condition and the check is for whether the property has a value at all. Any assigned value will evaluate the condition to true):

      msiexec.exe /x {11111111-1111-1111-1111-1111111111GH} DOREBOOT=""
      
    • If DOREBOOT=1 is specified as condition for the custom action, then you must set the property value equal to something else (to make the condition evaluate to false):

      msiexec.exe /x {11111111-1111-1111-1111-1111111111GH} DOREBOOT=0
      
    • And in a twist of further complexity - as if the above isn't enough - the DOREBOOT property must be listed as a "Secure" property by adding it to the list of such secure custom properties. The property which holds this list is referred to as SecureCustomProperties. It is a delimited list of properties allowed to pass to deferred installation mode for non-admin users. Failing to add the DOREBOOT property to this list will cause this log file warning: "Ignoring disallowed property DOREBOOT" - and any value you set to it will be ignored and have no effect. And the reboot will still happen if it is scheduled to.
    • UPDATE: In another interesting twist it looks like an msiexec.exe session launched from an elevated command prompt actually does accept property values specified on the command line even if they are not set as secured (given that you are running as admin), but when you launch from a non-elevated command prompt and then elevate via the UAC dialog - then the properties are flushed and will not apply in the session. Bug or feature? I don't know which - certainly unexpected and "high astonishment factor". Windows Installer system policies such as EnableUserControl may also come into play here with unpredictable results. There is a smell of real MSI bugs here.
    • So Update SecureCustomProperties to include DOREBOOT (or equivalent property). This is done in the Property table.
    • In conclusion: for all practical purposes this option is generally only an option "in theory". As the saying goes: in theory there is no difference between theory and practice, but in practice there is. However...
    • Installshield setups seem to have a property called ISSCHEDULEREBOOT that conditions the ScheduleReboot custom action. It might be possible to use this property to suppress a reboot - in Installshield-made setups (only), because it will also be marked as secure automatically.
    • If you deploy via a deployment system (SCCM) the deploying user probably has all these required privileges and permissions and the problems above may not surface, but this may be different in smaller companies with more haphazard deployment.
    • Way too many digressions, let's get to the option that should work.
  2. Create a minor upgrade for the problem MSI, apply it either as a minor upgrade or as a minor upgrade patch package (smaller file size, more complex to make). Then call the uninstall for the package as before with the command line seen above (with REBOOT=ReallySuppress set as well).

    • This will "hotfix" the cached MSI database on the system before its uninstall sequence is called. This means we can disable the custom action in question (and generally we can override whatever we want in the cache MSI).
    • I have used this approach many times and it is the only one I would recommend for any deployment of some scope (more than a few machines).

Implementation

For large scale deployment I would choose option 2 - although it is very much a hassle. To get going, get hold of a tool capable of viewing an MSI file and open the MSI in question. Look in the Custom Action table for any custom action that could be related to such a dialog. Maybe it says "ShowRebootDialog" or similar.

Once you find a candidate custom action, locate it in the InstallExecuteSequence table (I'd check in the InstallUISequence as well - just in case you run with minimal GUI and not suppressed GUI). Does the custom action have an associated condition? If so you might be able to set such a condition by command line to always be false, as explained above in option 1 - in which case the custom action will never run. But as explained: only reliable if the property in question isn't set inside the setup itself.

A minor upgrade needs a new package GUID and a incremented version number (generally the third version number). Set this in your MSI and save it - use the same file name as before. If you are using Orca you can do this by going View => Summary Information... => New GUID for PackageCode, and then you tweak the ProductVersion property in the Property table.

  1. In your cleanup package / script you must now first install the minor upgrade (some help material) - this command runs interactively for your testing (add /QN for silent mode):

      msiexec.exe /i YourSetup.msi REINSTALLMODE=vomus REINSTALL=ALL
    
  2. Once the minor upgrade is successfully installed you can call the uninstall sequence the normal way. Puh! Provided the minor upgrade ran properly you should finally have successfully suppressed the reboot dialog - this command runs interactively for your testing (add /QN for silent mode).

      msiexec.exe /x {11111111-1111-1111-1111-1111111111GH} REBOOT=ReallySuppress
    

There might be a simpler way that I haven't thought of. The TRANSFORMS hack linked to above should work - but it is too ugly for me to try. Such hacks tend to break when you least expect it (after Windows Updates for example - new restrictions and features close the loophole). Your risk. When you write directly to the registry in locations that are actually protected and "implementation details" (the internals of the engine), then all bets are off as to what might happen (different behavior may result on different OS versions for example, and updates can lock things to name but two things - you also need a package to deploy the transform itself, hack the registry and then to kick off the uninstall - generally more complex and vulnerable than using built-in MSI features. Go via APIs, don't go direct).

Since the UAC prompt is hidden when running in silent mode (elevation fails without warning - except in the log file - if created), you must use admin rights when running these command lines quietly. Add /QN to each command line. To add logging, add /L*V "C:\msilog.log". Quick sample (remember the admin rights):

msiexec.exe /x {11111111-1111-1111-1111-1111111111GH} /QN /L*V "C:\msilog.log" REBOOT=ReallySuppress

Alternatively, enable logging for all MSI installations. See installsite.org on logging (section "Globally for all setups on a machine") for how to do this. I prefer this default logging switched on for dev-boxes, but it does affect installation performance and adds a lot of log files to the temp folder (that you can just zap once in a while). Typically you suddenly see an MSI error and you wish you had a log - now you can, always ready in %tmp%.