Wednesday, October 17, 2012

How to force GUI uninstall using ARPNOREMOVE property?

 

Background:

Whenever you install a product using its .msi installer, it shows itself in Add/Remove programs list. To modify it, you mostly have 3 options:

  1. Uninstall
  2. Change
  3. Repair
 

AddRemoveProgramsOptions
(Screen shot from Add Remove applet)

If you choose “Uninstall” from this menu, it would simply shows you the Windows Installer progress bar dialog and may remove your product during that i.e. no user input required. Here is the progress bar dialog you may have observed:

NoGUI_Uninstall

And if you choose option 3 i.e. Repair, same progress bar dialog would be presented while reinstalling your product.

In these two options, certain things are absent which are normally available in GUI mode. For instance, custom actions scheduled in InstallUISequence. Custom actions to be executed through Button click events in any dialog, properties defined during UI mode, custom dialogs etc. That is, this would simply skip all these. However custom action scheduled in InstallExecuteSequence may execute here.

However, if you choose option 2 i.e. Change, (Or double click on the program in Add/Remove list) you are presented with a Windows Installer Maintenance Dialog box which leads you to this option dialog:

image

Now again, you have 3 same options, but this time, there would be full GUI mode unlike only progress bar dialog.

Change will let you modify your installer product i.e. you may add/remove features etc.
Repair would simply reinstall the product with the options used to install it last time.
Remove would allow you to uninstall the product, but unlike simple progress bar mode, you will have a full GUI mode with progress bar, buttons etc.

 

Problem:

I got a situation in which I was asked to prevent the users from uninstalling the product using progress bar mode. And I also faced another problem with another product that whenever I was uninstalling using progress bar mode, it only shows that it is removing the product, but was doing nothing. However problems where not there whenever we used GUI mode.

So initially I defined ARPNOREMOVE like:

<Property Id="ARPNOREMOVE" Value="1" />

But this would actually remove the option to Uninstall your product from both Control Panel and its Maintenance Dialog box as shown in following shots:







image image

The only option left to remove this product was from original msi. It will always giving you the option Uninstall in its context menu, even if ARPNOREMOVE is define, as shown in this shot:
image


And this would remove product using progress bar mode.


But this has solved nothing. We want our users to use GUI mode and should be able to uninstall properly.


So to achieve this, I had to redefine Maintenance Dialog while enabling the Remove button forcefully using Condition element so that users would only be able to remove the product from Maintenance Dialog by clicking Remove button.




Maintenance Dialog Redefined



  1. <Dialog Id="My_MaintenanceTypeDlg" Width="370" Height="270" Title="!(loc.MaintenanceTypeDlg_Title)">

  2.         <Control Id="ChangeButton" Type="PushButton" X="40" Y="65" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgChangeButtonTooltip)" Default="yes" Text="!(loc.MaintenanceTypeDlgChangeButton)">

  3.           <Publish Property="WixUI_InstallMode" Value="Change">1</Publish>

  4.           <Condition Action="disable">ARPNOMODIFY</Condition>

  5.         </Control>

  6.         <Control Id="ChangeText" Type="Text" X="60" Y="85" Width="280" Height="20" Text="!(loc.MaintenanceTypeDlgChangeText)">

  7.           <Condition Action="hide">ARPNOMODIFY</Condition>

  8.         </Control>

  9.         <Control Id="ChangeDisabledText" Type="Text" X="60" Y="85" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgChangeDisabledText)" Hidden="yes">

  10.           <Condition Action="show">ARPNOMODIFY</Condition>

  11.         </Control>

  12.         <Control Id="RepairButton" Type="PushButton" X="40" Y="118" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgRepairButtonTooltip)" Text="!(loc.MaintenanceTypeDlgRepairButton)">

  13.           <Publish Property="WixUI_InstallMode" Value="Repair">1</Publish>

  14.           <Condition Action="disable">ARPNOREPAIR</Condition>

  15.         </Control>

  16.         <Control Id="RepairText" Type="Text" X="60" Y="138" Width="280" Height="30" Text="!(loc.MaintenanceTypeDlgRepairText)">

  17.           <Condition Action="hide">ARPNOREPAIR</Condition>

  18.         </Control>

  19.         <Control Id="RepairDisabledText" Type="Text" X="60" Y="138" Width="280" Height="30" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRepairDisabledText)" Hidden="yes">

  20.           <Condition Action="show">ARPNOREPAIR</Condition>

  21.         </Control>

  22.         <Control Id="RemoveButton" Type="PushButton" X="40" Y="171" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgRemoveButtonTooltip)" Text="!(loc.MaintenanceTypeDlgRemoveButton)">

  23.           <Publish Property="WixUI_InstallMode" Value="Remove">1</Publish>

  24.         </Control>

  25.         <Control Id="RemoveText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveText)">

  26.         </Control>

  27.         <Control Id="RemoveDisabledText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveDisabledText)" Hidden="yes">

  28.           <Condition Action="hide">ARPNOREMOVE</Condition>

  29.         </Control>

  30.         <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />

  31.         <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUINext)" />

  32.         <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">

  33.           <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>

  34.         </Control>

  35.         <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.MaintenanceTypeDlgBannerBitmap)" />

  36.         <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />

  37.         <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />

  38.         <Control Id="Title" Type="Text" X="15" Y="6" Width="340" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgTitle)" />

  39.         <Control Id="Description" Type="Text" X="25" Y="23" Width="340" Height="20" Transparent="yes" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgDescription)" />

  40.       </Dialog>





Please note that following are the original lines which are modified in above code segment:


<Control Id="RemoveButton" Type="PushButton" X="40" Y="171" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgRemoveButtonTooltip)" Text="!(loc.MaintenanceTypeDlgRemoveButton)">
<Publish Property="WixUI_InstallMode" Value="Remove">1</Publish>
<Condition Action="disable">ARPNOREMOVE</Condition>
</Control>
<Control Id="RemoveText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveText)">
<Condition Action="hide">ARPNOREMOVE</Condition>
</Control>
<Control Id="RemoveDisabledText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveDisabledText)" Hidden="yes">
<Condition Action="show">ARPNOREMOVE</Condition>
</Control>


That is, RemoveButton and RemoveText controls are no more bound to APRNOREMOVE property, so these would be active in any case, while RemoveDisabledText would be hidden if ARPNOREMOVE property is present.


Now that would result in forcing the user to be only able to remove product using our Maintenance Dialog –> Remove button which makes sure that we would have InstallUISequence and any other UI related thingies available.


TIP: From above dialog code, you can also extract the use of ARPNOREPAIR and ARPNOMODIFY properties in different ways according to your requirements.

3 comments:

Alek Davis said...

Hmmm. Interesting. Thanks for posting. Although, I'm not sure I understand your solution. Are you saying that Add/Remove Programs now do not allow you to uninstall (directly) and will only give you an option to change and repair (and you can uninstall from the maintenance dialog)? If this is correct, wouldn't it confuse users (i.e. not seeing and option to uninstall from ARP)? Or did I misunderstand?

Farrukh said...

Alek, sorry for such a late response. I was actually too much busy and not getting chance to see blogs.

So basically you are right, this may confuse users. But I had to do this in a very specific situation, due to some specific requirements.

Chris said...

Thank you, been searching for this solution for a long time.