How to uninstall MSI using its Product Code in c# - c#

I know We can uninstall a windows application using its MSI by passing command arguments as follows,
Process p = new Process();
p.StartInfo.FileName = "msiexec.exe";
p.StartInfo.Arguments = "/x \"C:\\MyApplication.msi\"/qn";
p.Start();
But what i want to know is how can we uninstall the application without using MSI ? In the above scenario I should have the MSI in the specific location to uninstall this application, If i could unstaill using product code then I dont need to have the MSI in the target machine.

According to MSDN, You can uninstall it using the product code:
msiexec.exe /x {your-product-code-guid}
When you use the product code, it uses the cached MSI from C:\WINDOWS\Installer.

Along the lines of PhilmE's answer, Windows Installer XML (WiX) ships the Microsoft.Deployment.WindowsInstaller interop library as part of Deployment Tools Foundation (DTF). This skips the COM interop and encapsulates the Win32 API instead.
using Microsoft.Deployment.WindowsInstaller;
public static void Uninstall( string productCode)
{
Installer.ConfigureProduct(productCode, 0, InstallState.Absent, #"REBOOT=""R"" /l*v uninstall.log");
}

Probably for your case, knowing the "/x" Parameter was sufficient. Two remarks on that:
More secure is adding a "REBOOT=R" part to your commandline. And you can add a logfile path:
msiexec /x "..." /qn REBOOT=R /L*v "c:\mylogdir\mymsi.log"
Second, don't try to change anything to "the caching". You don't need even to understand it. If the cached package would be broken, a regular uninstallation is no longer possible, which could bring the computer in a "support needed" state.
Because your question was originally talking about C# .. You don't have to use msiexec for it:
a) Use the original C/C++ API with the function MsiInstallProduct() or MsiConfigureProduct().
MSDN ref:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa370315(v=vs.85).aspx
You have to use interop to use that in C#.
or b)
Use the Windows Installer Object.
For example, this related case was already answered here in stackoverflow:
Programmatically installing MSI packages
But using this function needs the physical package, also for uninstallation.
With a slight indirection, here is the better code for uninstallation:
First, add a reference to COM object "Microsoft Windows Installer Object Library" to your project.
using WindowsInstaller;
public static class MyMsiLib
{
public static void Uninstall(string productCode)
{
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installer = (Installer)Activator.CreateInstance(type);
installer.UILevel=msiUILevelNone;
installer.ConfigureProduct(productCode, 0, msiInstallStateAbsent);
}
}
The UILevel property before is set here hardcoded to determine the UI level silent as you seem to want. Same for other properties. See MSDN documentation e.g. mentioned in the link above.
Of course "real programmers" work with the original API instead of the "Installer Object" :-)
But for small purposes it is sufficient. And easier.

This command works on the command line:
msiexec /x {3A40307D-6DF2-4412-842F-B1D848043367} /quiet
I haven't tried it in C#, but replacing your arguments with the key shown above should work. You can find the GUID in the registry key for the app you are trying to uninstall.

Related

Microsoft Visual Studio Installer Projects - how to provide restart prompt to MSI executed with Process.Start(), but without Repair option

The problem is following: I have my custom uninstaller called before MSI uninstall. After shutting down my application properly it calls msiexec to use Windows Installer to uninstall MSI.
It's done by executing something like "msiexec /x{PRODUCT_CODE} /promptrestart".
And here is important thing - if the system is not restarted after uninstallation, and then the user installs the app again, some of its files will be deleted after next restart, so it's not acceptable. The restart is required, however, I need prompt, automatic and unconditional restart is evil and should never ever be used.
So, the invocation above displays STUPID "uninstall / repair" dialog. I do not want it. When I use "msiexec /x{PRODUCT_CODE} /qr /promptrestart" - then it uninstalls nicely, however it refuses propt for restart afterwards.
I have read about setting ARPNOREPAIR property.
But the idiots who gave that answer wouldn't care to say WHERE and HOW that property could be set. Even... Where the property belongs, it's the property of what? MSI file?
Then, maybe is it another way to achieve this, like invoke the prompt for restart from my code, but... how? The uninstaller should remove all my files until that moment. Maybe it's possible to execute a kind of script after the uninstallation process is complete?
(One more problem, the Windows Installer doesn't delete ALL files and directories created by my app. It would be nice if I could execute some code to clean up better.)
UPDATE
I see 2 paths ahead: make a script to be run once the uninstallation ends (like using Registry or Task Scheduler or IDK), use Win32 API to modify MSI file, because AFAIK it's possible to change its properties that way.
Questions: Some questions first.
Restart Manager: Are you familiar with the Restart Manager feature of MSI? Intended to help shut down and restart
applications without the need for reboots. I would give it a quick
skim? I think this is your real solution?
Alternative MSI Tools: There are many tools available for creating MSI setups. This link also contains further links to a summary of the shortcomings of Visual Studio Installer Projects.
Using the free, open-source WiX toolset - for example - you can change MSI dialogs. Github sample. SO question 1. SO question 2. And here is the official WiX toolset site.
Adding my own answer from SO: Changing text color to Wix dialogs (please do skim)
I am not familiar with how to change dialogs in Visual Studio Installer Projects. Commercial products Advanced Installer and Installshield can certainly change dialogs.
Services: What is the nature of the product you are installing? Does it has a lot of services for example? Services can be shut down and restarted via MSI tables if you use a proper tool to build the MSI.
REINSTALLMODE: Do you use a custom REINSTALLMODE for your setup? (some settings can cause more reboot prompts).
Custom Uninstaller: How do you invoke that custom uninstaller of yours? Manually or do you register an uninstall command line with Add / Remove Programs? (this latter approach is not recommended).
ARP Applet vs MSI Dialogs: The ARPNOREPAIR property is set in the MSI itself - in the property table. It affects only what is seen in Windows' Add / Remove Programs applet (ARP = Add / Remove Programs), and not what you see when your MSI is invoked via command line. Then you see the dialogs defined in that MSI itself (which can be changed - not entirely trivial to do).
ARP / Add Remove Programs Applet: A quick review of this applet below:
Hold Windows Key and Tap R. Type: appwiz.cpl and press Enter. This opens the Add /Remove Programs Applet.
Select the different package entries in the list to see different settings for ARPNOREPAIR, ARPNOMODIFY, etc...
If ARPNOREPAIR is set in the MSI's property table then the Repair entry is missing.
If ARPNOMODIFY is set in the MSI's property table then the Change entry is missing.
If ARPNOREMOVE is set in the MSI's property table then the Remove entry is missing.
If the special ARPSYSTEMCOMPONENT property is set, then the MSI will be missing from ARP altogether.
Links:
In-use files not updated by MSI-installer (Visual Studio Installer project)
So, there is an "ugly hack" which solves the exact problem:
First - we need an executable, that isn't affected by the installer. This one is easy, we just copy that one installed exe to a TEMP directory and run it from there.
The next step is to that file must wait unit the uninstall phase is done. There are a couple of ways of doing so. You can observe the installer process, you can observe the file system if main program file is deleted. Considering the pace of common installer operations, polling once a second is a good enough option.
The next step is optional - you remove remaining files created by application, empty directories and such.
The next step is reboot itself, MessageBox.Show() from PresentationFramework is fine to ask user, when user answer OK or YES, then reboot itself can be performed in many ways, I use ExitWindowsEx() from user32.dll since it's probably what msiexec calls internally.
Here's example code:
if (MessageBox.Show(RestartPromptMsg, "", MessageBoxButton.OKCancel, MessageBoxImage.Exclamation) == MessageBoxResult.OK) {
NativeMethods.ExitWindowsEx(
NativeMethods.Flags.Reboot,
NativeMethods.Reason.MajorApplication | NativeMethods.Reason.MinorInstallation | NativeMethods.Reason.FlagPlanned
);
}
Of course some parameters could be passed to our special clean up executable, so it could do some extra things, like skip the restart prompt if it's not really required.
The last step is to delete our executable itself. It's easy, but it's tricky. Again I hope my example code would help:
var cleanUpTempPath = Path.Combine(Path.GetTempPath(), CleanUpExe);
File.Copy(CleanUpPath, cleanUpTempPath, overwrite: true);
Process.Start(new ProcessStartInfo {
FileName = "cmd",
Arguments = $"/c (\"{cleanUpTempPath}\" -purge \"{InstallerDir}\") & (del \"{cleanUpTempPath}\")",
UseShellExecute = false,
CreateNoWindow = true
});
We use cmd.exe special feature, the power of & and (). Commands separated with & gets executed when previous command exits. So when our clen up exe completes, it's gets deleted by the same cmd instance which called it. Remember to quote all paths, they can contain spaces. Remember to enclose a command with arguments in parentheses, because otherwise the & operator would be seen as a parameter to the previous command, not the cmd.exe.
I tested it in my big production application and it works as charm. The code examples don't work when just pasted, if you're looking for complete code, just google for it, there are plenty of working examples on pinvoke.net and StackOverflow.

Automate deployment of Excel AddIn

I have created an Excel Add-in using AddIn Express .Net component. Business users install the add-in using the MSI provided by the build team. Everytime we make any changes to the product and provide it to business users, they need to manually uninstall existing Add-in and then install new one with the updated MSI.
I wanted to know if there is any way this process can be automated using some windows batch file, scriptcs or a small C# console program. Ideally, it should uninstall existing Add-in, wait for uninstallion process to complete and then install new AddIn.
I tried multiple options using Msiexec, scriptcs etc, but without any success so far. My main problem is once the existing add-in uninstallion process starts, it immediately starts installing new Addin, which then pops up standard windows message that 'Installation is already in progress...'
Any help would be appreciated.
Thanks
I answered already a similiar question where it seemed to help:
Windows batch file does not wait for commands to complete
Normally, when you have a batch file with two lines:
call msiexec /x {...} /qb
call msiexec /i "c:\myPath\myProduct.msi" /qb
it should work in the sense that the uninstall waits before install starts.
The "call" is important !
For uninstalls of previous versions you have to use /x {ProductCode to fill in} instead of /x "filename" . In each case using the product code is safer.
To be sure what happens, you can add a pause line between the two and at the end.
If it still seems not to work you have to loop until the product is really uninstalled, wait two seconds and proceed then with an install.
There are several possibilities to find out, if a program is still installed.
Most people would recommend a VB script as the easiest solution, at least these are most known.
Here is a VBS snippet from saschabeaumont for an uninstall call from another question:
MSI Install Fails because "Another version of this product is already installed"
It mainly finds out the ProductCode of a given productname (part), and starts uninstall, if it fits (be careful about partial matches). You can use it for the same thing and additionally copy the algorithm a second time to ask asynchronously, if the uninstall has already been finished (= product is no longer in list of installed products).
Of course it is possible in other script languages, namely JScript, Powershell and traditional programming languages.
It is difficult to do in pure batch scripts- for example you could test the ProductCode registry entry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, if a product is installed. But only one disadvantage to mention: You have to consider the difference, if batch is started from 32/64 bit subsystem and/or MSI is 32/64 bit. Therefore I would advise instead using VBS instead of a batch (you can call it from the batch with cscript xxx.vbs).
A few things here:
First question is if you are aware of the xlstart folder in Excel which allows you to easily load certain addin files on Excel startup just by putting them into this folder. As far as I know you can't disable addins this way via the Excel GUI, you have to remove them from the xlstart folder to not load them into Excel.
Second issue is that you should update your MSI file to use a major upgrade so that it automatically removes the existing MSI whilst installing the new one. This would probably remove the whole problem that you describe.
Finally you should be able to use start wait msiexec.exe /i /qn File.msi to have your batch file wait for msiexec to return from the first msiexec call. Check Waiting for msiexec.exe to finish. Or you can try MSI Software Deployment Using Batch File.

Uninstall exe program in through cmd

I am writing an app to remove an old version of Qmuzik ERP software from a network and install the new version. I have used the Process methods in C# to execute the msi through a cmd command to install the new version. The command I use is:
msiexec /qn /i "MSI Path"
This works beautifully.
The reason I am doing this is because of the fact that there are more than one msi which has to be run in sequence. Using the process method I can watch the process and detect when it is done and execute the next msi in the sequence. All of this happens quietly on the users pc.
The problem is, before I can install the new version I have to uninstall the previous version. The old version was installed on to the machines using an exe setup file. I have tried converting the exe file to msi and using msiexec to uninstall through command line but it has no effect on the instances that has been installed with the exe and not the converted msi (which is the entire network)
Is there any Command that I can use in cmd to uninstall these instances that has been installed using the exe?
When you install a program in Windows a registry key for its uninstallation is created holding several values, among them is the UninstallString value which is the command line the Add or remove programs uses when you click Remove.
The parent key is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
And these UninstallStrings usually have the following format:
MsiExec.exe /I{0826F9E4-787E-481D-83E0-BC6A57B056D5}
In order to acomplish what you are trying to do you will have to create a RegistryKey object, read the UninstallString value for the application you want to remove and run the command line.
More info here.
You can use revo uninstaller, not for its uninstallation features, but for its main view, that shows the registered uninstall string of your application.
But it's only a starting point. Every setup engine can have its own uninstall string. You have to find the correct one, if you are lucky enough for it to exists (try /quiet, /passive, etc. until you find the correct one).
Go to the FIle Path, looks for a .exe which you need to uninstall. In command prompt open the path of .exe located and execute "xxxx.exe --uninstall".

Programmatically installing MSI packages

I would like to install a given .msi package programmatically from my C# .NET application, preferably with the installation parameters that my application specifies (like the installation path, decline crapware, etc.).
I did some searches, but I haven't really found anything useful. The most promising hit was this topic, but I cannot find any documentation of Microsoft.Deployment.WindowsInstaller or of WindowsInstaller.Installer for that matter.
I find the Deployment Tools Foundation project mentioned above to be a solid way to do this from .NET. Having referenced Microsoft.Deployment.WindowsInstaller.dll, use code like this to install a package:
Installer.SetInternalUI(InstallUIOptions.Silent);
Installer.InstallProduct(msiFilename, "ACTION=INSTALL ALLUSERS=2 MSIINSTALLPERUSER=");
The documentation for the .NET wrapper is in a .chm file in the Windows Installer XML installation directory in Program Files. Some parts of that DLL loosely wrap the native Windows APIs so the documentation here can be useful as well, which is how I worked out the string in the above snippet to suit my situation.
There's a COM object that offers an API for the installer:
First add a reference to COM object "Microsoft Windows Installer Object Library" to your project. Then you can start with the following code:
using System;
using WindowsInstaller;
namespace TestApp
{
public class InstallerTest
{
public static void Install()
{
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installer = (Installer)Activator.CreateInstance(type);
installer.InstallProduct("YourPackage.msi");
}
}
}
And there's a documentation about the Installer Object.
The "Deployment Tools Foundation" project which is a part of the WIX3.5 install contains a .NET wrapper for most (if not all) of the Windows Installer API. Get it by downloading and installing the WiX install: http://wixtoolset.org/ (currently WiX 3.11, updated Aug.2017).
Locate the Microsoft.Deployment.WindowsInstaller.dll file in the %ProgramFiles%\Windows Installer XML v3.??\SDK\ folder. Set a reference in your C# project and try to run the different APIs and see if you get the desired functionality.
I highly recommend using Deployment Tools Foundation over any COM Interop from .NET code.
The basic Win32 API (that can be pinvoked if necessary) is MsiInstallProduct. This is where practically all other mentioned APIs and calls will end up.
https://msdn.microsoft.com/en-us/library/aa370315(v=vs.85).aspx
Just pass the full path to the MSI file and your command line (including quiet options etc) and check the result to see if it installed correctly.
Note that there is a simple p/invoke declaration for managed code:
[DllImport("msi.dll", CharSet = CharSet.Auto, SetLastError=true)]
static extern UInt32 MsiInstallProduct(string packagePath, string commandLine);
The very simplest solution is to use msiexec to invoke the installer on the .msi.
You can customise the installation using command line settings including setting .msi properties, silent installation etc.
There are two approaches to solving your problem.
The first one as mentioned by #Glytzhkof is to use the Microsoft.Deployment.WindowsInstaller .NET wrapper API. This is some seriously powerful stuff but requires some time to get familiar with. You can get the latest version here (UPDATE: Stein Åsmul 28.12.2018: DTF is now part of the WiX toolkit).
The other approach is to use Transforms (.MST files). Transform files can be generated using Microsoft Orca or InstallShiled. The MSTs contains all the customizations that you need and can be applied on the MSI using this command line:
msiexec /i somemsi.msi TRANSFORMS=somemst.mst /qb
Additionally you can pass parameters directly in the command line:
msiexec /i <somemsi.msi> /qb AGREETOLICENSE=YES INSTALLDIR=C:\Temp
etc...
However, you will need to edit the MSI in an ORCA/InstallShield to determine which parameters are actually used.
The parameters used in the above example are not universal.
The actual installation can be complicated because of the presence of custom actions etc. In fact there is a whole industry that is built around msi customizations. Its called Applications Repackaging.

Detect silent install in .NET Custom Action

How do you detect that the install is running in silent mode?
I have a custom application that I've added a .msi setup project. I run the .msi file with the /qb switch, and in my custom installer c# code I would like to be able to detect this.
Edit: nobugs says to test the UILevel property. How do I access the UILevel property from a class derived from the System.Configuration.Install.Installer class?
Taking the hint from nobugz, I did the following:
On the Custom Actions view of the .msi setup project, I added the following to my CustomActionData (to pass the UILevel through to my custom installer):
/UILevel="[UILevel]"
Within my C# code for the code derived from base class Installer, I added code to get the value:
string uiLevelString = Context.Parameters["UILevel"];
It was then simple to parse the string for an int value. If the value is <= 3, it is a silent install.
First I would point out that InstallUtil is a very bad pattern. They run out of process, tatoo the process with a CLR version and when they fail they raise a 1001 error modal dialog even during a silent install.
Instead you should use WiX's DTF pattern.
MsiGetMode ( Session.Mode ) is limited during deferred execution so you will have to serialize and deserialize the UILevel.
http://www.msifaq.com/a/1044.htm

Categories

Resources