8

I am trying to write a plugin (actually a visual studio template wizard that generates the plugin) for an existing application. As part of the plugin installation, I have to insert entries into a configuration database. Often there are multiple DLLs corresponding to different parts of the functionality, sometimes each requiring entries in the same tables, but usually with different combinations of table entries. I need to add those from my installer.

Everything is in C#, for policy reasons.

I'm currently using Visual Studio Installer (VS2010) to create the installer. I'd prefer to use something that is likely to be installed on the user's machine to keep the template / wizard installation simple, but I could (if necessary) bundle / chain-install an open-source (or at least freely redistributable) alternative installer.

For example, to add a menu entry to the application, I have to insert entries into a couple of tables. In the past, I've done this by using an installer helper (sometimes an application, sometimes an installer class) that is called from the installer application. In that design, I would embed the settings I need to add to configuration tables into the installer helper, then execute SQL (via C# :-)) to actually do the add.

The problem is that this leads to repeating the same information into two places, and it's not very maintainable or clean in a wizard environment. I'd really prefer to introspect this information from some attribute I can set on the plugin assemblies.

However I've stumbled at the first step - how can my Installer class find out which assemblies were installed (or will be installed) by this installation?

I did consider trying to embed the list of DLLs into a custom installer property, but that will be hard to generate from my plugin wizard too. Ideally there would be some existing event I can register for, or an existing property I can read, but I haven't discovered it yet.

BradHards
  • 650
  • 9
  • 27
  • So you have an .MSI file and what to determine what DLL it includes? – Simon Mourier Jan 12 '14 at 14:55
  • I have an installer project (of which the output is the .msi). The installer class should work with that .msi (either the DLLs it contains and the selected configuration), or the DLLs it installed. – BradHards Jan 12 '14 at 20:48

2 Answers2

3

What you can do is use the Windows Installer API to dump all the DLLs contained in a given MSI package.

In C#, there are various libraries or sample code available to do this.

Here, I will use the open source Wix toolset project's DLLs. So, you just need to download the binaries (not the wix installer), create a project and add references to Microsoft.Deployment.WindowsInstaller.dll and Microsoft.Deployment.WindowsInstaller.Package.dll in this binaries directory

Here is a sample program that writes out all DLL files part of the package.

class Program
{
    static void Main(string[] args)
    {
        string path = @"... myfile.msi";
        using (InstallPackage package = new InstallPackage(path, DatabaseOpenMode.ReadOnly))
        {
            foreach (var kvp in package.Files.Where(f => Path.GetExtension(f.Value.TargetName) == ".dll"))
            {
                Console.WriteLine(kvp.Value.TargetName);
            }
        }
    }
}

}

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Thanks. Is there a way to tell the Installer class which MSI it is being called from? That is, to avoid hard-coding the @"... myfile.msi" part. – BradHards Jan 13 '14 at 07:21
  • Check this out: http://stackoverflow.com/questions/17390867/how-to-get-the-path-where-msi-is-being-installed-within-installer-class – Simon Mourier Jan 13 '14 at 08:49
  • @SimonMourier: See http://stackoverflow.com/questions/368154/i-require-a-msi-custom-action-that-copies-a-file-from-the-msi-source-directory for a mechanism for passing the MSI path to the installer. – Nicole Calinoiu Jan 14 '14 at 15:39
2

I'm not clear on what happens between running the wizard and seeing the .msi. (Is the output of the wizard a VS project or the .msi itself [in which case, end-user wouldn't need VS at all]?) But, let's get started...

It seems like your bottleneck is Visual Studio Installer. It is also a timebomb because it will die with VS2010. Many projects (e.g. Visual Studio itself) use the WiX Toolset instead. For Visual Studio and SharpDevelop users, WiX is "is likely to be installed on the user's machine" and is "open-source." It is available as binaries you can include in your wizard, or the full product can be installed (including a VS extension) from NuGet, Chocolatey or exe by or for the user.

With WiX, you write or generate XML files that describe the contents, UI and action sequence of a Windows Installer package. WiX executables are invoked to do the actual build. If you install WiX's MSBuild files, you can use an MSBuild project to orchestrate the build. If you install WiX's VS extension, you can use VS to edit the project. (A modern VS project is an MSBuild project.) MSBuild comes with .NET (until the next version, where it will be a separate addon).

So, the wizard can generate files for WiX based on user input and data from the plugins. It can also build the MSI. You could still use your Install Helper but it would be better to use WiX's SQL Server custom actions (if they apply in your case) to reliably support uninstallation and upgrading.

Since at least some of the plugin installation and configuration data comes from the plugins, you can have the plugin projects generate it so it can installed with your wizard. WiX supports placing information for separate components in separate files so you wouldn't need to maintain a combined file for your wizard. If the informations is simple, the plugin author could maintain it manually. Otherwise, it can be generated at plugin edit-time or plugin build-time. At edit-time, you could use a T4 template (content mixed with C# code). At build-time, you could use an inline or compiled custom MSBuild task (written in C#). [.csproj files are MSBuild projects.] Again, your wizard installer would grab the generated files along with the plugin DLL to install on the machine where the wizard runs.

You can also use WiX to install your wizard. If your wizard is a VSIX package, use WiX's VSIX custom action. Heck, you could probably use the Wix files for the plugins for both the wizard installer and the installer generated for the user.

Tom Blodget
  • 20,260
  • 3
  • 39
  • 72
  • Thanks for the WiX toolkit suggestion. I'm probably not explaining the wizard idea pretty well, but in advance of updating the question: the wizard creates (or updates) a set of VS projects, one of which is the install helper, one of which is the installer, and then there are one or more "payload" plugin DLLs. – BradHards Jan 13 '14 at 07:25
  • Okay, your wizard can add/maintain a WiX setup project in the solution. Should be a lot easier than maintaining a Visual Studio Installer project. – Tom Blodget Jan 17 '14 at 00:10