Uninstalling by Force

Sometimes an application fails to uninstall and you are stuck in a situation where you can’t repair, remove or update it. Fortunately Microsoft provides a tool called msizap that removes all the necessary MSI metadata enabling you to again reinstall the application.

Msizap is distributed as a part of the Windows SDK available at the Microsoft download center.

To remove a installation do the following:

  1. Start the SDK command line (CMD) through the Windows SDK menu in your start menu
  2. Type in the command “msizap TP <path to the msi package>”

Even if the msizap fails to remove some data, the Windows installer should again allow you to reinstall. Note that msizap will only remove the MSI metadata, leaving all files and registry keys installed.

For more information check out MSDN.

Setup Project: Custom Actions

Sometimes you need to add some custom actions to your setup project to complete the installation by running tools like regasm, ngen or caspol. The last time I was forced to do this was when my Inventor Add-in (COM based) did not get registered properly. The problem was that the Register=vsdrpCOM setting of the project output did not get the assembly codebase set. I needed to get my setup to include the codebase in the assembly registration to get my add-in installed.

The first step was that I added a new class library project to my solution and added there a new class called RegasmInstaller.cs:

[RunInstaller(true)]
public partial class RegasmInstaller : Installer
{
    public RegasmInstaller()
    {
        InitializeComponent();
    }

    public override void Install(IDictionary stateSaver)
    {
        base.Install(stateSaver);

        Regasm(true);
    }

    public override void Rollback(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    public override void Uninstall(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    private void Regasm(bool register)
    {
        string file = base.Context.Parameters["Assembly"];
        if (string.IsNullOrEmpty(file))
            throw new InstallException("Assembly not defined");
        if (!File.Exists(file))
            return;

        RegistrationServices regsrv = new RegistrationServices();
        Assembly assembly = Assembly.LoadFrom(file);

        if (register)
        {
            regsrv.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
        }
        else
        {
            try
            {
                regsrv.UnregisterAssembly(assembly);
            }
            catch
            {
                //Exceptions are ignored: even if the unregistering failes
                //it should notprevent the user from uninstalling
            }
        }
    }
}

The basic idea in writing a installer class is to override the methods you need. Override the install method to perform whatever actions you need and override the rollback and uninstall methods to undo these changes. You usually only need to override the commit method if you need to cleanup any temporary files. Don’t forget to start every override by calling the base function first!

The CustomActionData property of each custom action can be used to pass parameters to the installer class. In the installer class these parameter values can be read from the Context.Parameters dictionary. In my installer I used the property to define the assembly I wanted to get registered.

To get my installer class to actually register an assembly I had two options:  to use the Process class to call regasm.exe or to use the RegistrationServices class. My first version used the regasm.exe, but I later changed it to use the service class, both options are fine.

The second step was to add the project output of the installer class library to the setup project and then add the actions I needed.  For each action I selected the project output containing my installer class.

Setup Actions

Finally I set the CustomActionData property of each action to define the path to the target assembly.

CustomActionData Property