Example #1
0
        public void CanBuildSimpleModule()
        {
            var folder = TestData.Get(@"TestData\SimpleModule");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Module.wxs"),
                    "-loc", Path.Combine(folder, "Module.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(intermediateFolder, @"bin\test.msm")
                });

                result.AssertSuccess();

                var msmPath = Path.Combine(intermediateFolder, @"bin\test.msm");
                Assert.True(File.Exists(msmPath));
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));

                var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();

                var dirSymbols = section.Symbols.OfType <DirectorySymbol>().OrderBy(d => d.Id.Id).ToList();
                WixAssert.CompareLineByLine(new[]
                {
                    "MergeRedirectFolder\tTARGETDIR\t.",
                    "NotTheMergeRedirectFolder\tTARGETDIR\t.",
                    "TARGETDIR\t\tSourceDir"
                }, dirSymbols.Select(d => String.Join("\t", d.Id.Id, d.ParentDirectoryRef, d.Name)).ToArray());

                var fileSymbols = section.Symbols.OfType <FileSymbol>().OrderBy(d => d.Id.Id).ToList();
                WixAssert.CompareLineByLine(new[]
                {
                    $"File1\t{Path.Combine(folder, @"data\test.txt")}\ttest.txt",
                    $"File2\t{Path.Combine(folder, @"data\test.txt")}\ttest.txt",
                }, fileSymbols.Select(fileSymbol => String.Join("\t", fileSymbol.Id.Id, fileSymbol[FileSymbolFields.Source].AsPath().Path, fileSymbol[FileSymbolFields.Source].PreviousValue.AsPath().Path)).ToArray());

                var data     = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                var fileRows = data.Tables["File"].Rows;
                Assert.Equal(new[]
                {
                    "File1.243FB739_4D05_472F_9CFB_EF6B1017B6DE",
                    "File2.243FB739_4D05_472F_9CFB_EF6B1017B6DE",
                }, fileRows.Select(r => r.FieldAsString(0)).ToArray());

                var cabPath = Path.Combine(intermediateFolder, "msm-test.cab");
                Query.ExtractStream(msmPath, "MergeModule.CABinet", cabPath);
                var files = Query.GetCabinetFiles(cabPath);
                Assert.Equal(new[]
                {
                    "File1.243FB739_4D05_472F_9CFB_EF6B1017B6DE",
                    "File2.243FB739_4D05_472F_9CFB_EF6B1017B6DE",
                }, files.Select(f => Path.Combine(f.Path, f.Name)).ToArray());
            }
        }
Example #2
0
        public void CanBuildSetProperty()
        {
            var folder = TestData.Get(@"TestData\SetProperty");

            using (var fs = new DisposableFileSystem())
            {
                var baseFolder         = fs.GetFolder();
                var intermediateFolder = Path.Combine(baseFolder, "obj");

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    Path.Combine(folder, "PackageComponents.wxs"),
                    "-loc", Path.Combine(folder, "Package.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(baseFolder, @"bin\test.msi")
                });

                result.AssertSuccess();

                var output = WindowsInstallerData.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"), false);
                var caRows = output.Tables["CustomAction"].Rows.Single();
                Assert.Equal("SetINSTALLLOCATION", caRows.FieldAsString(0));
                Assert.Equal("51", caRows.FieldAsString(1));
                Assert.Equal("INSTALLLOCATION", caRows.FieldAsString(2));
                Assert.Equal("[INSTALLFOLDER]", caRows.FieldAsString(3));
            }
        }
Example #3
0
        public void CanLoadPdbGeneratedByBuildViaWixOutput()
        {
            var folder = TestData.Get(@"TestData\MultiFileCompressed");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    Path.Combine(folder, "PackageComponents.wxs"),
                    "-d", "MediaTemplateCompressionLevel",
                    "-loc", Path.Combine(folder, "Package.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(intermediateFolder, @"bin\test.msi")
                });

                result.AssertSuccess();

                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi")));
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\cab1.cab")));

                var pdbPath = Path.Combine(intermediateFolder, @"bin\test.wixpdb");
                Assert.True(File.Exists(pdbPath));

                var wixOutput = WixOutput.Read(pdbPath);
                var output    = WindowsInstallerData.Load(wixOutput, suppressVersionCheck: true);
                Assert.NotNull(output);
            }
        }
 public CopyTransformDataCommand(IMessaging messaging, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, bool copyOutFileRows)
 {
     this.Messaging        = messaging;
     this.Output           = output;
     this.TableDefinitions = tableDefinitions;
     this.CopyOutFileRows  = copyOutFileRows;
 }
Example #5
0
 public CreateInstanceTransformsCommand(IntermediateSection section, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, IBackendHelper backendHelper)
 {
     this.Section          = section;
     this.Output           = output;
     this.TableDefinitions = tableDefinitions;
     this.BackendHelper    = backendHelper;
 }
Example #6
0
        public ModularizeCommand(WindowsInstallerData output, string modularizationSuffix, IEnumerable <WixSuppressModularizationSymbol> suppressSymbols)
        {
            this.Output = output;
            this.ModularizationSuffix = modularizationSuffix;

            // Gather all the unique suppress modularization identifiers.
            this.SuppressModularizationIdentifiers = new HashSet <string>(suppressSymbols.Select(s => s.SuppressIdentifier));
        }
Example #7
0
 /// <summary>
 /// Instantiates a new Differ class.
 /// </summary>
 public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool preserveUnchangedRows, bool showPedanticMessages)
 {
     this.messaging             = messaging;
     this.TargetOutput          = targetOutput;
     this.UpdatedOutput         = updatedOutput;
     this.PreserveUnchangedRows = preserveUnchangedRows;
     this.ShowPedanticMessages  = showPedanticMessages;
 }
Example #8
0
 public UpdateTransformsWithFileFacades(IMessaging messaging, WindowsInstallerData output, IEnumerable <SubStorage> subStorages, TableDefinitionCollection tableDefinitions, IEnumerable <FileFacade> fileFacades)
 {
     this.Messaging        = messaging;
     this.Output           = output;
     this.SubStorages      = subStorages;
     this.TableDefinitions = tableDefinitions;
     this.FileFacades      = fileFacades;
 }
        /// <summary>
        /// Generates the WixFile table based on a path to an admin image msi and an Output.
        /// </summary>
        /// <param name="databaseFile">The path to the msi database file in an admin image.</param>
        /// <param name="output">The Output that represents the msi database.</param>
        private void GenerateWixFileTable(string databaseFile, WindowsInstallerData output)
        {
            throw new NotImplementedException();
#if TODO_FIX_UNBINDING_FILES
            var adminRootPath = Path.GetDirectoryName(databaseFile);

            var componentDirectoryIndex = new Hashtable();
            var componentTable          = output.Tables["Component"];
            foreach (var row in componentTable.Rows)
            {
                componentDirectoryIndex.Add(row[0], row[2]);
            }

            // Index full source paths for all directories
            var directoryDirectoryParentIndex = new Hashtable();
            var directoryFullPathIndex        = new Hashtable();
            var directorySourceNameIndex      = new Hashtable();
            var directoryTable = output.Tables["Directory"];
            foreach (var row in directoryTable.Rows)
            {
                directoryDirectoryParentIndex.Add(row[0], row[1]);
                if (null == row[1])
                {
                    directoryFullPathIndex.Add(row[0], adminRootPath);
                }
                else
                {
                    directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2]));
                }
            }

            foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex)
            {
                if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key))
                {
                    this.GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
                }
            }

            var fileTable    = output.Tables["File"];
            var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]);
            foreach (var row in fileTable.Rows)
            {
                var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]);
                wixFileRow.File      = (string)row[0];
                wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]];
                wixFileRow.Source    = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2]));

                if (!File.Exists(wixFileRow.Source))
                {
                    throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source));
                }

                wixFileTable.Rows.Add(wixFileRow);
            }
#endif
        }
 public ExtractCabinetsCommand(WindowsInstallerData output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder, bool treatOutputAsModule = false)
 {
     this.Output              = output;
     this.Database            = database;
     this.InputFilePath       = inputFilePath;
     this.ExportBasePath      = exportBasePath;
     this.IntermediateFolder  = intermediateFolder;
     this.TreatOutputAsModule = treatOutputAsModule;
 }
Example #11
0
        public Row CreateRow(IntermediateSection section, IntermediateSymbol symbol, WindowsInstallerData output, TableDefinition tableDefinition)
        {
            var table = output.EnsureTable(tableDefinition);

            var row = table.CreateRow(symbol.SourceLineNumbers);
            row.SectionId = section.Id;

            return row;
        }
Example #12
0
        private WindowsInstallerData GetWindowsInstallerData()
        {
            if (this.WiData == null)
            {
                using var wixOutput = WixOutput.Read(this.PackagePdb);
                this.WiData         = WindowsInstallerData.Load(wixOutput);
            }

            return(this.WiData);
        }
        public void CanBuildWithDefaultProductLanguage()
        {
            var folder = TestData.Get(@"TestData", "Language");

            using (var fs = new DisposableFileSystem())
            {
                var baseFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    "-loc", Path.Combine(folder, "Package.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", Path.Combine(baseFolder, "obj"),
                    "-o", Path.Combine(baseFolder, @"bin\test.msi")
                });

                result.AssertSuccess();

                var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();

                var directorySymbols = section.Symbols.OfType <DirectorySymbol>();
                WixAssert.CompareLineByLine(new[]
                {
                    "INSTALLFOLDER:Example Corporation\\MsiPackage",
                    "ProgramFilesFolder:PFiles",
                    "TARGETDIR:SourceDir"
                }, directorySymbols.OrderBy(s => s.Id.Id).Select(s => s.Id.Id + ":" + s.Name).ToArray());

                var propertySymbol = section.Symbols.OfType <PropertySymbol>().Single(p => p.Id.Id == "ProductLanguage");
                Assert.Equal("0", propertySymbol.Value);

                var summaryPlatform = section.Symbols.OfType <SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
                Assert.Equal("Intel;0", summaryPlatform.Value);

                var summaryCodepage = section.Symbols.OfType <SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.Codepage);
                Assert.Equal("1252", summaryCodepage.Value);

                var data          = WindowsInstallerData.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var directoryRows = data.Tables["Directory"].Rows;
                WixAssert.CompareLineByLine(new[]
                {
                    "d4EceYatXTyy8HXPt5B6DT9Rj.wE:u7-b4gch|Example Corporation",
                    "INSTALLFOLDER:oekcr5lq|MsiPackage",
                    "ProgramFilesFolder:PFiles",
                    "TARGETDIR:SourceDir"
                }, directoryRows.Select(r => r.FieldAsString(0) + ":" + r.FieldAsString(2)).ToArray());
            }
        }
Example #14
0
        public void CanBuildSimpleModule()
        {
            var folder = TestData.Get(@"TestData\SimpleModule");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Module.wxs"),
                    "-loc", Path.Combine(folder, "Module.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(intermediateFolder, @"bin\test.msm")
                });

                result.AssertSuccess();

                var msmPath = Path.Combine(intermediateFolder, @"bin\test.msm");
                Assert.True(File.Exists(msmPath));
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));

                var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();

                var fileSymbol = section.Symbols.OfType <FileSymbol>().Single();
                Assert.Equal("filyIq8rqcxxf903Hsn5K9L0SWV73g", fileSymbol.Id.Id);
                Assert.Equal(Path.Combine(folder, @"data\test.txt"), fileSymbol[FileSymbolFields.Source].AsPath().Path);
                Assert.Equal(@"test.txt", fileSymbol[FileSymbolFields.Source].PreviousValue.AsPath().Path);

                var data     = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                var fileRows = data.Tables["File"].Rows;
                Assert.Equal(new[]
                {
                    "filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE"
                }, fileRows.Select(r => r.FieldAsString(0)).ToArray());

                var cabPath = Path.Combine(intermediateFolder, "msm-test.cab");
                Query.ExtractStream(msmPath, "MergeModule.CABinet", cabPath);
                var files = Query.GetCabinetFiles(cabPath);
                Assert.Equal(new[]
                {
                    "filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE"
                }, files.Select(f => Path.Combine(f.Path, f.Name)).ToArray());
            }
        }
Example #15
0
        public void CanBuildInstanceTransform()
        {
            var folder = TestData.Get(@"TestData\InstanceTransform");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    Path.Combine(folder, "PackageComponents.wxs"),
                    "-loc", Path.Combine(folder, "Package.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(intermediateFolder, @"bin\test.msi")
                });

                result.AssertSuccess();

                var output     = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"), false);
                var substorage = output.SubStorages.Single();
                Assert.Equal("I1", substorage.Name);

                var data = substorage.Data;
                Assert.Equal(new[]
                {
                    "_SummaryInformation",
                    "Property",
                    "Upgrade"
                }, data.Tables.Select(t => t.Name).ToArray());

                Assert.Equal(new[]
                {
                    "INSTANCEPROPERTY\tI1",
                    "ProductName\tMsiPackage (Instance 1)",
                }, JoinRows(data.Tables["Property"]));

                Assert.Equal(new[]
                {
                    "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED",
                    "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t0\t0",
                    "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED",
                    "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t0\t0"
                }, JoinRows(data.Tables["Upgrade"]));
            }
        }
        public PackageInstaller(WixTestContext testContext, string filename)
        {
            this.Package     = Path.Combine(testContext.TestDataFolder, $"{filename}.msi");
            this.PackagePdb  = Path.Combine(testContext.TestDataFolder, $"{filename}.wixpdb");
            this.TestContext = testContext;

            using var wixOutput = WixOutput.Read(this.PackagePdb);

            var intermediate    = Intermediate.Load(wixOutput);
            var section         = intermediate.Sections.Single();
            var platformSummary = section.Symbols.OfType <SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
            var platformString  = platformSummary.Value.Split(new char[] { ';' }, 2)[0];

            this.IsX64 = platformString != "Intel";

            this.WiData = WindowsInstallerData.Load(wixOutput);
        }
        public void CanGetWithMultiNestedSubdirectory()
        {
            var folder = TestData.Get(@"TestData");

            using (var fs = new DisposableFileSystem())
            {
                var baseFolder         = fs.GetFolder();
                var intermediateFolder = Path.Combine(baseFolder, "obj");
                var msiPath            = Path.Combine(baseFolder, @"bin\test.msi");

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    "-arch", "x64",
                    Path.Combine(folder, "Directory", "Nested.wxs"),
                    Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"),
                    "-bindpath", Path.Combine(folder, "SingleFile", "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", msiPath
                });

                result.AssertSuccess();

                var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();

                var dirSymbols = section.Symbols.OfType <WixToolset.Data.Symbols.DirectorySymbol>().ToList();
                Assert.Equal(new[]
                {
                    "BinFolder:ProgramFilesFolder:Example Corporation\\Test Product\\bin",
                    "ProgramFilesFolder:TARGETDIR:PFiles",
                    "TARGETDIR::SourceDir"
                }, dirSymbols.OrderBy(d => d.Id.Id).Select(d => d.Id.Id + ":" + d.ParentDirectoryRef + ":" + d.Name).ToArray());

                var data          = WindowsInstallerData.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var directoryRows = data.Tables["Directory"].Rows;
                Assert.Equal(new[]
                {
                    "d4EceYatXTyy8HXPt5B6DT9Rj.wE:ProgramFilesFolder:u7-b4gch|Example Corporation",
                    "dSJ1pgiASlW7kJTu0wqsGBklJsS0:d4EceYatXTyy8HXPt5B6DT9Rj.wE:vjj-gxay|Test Product",
                    "BinFolder:dSJ1pgiASlW7kJTu0wqsGBklJsS0:bin",
                    "ProgramFilesFolder:TARGETDIR:PFiles",
                    "TARGETDIR::SourceDir"
                }, directoryRows.Select(r => r.FieldAsString(0) + ":" + r.FieldAsString(1) + ":" + r.FieldAsString(2)).ToArray());
            }
        }
Example #18
0
        public void CanMergeModule()
        {
            var folder = TestData.Get(@"TestData\SimpleMerge");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();
                var msiPath            = Path.Combine(intermediateFolder, @"bin\test.msi");
                var cabPath            = Path.Combine(intermediateFolder, @"bin\cab1.cab");

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    "-loc", Path.Combine(folder, "Package.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, ".data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", msiPath
                });

                result.AssertSuccess();

                Assert.True(File.Exists(msiPath));
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));

                var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();
                Assert.Empty(section.Symbols.OfType <FileSymbol>());

                var data = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
                Assert.Null(data.Tables["File"]);

                var results = Query.QueryDatabase(msiPath, new[] { "File" });
                Assert.Equal(new[]
                {
                    "File:filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tModuleComponent.243FB739_4D05_472F_9CFB_EF6B1017B6DE\ttest.txt\t17\t\t\t512\t0"
                }, results);

                var files = Query.GetCabinetFiles(cabPath);
                Assert.Equal(new[]
                {
                    "filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE"
                }, files.Select(f => f.Name).ToArray());
            }
        }
        public void CanGetDefaultName()
        {
            var folder = TestData.Get(@"TestData");

            using (var fs = new DisposableFileSystem())
            {
                var baseFolder         = fs.GetFolder();
                var intermediateFolder = Path.Combine(baseFolder, "obj");
                var msiPath            = Path.Combine(baseFolder, @"bin\test.msi");

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Directory", "DefaultName.wxs"),
                    Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"),
                    "-bindpath", Path.Combine(folder, "SingleFile", "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", msiPath
                });

                result.AssertSuccess();

                var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var section      = intermediate.Sections.Single();

                var dirSymbols = section.Symbols.OfType <WixToolset.Data.Symbols.DirectorySymbol>().ToList();
                WixAssert.CompareLineByLine(new[]
                {
                    "BinFolder\tCompanyFolder\t.",
                    "CompanyFolder\tProgramFilesFolder\tExample Corporation",
                    "ProgramFilesFolder\tTARGETDIR\tPFiles",
                    "TARGETDIR\t\tSourceDir"
                }, dirSymbols.OrderBy(d => d.Id.Id).Select(d => String.Join('\t', d.Id.Id, d.ParentDirectoryRef, d.Name)).ToArray());

                var data          = WindowsInstallerData.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
                var directoryRows = data.Tables["Directory"].Rows;
                WixAssert.CompareLineByLine(new[]
                {
                    "BinFolder\tCompanyFolder\t.",
                    "CompanyFolder\tProgramFilesFolder\tu7-b4gch|Example Corporation",
                    "ProgramFilesFolder\tTARGETDIR\tPFiles",
                    "TARGETDIR\t\tSourceDir"
                }, directoryRows.Select(r => String.Join('\t', r.FieldAsString(0), r.FieldAsString(1), r.FieldAsString(2))).ToArray());
            }
        }
Example #20
0
        public void CanBuildManualUpgrade()
        {
            var folder = TestData.Get(@"TestData\ManualUpgrade");

            using (var fs = new DisposableFileSystem())
            {
                var intermediateFolder = fs.GetFolder();

                var result = WixRunner.Execute(new[]
                {
                    "build",
                    Path.Combine(folder, "Package.wxs"),
                    Path.Combine(folder, "PackageComponents.wxs"),
                    "-loc", Path.Combine(folder, "Package.en-us.wxl"),
                    "-bindpath", Path.Combine(folder, "data"),
                    "-intermediateFolder", intermediateFolder,
                    "-o", Path.Combine(intermediateFolder, @"bin\test.msi")
                }, out var messages);

                Assert.Equal(0, result);

                var pdbPath = Path.Combine(intermediateFolder, @"bin\test.wixpdb");
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi")));
                Assert.True(File.Exists(pdbPath));
                Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\test.txt")));

                var intermediate = Intermediate.Load(pdbPath);
                var section      = intermediate.Sections.Single();

                var upgradeSymbol = section.Symbols.OfType <UpgradeSymbol>().Single();
                Assert.False(upgradeSymbol.ExcludeLanguages);
                Assert.True(upgradeSymbol.IgnoreRemoveFailures);
                Assert.False(upgradeSymbol.VersionMaxInclusive);
                Assert.True(upgradeSymbol.VersionMinInclusive);
                Assert.Equal("13.0.0", upgradeSymbol.VersionMax);
                Assert.Equal("12.0.0", upgradeSymbol.VersionMin);
                Assert.False(upgradeSymbol.OnlyDetect);
                Assert.Equal("BLAHBLAHBLAH", upgradeSymbol.ActionProperty);

                var pdb = WindowsInstallerData.Load(pdbPath, suppressVersionCheck: false);
                var secureProperties = pdb.Tables["Property"].Rows.Where(row => row.GetKey() == "SecureCustomProperties").Single();
                Assert.Contains("BLAHBLAHBLAH", secureProperties.FieldAsString(1));
            }
        }
        private WindowsInstallerData GetData(string path)
        {
            var ext = Path.GetExtension(path);

            if (".msi".Equals(ext, StringComparison.OrdinalIgnoreCase))
            {
                using (var database = new Database(path, OpenDatabase.ReadOnly))
                {
                    var exportBasePath = Path.Combine(this.IntermediateFolder, "_msi"); // TODO: come up with a better path.

                    var isAdminImage = false;                                           // TODO: need a better way to set this

                    var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, path, OutputType.Product, exportBasePath, this.IntermediateFolder, isAdminImage, suppressDemodularization: true, skipSummaryInfo: true);
                    return(command.Execute());
                }
            }
            else // assume .wixpdb (or .wixout)
            {
                var data = WindowsInstallerData.Load(path, true);
                return(data);
            }
        }
        /// <summary>
        /// See <see cref="IWindowsInstallerBackendBinderExtension.TryProcessSymbol(IntermediateSection, IntermediateSymbol, WindowsInstallerData, TableDefinitionCollection)"/>
        /// </summary>
        public virtual bool TryProcessSymbol(IntermediateSection section, IntermediateSymbol symbol, WindowsInstallerData data, TableDefinitionCollection tableDefinitions)
        {
            if (this.TableDefinitions.Any(t => t.SymbolDefinition == symbol.Definition))
            {
                return(this.BackendHelper.TryAddSymbolToMatchingTableDefinitions(section, symbol, data, tableDefinitions));
            }

            return(false);
        }
Example #23
0
        private void GenerateDatabase(WindowsInstallerData output, string databaseFile)
        {
            var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false);

            command.Execute();
        }
Example #24
0
        public WindowsInstallerData Execute()
        {
            var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile));

            transform.Type = OutputType.Transform;

            // get the summary information table
            using (var summaryInformation = new SummaryInformation(this.TransformFile))
            {
                var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);

                for (var i = 1; 19 >= i; i++)
                {
                    var value = summaryInformation.GetProperty(i);

                    if (0 < value.Length)
                    {
                        var row = table.CreateRow(transform.SourceLineNumbers);
                        row[0] = i;
                        row[1] = value;
                    }
                }
            }

            // create a schema msi which hopefully matches the table schemas in the transform
            var schemaOutput    = new WindowsInstallerData(null);
            var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi");

            foreach (var tableDefinition in this.TableDefinitions)
            {
                // skip unreal tables and the Patch table
                if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name)
                {
                    schemaOutput.EnsureTable(tableDefinition);
                }
            }

            var   addedRows = new Dictionary <string, Row>();
            Table transformViewTable;

            // Bind the schema msi.
            this.GenerateDatabase(schemaOutput, msiDatabaseFile);

            // apply the transform to the database and retrieve the modifications
            using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
            {
                // apply the transform with the ViewTransform option to collect all the modifications
                msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);

                // unbind the database
                var unbindCommand       = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
                var transformViewOutput = unbindCommand.Execute();

                // index the added and possibly modified rows (added rows may also appears as modified rows)
                transformViewTable = transformViewOutput.Tables["_TransformView"];
                var modifiedRows = new Hashtable();
                foreach (var row in transformViewTable.Rows)
                {
                    var tableName   = (string)row[0];
                    var columnName  = (string)row[1];
                    var primaryKeys = (string)row[2];

                    if ("INSERT" == columnName)
                    {
                        var index = String.Concat(tableName, ':', primaryKeys);

                        addedRows.Add(index, null);
                    }
                    else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
                    {
                        var index = String.Concat(tableName, ':', primaryKeys);

                        modifiedRows[index] = row;
                    }
                }

                // create placeholder rows for modified rows to make the transform insert the updated values when its applied
                foreach (Row row in modifiedRows.Values)
                {
                    var tableName   = (string)row[0];
                    var columnName  = (string)row[1];
                    var primaryKeys = (string)row[2];

                    var index = String.Concat(tableName, ':', primaryKeys);

                    // ignore information for added rows
                    if (!addedRows.ContainsKey(index))
                    {
                        var table = schemaOutput.Tables[tableName];
                        this.CreateRow(table, primaryKeys, true);
                    }
                }
            }

            // Re-bind the schema output with the placeholder rows.
            this.GenerateDatabase(schemaOutput, msiDatabaseFile);

            // apply the transform to the database and retrieve the modifications
            using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
            {
                try
                {
                    // apply the transform
                    msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All);

                    // commit the database to guard against weird errors with streams
                    msiDatabase.Commit();
                }
                catch (Win32Exception ex)
                {
                    if (0x65B == ex.NativeErrorCode)
                    {
                        // this commonly happens when the transform was built
                        // against a database schema different from the internal
                        // table definitions
                        throw new WixException(ErrorMessages.TransformSchemaMismatch());
                    }
                }

                // unbind the database
                var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
                var output        = unbindCommand.Execute();

                // index all the rows to easily find modified rows
                var rows = new Dictionary <string, Row>();
                foreach (var table in output.Tables)
                {
                    foreach (var row in table.Rows)
                    {
                        rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
                    }
                }

                // process the _TransformView rows into transform rows
                foreach (var row in transformViewTable.Rows)
                {
                    var tableName   = (string)row[0];
                    var columnName  = (string)row[1];
                    var primaryKeys = (string)row[2];

                    var table = transform.EnsureTable(this.TableDefinitions[tableName]);

                    if ("CREATE" == columnName) // added table
                    {
                        table.Operation = TableOperation.Add;
                    }
                    else if ("DELETE" == columnName) // deleted row
                    {
                        var deletedRow = this.CreateRow(table, primaryKeys, false);
                        deletedRow.Operation = RowOperation.Delete;
                    }
                    else if ("DROP" == columnName) // dropped table
                    {
                        table.Operation = TableOperation.Drop;
                    }
                    else if ("INSERT" == columnName) // added row
                    {
                        var index    = String.Concat(tableName, ':', primaryKeys);
                        var addedRow = rows[index];
                        addedRow.Operation = RowOperation.Add;
                        table.Rows.Add(addedRow);
                    }
                    else if (null != primaryKeys) // modified row
                    {
                        var index = String.Concat(tableName, ':', primaryKeys);

                        // the _TransformView table includes information for added rows
                        // that looks like modified rows so it sometimes needs to be ignored
                        if (!addedRows.ContainsKey(index))
                        {
                            var modifiedRow = rows[index];

                            // mark the field as modified
                            var indexOfModifiedValue = -1;
                            for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i)
                            {
                                if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal))
                                {
                                    indexOfModifiedValue = i;
                                    break;
                                }
                            }
                            modifiedRow.Fields[indexOfModifiedValue].Modified = true;

                            // move the modified row into the transform the first time its encountered
                            if (RowOperation.None == modifiedRow.Operation)
                            {
                                modifiedRow.Operation = RowOperation.Modify;
                                table.Rows.Add(modifiedRow);
                            }
                        }
                    }
                    else // added column
                    {
                        var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
                        column.Added = true;
                    }
                }
            }

            return(transform);
        }
        public WindowsInstallerData Execute()
        {
            this.exportedFiles = new List <string>();

            string modularizationGuid = null;
            var    output             = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath));
            View   validationView     = null;

            // set the output type
            output.Type = this.OutputType;

            Directory.CreateDirectory(this.IntermediateFolder);

            // get the codepage
            this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt");
            using (var sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt")))
            {
                string line;

                while (null != (line = sr.ReadLine()))
                {
                    var data = line.Split('\t');

                    if (2 == data.Length)
                    {
                        output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
                    }
                }
            }

            // get the summary information table if it exists; it won't if unbinding a transform
            if (!this.SkipSummaryInfo)
            {
                using (var summaryInformation = new SummaryInformation(this.Database))
                {
                    var table = new Table(this.TableDefinitions["_SummaryInformation"]);

                    for (var i = 1; 19 >= i; i++)
                    {
                        var value = summaryInformation.GetProperty(i);

                        if (0 < value.Length)
                        {
                            var row = table.CreateRow(output.SourceLineNumbers);
                            row[0] = i;
                            row[1] = value;
                        }
                    }

                    output.Tables.Add(table);
                }
            }

            try
            {
                // open a view on the validation table if it exists
                if (this.Database.TableExists("_Validation"))
                {
                    validationView = this.Database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?");
                }

                // get the normal tables
                using (var tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables"))
                {
                    foreach (var tableRecord in tablesView.Records)
                    {
                        var tableName = tableRecord.GetString(1);

                        using (var tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
                        {
                            var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView);
                            var table           = new Table(tableDefinition);

                            foreach (var rowRecord in tableView.Records)
                            {
                                var recordCount = rowRecord.GetFieldCount();
                                var row         = table.CreateRow(output.SourceLineNumbers);

                                for (var i = 0; recordCount > i && row.Fields.Length > i; i++)
                                {
                                    if (rowRecord.IsNull(i + 1))
                                    {
                                        if (!row.Fields[i].Column.Nullable)
                                        {
                                            // TODO: display an error for a null value in a non-nullable field OR
                                            // display a warning and put an empty string in the value to let the compiler handle it
                                            // (the second option is risky because the later code may make certain assumptions about
                                            // the contents of a row value)
                                        }
                                    }
                                    else
                                    {
                                        switch (row.Fields[i].Column.Type)
                                        {
                                        case ColumnType.Number:
                                            var success  = false;
                                            var intValue = rowRecord.GetInteger(i + 1);
                                            if (row.Fields[i].Column.IsLocalizable)
                                            {
                                                success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
                                            }
                                            else
                                            {
                                                success = row.BestEffortSetField(i, intValue);
                                            }

                                            if (!success)
                                            {
                                                this.Messaging.Write(WarningMessages.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
                                            }
                                            break;

                                        case ColumnType.Object:
                                            var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";

                                            if (null != this.ExportBasePath)
                                            {
                                                var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
                                                sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile);

                                                // ensure the parent directory exists
                                                System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));

                                                using (var fs = System.IO.File.Create(sourceFile))
                                                {
                                                    int bytesRead;
                                                    var buffer = new byte[512];

                                                    while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
                                                    {
                                                        fs.Write(buffer, 0, bytesRead);
                                                    }
                                                }

                                                this.exportedFiles.Add(sourceFile);
                                            }

                                            row[i] = sourceFile;
                                            break;

                                        default:
                                            var value = rowRecord.GetString(i + 1);

                                            switch (row.Fields[i].Column.Category)
                                            {
                                            case ColumnCategory.Guid:
                                                value = value.ToUpper(CultureInfo.InvariantCulture);
                                                break;
                                            }

                                            // de-modularize
                                            if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
                                            {
                                                var modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");

                                                if (null == modularizationGuid)
                                                {
                                                    var match = modularization.Match(value);
                                                    if (match.Success)
                                                    {
                                                        modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
                                                    }
                                                }

                                                value = modularization.Replace(value, String.Empty);
                                            }

                                            // escape "$(" for the preprocessor
                                            value = value.Replace("$(", "$$(");

                                            // escape things that look like wix variables
                                            // TODO: Evaluate this requirement.
                                            //var matches = Common.WixVariableRegex.Matches(value);
                                            //for (var j = matches.Count - 1; 0 <= j; j--)
                                            //{
                                            //    value = value.Insert(matches[j].Index, "!");
                                            //}

                                            row[i] = value;
                                            break;
                                        }
                                    }
                                }
                            }

                            output.Tables.Add(table);
                        }
                    }
                }
            }
            finally
            {
                if (null != validationView)
                {
                    validationView.Close();
                }
            }

            // set the modularization guid as the PackageCode
            if (null != modularizationGuid)
            {
                var table = output.Tables["_SummaryInformation"];

                foreach (var row in table.Rows)
                {
                    if (9 == (int)row[0]) // PID_REVNUMBER
                    {
                        row[1] = modularizationGuid;
                    }
                }
            }

            if (this.IsAdminImage)
            {
                this.GenerateWixFileTable(this.DatabasePath, output);
                this.GenerateSectionIds(output);
            }

            return(output);
        }
        /// <summary>
        /// Creates section ids on rows which form logical groupings of resources.
        /// </summary>
        /// <param name="output">The Output that represents the msi database.</param>
        private void GenerateSectionIds(WindowsInstallerData output)
        {
            // First assign and index section ids for the tables that are in their own sections.
            this.AssignSectionIdsToTable(output.Tables["Binary"], 0);
            var componentSectionIdIndex    = this.AssignSectionIdsToTable(output.Tables["Component"], 0);
            var customActionSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["CustomAction"], 0);

            this.AssignSectionIdsToTable(output.Tables["Directory"], 0);
            var featureSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["Feature"], 0);

            this.AssignSectionIdsToTable(output.Tables["Icon"], 0);
            var digitalCertificateSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["MsiDigitalCertificate"], 0);

            this.AssignSectionIdsToTable(output.Tables["Property"], 0);

            // Now handle all the tables that rely on the first set of indexes but also produce their own indexes. Order matters here.
            var fileSectionIdIndex           = ConnectTableToSectionAndIndex(output.Tables["File"], componentSectionIdIndex, 1, 0);
            var appIdSectionIdIndex          = ConnectTableToSectionAndIndex(output.Tables["Class"], componentSectionIdIndex, 2, 5);
            var odbcDataSourceSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDataSource"], componentSectionIdIndex, 1, 0);
            var odbcDriverSectionIdIndex     = ConnectTableToSectionAndIndex(output.Tables["ODBCDriver"], componentSectionIdIndex, 1, 0);
            var registrySectionIdIndex       = ConnectTableToSectionAndIndex(output.Tables["Registry"], componentSectionIdIndex, 5, 0);
            var serviceInstallSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ServiceInstall"], componentSectionIdIndex, 11, 0);

            // Now handle all the tables which only rely on previous indexes and order does not matter.
            foreach (var table in output.Tables)
            {
                switch (table.Name)
                {
                case "WixFile":
                case "MsiFileHash":
                    ConnectTableToSection(table, fileSectionIdIndex, 0);
                    break;

                case "MsiAssembly":
                case "MsiAssemblyName":
                    ConnectTableToSection(table, componentSectionIdIndex, 0);
                    break;

                case "MsiPackageCertificate":
                case "MsiPatchCertificate":
                    ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1);
                    break;

                case "CreateFolder":
                case "FeatureComponents":
                case "MoveFile":
                case "ReserveCost":
                case "ODBCTranslator":
                    ConnectTableToSection(table, componentSectionIdIndex, 1);
                    break;

                case "TypeLib":
                    ConnectTableToSection(table, componentSectionIdIndex, 2);
                    break;

                case "Shortcut":
                case "Environment":
                    ConnectTableToSection(table, componentSectionIdIndex, 3);
                    break;

                case "RemoveRegistry":
                    ConnectTableToSection(table, componentSectionIdIndex, 4);
                    break;

                case "ServiceControl":
                    ConnectTableToSection(table, componentSectionIdIndex, 5);
                    break;

                case "IniFile":
                case "RemoveIniFile":
                    ConnectTableToSection(table, componentSectionIdIndex, 7);
                    break;

                case "AppId":
                    ConnectTableToSection(table, appIdSectionIdIndex, 0);
                    break;

                case "Condition":
                    ConnectTableToSection(table, featureSectionIdIndex, 0);
                    break;

                case "ODBCSourceAttribute":
                    ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0);
                    break;

                case "ODBCAttribute":
                    ConnectTableToSection(table, odbcDriverSectionIdIndex, 0);
                    break;

                case "AdminExecuteSequence":
                case "AdminUISequence":
                case "AdvtExecuteSequence":
                case "AdvtUISequence":
                case "InstallExecuteSequence":
                case "InstallUISequence":
                    ConnectTableToSection(table, customActionSectionIdIndex, 0);
                    break;

                case "LockPermissions":
                case "MsiLockPermissions":
                    foreach (var row in table.Rows)
                    {
                        var lockObject = (string)row[0];
                        var tableName  = (string)row[1];
                        switch (tableName)
                        {
                        case "File":
                            row.SectionId = (string)fileSectionIdIndex[lockObject];
                            break;

                        case "Registry":
                            row.SectionId = (string)registrySectionIdIndex[lockObject];
                            break;

                        case "ServiceInstall":
                            row.SectionId = (string)serviceInstallSectionIdIndex[lockObject];
                            break;
                        }
                    }
                    break;
                }
            }

            // Now pass the output to each unbinder extension to allow them to analyze the output and determine thier proper section ids.
            //foreach (IUnbinderExtension extension in this.unbinderExtensions)
            //{
            //    extension.GenerateSectionIds(output);
            //}
        }
Example #27
0
        private WixOutput CreateWixout(List <ITrackedFile> trackedFiles, Intermediate intermediate, WindowsInstallerData output)
        {
            WixOutput wixout;

            if (String.IsNullOrEmpty(this.OutputPdbPath))
            {
                wixout = WixOutput.Create();
            }
            else
            {
                var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final);
                trackedFiles.Add(trackPdb);

                wixout = WixOutput.Create(trackPdb.Path);
            }

            intermediate.Save(wixout);

            output.Save(wixout);

            wixout.Reopen();

            return(wixout);
        }
        public void Execute()
        {
            var transformFlags = 0;

            var targetOutput  = new WindowsInstallerData(null);
            var updatedOutput = new WindowsInstallerData(null);

            // TODO: handle added columns

            // to generate a localized transform, both the target and updated
            // databases need to have the same code page. the only reason to
            // set different code pages is to support localized primary key
            // columns, but that would only support deleting rows. if this
            // becomes necessary, define a PreviousCodepage property on the
            // Output class and persist this throughout transform generation.
            targetOutput.Codepage  = this.Transform.Codepage;
            updatedOutput.Codepage = this.Transform.Codepage;

            // remove certain Property rows which will be populated from summary information values
            string targetUpgradeCode  = null;
            string updatedUpgradeCode = null;

            if (this.Transform.TryGetTable("Property", out var propertyTable))
            {
                for (int i = propertyTable.Rows.Count - 1; i >= 0; i--)
                {
                    Row row = propertyTable.Rows[i];

                    if ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0])
                    {
                        propertyTable.Rows.RemoveAt(i);

                        if ("UpgradeCode" == (string)row[0])
                        {
                            updatedUpgradeCode = (string)row[1];
                        }
                    }
                }
            }

            var targetSummaryInfo    = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
            var updatedSummaryInfo   = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
            var targetPropertyTable  = targetOutput.EnsureTable(this.TableDefinitions["Property"]);
            var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]);

            // process special summary information values
            foreach (var row in this.Transform.Tables["_SummaryInformation"].Rows)
            {
                var summaryId   = row.FieldAsInteger(0);
                var summaryData = row.FieldAsString(1);

                if ((int)SummaryInformation.Transform.CodePage == summaryId)
                {
                    // convert from a web name if provided
                    var codePage = summaryData;
                    if (null == codePage)
                    {
                        codePage = "0";
                    }
                    else
                    {
                        codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture);
                    }

                    var previousCodePage = row.Fields[1].PreviousData;
                    if (null == previousCodePage)
                    {
                        previousCodePage = "0";
                    }
                    else
                    {
                        previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture);
                    }

                    var targetCodePageRow = targetSummaryInfo.CreateRow(null);
                    targetCodePageRow[0] = 1; // PID_CODEPAGE
                    targetCodePageRow[1] = previousCodePage;

                    var updatedCodePageRow = updatedSummaryInfo.CreateRow(null);
                    updatedCodePageRow[0] = 1; // PID_CODEPAGE
                    updatedCodePageRow[1] = codePage;
                }
                else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ||
                         (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId)
                {
                    // the target language
                    var propertyData = summaryData.Split(';');
                    var lang         = 2 == propertyData.Length ? propertyData[1] : "0";

                    var tempSummaryInfo   = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo;
                    var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable;

                    var productLanguageRow = tempPropertyTable.CreateRow(null);
                    productLanguageRow[0] = "ProductLanguage";
                    productLanguageRow[1] = lang;

                    // set the platform;language on the MSI to be generated
                    var templateRow = tempSummaryInfo.CreateRow(null);
                    templateRow[0] = 7; // PID_TEMPLATE
                    templateRow[1] = summaryData;
                }
                else if ((int)SummaryInformation.Transform.ProductCodes == summaryId)
                {
                    var propertyData = summaryData.Split(';');

                    var targetProductCodeRow = targetPropertyTable.CreateRow(null);
                    targetProductCodeRow[0] = "ProductCode";
                    targetProductCodeRow[1] = propertyData[0].Substring(0, 38);

                    var targetProductVersionRow = targetPropertyTable.CreateRow(null);
                    targetProductVersionRow[0] = "ProductVersion";
                    targetProductVersionRow[1] = propertyData[0].Substring(38);

                    var updatedProductCodeRow = updatedPropertyTable.CreateRow(null);
                    updatedProductCodeRow[0] = "ProductCode";
                    updatedProductCodeRow[1] = propertyData[1].Substring(0, 38);

                    var updatedProductVersionRow = updatedPropertyTable.CreateRow(null);
                    updatedProductVersionRow[0] = "ProductVersion";
                    updatedProductVersionRow[1] = propertyData[1].Substring(38);

                    // UpgradeCode is optional and may not exists in the target
                    // or upgraded databases, so do not include a null-valued
                    // UpgradeCode property.

                    targetUpgradeCode = propertyData[2];
                    if (!String.IsNullOrEmpty(targetUpgradeCode))
                    {
                        var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null);
                        targetUpgradeCodeRow[0] = "UpgradeCode";
                        targetUpgradeCodeRow[1] = targetUpgradeCode;

                        // If the target UpgradeCode is specified, an updated
                        // UpgradeCode is required.
                        if (String.IsNullOrEmpty(updatedUpgradeCode))
                        {
                            updatedUpgradeCode = targetUpgradeCode;
                        }
                    }

                    if (!String.IsNullOrEmpty(updatedUpgradeCode))
                    {
                        var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null);
                        updatedUpgradeCodeRow[0] = "UpgradeCode";
                        updatedUpgradeCodeRow[1] = updatedUpgradeCode;
                    }
                }
                else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId)
                {
                    transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture);
                }
                else if ((int)SummaryInformation.Transform.Reserved11 == summaryId)
                {
                    // PID_LASTPRINTED should be null for transforms
                    row.Operation = RowOperation.None;
                }
                else
                {
                    // add everything else as is
                    var targetRow = targetSummaryInfo.CreateRow(null);
                    targetRow[0] = row[0];
                    targetRow[1] = row[1];

                    var updatedRow = updatedSummaryInfo.CreateRow(null);
                    updatedRow[0] = row[0];
                    updatedRow[1] = row[1];
                }
            }

            // Validate that both databases have an UpgradeCode if the
            // authoring transform will validate the UpgradeCode; otherwise,
            // MsiCreateTransformSummaryinfo() will fail with 1620.
            if (((int)TransformFlags.ValidateUpgradeCode & transformFlags) != 0 &&
                (String.IsNullOrEmpty(targetUpgradeCode) || String.IsNullOrEmpty(updatedUpgradeCode)))
            {
                this.Messaging.Write(ErrorMessages.BothUpgradeCodesRequired());
            }

            string emptyFile = null;

            foreach (var table in this.Transform.Tables)
            {
                // Ignore unreal tables when building transforms except the _Stream table.
                // These tables are ignored when generating the database so there is no reason
                // to process them here.
                if (table.Definition.Unreal && "_Streams" != table.Name)
                {
                    continue;
                }

                // process table operations
                switch (table.Operation)
                {
                case TableOperation.Add:
                    updatedOutput.EnsureTable(table.Definition);
                    break;

                case TableOperation.Drop:
                    targetOutput.EnsureTable(table.Definition);
                    continue;

                default:
                    targetOutput.EnsureTable(table.Definition);
                    updatedOutput.EnsureTable(table.Definition);
                    break;
                }

                // process row operations
                foreach (var row in table.Rows)
                {
                    switch (row.Operation)
                    {
                    case RowOperation.Add:
                        var updatedTable = updatedOutput.EnsureTable(table.Definition);
                        updatedTable.Rows.Add(row);
                        continue;

                    case RowOperation.Delete:
                        var targetTable = targetOutput.EnsureTable(table.Definition);
                        targetTable.Rows.Add(row);

                        // fill-in non-primary key values
                        foreach (var field in row.Fields)
                        {
                            if (!field.Column.PrimaryKey)
                            {
                                if (ColumnType.Number == field.Column.Type && !field.Column.IsLocalizable)
                                {
                                    field.Data = field.Column.MinValue;
                                }
                                else if (ColumnType.Object == field.Column.Type)
                                {
                                    if (null == emptyFile)
                                    {
                                        emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                    }

                                    field.Data = emptyFile;
                                }
                                else
                                {
                                    field.Data = "0";
                                }
                            }
                        }
                        continue;
                    }

                    // Assure that the file table's sequence is populated
                    if ("File" == table.Name)
                    {
                        foreach (var fileRow in table.Rows)
                        {
                            if (null == fileRow[7])
                            {
                                if (RowOperation.Add == fileRow.Operation)
                                {
                                    this.Messaging.Write(ErrorMessages.InvalidAddedFileRowWithoutSequence(fileRow.SourceLineNumbers, (string)fileRow[0]));
                                    break;
                                }

                                // Set to 1 to prevent invalid IDT file from being generated
                                fileRow[7] = 1;
                            }
                        }
                    }

                    // process modified and unmodified rows
                    var modifiedRow = false;
                    var targetRow   = table.Definition.CreateRow(null);
                    var updatedRow  = row;
                    for (var i = 0; i < row.Fields.Length; i++)
                    {
                        var updatedField = row.Fields[i];

                        if (updatedField.Modified)
                        {
                            // set a different value in the target row to ensure this value will be modified during transform generation
                            if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable)
                            {
                                var data = updatedField.AsNullableInteger();
                                targetRow[i] = (data == 1) ? 2 : 1;
                            }
                            else if (ColumnType.Object == updatedField.Column.Type)
                            {
                                if (null == emptyFile)
                                {
                                    emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                }

                                targetRow[i] = emptyFile;
                            }
                            else
                            {
                                var data = updatedField.AsString();
                                targetRow[i] = (data == "0") ? "1" : "0";
                            }

                            modifiedRow = true;
                        }
                        else if (ColumnType.Object == updatedField.Column.Type)
                        {
                            var objectField = (ObjectField)updatedField;

                            // create an empty file for comparing against
                            if (null == objectField.PreviousData)
                            {
                                if (null == emptyFile)
                                {
                                    emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                }

                                targetRow[i] = emptyFile;
                                modifiedRow  = true;
                            }
                            else if (!this.FileSystemManager.CompareFiles(objectField.PreviousData, (string)objectField.Data))
                            {
                                targetRow[i] = objectField.PreviousData;
                                modifiedRow  = true;
                            }
                        }
                        else // unmodified
                        {
                            if (null != updatedField.Data)
                            {
                                targetRow[i] = updatedField.Data;
                            }
                        }
                    }

                    // modified rows and certain special rows go in the target and updated msi databases
                    if (modifiedRow ||
                        ("Property" == table.Name &&
                         ("ProductCode" == (string)row[0] ||
                          "ProductLanguage" == (string)row[0] ||
                          "ProductVersion" == (string)row[0] ||
                          "UpgradeCode" == (string)row[0])))
                    {
                        var targetTable = targetOutput.EnsureTable(table.Definition);
                        targetTable.Rows.Add(targetRow);

                        var updatedTable = updatedOutput.EnsureTable(table.Definition);
                        updatedTable.Rows.Add(updatedRow);
                    }
                }
            }

            //foreach (BinderExtension extension in this.Extensions)
            //{
            //    extension.PostBind(this.Context);
            //}

            // Any errors encountered up to this point can cause errors during generation.
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            var transformFileName   = Path.GetFileNameWithoutExtension(this.OutputPath);
            var targetDatabaseFile  = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi"));
            var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi"));

            try
            {
                if (!String.IsNullOrEmpty(emptyFile))
                {
                    using (var fileStream = File.Create(emptyFile))
                    {
                    }
                }

                this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false);
                this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true);

                // make sure the directory exists
                Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));

                // create the transform file
                using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly))
                    using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly))
                    {
                        if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath))
                        {
                            updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF));
                        }
                        else
                        {
                            this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers));
                        }
                    }
            }
            finally
            {
                if (!String.IsNullOrEmpty(emptyFile))
                {
                    File.Delete(emptyFile);
                }
            }
        }
        private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns)
        {
            var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true);

            command.Execute();
        }
 public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions)
 {
     this.Messaging          = messaging;
     this.BackendHelper      = backendHelper;
     this.FileSystemManager  = fileSystemManager;
     this.IntermediateFolder = intermediateFolder;
     this.Transform          = transform;
     this.OutputPath         = outputPath;
     this.TableDefinitions   = tableDefinitions;
 }