I have recently been creating a new UI Module for IIS 7 and as part of this I wanted to install my module automatically using a MSI file. With the help of CarlosAg from the IIS product team I have made my UI module work really well and also managed to create a setup for it.
The setup installs the files into the GAC, and does the registration and de-registration in the IIS administration.config file during install/uninstall.
For details on how to do this please go to http://blogs.msdn.com/carlosag/archive/2008/11/10/CreatingSetupProjectForIISusingVisualStudio2008.aspx
So it was all working fine but when I deployed it to a 64Bit machine I got the following error during the install.
Now I was lost but Carlos did some digging and found an article that explained what happened which you can read by clicking the link below.
http://blogs.msdn.com/heaths/archive/2006/02/01/64-bit-managed-custom-actions-with-visual-studio.aspx
Basically the problem is (quoted from heath's blog)
When you build the Windows Installer project in Visual Studio it embeds the 32-bit version of InstallUtilLib.dll into the Binary table as InstallUtil. When Windows Installer executes your managed custom action it actually is calling the ManagedInstall entry point function from InstallUtilLib.dll as a type 1 deferred custom action (1025) which creates an instance of the CCW System.Configuration.Install.IManagedInstaller interface and runs your Installer classes. Since the native InstallUtilLib.dll is 32-bit it loads the 32-bit Framework which will throw the BadImageFormatException since your managed class library is 64-bit.
So his blog goes on with a method you can use to update the DLLs binary data in the MSI file using a tool called ORCA which can be downloaded from http://www.technipages.com/download-orca-msi-editor.html
I wanted a more automated solution so I wrote the following code which uses the Windows Installer COM Object to open the database and replace the binary file.
My App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="InstallUtil64BitFileSpec" value="\\ccrowe-test\c$\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtilLib.dll"/>
<add key="MSIFileSpec" value="C:\dev\Projects\IIS7\Extensibility\FTPADUserEditor\SetupFTPADUserEditor\Debug\SetupFTPADUserEditor.msi"/>
</appSettings>
</configuration>
My C# Source
using System;
using System.Configuration;
// You must add a reference to the COM Object "Microsoft Windows Installer Object Library 1.0"
// which is located in c:\windows\system32\msi.dll
// You must also add a reference to System.Configuration
// For more details of why this code was written see the following site
// http://blogs.msdn.com/heaths/archive/2006/02/01/64-bit-managed-custom-actions-with-visual-studio.aspx
namespace Fix64BitMSIFile
{
class Program
{
static void Main(string[] args)
{
bool ChangesMade = false;
Console.WriteLine("Update an MSI File with a 64bit version of InstallUtilLib");
Console.WriteLine();
string InstallUtil64BitFileSpec = ConfigurationManager.AppSettings["InstallUtil64BitFileSpec"];
string MSIFileSpec = ConfigurationManager.AppSettings["MSIFileSpec"];
if (System.IO.File.Exists(InstallUtil64BitFileSpec) == false)
{
Console.WriteLine("File Not Found : " + InstallUtil64BitFileSpec);
return;
}
if (System.IO.File.Exists(MSIFileSpec) == false)
{
Console.WriteLine("File Not Found : " + MSIFileSpec);
return;
}
Console.WriteLine("MSIFile:" + Environment.NewLine + MSIFileSpec);
Console.WriteLine();
Console.WriteLine("InstallUtil: " + Environment.NewLine + InstallUtil64BitFileSpec);
Console.WriteLine();
Console.WriteLine("Opening the MSI File");
WindowsInstaller.Installer i = (WindowsInstaller.Installer)new Fix64BitMSIFile.Installer();
WindowsInstaller.Database db = i.OpenDatabase(MSIFileSpec, WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeTransact);
Console.WriteLine("Running SQL Query for InstallUtil in the Binary MSI table");
// NOTE: The ` is correct in the SQL statement below - it is not " or '
WindowsInstaller.View v = db.OpenView("SELECT `Name`,`Data` FROM `Binary` where `Binary`.`Name` = 'InstallUtil'");
v.Execute(null);
WindowsInstaller.Record Record = v.Fetch();
if (Record != null)
{
Console.WriteLine("Updating the Binary Data for InstallUtil");
Record.SetStream(2, InstallUtil64BitFileSpec);
v.Modify(WindowsInstaller.MsiViewModify.msiViewModifyUpdate, Record);
ChangesMade = true;
}
else
{
Console.WriteLine("Error : InstallUtil not found in the Binary MSI Table");
}
v.Close();
if (ChangesMade)
{
Console.WriteLine("Commiting the changes to the database");
db.Commit();
}
Console.WriteLine();
Console.WriteLine("Completed.....");
}
}
}
Hopefully this may help someone else in the future.
In the mean time it is back to the development of my IIS 7 Module....