wixdtf

How to modify folder structure while extracting an MSI using WiX DTF


I need to extract the files from an MSI into a folder, but change the directory structure of the files, all from within a C# application (ie: not by doing an admin install, or any other install of the MSI). Conceptually, I want to "unzip" the MSI's embedded cab into the folder of my choosing. To do the extraction, I'm using the WiX 3.6 DTF libraries, but am unable to determine how to change the folder structure.

So, for instance, if I ran the MSI installer, the target folder for "Component1" would be c:\Program Files(x86)\Company Name\Demo Product Installer\Component1\, but at runtime in my extractor application, I'd like to put change that to c:\SomeOtherPlace\Demo Product Installer\Component1\, preferably by changing the APPLICATIONFOLDER directory path (see below).

For the MSI, I've defined the directory structure like this:

<Fragment>
  <Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="ProgramFilesFolder">
      <Directory Id="APPLICATIONFOLDER" Name="Company Name">
        <Directory Id="ProductFolder" Name="Demo Product Installer">
          <Directory Id="Cmp1Folder" Name="Component1" />
          <Directory Id="Cmp2Folder" Name="Component2" />
        </Directory>
      </Directory>
    </Directory>
  </Directory>
</Fragment>

Then, in the code that needs to extract the files, I've done this:

var msiFilePath = "myInstallerFile.msi";
var targetFolder = @"c:\SomeOtherPlace\";
using (var msiPackage = new InstallPackage(msiFilePath, DatabaseOpenMode.ReadOnly))
{
    msiPackage.WorkingDirectory = targetFolder;

    var dirMapping = msiPackage.Directories;    
    if (dirMapping.ContainsKey("APPLICATIONFOLDER"))                    
    {
        //This doesn't work, but represents what I hope to do:
        var oldInstallPath = dirMapping["APPLICATIONFOLDER"];                        
        oldInstallPath.TargetPath = targetFolder;                   
    }

    msiPackage.UpdateDirectories();                    
    msiPackage.ExtractFiles();                    
    msiPackage.Close();
}

Is there a way to modify the folder structure like this at runtime using the DTF objects? I know I can just move the files around after the fact, but if I can do it this way, it'd be much cleaner.


Solution

  • Based on the hint ("You might be albe to do it with in memory updates of the tables...") from Chirtopher's answer, I was able to find a working solution:

    var msiFilePath = "myInstallerFile.msi";
    var targetFolder = @"c:\SomeOtherPlace\";
    using (var msiPackage = new InstallPackage(msiFilePath, DatabaseOpenMode.Transact))
    {
        msiPackage.WorkingDirectory = targetFolder;
    
        var dirMapping = msiPackage.Directories;    
        if (dirMapping.ContainsKey("APPLICATIONFOLDER"))                    
        {
            //**** Modified code starts here ***//           
            // Changed the "APPLICATIONFOLDER" entry from "Company Name" to ".",
            // which is a special value to denote the extracted folder.
            var record = new Record(".", "APPLICATIONFOLDER");
            msiPackage.Execute("UPDATE `Directory` SET `DefaultDir` = ? WHERE `Directory` = ?", record);
            //**** Modified code ends here ***//
        }
    
        msiPackage.UpdateDirectories();                    
        msiPackage.ExtractFiles();  
    
        //Close **without** calling Commit() to ensure changes are not persisted                  
        msiPackage.Close();
    }
    

    Note that I also changed the DatabaseOpenMode to Transact.

    Of course, if you wanted to do more advanced changes to the folder structure, you'd have to modify/insert/remove additional records from the Directory table. (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa368295(v=vs.85).aspx for reference)