Running a program on uninstall - wix

I want to run a program when my software is being un-installed, it is a simple form that should gather some feedback on why people are un-installing my software.
I found some WiX examples that works to some degree.
It works pretty fine with a standard windows program (notepad), but when I try to run my own program, it does not work. I think the problems is that the program is removed, before it has been run.
I have tried to print the logs, but they did not give me any clues of what to do.
My code so far:
<CustomAction Id="LaunchFeedBackForm"
ExeCommand="notepad.exe" Directory="INSTALLDIR"
Return="asyncWait" >REMOVE="ALL"</CustomAction>
<InstallExecuteSequence>
<Custom Action="LaunchFeedBackForm" After="InstallValidate"/>
</InstallExecuteSequence>
So I need in some way the un-install process to halt or what ever, until the user has closed the feedback form. After the form has been closed, it should continue and remove all software including the feedback form program.

The way you'd like to get user's feedback seems not that natural to me. If I understand you correctly, you'd like to show this feedback form and wait while a user fills it in, and later on continue with uninstallation, right?
To my own experience, when a user decides to uninstall software, he/she would like it to get uninstalled as quickly and clear as possible. Bringing a "must fill" form in front of them would only negatively affect the user experience. Moreover, as you can see, it is more difficult from the technical point of view. I suppose you've also thought about passing this feedback on to your side, right? Is it emailing the info entered by user? How do you ensure the email gets sent?
Alternatively, you can have this form online on a certain web page of your site and start it when the uninstall is done (NOT in progress). In this way, you don't annoy the user blocking the uninstall process.
So, I would do the following:
have a custom action that starts a browser with a URL you need
the installation program SHOULD NOT WAIT for this to complete
BTW, do not expect lots of feedback - people rarely bother spending some time to give feedback :)

If your EXE is in the MSI, try using the FileKey attribute: http://wix.sourceforge.net/manual-wix2/wix_xsd_customaction.htm
For example, if your EXE is defined like this:
<File Id="FeedbackExe" Name="FeedbackExe.exe"/>
you can use:
<CustomAction Id="LaunchFeedBackForm"
FileKey="FeedbackExe" Execute="deferred"
Return="asyncWait">REMOVE="ALL"</CustomAction>
You can also try creating an uninstall log to see what happens with the custom action:
msiexec.exe /x <ProductCode> /L*V "C:\uninstall.log"
where you use your actual ProductCode.

So I need in some way the un-install process to halt or what ever,
until the user has closed the feedback form. After the form has been
closed, it should continue and remove all software including the
feedback form program.
To do this one would set After="InstallValidate"
<InstallExecuteSequence>
<Custom Action="InstallCustomLogic" After="InstallFinalize" />
<Custom Action="UninstallCustomLogic"
After="InstallValidate" >
NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>
Set Return attribute to check so that the installer will halt until the specified executable returns.
<CustomAction Id="UninstallCustomLogic"
Directory="INSTALLFOLDER"
ExeCommand="[INSTALLFOLDER]\RetailConnectCustomLogic.exe uninstall"
Return="check"
/>
This is useful in the event where you need to run something located in the program directory before uninstalling the program. One could also set the attribute to ignore if it doesn't return 0

Related

Invoking Data Protection API from within a CustomAction

I am trying to run some unmanaged code from a C# ca within WIX. I have a wrapper around the CryptProtectData function, this code works fine if run from an admin prompt. I have scheduled the ca as deferred hoping it would run during the elevated session however it fails in this context.
The scheduling is as follows:
<CustomAction Id="SetCustomActionDataValue" Property="EncryptValues" Value="foo=bar;...;" />
<CustomAction Id="EncryptValues" BinaryKey="InstallerCustomAction.CA.dll" DllEntry="EncryptValues" Execute="deferred" Impersonate="no" HideTarget="no" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetCustomActionDataValue" After="InstallFiles" />
<Custom Action="EncryptValues" After="SetCustomActionDataValue" />
</InstallExecuteSequence>
What needs to be done to allow something like this to run during the installer, preferably after the files and registry entries are committed to the system?
Do you have any logging in your CA? Can you confirm that your CustomActionData made it over?
One thing you might try is to wire this up as a simple console app and use PSExec to invoke a cmd.exe prompt as SYSTEM.
I've never used this API but the comment "Typically, only a user with the same logon credential as the user who encrypted the data can decrypt the data." makes me think that this might not run well as SYSTEM. This article seems to confirm that. http://www.obviex.com/samples/dpapi.aspx
FWIW, I'm not sure what you are trying to achieve but I'd consider moving it from the installer to the application if possible. Installers are generally best kept simple and reliable.

Wix: Executing installed exe as a first action on Uninstall

In my msi, I have to call the installed exe file with some parameters, as a very first action of uninstall. Here is the code:
<CustomAction Id="UnRegisterOnUninstallApplication"
Execute="immediate"
FileKey="MyProgram.exe" ExeCommand="/unregister" Return="asyncNoWait" />
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
<Custom Action="UnRegisterOnUninstallApplication" Before="UnpublishComponents" >Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
</InstallExecuteSequence>
Although, it is scheduled Before UnpublishComponents, but issue is, at some computers, custom action is called during or after dependency dlls are uninstalled and so MyProgram.exe crashes here.
This is something seems unpredictable...
Can anyone please guide, if I'm missing something or doing something wrong???
Thanks a bunch.
There are several things incorrect here:
The design issue is that you shouldn't be running code to register or unregister. The recommended way is to capture the registry entries and add them as registry entries in the same component as that binary. Then it all just works.
Your custom action is asyncNoWait, and that means the uninstall continues while the program runs. If it takes a long time for some reason, or simply doesn't get enough of the processor, then yes, files may have been removed by the time it runs.
It's an immediate custom action, so it can run and start unregistering as the uninstall proceeds. However if the uninstall fails and rolls back the deleted registration will not be restored, so you'll end up with a broken product still installed, those registration entries will stay removed. It should be a deferred custom action and return = ignore or check, depending on whether you care if the program fails.
I might schedule the action before 'RemoveFiles' so that no dependent dlls have been removed before your custom action gets to run.

PerUser install with Custom Action - UAC disabled

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.

How to prompt user for active processes and terminate them during uninstall

I am using Wix 3.5 with such codes:
<util:CloseApplication Id="CloseMyApp" CloseMessage="yes" Target="Foo.exe" RebootPrompt="no"/>
<InstallExecuteSequence>
<Custom Action='WixCloseApplications' Before='InstallValidate' />
</InstallExecuteSequence>
It does really terminate the applications forcefully and silently before uninstall actually happens.
However, I want to know if it is possible to prompt users before such termination happens with a message box, and proceed termination if allowed?
Thanks
I think you are missing some references
Have a look at this question

WiX - trying to figure out install sequences

I'm installing a large app, and part of it is a custom written tool called "DbUpdateManager" to mass execute SQL scripts against our target database.
Right now, the WiX 2.x install works - but it has one flaw: during install, I also install a couple of Windows services, which can be optionally started right away. Those however will fail, if the DbUpdateManager hasn't been run yet.
So what I'm trying to accomplish is this:
Install DbUpdateManager and my services from my MSI
Run DbUpdateManager BEFORE any of the services start up
My current WiX source looks something like this:
<Directory Id='INSTALLDIR' Name='DbUpdMgr' LongName='DbUpdateManager' >
<!-- DbUpdateManager component with the necessary files -->
<Component Id='DbUpdateManagerComponent' Guid='...' DiskId='1'>
<File Id='DbUpdateManagerFile' LongName='DbUpdateManager.Wizard.exe'
Name='DbUmWz.exe' src='DbUpdateManager.Wizard.exe' KeyPath='no' />
</Component>
<!-- Component to install one of my Windows services -->
<Component Id='InstallServiceComponent' Guid='...' DiskId='1'>
<File Id='InstallServiceFile' LongName='MyService.exe'
Name='MyServic.exe' src='MyService.exe' KeyPath='yes'/>
<ServiceInstall Id='InstallMyService' Name='MyService'
Description='My Service' ErrorControl='normal'
Start='auto' Type='ownProcess' Vital='yes' />
<ServiceControl Id='UninstallMyService' Name='MyService'
Remove='uninstall' Wait='yes' />
</Component>
<!-- Feature for the DbUpdateManager referencing the above component -->
<Feature Id='DbUpdateManager' ConfigurableDirectory='INSTALLDIR'
AllowAdvertise='no' Description='DbUpdateManager' Level='1'
Title='Database Update Manager'>
<ComponentRef Id='DbUpdateManagerComponent'/>
</Feature>
<!-- Custom action for running DbUpdateManager -->
<CustomAction Id='RunDbUpdateManagerAction' FileKey='DbUpdateManagerFile'
ExeCommand='' Return='asyncWait' />
<!-- Calling the custom action in the install sequence -->
<InstallExecuteSequence>
<RemoveExistingProducts After='InstallInitialize' />
<Custom Action='RunDbUpdateManagerAction'
After='InstallFinalize'>&DbUpdateManager=3</Custom>
I inherited this WIX, and it works - but as I said - the DbUpdateManager gets called too late in the process (only "After=InstallFinalize") and thus the services will fail to start up properly at first (the run fine the second time around when you restart them manually after DbUpdateManager has run).
I poked around the MSI documentation a bit and found a nice step called "StartServices", so my hunch was to just change my calling the custom action to this:
<InstallExecuteSequence>
<Custom Action='RunDbUpdateManagerAction'
Before='StartServices'>&DbUpdateManager=3</Custom>
Unfortunately, in this case, nothing at all happens - DbUpdateManager NEVER gets called....
Any ideas why? Debugging the MSI/WiX stuff is really really tricky, and I can't seem to see the forest for the trees anymore....
Thanks!
Marc
EDIT: The "RunDbUpdateManagerAction" is placed in the right position in the InstallExecuteSequence table in my MSI - right AFTER InstallServices and just BEFORE StartServices - and yet it doesn't work.... DbUpdateManager (a Winforms utility) does not show up during installation :-(
EDIT 2: now my action appears to be executed and at the right time - unfortunately, I'm just not seeing my wizard :-( What I'm seeing is an error code "return value 1631" which means something like "MSI Service could not be started" - wtf ???
MSI (s) (2C:D8) [20:53:36:383]: Doing action: RunDbUpdateManagerAction
Action 20:53:36: RunDbUpdateManagerAction.
Action started at 20:53:36: RunDbUpdateManagerAction.
MSI (s) (2C:D8) [20:53:36:383]: Doing action: StartServices
Action 20:53:36: StartServices. Services are being started
Action started at 20:53:36: StartServices.
Action finished at 20:53:36: RunDbUpdateManagerAction. Return value 1631.
Well, I finally got it working - with a bit of help from everyone who responded, and by consulting some of the WiX tutorials and help pages out there on the web. MSI installer stuff isn't easy to figure out and learn......
Basically, I changed execution of my custom action to "deferred" (as suggested by Rob) and I moved the point in the sequence where it gets executed to "After=InstallFiles". I also changed the condition in the <Custom> tag to "NOT Installed" which seems to work just fine in my scenario.
Contrary to Rob's fear, the Db Update Manager and its UI come up quite nicely this way, and the process of updating our database is now completed before any of our services (that depend on the database) get started.
Looking forward to a full RTM release of WiX 3.0 (and its future) !
Thanks to everyone - unfortunately, I could only accept one answer - all would have deserved it.
Marc
It appears that your CustomAction depends on the 'DbUpdateManagerFile' being installed. That means that your CustomAction needs to be scheduled after InstallFiles executes. Remember there are two passes to the InstallExecuteSequence. First, the "immediate" (or "scheduled" or "script generation") actions are executed to build the transaction log (aka: "install script"). Second, the "deferred" actions in the transaction log are executed.
Right now your CustomAction is "immediate" (the default) so it is trying to run before the files are actually copied to the machine. The InstallFiles action is in the script before your CustomAction but it hasn't been executed yet.
So, you need to mark your CustomAction "deferred" to get it to run after your files are installed.
Note: you are not probably not able to show UI from a deferred CA. I wasn't sure if this tool of yours was expected to show UI.
PS: sorry if I didn't explain that well, it's been a long day.
Try getting a log file of the Installation, and look for the sequence order in there and the value of the condition to perform the Custom Action
Use this in the command line:
msiexec /i [msiname] /l*v [filename]
EDIT: After reading your comment have a look at this page here you could try to add NOT INSTALLED in the condition
EDIT2: I found this page Search for your error Number 1631
You can open the .msi in Orca and look at the InstallExecuteSequence table to see what order things are actually happening in. This may give you a good idea of what's actually happening when.