Site Tools


VS2005 Deployment Projects and the x64 Registry

Due to a bug in VS2005, MSI files created using VS2005 Deployment Projects (.vdproj) will fail to read the normal x64 registry, even when they have a TargetPlatform property of x64. This becomes a problem when you want to write an installer for your x64 Rhino plugin, since you will not be able to find out whether or not Rhino is even installed on the target machine, let alone the location of the installation.

The process of verifying and locating a Rhino installation using a VS2005 Deployment Project is generally accomplished as follows:

  • if you have not done so, add a Deployment Project to your Solution (for this example, set its TargetPlatform to x64)
  • right-click the deployment project node in the Solution Explorer and select View > Launch Conditions
  • right-click the Search Target Machine node in the Launch Conditions window and choose Add Registry Search

The job of this search will be to locate Rhino's MostRecent value, which contains the date-code of the most recent Rhino installation, if there is one. Therefore, the Registry Search we have just added may be set up as follows:

(Name)MOSTRECENT
PropertyMOSTRECENT
RegKeySoftware\McNeel\Rhinoceros\5.0x64
RootvsdrrHKLM
ValueMostRecent

The value of the Property property is how the search will be referred to in the rest of our .vdproj. It is also the MSI installer property which needs to be fixed in the compiled MSI file. If this is not done, the search will be performed on the Wow6432Node virtualized 32bit registry, and there will definitely be no Rhino registry key named 5.0x64 found there.

To continue, the next thing we need to do is add a Launch Condition which will check whether or not our MOSTRECENT search has successfully returned a value on the target machine. To do so:

  • return to the Launch Conditions window
  • right-click the Launch Conditions node and choose Add Launch Condition

The new Launch Condition may be set up as follows:

(Name)RHINOISINSTALLED
ConditionMOSTRECENT
InstallUrlhttp://www.rhino3d.com
MessageNo Rhino x64 installation was found on this machine.

When the compiled MSI is executed, this Launch Condition will inspect results of our MOSTRECENT search and abort the installation if it was unable to locate Rhino's MostRecent registry value. If it aborts, it will show the user a dialog box with the specified Message; if the user then clicks Yes, their browser will be opened, pointed at www.rhino3d.com. If, on the other hand, the search was successful, then Rhino's MostRecent value will be contained in the MOSTRECENT installer property, and we will use it to register our plugin with Rhino.

So now that we have an x64 Deployment Project which is ready to install our x64 Rhino plugin, we need to fix it so that it will actually work on an x64 system. The core of the problem is that VS2005 writes an incorrect value into the definition of our MOSTRECENT property, which is stored in the MSI's RegLocator table. The value which is written incorrectly is the property's Type, which is a bitwise OR-ed value specified using the following table:

ConstantHexadecimalDecimalDescription
msidbLocatorTypeDirectory0x0000Key path is a directory.
msidbLocatorTypeFileName0x0011Key path is a file name.
msidbLocatorTypeRawValue0x0022Key path is a registry value.
msidbLocatorType64bit0x01016Set this bit to have the installer search the 64-bit portion of the registry. Do not set this bit to have the installer search the 32-bit portion of the registry.

(http://msdn.microsoft.com/en-us/library/aa371171(VS.85).aspx)

The value that VS2005 writes for the property's Type value is 2, regardless whether the the project's TargetPlatform has been set to x64 or not. So, to fix it, we need to open the MSI and change Type to 18, so that msiexec.exe will know we want to look in the normal x64 registry. Editing this value in the MSI's RegLocator table may be done manually using Orca, which is the MSI database editor supplied with the Platform SDK. However, doing so is not a reliable development strategy; it would be better to automate the process so that we cannot ever accidentally forget to apply this fix. To that end, we will prefer to use a script, calling it from our Deployment Project's Post Build event. Here is a script which will do this for us:

//////////////////////////////////
 x64regfix.js
//////////////////////////////////
var msiOpenDatabaseModeTransact = 1;
var msiViewModifyInsert         = 1;
var msiViewModifyUpdate         = 2;
var msiViewModifyAssign         = 3;
var msiViewModifyReplace        = 4;
var msiViewModifyDelete         = 6;
if (WScript.Arguments.Length != 2)
{
   WScript.StdErr.WriteLine("Usage: " + WScript.ScriptName + " <Product.msi>" + " <PropertyName>");
   WScript.Quit(2);
}
var filespec = WScript.Arguments(0);
var property = WScript.Arguments(1);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql;
var view;
var record;
 Get the Signature out of the AppSearch table
sql = "SELECT * FROM `AppSearch` WHERE `Property`='" + property + "'";
view = database.OpenView(sql);
view.Execute();
record = view.Fetch();
 Give error if could not find the appropriate AppSearch entry
if (record == null)
{
   WScript.StdErr.WriteLine("Unable to find entry in AppSearch table.");
   view.Close();
   WScript.Quit(3);
}
 Grab the signature
var signature = record.StringData(2);
view.Close();
 Find the corresponding value in the RegLocator table
sql = "SELECT * FROM `RegLocator` WHERE `Signature_`='" + signature + "'";
view = database.OpenView(sql);
view.Execute();
record = view.Fetch();
 Give error if could not find the appropriate RegLocator entry
if (record == null)
{
   WScript.StdErr.WriteLine("Unable to find entry in RegLocator table.");
   view.Close();
   WScript.Quit(4);
}
 Add the msidbLocatorType64bit
if (record.IntegerData(5) < 16)
{
   record.IntegerData(5) = record.IntegerData(5) + 16;
   view.Modify(msiViewModifyReplace, record);
}
view.Close();
WScript.StdOut.WriteLine("x64regfix.js modified Property '" + property + "' in the RegLocator table");
database.Commit();

(note the peculiar back-slanted apostrophes in the SQL statements, the script will fail if these are changed)

If this script is saved as x64regfix.js and placed in the Deployment Project's directory, it can then be called from the project's Post Build event as follows:

cscript.exe "$(ProjectDir)x64regfix.js" "$(BuiltOuputPath)" "MOSTRECENT"

This will open the compiled MSI, located at $(BuildOutputPath), and modify our MOSTRECENT property so that it correctly locates Rhino's MostRecent value in the target machine's x64 registry.

developer/vs2005deploymentprojects.txt ยท Last modified: 2015/09/14 (external edit)