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

Going GAC

There are basically two ways to link an assembly to a application. Normally you do not need to share the assemblies so copying the assembly DLL along with the application works just fine. These are so called private assemblies. The CLR will probe for the the file starting from the application folder. No version checking is performed, so updating can be done simply by XCOPY.

In some cases you need to enable applications to share the same assembly. In cases like this the GAC (Global Assembly Cache) comes in handy. The GAC enables you to register assemblies for applications to load. The only requirement is that the assembly must have a strong name for identification. One of the advantages of using the GAC is that you can have multiple versions of the same assembly available. The assemblies in the GAC are called public assmeblies and registering them is a trivial task. During development you might just want to drag-n-drop the DLL file to the C:\Windows\Assemblies folder using the Windows Explorer or use the gacutil.exe through the command prompt. Setting up an installer to register you assemblies on the user systems is also a simple task and will be described next.

What you need is a simple setup project.  To get the assembly registered just add a “Global Assembly Cache Folder” to the file system by using the right mouse button context menu on the folder tree, and add your project output there.

GAC setup

Now your assemblies will be registered during install.

You might also want to enable developers to easily select your assembly through the Add Reference dialog. To make this possibole you just have to tell the .NET framework where to find your public assemblies. So during installation, you might want to register the application folder or one of its subfolders by setting a default value for the following Machine level registry key.

 \Software\Microsoft\.NETFramework\AssemblyFolders\<company or application name>

Set the key value to point to the folder containing your assemblies, e.g. “[TARGETDIR]\lib\”.

That’s it!