posts - 343, comments - 0, trackbacks - 0

64-bit Managed Custom DLL Actions with Visual Studio do not work properly

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.

64BitIIS7InstallError

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....

Print | posted on Thursday, November 13, 2008 9:52 AM |

Powered by:
Powered By Subtext Powered By ASP.NET